اذهب إلى المحتوى

بناء بوت Bot تلغرام باستخدام لارافيل وبوتمان BotMan


سامر سليمان

نستطيع استخدام البوتات الآلية (يُطلق عليها اسم بوتات bots عندما تكون في الويب) لتقديم بيانات مخصصة للمستخدمين بناءً على طلباتهم، إذ يتيح إطارا عمل لارافيل Laravel وبوتمان Potman الأدوات اللازمة لإنشاء بوتات قادرة على تنفيذ هذه المهمة بسهولة. نعمل في هذا المقال على إنشاء بوت تلغرام لمحبي الكلاب باستخدام DOG API، ويمكن اختيار أي نوعٍ مختلفٍ من البيانات عند الرغبة. يظهر البوت عند الانتهاء من العمل كما في الشكل التالي:

bot

تثبيت لارافيل وبوتمان

يُعد إطار عمل بوتمان مكتبةً خاصةً بلغة البرمجة PHP، وصُمِّمَت لتبسيط عملية تطوير بوتات مبتكرة لمنصات المراسلة المتعددة، مثل منصة سلاك Slack، وتلغرام، وإطار عمل مايكروسوفت بوت وNixmo وفيسبوك وغيرها.

$botman->hears('Bot, What’s the best Web Development training website?', function (BotMan $bot) {
    $bot->reply('DigitalOcean for sure. Give me a harder question!!');
});

تثبيت بوتمان استوديو

وفّر المطور مارسيل بوكيوت Marcel Pociot كثيرًا من الوقت على المطورين من خلال تطويره برنامج بوتمان استوديو Botman Studio بالاعتماد على لارافيل مع بوتمان لنحصل على برنامج سهل الاستخدام محدّث باستمرار. ننشئ مشروعًا جديدًا ضمن بوتمان استوديو بتنفيذ التعليمة التالية في موجه الأوامر:

composer create-project --prefer-dist botman/studio ilovedogs

نتحقق من نجاح عملية التثبيت بتنفيذ التعليمة التالية في موجه الأوامر:

php artisan botman:tinker

يمكن إدخال الكلمة "Hi" في موجه الأوامر ليظهر الرد بعبارة "Hello" في حالة العمل الصحيح كما هو موضح بالشكل التالي:

Test_app_work

إنشاء الأوامر

يتميز البوت الذي ننشئه بالقدرة على الرد على أنواع مختلفة من الرسائل والأوامر التي تصله، أي أنه يستطيع التعامل مع هذه الحالات:

  • إرسال صورة عشوائية للكلاب من جميع السلالات.
  • إرسال صورة عشوائية للكلب حسب سلالته.
  • إرسال صورة عشوائية للكلب حسب سلالته وسلالته الفرعية.
  • إجراء محادثة وتقديم المساعدة.
  • الاستجابة للأوامر غير المعروفة.

لتحقيق هذه الوظائف، نفرغ محتوى الملف "routes/botman.php" لأننا سنضيف له محتوى جديد كليًا ويجب إزالة المحتوى القديم لعدم حصول تعارض.

إرسال صورة عشوائية للكلاب من جميع السلالات

نحصل على صورة كلب عشوائي من البوت بإرسال الأمر "random/" ونعدل على النص البرمجي في ملف "routes/botman.php" ليصبح كما يلي:

<?php

use App\Conversations\StartConversation;

$botman = resolve('botman');

$botman->hears('/random', 'App\Http\Controllers\AllBreedsController@random');

ننشئ المتحكم باستخدام التعليمة التالية:

php artisan make:controller AllBreedsController

نكتب الشيفرة البرمجية التالية في المتحكم الذي أنشأناه:

<?php

namespace App\Http\Controllers;

use App\Services\DogService;
use App\Http\Controllers\Controller;

class AllBreedsController extends Controller
{
    /**
     * الباني الخاص بالمتحكم
     * 
     * @return void
     */
    public function __construct()
    {
        $this->photos = new DogService;
    }

    /**
     * إعادة صورة عشوائية لكلب من إحدى السلالات
     *
     * @return void
     */
    public function random($bot)
    {
        // $this->photos->random() يمثل رابط الصورة ‫URL التي تعيدها هذه الخدمة‫‪
        // $bot->reply تمثل ما نستخدمه لإرسال الصورة إلى المستخدم
        $bot->reply($this->photos->random());
    }

}

ننشئ عينةً من الصنف DogService الموجود ضمن الملف "app//services/DogService.php" ليكون مسؤولاً عن الاستجابة لاستدعاءات الواجهة البرمجية API الخاصة بجلب الصور ونضع به الشيفرة البرمجية التالية:

<?php

namespace App\Services;

use Exception;
use GuzzleHttp\Client;

class DogService
{
    // عنوان الوجهة التي سنحصل منها على صورة الكلاب
    const RANDOM_ENDPOINT = 'https://dog.ceo/api/breeds/image/random';

    /**
     * Guzzle client.
     *
     * @var GuzzleHttp\Client
     */
    protected $client;

    /**
     * DogService constructor
     * 
     * @return void
     */
    public function __construct()
    {
        $this->client = new Client;
    }

    /**
     *  إحضار وإعادة صورة كلب عشوائية من إحدى السلالات
     *
     * @return string
     */
    public function random()
    {
        try {
            // التي استلمناها JSON فك ترميز استجابة
            $response = json_decode(
               // التي تعيد الجسم الخاص بالاستجابة API استدعاء
                $this->client->get(self::RANDOM_ENDPOINT)->getBody()
            );

            // إعادة رابط الصورة
            return $response->message;
        } catch (Exception $e) {
             // نستخدم هذا السطر لإعادة رسالة الخطأ للمستخدم في حال وقوع مشكلة ما.
            return 'An unexpected error occurred. Please try again later.';
        }
    }
}

إرسال صورة عشوائية للكلب حسب سلالته

نكرر العملية السابقة ولكن نضيف الأمر ‎/b {breed}‎، ونعدل الملف "routes/botman.php" بإضافة السطر البرمجي التالي:

botman->hears('/b {breed}', 'App\Http\Controllers\AllBreedsController@byBreed');

نضيف ما يلي على المتحكم AllBreedsController الذي أنشأناه مسبقًا:

/**
 * إعادة صورة كلب من سلالة محددة
*
 * @return void
 */
public function byBreed($bot, $name)
{
    // نترك أمر معالجة استدعاء الواجهة البرمجية إلى صنف الخدمة ونرد على الطلب القادم بالنتيجة فور وصولها إلينا
    $bot->reply($this->photos->byBreed($name));
}

نضيف التابع byBreed إلى الصنف DogService الذي أنشأناه على النحو التالي:

/**
 * جلب وإعادة صورة عشوائية من سلالة محددة
 *
 * @param string $breed
 * @return string
 */
public function byBreed($breed)
{
    try {
        // لدى الطرف الآخر باسم الصنف المحدد %s نستبدل
        $endpoint = sprintf(self::BREED_ENDPOINT, $breed);

        $response = json_decode(
            $this->client->get($endpoint)->getBody()
        );

        return $response->message;
    } catch (Exception $e) {
        return "Sorry I couldn\"t get you any photos from $breed. Please try with a different breed.";
    }
}

يجب إضافة نقطة النهاية const المُستخدمة أعلاه إلى نفس الملف على النحو التالي:

// نقطة النهاية التي سنصل إليها للحصول على صورة عشوائية باسم سلالة معطى
const BREED_ENDPOINT = 'https://dog.ceo/api/breed/%s/images/random';

إرسال صورة عشوائية للكلب حسب سلالته وسلالته الفرعية

نضيف الأمر ‎/s {breed}:{subBreed}‎ من أجل السلالات الفرعية على النحو التالي:

botman->hears('/s {breed}:{subBreed}', 'App\Http\Controllers\SubBreedController@random');

ننشئ متحكمًا جديدًا :

php artisan make:controller SubBreedController

نعرّف التابع random على النحو التالي:

<?php

namespace App\Conversations;

use App\Services\DogService;
use App\Http\Controllers\Controller;

class SubBreedController extends Controller
{
    /**
     * التابع الباني للمتحكم
     * 
     * @return void
     */
    public function __construct()
    {
        $this->photos = new DogService;
    }

    /**
     * إعادة صورة عشوائية لكلب من إحدى السلالات
     *
     * @return void
     */
    public function random($bot, $breed, $subBreed)
    {
        $bot->reply($this->photos->bySubBreed($breed, $subBreed));
    }
}

يجب إضافة نقطة الاتصال النهائية والتابع الذي حدثنا محتواه إلى الصنف DogService:

// تعيد نقطة الاتصال النهائية صورة عشوائية لكلب من سلالة محددة وسلالة فرعية محددة
const SUB_BREED_ENDPOINT = 'https://dog.ceo/api/breed/%s/%s/images/random';
/**
 * جلب وإعادة صورة عشوائية لصورة عشوائية من سلالة معينة وسلالة فرعية
 *
 * @param string $breed
 * @param string $subBreed
 * @return string
 */
public function bySubBreed($breed, $subBreed)
{
    try {
        $endpoint = sprintf(self::SUB_BREED_ENDPOINT, $breed, $subBreed);

        $response = json_decode(
            $this->client->get($endpoint)->getBody()
        );

        return $response->message;
    } catch (Exception $e) {
        return "Sorry I couldn\"t get you any photos from $breed. Please try with a different breed.";
    }
}

إجراء محادثة وتقديم المساعدة

لا نحبّذ الرد على كلمات رئيسية مفردة عند برمجة بوت الدردشة، وإنما نجمع معلومات المستخدمين من خلال المحادثة، بحيث يتعامل بوت الدردشة مع كل مستخدم بصورةٍ مناسبة تتوافق مع بياناته. لتحقيق ذلك نضيف الى الملف "routes/botman.php" ما يلي:

botman->hears('Start conversation', 'App\Http\Controllers\ConversationController@index');

ننشئ متحكمًا جديدًا:

php artisan make:controller ConversationController

نعرّف التابع index ضمن الصنف على النحو التالي:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Conversations\DefaultConversation;

class ConversationController extends Controller
{
    /**
     * إنشاء محادثة جديدة
     *
     * @return void
     */
    public function index($bot)
    {
        // التي يوفرها إطار العمل بوتمان لبدء محادثة جديدة startConversation نستخدم التابع
        $bot->startConversation(new DefaultConversation);
    }
}

إذا كنا نستخدم بوتمان ستوديو، نجد مجلد باسم "Conversations" ضمن مجلد التطبيق، لذلك ننشئ داخل هذا المجلد ملفًا جديدًا ونسميه "DefaultConversation.php" ونضع فيه المحتوى التالي:

<?php

namespace App\Conversations;

use BotMan\BotMan\Messages\Incoming\Answer;
use BotMan\BotMan\Messages\Outgoing\Question;
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
use BotMan\BotMan\Messages\Conversations\Conversation;

class DefaultConversation extends Conversation
{
    /**
     * بدء المحادثة بالسؤال الاول
     *
     * @return void
     */
    public function defaultQuestion()
    {
        // نبدأ المحادثة بإضافة السؤال الأول ونضبط الخيارات المتاحة للإجابة على هذا السؤال
        $question = Question::create('Huh - you woke me up. What do you need?')
            ->addButtons([
                Button::create('Random dog photo')->value('random'),
                Button::create('A photo by breed')->value('breed'),
                Button::create('A photo by sub-breed')->value('sub-breed'),
            ]);

        //  نسأل المستخدم السؤال الذي أعددناه
        return $this->ask($question, function (Answer $answer) {
            //     نفحص إذا كان المستخدم قد كتب عبارة ما أم ضغط على أحد الخيارات
            if ($answer->isInteractiveMessageReply()) {
                // نقارن جواب المستخدم مع الإجابات المعدة مسبقًا
                switch ($answer->getValue()) {
                case 'random':
                    $this->say((new App\Services\DogService)->random());
                    break;
                    case 'breed':
                        $this->askForBreedName();
                        break;
                    case 'sub-breed':
                        $this->askForSubBreed();
                        break;
                }
            }
        });
    }

    /**
     * السؤال عن سلالة الكلب وسلالته الفرعية
     *
     * @return void
     */
    public function askForBreedName()
    {
        $this->ask('What\'s the breed name?', function (Answer $answer) {
            $name = $answer->getText();

            $this->say((new App\Services\DogService)->byBreed($name));
        });
    }

    /**
     *طلب اسم السلالة وإرسال الصورة
     *
     * @return void
     */
    public function askForSubBreed()
    {
        $this->ask('What\'s the breed and sub-breed names? ex:hound:afghan', function (Answer $answer) {
            $answer = explode(':', $answer->getText());

            $this->say((new App\Services\DogService)->bySubBreed($answer[0], $answer[1]));
        });
    }

    /**
     * بدء المحادثة
     *
     * @return void
     */
    public function run()
    {
        //   يُعد هذه التابع إقلاعي فهو ما سيجري تنفيذه أولًا
        $this->defaultQuestion();
    }
}

الاستجابة للأوامر غير المعروفة

تُستخدم هذه الحالة عندما يرسل المستخدم رسالةً لا يتعرف عليها البوت فلا بد من إعلامه بإعادة طلبه على نحوٍ صحيح. نضيف إلى الملف "routes/botman.php" السطر البرمجي التالي:

botman->fallback('App\Http\Controllers\FallbackController@index');

ننشئ متحكمًا جديدًا على النحو التالي:

php artisan make:controller FallbackController

نضيف الرسالة التي يراها المستخدم عند إرساله لأمر غير معروف:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;

class FallbackController extends Controller
{
    /**
     * الإجابة برسالة عامة
     *
     * @param Botman $bot
     * @return void
     */
    public function index($bot)
    {
        $bot->reply('Sorry, I did not understand these commands. Try: \'Start Conversation\'');
    }
}

اختبار البوت

  • إرسال صورة عشوائية للكلاب من جميع السلالات.

random_dog.gif

  • إرسال صورة عشوائية للكلب حسب سلالته.

random_dog_photo_by_its_breed.gif

  • إرسال صورة عشوائية للكلب حسب سلالته وسلالته الفرعية.

random_dog_photo_by_a_its_breed_and_sub_breed.gif

  • إجراء محادثة وتقديم المساعدة.

conversation_and_provide_help.gif

  • الاستجابة للأوامر غير المعروفة.

unrecognised_commands.gif

تثبيت برنامج تشغيل تلغرام

بعد الانتهاء من تطوير التطبيق يجب دمجه مع برنامج تلغرام، ولتحقيق ذلك نحتاج برنامج تشغيل التلغرام Telegram driver الذي يقدمه بوتمان والذي نحصل عليه بتنفيذ الأمر:

composer require botman/driver-telegram

إنشاء بوت تلغرام

نفتح تطبيق تلغرام ونبحث عن BotFather ونكتب "‎/newbot" وندخل اسم مستخدم البوت كما في الشكل:

create_new_bot

نضيف الرمز إلى ملف "env." ونستبدل YOUR_TOKEN بالرمز المميز Token الذي يقدمه تلغرام:

TELEGRAM_TOKEN=YOUR_TOKEN

تثبيت وتشغيل الأداة ngrok

يتطلّب التلغرام استخدام رابط URL صالح وآمن لإعداد الخطافات webhooks وتلقي الرسائل من المستخدمين، ولتحقيق ذلك نستخدم ngrok أو نضع التطبيق على خادم ونُعِد له شهادة SSL، ولكن لسهولة التنفيذ سنستخدم ngrok. نستطيع الحصول عليه من الموقع الرسمي باختيار النسخة المناسبة لنظام التشغيل الذي سنعمل عليه.

ننتقل إلى مجلد التطبيق وننفّذ التعليمة:

 php artisan serve

run_app

ننتقل إلى المجلد الذي ثبتنا البرنامج ضمنه ونشغل الأداة ngrok بتنفيذ الأمر التالي:

 /ngrok http 8000

run_ngrok

ربط البوت بالتلغرام

نربط تطبيقنا ببوت التلغرام الذي أنشأناه مسبقاً بإرسال طلب من نوع POST إلى عنوان الرابط هذا وتمرير عنوان الرابط الذي ولّده ngrok:

https://api.telegram.org/bot{TOKEN}/setWebhook

نستخدم برنامج بوستمان أو أداة "CURL" لتحقيق ذلك على النحو التالي:

curl -X POST -F 'url=https://{YOU_URL}/botman' https://api.telegram.org/bot{TOKEN}/setWebhook

يصل الرد التالي بصيغة JSON في حال نجاح العملية:

{
    "ok": true,
    "result": true,
    "description": "Webhook was set"
}

الاختبار باستخدام تلغرام

  • إرسال صورة عشوائية للكلاب من جميع السلالات.

random_dog_last_test

  • إرسال صورة عشوائية للكلب حسب سلالته.

random_dog_photo_by_its_breed_last_test

  • إرسال صورة عشوائية للكلب حسب سلالته وسلالته الفرعية.

breed_and_sub_breed_last_test

  • إجراء محادثة وتقديم المساعدة.

conversation_provide_help_last_test

  • الاستجابة للأوامر غير المعروفة.

unrecognised_commands_last_test

الخاتمة

قدم هذا المقال طريقةً لتطوير بوت وربطه بإحدى وسائل التواصل الاجتماعي الشهيرة وهي التلغرام، إذ يمكن الاعتماد على إطار العمل بوتمان للربط مع العديد من وسائل التواصل الاجتماعي لتطوير بوت قادر على الاتصال بشريحة واسعة من المستخدمين، كما تضمن المقال خطوات تحديد الأوامر المتاحة للبوت ليتخاطب بها مع المستخدمين وطريقة التعامل مع الحالات الخاصة.

ترجمة -وبتصرف- للمقال How To Build a Telegram Bot with Laravel and BotMan لصاحبه Rachid Laasri.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

شكرا لك أخي الكريم على هذا المقالة.
هل BotMan صالح الاستخدام مع Laravel 9 و PHP 8 ؟

حينما أقوم بكتابة الأمر:

composer create-project --prefer-dist botman/studio ilovedogs

لا يعمل بسبب عدم التوافق مع اصدرار ال PHP
والمشكلة لدي أن سيرفر ال VPS يحتوي على PHP 8.1 ولدي عدة مشاريع مبنية على هذا الأساس.

هل يوجد حل ؟

رابط هذا التعليق
شارك على الشبكات الإجتماعية

نصيحتي لك أن تقوم بتثبيت أكثر من إصدار لل PHP علي سيرفر ال VPS الخاص بك, وهناك أكثر من طريقة لفعل ذلك هذه بعضها.

استخدام نظام وهمي (Virtual Machine) 

من خلال تثبيت نظام وهمي علي السيرفر الخاص بك قم بتثبيت الإصدار الذي تريد من لغة PHP علي  هذا النظام الوهمي.

استخدم Docker وقم بعمل أكثر من حاوية بأكثر من إصدار

وهذه نبدة مختصرة عن Docker.

اقتباس

Docker عبارة عن أداة جديدة تستغل ميزات الإصدارات الأخيرة من نواة Linux الخاصة بعزل المهام والعمليات (processes)، عمليات الإدخال والإخراج (i/o)، حجز الذاكرة وتحديدها، صلاحيات القراءة والكتابة للقرص الصلب... وغير ذلك، في إنشاء حاويات (containers) ركز على هذه الكلمة جيدا، حيث أن هذه الحاويات تلعب دور غلاف حاوي لتطبيق ما (مشروع ويب مثلا)، بحيث يصبح قائما بذاته، مكتفٍ ذاتيا.

أي أن مشروع الويب وكامل الاعتماديات التي يحتاجها ليعمل + التوزيعة المناسبة له (Fedora, Ubuntu.. الخ) بجميع التهيئات ومتغيرات البيئة التي يحتاجها، كل هذا في حاوية (قد تكون حاوية واحدة أو عدة حاويات تتخاطب في ما بينها عملا بمبدأ "عزل الاهتمامات" SoC).

يمكنك معرفة المزيد عن Docker و كيف يمكنك استخدامه لحل مشكلة الإصدارات والاعتماديات من خلال هذه المقالة.

استخدم خادم الويب لتثبيت أكثر من إصدار لل PHP علي منافذ (Ports)محتلفة

يمكنك فعل ذلك من خلال خادم الويب مثل Apache أو Nginx بتثبيت أكثر من إصدار لل PHP وجعل كل إصدار يعمل علي منفذ مختلف.

لذلك سوف تحتاج إلي عمل بعض الأعدادت علي الشيفرة البرمجية الخاصة بك لكي تجعل كل منفذ يعمل مع إصدار PHP معين.

 

رابط هذا التعليق
شارك على الشبكات الإجتماعية

بتاريخ On 10‏/1‏/2023 at 11:13 قال Mohammed Fahmy:

نصيحتي لك أن تقوم بتثبيت أكثر من إصدار لل PHP علي سيرفر ال VPS الخاص بك, وهناك أكثر من طريقة لفعل ذلك هذه بعضها.

استخدام نظام وهمي (Virtual Machine) 

من خلال تثبيت نظام وهمي علي السيرفر الخاص بك قم بتثبيت الإصدار الذي تريد من لغة PHP علي  هذا النظام الوهمي.

استخدم Docker وقم بعمل أكثر من حاوية بأكثر من إصدار

وهذه نبدة مختصرة عن Docker.

يمكنك معرفة المزيد عن Docker و كيف يمكنك استخدامه لحل مشكلة الإصدارات والاعتماديات من خلال هذه المقالة.

استخدم خادم الويب لتثبيت أكثر من إصدار لل PHP علي منافذ (Ports)محتلفة

يمكنك فعل ذلك من خلال خادم الويب مثل Apache أو Nginx بتثبيت أكثر من إصدار لل PHP وجعل كل إصدار يعمل علي منفذ مختلف.

لذلك سوف تحتاج إلي عمل بعض الأعدادت علي الشيفرة البرمجية الخاصة بك لكي تجعل كل منفذ يعمل مع إصدار PHP معين.

 

شكرا جزيلا لك.
هذه الإجابة كانت قيمة جدا بالنسبة لي.
أول طريقة سأجربها هي استخدام docker.

رابط هذا التعليق
شارك على الشبكات الإجتماعية



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...