Hi everyone. How can I make very simple InlineKeyboard with text message update?
I have a message "Choose category" and under it there should be three buttons of InlineKeyboard: "Oranges", "Apples", "Pears". When I select a category, the message text must be updated. How can this be realized? Please show the code that corresponds to my example.
I delved into the files "InlineKeyboard" and "Inlinequery" but code is too complicated for understanding...
Help me pls!
Hi @MyZik
Right, here is an example which you can start playing around with:
CallbackqueryCommand.php
<?php
namespace Longman\TelegramBot\Commands\SystemCommands;
use Longman\TelegramBot\Commands\AdminCommands\CategoryCommand;
use Longman\TelegramBot\Commands\SystemCommand;
use Longman\TelegramBot\Request;
class CallbackqueryCommand extends SystemCommand
{
protected $name = 'callbackquery';
protected $description = 'Reply to callback query';
protected $version = '1.0.0';
public function execute()
{
$update = $this->getUpdate();
$callback_query = $update->getCallbackQuery();
$callback_data = $callback_query->getData();
// Only do something for the 'category' selection.
if (strpos($callback_data, 'category_') !== 0) {
return Request::emptyResponse();
}
// Get the real category from the callback_data.
$category = substr($callback_data, strlen('category_'));
return Request::editMessageText([
'chat_id' => $callback_query->getMessage()->getChat()->getId(),
'message_id' => $callback_query->getMessage()->getMessageId(),
'text' => CategoryCommand::$categories[$category],
]);
}
}
CategoryCommand.php
<?php
namespace Longman\TelegramBot\Commands\AdminCommands;
use Longman\TelegramBot\Commands\UserCommand;
use Longman\TelegramBot\Entities\InlineKeyboard;
use Longman\TelegramBot\Entities\InlineKeyboardButton;
use Longman\TelegramBot\Request;
class CategoryCommand extends UserCommand
{
protected $name = 'category';
protected $description = 'Choose a category, inline.';
protected $usage = '/category';
protected $version = '1.0.0';
/** @var array Inline button categories (can also be dynamically generated) */
public static $categories = [
'apple' => 'Yay, an apple!',
'orange' => 'Sweetness...',
'cherry' => 'A cherry on top.',
];
public function execute()
{
$keyboard_buttons = [];
foreach (self::$categories as $key => $value) {
$keyboard_buttons[] = new InlineKeyboardButton([
'text' => ucfirst($key),
'callback_data' => 'category_' . $key,
]);
}
$data = [
'chat_id' => $this->getMessage()->getChat()->getId(),
'text' => 'Choose a category:',
'reply_markup' => new InlineKeyboard($keyboard_buttons),
];
return Request::sendMessage($data);
}
}
Just put these in a folder and add them as custom commands paths using $telegram->addCommandsPath() in your hook.php.
Hope this helps to get you started 👍
Hi, @noplanman
Greath thanks for your answer! I have two small questions..
Here is my code. It has Conservation, Keyboard and your code.
<?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\Conversation;
use Longman\TelegramBot\Commands\SystemCommand;
use Longman\TelegramBot\Entities\InlineKeyboard;
use Longman\TelegramBot\Entities\InlineKeyboardButton;
use Longman\TelegramBot\Entities\Keyboard;
use Longman\TelegramBot\Entities\KeyboardButton;
use Longman\TelegramBot\Request;
/**
* Start command
*/
class StartCommand extends SystemCommand
{
/**
* @var string
*/
protected $name = 'start';
protected $description = 'Start command';
protected $usage = '/start';
protected $version = '1.1.0';
public static $categories = [
'яблоки' => 'Вы выбрали овощи.', // Vegetables ("You choise vegetables")
'ягоды' => 'Вы выбрали ягоды.', // Berries ("You choise berries")
'фрукты' => 'Вы выбрали фрукты.', // Fruits ("You choise fruits")
];
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();
$data = [
'chat_id' => $chat_id,
];
if ($chat->isGroupChat() || $chat->isSuperGroup()) {
$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();
switch ($state) {
case 0:
$keyboards = [];
$keyboards[] = new Keyboard(
['Овощи'],
['Ягоды', 'Фрукты']
);
$keyboard = $keyboards[0]
->setResizeKeyboard(true)
->setOneTimeKeyboard(false)
->setSelective(false);
if ($text === '') {
$notes['state'] = 0;
$this->conversation->update();
$data['text'] = 'Выберите интересующий Вас раздел';
$data['reply_markup'] = $keyboard;
$result = Request::sendMessage($data);
// break;
}
elseif ($text === 'Овощи') {
$this->conversation->update();
$data['text'] = file_get_contents(__DIR__ . '/../../other/texts/vegetables.txt');
$data['reply_markup'] = $keyboard;
$result = Request::sendMessage($data);
}
elseif ($text === 'Ягоды') {
$this->conversation->update();
$data['text'] = file_get_contents(__DIR__ . '/../../other/texts/berries.txt');
$data['reply_markup'] = $keyboard;
$result = Request::sendMessage($data);
}
/**
* by @nopalman
*/
elseif ($text === 'Фрукты') {
$this->conversation->update();
$keyboard_buttons = [];
foreach (self::$categories as $key => $value) {
$keyboard_buttons[] = new InlineKeyboardButton([
'text' => ucfirst($key),
'callback_data' => 'category_' . $key,
]);
}
$data['text'] = 'Выберите фрукты';
$data['reply_markup'] = new InlineKeyboard($keyboard_buttons);
$result = Request::sendMessage($data);
}
/**
Close menu
*/
elseif ($text === 'Спрятать меню') {
$this->conversation->stop();
$data['text'] = 'Готово! Чтобы вызвать меню заново - наберите /start';
$data['reply_markup'] = Keyboard::remove();
$result = Request::sendMessage($data);
}
$notes['name'] = $text;
$text = '';
// break;
}
return $result;
}
}
Thank you in advance!
@MyZik Ok, let's see.
If I have more commands with InlineKeyboard, Callback and Queries, should I recreate the file as your CallbackQuery (for example)?
All callback queries get sent to the CallbackqueryCommand class, so all handling must be made in there. Of course you can write your specific code somewhere else and then refer to it, instead of putting everything into 1 method (namely CallbackqueryCommand::execute()).
How to make sure that when I select a category, the buttons do not disappear?
Just set the reply_markup when you call Request::editMessageText(). For this, it's easiest to create a static method that generates the keyboard:
CategoryCommand.php
public static function getMyInlineKeyboard()
{
$keyboard_buttons = [];
foreach (self::$categories as $key => $value) {
$keyboard_buttons[] = new InlineKeyboardButton([
'text' => ucfirst($key),
'callback_data' => 'category_' . $key,
]);
}
return new InlineKeyboard($keyboard_buttons);
}
CallbackqueryCommand.php (note the reply_markup here)
return Request::editMessageText([
'chat_id' => $callback_query->getMessage()->getChat()->getId(),
'message_id' => $callback_query->getMessage()->getMessageId(),
'text' => CategoryCommand::$categories[$category],
'reply_markup' => CategoryCommand::getMyInlineKeyboard(),
]);
How to enable Russian language support?
What do you mean? Does Telegram not display the characters properly?
If you're talking about saving data to the database that isn't working, make sure you have a MySQL collation that supports those characters.
@noplanman thanks for your aswer. You helped me a lot! Development has become even more interesting for me c:
@noplanman Sorry for the stupid question, but if I created a new method in Callbackquery, how can I use it in CategoryCommand? Thanks.
Easiest would be to make it static, then call CallbackqueryComman::whatever().
BUT, what exactly is the method for?
Maybe it doesn't even belong in CallbackqueryCommand, as any methods in there "should" be mainly for the Callback Query itself.
@noplanman I want to create a new command, say, UserslistCommand, and I want the buttons InlineKeyboard to show data from the database. For example,
id: 1, name: Mojo, nickname: Mojo7
id 2, name: Alex, nickname: Alex__12
And that under the message there were buttons of turning the data. Something like that: img
A quick search and I found this: http://stackoverflow.com/a/42879866
It's in node.js, but I think the general idea should make sense to you.
You can create a static getPagination() method in CallbackqueryCommand if you like and add the necessary parameters, like callback_id, current, max.
callback_id: A unique ID to know from which inline keyboard the message came from.
current: The current page, to know which buttons are put in which order.
max: To know how far the list goes.
Then, inside your UserslistCommand, you can simply call CallbackqueryCommand::getPagination('userslist', $current, $max); to get the inline keyboard. Of course you'll have to do some maths for $current and $max according to the items you have to display.
Does that make sense to you? I haven't tested this, but the general idea "should" work :wink:
@noplanman oh, how can I connect my table users from database? :(
public static function getPagination()
{
$update = $this->getUpdate();
$callback_query = $update->getCallbackQuery();
$callback_data = $callback_query->getData();
if (strpos($callback_data, 'user_') !== 0) {
return Request::emptyResponse();
}
// Get the real category from the callback_data.
$category = substr($callback_data, strlen('user_'));
return Request::editMessageText([
'chat_id' => $callback_query->getMessage()->getChat()->getId(),
'message_id' => $callback_query->getMessage()->getMessageId(),
'text' => CategoryCommand::$categories[$category],
]);
}
And I don't understand, how can I create a method in CategoryCommand, with users paginator. Sorry 😢
You can get access to the database using DB::getPdo(), which gives you the PDO object, allowing you to execute SQL commands.
I'll have to first create a working example of this to give to you, as I've never done this before either 😄
@noplanman I tried to call the getPagination method in my command file:
Callbackquery::getPagination()
error: PHP Fatal error: Class 'Longman\\TelegramBot\\Commands\\AdminCommands\\Callbackquery' not found
<?php
namespace Longman\TelegramBot\Commands\AdminCommands;
use Longman\TelegramBot\Commands\UserCommand;
use Longman\TelegramBot\DB;
use PDO;
use Longman\TelegramBot\Entities\InlineKeyboard;
use Longman\TelegramBot\Entities\InlineKeyboardButton;
use Longman\TelegramBot\Request;
class UserlistCommand extends UserCommand
{
...
public function execute()
{
Callbackquery::getPagination();
...
}
It's called CallbackqueryCommand 👍 (the Command was missing)
@noplanman now Class 'Longman\\TelegramBot\\Commands\\AdminCommands\\CallbackqueryCommand'not found :D
Ah of course, because you're in the AdminCommands namespace.
You also need to add use Longman\TelegramBot\Commands\SystemCommands\CallbackqueryCommand;
@noplanman PHP Fatal error: Using $this when not in object context
Sorry, but i really don't know what to do...
I'm on this, almost finished. Will post something tomorrow 👍
@MyZik Please excuse my delayed reply...
Did you manage to get this working? Can I close off here?
Most helpful comment
Hi @MyZik
Right, here is an example which you can start playing around with:
CallbackqueryCommand.php
CategoryCommand.php
Just put these in a folder and add them as custom commands paths using
$telegram->addCommandsPath()in yourhook.php.Hope this helps to get you started 👍