Core: Generic Message and Conversation

Created on 12 Jul 2017  路  7Comments  路  Source: php-telegram-bot/core

GenericmessageCommand.php :

<?php

namespace Longman\TelegramBot\Commands\SystemCommands;

use Longman\TelegramBot\Commands\SystemCommand;
use Longman\TelegramBot\Commands\UserCommands\AllCoursesCommand;
use Longman\TelegramBot\Commands\UserCommands\RegisterCoursesCommand;
use Longman\TelegramBot\Entities\Update;
use Longman\TelegramBot\Request;

class GenericmessageCommand extends SystemCommand
{
    protected $name = 'Genericmessage';
    protected $description = 'Handle generic message';
    protected $version = '1.0.0';

    public function execute()
    {
        $text = trim($this->getMessage()->getText(true));

        $update = json_decode($this->update->toJson(), true);

        if ($text === 'All Courses') {
            $update['message']['text'] = '/all_courses';
            return (new AllCoursesCommand($this->telegram, new Update($update)))->preExecute();
        } elseif ($text === 'Register') {
            $update['message']['text'] = '/register_courses';
            return (new RegisterCoursesCommand($this->telegram, new Update($update)))->preExecute();
        }

        return Request::emptyResponse();
    }
}

In RegisterCoursesCommand.php I want to response 'What is your name?!' then parse the user message, then response 'What is your age?!' then parse user age (message) and ...

How to do that?!

Most helpful comment

Right, there are a few things that need changing in your code, but it's quite easy 馃槂

Because the filename and class name are linked to the command name, your class must also have the _ in it. I know that's very unpretty, but unfortunately that's just how it works for now 馃槙
Commands with an underscore in their name aren't fully supported at the moment, as certain internal code isn't designed for them. So it's more of a hack...
I hope we have a better solution for all command names in the future!

So your command files would be Register_coursesCommand.php and All_coursesCommand.php, class names the same, just without the .php.

Then, inside your GenericmessageCommand class, you still need to handle conversations, otherwise the bot doesn't know that there is a current conversation happening.

So your GenericmessageCommand::execute() method should be like this:

public function execute()
{
    //If a conversation is busy, execute the conversation command after handling the message
    $conversation = new Conversation(
        $this->getMessage()->getFrom()->getId(),
        $this->getMessage()->getChat()->getId()
    );
    //Fetch conversation command if it exists and execute it
    if ($conversation->exists() && ($command = $conversation->getCommand())) {
        return $this->telegram->executeCommand($command);
    }

    $text   = trim($this->getMessage()->getText(true));
    $update = json_decode($this->update->toJson(), true);

    if ($text === 'All Courses') {
        $update['message']['text'] = '/all_courses';
        return (new All_coursesCommand($this->telegram, new Update($update)))->preExecute();
    } elseif ($text === 'Register') {
        $update['message']['text'] = '/register_courses';
        return (new Register_coursesCommand($this->telegram, new Update($update)))->preExecute();
    }

    return Request::emptyResponse();
}

Alternatively, you could also create a simple /courses command, which then could show a keyboard with a few buttons, Show All, Register, etc.

All 7 comments

Check out the /survey command.
You can copy that, rename it to RegisterCoursesCommand and then adjust the code.

Thank you for your reply, but it works just for the first property ("Type your name:") and when I response, bot does not send me the next question!

You have the database set up? Conversations require a database connection.

It still doesn't work !! :(

Here is all of my codes:

hook.php :

<?php

// Load composer
require __DIR__ . '/vendor/autoload.php';

use Longman\TelegramBot\Telegram;
use Longman\TelegramBot\TelegramLog;
use Longman\TelegramBot\Exception\TelegramException;

// Bot's API key and name
$bot_api_key = '************';
$bot_username = '************';

// Define all paths for custom commands
$commands_folder = __DIR__ . '/Commands/';

$mysql_credentials = [
    'host'     => 'localhost',
    'user'     => '************',
    'password' => '************',
    'database' => '************',
];

try {
    // Create Telegram API object
    $telegram = new Telegram($bot_api_key, $bot_username);

    // Add commands paths containing custom commands
    $telegram->addCommandsPath($commands_folder);

    // Logging (Error, Debug and Raw Updates)
    TelegramLog::initErrorLog(__DIR__ . "/{$bot_username}_error.log");
    TelegramLog::initDebugLog(__DIR__ . "/{$bot_username}_debug.log");
    TelegramLog::initUpdateLog(__DIR__ . "/{$bot_username}_update.log");

    // Set custom Upload and Download paths
    $telegram->setDownloadPath(__DIR__ . '/Download');
    $telegram->setUploadPath(__DIR__ . '/Upload');

    // Requests Limiter (tries to prevent reaching Telegram API limits)
    $telegram->enableLimiter();

    $telegram->enableMySQL($mysql_credentials);

    // Handle telegram webhook request
    $telegram->handle();
} catch (TelegramException $e) {
    TelegramLog::error($e);
}

RegisterCoursesCommand.php :

<?php

namespace Longman\TelegramBot\Commands\UserCommands;

use Longman\TelegramBot\Commands\UserCommand;
use Longman\TelegramBot\Conversation;
use Longman\TelegramBot\Entities\Keyboard;
use Longman\TelegramBot\Request;

/**
 * User "/survey" command
 */
class RegisterCoursesCommand extends UserCommand
{
    /**
     * @var string
     */
    protected $name = 'register_courses';
    /**
     * @var string
     */
    protected $description = 'Register courses';
    /**
     * @var string
     */
    protected $usage = '/register_courses';
    /**
     * @var string
     */
    protected $version = '1.0.0';
    /**
     * @var bool
     */
    protected $need_mysql = true;
    /**
     * Conversation Object
     *
     * @var \Longman\TelegramBot\Conversation
     */
    protected $conversation;

    /**
     * Command execute method
     *
     * @return \Longman\TelegramBot\Entities\ServerResponse
     * @throws \Longman\TelegramBot\Exception\TelegramException
     */
    public function execute()
    {
        $message = $this->getMessage();
        $chat = $message->getChat();
        $user = $message->getFrom();
        $text = trim($message->getText(true));
        $chat_id = $chat->getId();
        $user_id = $user->getId();
        //Preparing Response
        $data = [
            'chat_id' => $chat_id,
        ];
        if ($chat->isGroupChat() || $chat->isSuperGroup()) {
            //reply to message id is applied by default
            //Force reply is applied by default so it can work with privacy on
            $data['reply_markup'] = Keyboard::forceReply(['selective' => true]);
        }
        //Conversation start
        $this->conversation = new Conversation($user_id, $chat_id, $this->getName());
        $notes = &$this->conversation->notes;
        !is_array($notes) && $notes = [];
        //cache data from the tracking session if any
        $state = 0;
        if (isset($notes['state'])) {
            $state = $notes['state'];
        }
        $result = Request::emptyResponse();
        //State machine
        //Entrypoint of the machine state if given by the track
        //Every time a step is achieved the track is updated
        switch ($state) {
            case 0:
                if ($text === '') {
                    $notes['state'] = 0;
                    $this->conversation->update();
                    $data['text'] = 'Name: ';
                    $data['reply_markup'] = Keyboard::remove(['selective' => true]);
                    $result = Request::sendMessage($data);
                    break;
                }
                $notes['name'] = $text;
                $text = '';
            // no break
            case 1:
                if ($text === '' || !is_numeric($text)) {
                    $notes['state'] = 1;
                    $this->conversation->update();
                    $data['text'] = 'National ID Card NO: ';
                    if ($text !== '') {
                        $data['text'] = 'It must be numeric!';
                    }
                    $result = Request::sendMessage($data);
                    break;
                }
                $notes['N_ID_C'] = $text;
                $text = '';
            // no break
            case 2:
                if ($text === '') {
                    $notes['state'] = 2;
                    $this->conversation->update();
                    $data['text'] = 'Courses :';
                    $result = Request::sendMessage($data);
                    break;
                }
                $notes['courses'] = $text;
                $text = '';
            // no break
            case 3:
                // This field is optional
                $notes['state'] = 3;
                $this->conversation->update();
                $data['text'] = 'Grade :';
                $result = Request::sendMessage($data);
                $notes['grade'] = $text;
                break;

            // no break
            case 4:
                // This field is optional
                $notes['state'] = 4;
                $this->conversation->update();
                $data['text'] = 'School :';
                $result = Request::sendMessage($data);
                $notes['school'] = $text;
                break;
            // no break
            case 5:
                if ($text === '' || !is_numeric($text)) {
                    $notes['state'] = 5;
                    $this->conversation->update();
                    $data['text'] = 'Phone :';
                    if ($text !== '') {
                        $data['text'] = 'It must be numeric!';
                    }
                    $result = Request::sendMessage($data);
                    break;
                }
                $notes['phone'] = $text;
            // no break
            case 6:
                if ($text === '' || !filter_var($text, FILTER_VALIDATE_EMAIL)) {
                    $notes['state'] = 6;
                    $this->conversation->update();
                    $data['text'] = 'Email :';
                    if ($text !== '') {
                        $data['text'] = 'Email is not valid!';
                    }
                    $result = Request::sendMessage($data);
                    break;
                }
                $notes['email'] = $text;
            // no break
            case 7:
                $this->conversation->update();
                $out_text = 'Result :' . PHP_EOL;
                unset($notes['state']);
                foreach ($notes as $k => $v) {
                    $out_text .= PHP_EOL . ucfirst($k) . ': ' . $v;
                }
                $data['text'] = $out_text;
                $this->conversation->stop();
                $result = Request::sendMessage($data);
                break;
        }
        return $result;
    }
}

GenericmessageCommand.php :

<?php

namespace Longman\TelegramBot\Commands\SystemCommands;

use Longman\TelegramBot\Commands\SystemCommand;
use Longman\TelegramBot\Commands\UserCommands\AllCoursesCommand;
use Longman\TelegramBot\Commands\UserCommands\RegisterCoursesCommand;
use Longman\TelegramBot\Entities\Update;
use Longman\TelegramBot\Request;

class GenericmessageCommand extends SystemCommand
{
    protected $name = 'Genericmessage';
    protected $description = 'Handle generic message';
    protected $version = '1.0.0';

    public function execute()
    {
        $text = trim($this->getMessage()->getText(true));

        $update = json_decode($this->update->toJson(), true);

        if ($text === 'All Courses') {
            $update['message']['text'] = '/all_courses';
            return (new AllCoursesCommand($this->telegram, new Update($update)))->preExecute();
        } elseif ($text === 'Register') {
            $update['message']['text'] = '/register_courses';
            return (new RegisterCoursesCommand($this->telegram, new Update($update)))->preExecute();
        }

        return Request::emptyResponse();
    }
}

Right, there are a few things that need changing in your code, but it's quite easy 馃槂

Because the filename and class name are linked to the command name, your class must also have the _ in it. I know that's very unpretty, but unfortunately that's just how it works for now 馃槙
Commands with an underscore in their name aren't fully supported at the moment, as certain internal code isn't designed for them. So it's more of a hack...
I hope we have a better solution for all command names in the future!

So your command files would be Register_coursesCommand.php and All_coursesCommand.php, class names the same, just without the .php.

Then, inside your GenericmessageCommand class, you still need to handle conversations, otherwise the bot doesn't know that there is a current conversation happening.

So your GenericmessageCommand::execute() method should be like this:

public function execute()
{
    //If a conversation is busy, execute the conversation command after handling the message
    $conversation = new Conversation(
        $this->getMessage()->getFrom()->getId(),
        $this->getMessage()->getChat()->getId()
    );
    //Fetch conversation command if it exists and execute it
    if ($conversation->exists() && ($command = $conversation->getCommand())) {
        return $this->telegram->executeCommand($command);
    }

    $text   = trim($this->getMessage()->getText(true));
    $update = json_decode($this->update->toJson(), true);

    if ($text === 'All Courses') {
        $update['message']['text'] = '/all_courses';
        return (new All_coursesCommand($this->telegram, new Update($update)))->preExecute();
    } elseif ($text === 'Register') {
        $update['message']['text'] = '/register_courses';
        return (new Register_coursesCommand($this->telegram, new Update($update)))->preExecute();
    }

    return Request::emptyResponse();
}

Alternatively, you could also create a simple /courses command, which then could show a keyboard with a few buttons, Show All, Register, etc.

Yes It worked!
Thank you so much !!

Was this page helpful?
0 / 5 - 0 ratings