TelegramBot / Api

Native PHP Wrapper for Telegram BOT API
MIT License
1.06k stars 324 forks source link

Add function and example allow run already defined command from callbackdata #416

Closed tsybot closed 1 year ago

tsybot commented 1 year ago

This is pull request add function get_event in Client.php which get \TelegramBot\Api\Events\Event of already defined command from events array by command name. It what need in issue #380 . For example we define function ping and test_inline, when we run test_inline we see inline button for test, so when we push on inline button we see answer of /ping command because /ping command is triggered.

BoShurik commented 1 year ago

Hello! Thank you for contribution.

Unfortunately I vote against this change.

You can easily achieve this by modifying your ping command:

$bot->on(function (Update $update) use ($bot) {
    $chatId = $update->getMessage()?->getChat()->getId() ?? $update->getCallbackQuery()?->getFrom()->getId();

    $bot->sendMessage($chatId, 'pong!');
}, function (Update $update) {
    $text = $update->getMessage()?->getText() ?? $update->getCallbackQuery()?->getData();

    return \in_array($text, ['/ping', 'command_/ping']);
});

Secondly, your approach with reflection and relying on static name variable is quite fragile. As you can see your get_event function can't find event above.

P.S. Also you can use something like this:

//Handle /ping command
$bot->command('ping', $botCommand = function ($message) use ($bot) {
    $bot->sendMessage($message->getChat()->getId(), 'pong!');
});
//Handle command event
$bot->callbackQuery(function ($message) use ($botCommand) {
    //...
    $botCommand($message);
    //...
}
tsybot commented 1 year ago
$bot->on(function (Update $update) use ($bot) {
    $chatId = $update->getMessage()?->getChat()->getId() ?? $update->getCallbackQuery()?->getFrom()->getId();

    $bot->sendMessage($chatId, 'pong!');
}, function (Update $update) {
    $text = $update->getMessage()?->getText() ?? $update->getCallbackQuery()?->getData();

    return \in_array($text, ['/ping', 'command_/ping']);
});

im not understand this code, im not use $bot->on( funcionality in my app. It samed with my code whti /test_inline function ? I not undersnrat in_array function return true or false or what it do)

About P.S. way, thank, im try it. But so if we need multi command run in callback handle i need make array of botCommands with index name of command and value command and push it to callbackQuery via "use" yes ?)

$botCommands = Array();

$bot->command('ping', $botCommands['ping'] = function ($message) use ($bot) {
    $bot->sendMessage($message->getChat()->getId(), 'pong!');
});
//Handle command event
$bot->callbackQuery(function ($message) use ($botCommands) {
    //...
    $botCommands['ping']($message);
    //...
}

Like this?

BoShurik commented 1 year ago

im not understand this code

It replaces entire $bot->callbackQuery section. In on method first parameter is a command, second one is checker and if it returns true then command is executed. In this case checker get data from Message::$text or from CallbackQuery::$data and if it equals to /ping or command_/ping then ping command is executed.

About P.S. way, thank, im try it. But so if we need multi command run in callback handle i need make array of botCommands with index name of command and value command and push it to callbackQuery via "use" yes ?)

Yes

tsybot commented 1 year ago

я не понимаю этот код

Он заменяет весь $bot->callbackQueryраздел. В onметоде первый параметр - это команда, второй - чекер, и если он возвращается, trueто команда выполняется. В этом случае чекер получает данные из Message::$textили из CallbackQuery::$data, и если они равны /pingили, command_/pingто pingкоманда выполняется.

Насчет PS кстати, спасибо, попробую. Но поэтому, если нам нужно запустить несколько команд в дескрипторе обратного вызова, мне нужно создать массив botCommands с индексным именем команды и значением команды и отправить его в callbackQuery через «использовать», да?)

Да

Thanks for the explanation, but you mean something a little wrong on my opinion. I'm talking about executing an already defined event (a pair of checker + command) through the $bot->command( function when the command has a clear name. And you created a duplication of the Ping command code again describing its logic, although it is already defined through $bot->command( and can be both called by the client /ping and command_/ping via callbackdata in my proposal

BoShurik commented 1 year ago

And you created a duplication of the Ping command code again describing its logic

No. It's not a new command with duplication it's rewritten ping command. If it's suppose to run by message and callback_query so we need modify checker to accept both

tsybot commented 1 year ago

And you created a duplication of the Ping command code again describing its logic

No. It's not a new command with duplication it's rewritten ping command. If it's suppose to run by message and callback_query so we need modify checker to accept both

But the point is to have the command already defined via the $bot->command( function and use it instead of trying to remake the ping command in such a way that it poisons the ping message when processing callback_query, this makes no sense because we lose the /ping command in normal user mode

BoShurik commented 1 year ago

this makes no sense because we lose the /ping command in normal user mode

Why? In normal mode it's still works

BoShurik commented 1 year ago

There are many ways to solve your problem without modifying library:

$bot->callbackQuery(function ($message) use ($bot) {
    $user_id = $message->getMessage()->getChat()->getId();
    $callback_data = $message->getData();

    if(preg_match('/^command_/', $callback_data)) {
        $command = preg_replace('/^command_/', '', $callback_data);
        $message_json = $message->getMessage()->toJson();

        $bot->answerCallbackQuery($message->getId(), "Request command: {$command}. Loading...");
        // Mock Update message, just like in your example
        $messageSource = TelegramBot\Api\BotApi::jsonValidate($message_json, true);
        $messageSource['text'] = $command;
        $data_json = json_encode([
            "update_id" => rand(11111111,99999999), 
            "message" => $messageSource
        ]);
        $data = TelegramBot\Api\BotApi::jsonValidate($data_json, true);
        $update = TelegramBot\Api\Types\Update::fromResponse($data);
        // End mocking
        // Instead of finding event with reflection just fire client's handle method 
        // with mocked update message where event will be found automatically
        $bot->handle([$update]); 
    }
}
tsybot commented 1 year ago

There are many ways to solve your problem without modifying library:

$bot->callbackQuery(function ($message) use ($bot) {
    $user_id = $message->getMessage()->getChat()->getId();
    $callback_data = $message->getData();

    if(preg_match('/^command_/', $callback_data)) {
        $command = preg_replace('/^command_/', '', $callback_data);
        $message_json = $message->getMessage()->toJson();

        $bot->answerCallbackQuery($message->getId(), "Request command: {$command}. Loading...");
        // Mock Update message, just like in your example
        $messageSource = TelegramBot\Api\BotApi::jsonValidate($message_json, true);
        $messageSource['text'] = $command;
        $data_json = json_encode([
            "update_id" => rand(11111111,99999999), 
            "message" => $messageSource
        ]);
        $data = TelegramBot\Api\BotApi::jsonValidate($data_json, true);
        $update = TelegramBot\Api\Types\Update::fromResponse($data);
        // End mocking
        // Instead of finding event with reflection just fire client's handle method 
        // with mocked update message where event will be found automatically
        $bot->handle([$update]); 
    }
}

I realy realy like solition in this post! Thank you! Perfect! You could post it sooner as an answer to the issue #380 it solition of this issue im sure