hello, my goal is to make an inline menu. one of menu option is register. when a user clicked on register, RegisterCommand.php must be activated and when it finished came back to menu. i need to know how can i call commands in an inline keyboard. thanks alot.
@jacklul
$update = json_decode($this->update->toJson(), true);
$update['message']['text'] = '/signup';
return (new SignupCommand($this->telegram, new Update($update)))->preExecute();
it does not work for me!
SignupCommand is an survey command.
here is my menu file (MenuCommand.php):
<?php
/**
* This file is part of the TelegramBot package.
*
* (c) Avtandil Kikabidze aka LONGMAN <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Longman\TelegramBot\Commands\UserCommands;
use Longman\TelegramBot\Commands\UserCommand;
use Longman\TelegramBot\Entities\InlineKeyboard;
use Longman\TelegramBot\Request;
/**
* User "/inlinekeyboard" command
*
* Display an inline keyboard with a few buttons.
*/
class MenuCommand extends UserCommand
{
/**
* @var string
*/
protected $name = 'menucommand';
/**
* @var string
*/
protected $description = 'inline keyboard menu';
/**
* @var string
*/
protected $usage = '/menu';
/**
* @var string
*/
protected $version = '0.1.0';
/**
* Command execute method
*
* @return \Longman\TelegramBot\Entities\ServerResponse
* @throws \Longman\TelegramBot\Exception\TelegramException
*/
public function execute()
{
$chat_id = $this->getMessage()->getChat()->getId();
$inline_keyboard = new InlineKeyboard([
['text' => 'signup', 'callback_data' => 'register'],
['text' => 'advertisement', 'callback_data' => 'ads'],
], [
['text' => 'search', 'callback_data' => 'search'],
['text' => 'contact us', 'callback_data' => 'contact'],
]);
$data = [
'chat_id' => $chat_id,
'text' => 'here is my menu',
'reply_markup' => $inline_keyboard,
];
return Request::sendMessage($data);
}
}
and here is my CallbackqueryCommand.php:
<?php
/**
* This file is part of the TelegramBot package.
*
* (c) Avtandil Kikabidze aka LONGMAN <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Longman\TelegramBot\Commands\SystemCommands;
use Longman\TelegramBot\Commands\SystemCommand;
use Longman\TelegramBot\Commands\UserCommands\HelpCommand;
use Longman\TelegramBot\Commands\UserCommands\SignupCommand;
use Longman\TelegramBot\Commands\UserCommands;
use Longman\TelegramBot\Request;
use Longman\TelegramBot\Entities\InlineKeyboard;
use Longman\TelegramBot\Entities\Update;
/**
* Callback query command
*
* This command handles all callback queries sent via inline keyboard buttons.
*
* @see InlinekeyboardCommand.php
*/
class CallbackqueryCommand extends SystemCommand
{
/**
* @var string
*/
protected $name = 'callbackquery';
/**
* @var string
*/
protected $description = 'Reply to callback query';
/**
* @var string
*/
protected $version = '1.1.1';
/**
* Command execute method
*
* @return \Longman\TelegramBot\Entities\ServerResponse
* @throws \Longman\TelegramBot\Exception\TelegramException
*/
public function execute()
{
$callback_query = $this->getCallbackQuery();
$callback_query_id = $callback_query->getId();
$callback_data = $callback_query->getData();
switch ($callback_data){
case 'register':
$update = json_decode($this->update->toJson(), true);
$update['message']['text'] = '/signup';
return (new SignupCommand(new $this->telegram, new Update($update)))->preExecute();
}
}
}
its is fully like you said before on #485 but it doesn't work at all.
return (new SignupCommand(new $this->telegram, new Update($update)))->preExecute();
this new keyword is a mistake I guess
@sharkydog Yep!
Also, due to the fact that the callback query doesn't have the message data, this needs more tweaking.
Best solution IMHO is to move all logic from the execute method into a separate class, which can then be called from the command and the callback, passing all necessary parameters along with it.
For example:
// Pseudo code!
class AwesomeCommand
{
...
public function execute()
{
return self::doSomething(...);
}
public static function doSomething($message, $user_id, $whatever)
{
// Do something! Like Request::sendMessage(...) etc.
}
...
}
class CallbackqueryCommand
{
public function execute()
{
if ($whatever_callback_data) {
return AwesomeCommand::doSomething(...);
}
}
}
Other option, which is a very nasty hack though, and really not recommended (also not 100% working example):
// In CallbackqueryCommand::execute()
if ($whatever_callback_data) {
$update = (array) $this->update;
$update['message'] = $update['callback_query']['message'];
$update['message']['text'] = '/awesome';
return (new AwesomeCommand($this->telegram, new Update($update)))->preExecute();
}
// In CallbackqueryCommand::execute()
if ($whatever_callback_data) {
$update = (array) $this->update;
$update['message'] = $update['callback_query']['message'];
$update['message']['text'] = '/awesome';
return (new AwesomeCommand($this->telegram, new Update($update)))->preExecute();
}
That will call AwesomeCommand with a message from the bot itself, imagine that AwesomeCommand wants to open/start a conversation... will bump into a problem. So, "from" in the new message should also be "hacked" with "from" from the callback_query update.
I started testing inline keyboard and this can be done with default CallbackqueryCommand (CallbackQuery should be answered with answerCallbackQuery) using its static method to register callbacks, but it again involves similar hackery to construct a new message to send to the command.
@sharkydog As I mentioned, this hack really shouldn't be used, ever. It's really easy to extract the necessary code to a custom class / method.
Manipulating the Update object shouldn't be done, as it can also cause weird side-effects in the DB.
If the inline keyboard really needs to simulate a command 100%, then it will need a cleaner and more proof implementation, not all this messy array stuff 馃榿
@noplanman would you show me an example, how can i start a conversation in custom class/ method? i am really confused! i want to make register conversation custom class which active with register inlinekeyboard button.
i did what i want with these codes:
$callback_query = $this->getCallbackQuery();
$callback_query_id = $callback_query->getId();
$callback_data = $callback_query->getData();
switch ($callback_data){
case 'register':
$update = (array) $this->update;
$update['message'] = $update['callback_query']['message'];
$update['message']['text'] = '/signup';
$update['message']['from']['id']=$update['callback_query']['from']['id'];
return (new SignupCommand($this->telegram, new Update($update)))->preExecute();
}
it started conversation and it works, thanks for your helps, but is it gonna make my bot getting bugs in the future?
@sadeghesfahani
$update['message']['from'] = $update['callback_query']['from'];
class MenuCommand extends UserCommand {
protected $private_only = true;
// stuff
}
class CallbackqueryCommand extends SystemCommand {
public function execute() {
// stuff
(new SignupCommand($this->telegram, new Update($update)))->preExecute();
return Request::answerCallbackQuery(['callback_query_id' => $callback_query_id]);
}
}
And don't try to edit or delete the message, you will be doing that to the message from the bot with the inline keyboard.
Another way:
Start a conversation in CallbackqueryCommand, but with "signup" as command parameter and send the user some message, then the next message from the user will go to SignupCommand, routed through default GenericmessageCommand.
class CallbackqueryCommand extends SystemCommand {
public function execute() {
// stuff
$conv = new Conversation(
$callback_query->getFrom()->getId(),
$callback_query->getMessage()->getChat()->getId(),
'signup'
);
Request::sendMessage([
'chat_id' => $callback_query->getMessage()->getChat()->getId(),
'text' => 'Go sign up'
]);
return Request::answerCallbackQuery([
'callback_query_id' => $callback_query->getId()
]);
}
}
Don't forget to close the conversation in your command
class SignupCommand extends UserCommand {
public function execute() {
// stuff
$conv = new Conversation(
$this->getMessage()->getFrom()->getId()
$this->getMessage()->getChat()->getId()
);
$conv->stop();
return Request::sendMessage([
'chat_id' => $this->getMessage()->getChat()->getId(),
'text' => 'Sign up ended'
]);
}
}
oh really really thanks i was looking for this.
I ended up going the hacky way.
First, I extend the Telegram class and define new function:
public function executeCommandUpdate($command, \Longman\TelegramBot\Entities\Update $update) {
$this->update = $update;
return $this->executeCommand($command);
}
Then in CallbackqueryCommand:
public function execute() {
$query = $this->getUpdate()->getCallbackQuery();
$update = [
'update_id' => 0,
'message' => [
'message_id' => 0,
'from' => $query->getFrom()->getRawData(),
'date' => time(),
'text' => $query->getData()
]
];
if($query->getMessage()) {
$update['message']['chat'] = $query->getMessage()->getChat()->getRawData();
}
$update = new Update($update, $this->telegram->getBotUsername());
$command = $update->getMessage()->getCommand();
if(!$command) $command = 'genericmessage';
$this->telegram->executeCommandUpdate($command, $update);
return Request::answerCallbackQuery(['callback_query_id' => $query->getId()]);
}
And then the MenuCommand (the inline keyboard):
public function execute() {
$inline_keyboard = new InlineKeyboard(
[
['text' => 'Ping echo', 'callback_data' => '/echo ping'], // executes /echo ping command
['text' => 'Ping cmd', 'callback_data' => '/ping'], // executes /ping command
['text' => 'Ping', 'callback_data' => 'ping'] // plain text, if a command initiated a conversation, it will receive this text through GenericmessageCommand
]
);
return Request::sendMessage([
'chat_id' => $this->getMessage()->getChat()->getId(),
'text' => 'Menu',
'reply_markup' => $inline_keyboard
]);
}
Thank you for great work!! I am looking for this way a lot of time!
Most helpful comment
i did what i want with these codes:
it started conversation and it works, thanks for your helps, but is it gonna make my bot getting bugs in the future?