Coder Social home page Coder Social logo

php-telegram-bot / inline-keyboard-pagination Goto Github PK

View Code? Open in Web Editor NEW

This project forked from lartie/telegram-bot-pagination

29.0 7.0 13.0 102 KB

Telegram Bot inline keyboard pagination for CallbackQuery

License: MIT License

PHP 100.00%
telegram telegram-bot pagination php7 inline callback query php8

inline-keyboard-pagination's Introduction

Scrutinizer Code Quality Codecov Build Status

Latest Stable Version Total Downloads License

Installation

Composer

composer require php-telegram-bot/inline-keyboard-pagination

Usage

Test Data

$items        = range(1, 100); // required. 
$command      = 'testCommand'; // optional. Default: pagination
$selectedPage = 10;            // optional. Default: 1
$labels       = [              // optional. Change button labels (showing defaults)
    'default'  => '%d',
    'first'    => '« %d',
    'previous' => '‹ %d',
    'current'  => '· %d ·',
    'next'     => '%d ›',
    'last'     => '%d »',
];

// optional. Change the callback_data format, adding placeholders for data (showing default)
$callbackDataFormat = 'command={COMMAND}&oldPage={OLD_PAGE}&newPage={NEW_PAGE}'

How To Use

// Define inline keyboard pagination.
$ikp = new InlineKeyboardPagination($items, $command);
$ikp->setMaxButtons(7, true); // Second parameter set to always show 7 buttons if possible.
$ikp->setLabels($labels);
$ikp->setCallbackDataFormat($callbackDataFormat);

// Get pagination.
$pagination = $ikp->getPagination($selectedPage);

// or, in 2 steps.
$ikp->setSelectedPage($selectedPage);
$pagination = $ikp->getPagination();

Now, $pagination['keyboard'] is basically a row that contains the pagination.

// Use it in your request.
if (!empty($pagination['keyboard'])) {
    //$pagination['keyboard'][0]['callback_data']; // command=testCommand&oldPage=10&newPage=1
    //$pagination['keyboard'][1]['callback_data']; // command=testCommand&oldPage=10&newPage=7
    
    ...
    $data['reply_markup'] = [
        'inline_keyboard' => [
            $pagination['keyboard'],
        ],
    ];
    ...
}

To get the callback data, you can use the provided helper method (only works when using the default callback data format):

// e.g. Callback data.
$callback_data = 'command=testCommand&oldPage=10&newPage=1';

$params = InlineKeyboardPagination::getParametersFromCallbackData($callbackData);

//$params = [
//    'command' => 'testCommand',
//    'oldPage' => '10',
//    'newPage' => '1',
//];

// or, just use PHP directly if you like. (literally what the helper does!)
parse_str($callbackData, $params);

Code Quality

Run the PHPUnit tests via Composer script.

composer test

License

The MIT License (MIT). Please see License File for more information.

Project based on Telegram Bot Pagination by lartie.

inline-keyboard-pagination's People

Contributors

lartie avatar noplanman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

inline-keyboard-pagination's Issues

Mysterious page 0

There seems to be a bug when you have only one item per page, max buttons set to 8 and 9 or more elements in the array.
When you reach page 3 a mysterious page 0 shows up: https://i.imgur.com/nlMfcVm.png

Test code:

<?php

namespace Longman\TelegramBot\Commands\AdminCommands;

use Longman\TelegramBot\Commands\AdminCommand;
use Longman\TelegramBot\Entities\InlineKeyboard;
use Longman\TelegramBot\Entities\ServerResponse;
use Longman\TelegramBot\Request;
use TelegramBot\InlineKeyboardPagination\InlineKeyboardPagination;

class TestCommand extends AdminCommand
{
    public function execute(): ServerResponse
    {
        $this->getCallbackQuery() && parse_str($this->getCallbackQuery()->getData(), $callback_data);
        $current_page = $callback_data['p'] ?? 1;
        $array = [1,2,3,4,5,6,7,8,9];

        $ikp = new InlineKeyboardPagination($array, 'test', $current_page, 1);
        $ikp->setMaxButtons(8, true);
        $ikp->setCallbackDataFormat('c={COMMAND}&p={NEW_PAGE}');

        $data = [
            'chat_id' => $this->getMessage() ? $this->getMessage()->getChat()->getId() : $this->getCallbackQuery()->getFrom()->getId(),
            'text' => $array[$current_page - 1],
            'reply_markup' => new InlineKeyboard(...[$ikp->getPagination()['keyboard']])
        ];

        if ($this->getCallbackQuery() && $message_id = $this->getCallbackQuery()->getMessage()->getMessageId()) {
            return Request::editMessageText($data + [
                'message_id' => $message_id
            ]);
        }

        return Request::sendMessage($data);
    }
}

sendPhoto from DB field

Hi everyone!
@noplanman thanks for your great work.
I have a small question but it's really hard to realize.
How can I use sendPhoto method instead of sendMessage?

In my table fruits I have a photo field. And I need to show photo from that field, unique for each ID (page) in pagination.
Thanks in advance!

<?php
namespace Longman\TelegramBot\Commands\UserCommands;

use Longman\TelegramBot\Commands\UserCommand;
use Longman\TelegramBot\DB;
use PDO;
use Longman\TelegramBot\Entities\CallbackQuery;
use Longman\TelegramBot\Request;
use TelegramBot\InlineKeyboardPagination\Exceptions\InlineKeyboardPaginationException;
use TelegramBot\InlineKeyboardPagination\InlineKeyboardPagination;

class FruitsCommand extends UserCommand
{
    protected $name = 'fruits';
    protected $description = 'Display fruits, with inline pagination.';
    protected $usage = '/fruits';
    protected $version = '1.0.0';
    protected static $per_page = 1;

    public static function callbackHandler(CallbackQuery $query)
    {
        $params = InlineKeyboardPagination::getParametersFromCallbackData($query->getData());
        if ($params['command'] !== 'fruits') {
            return null;
        }

        $data = [
            'chat_id'    => $query->getMessage()->getChat()->getId(),
            'message_id' => $query->getMessage()->getMessageId(),
            'text'       => 'Empty',
        ];

        // Using pagination
        if ($pagination = self::getInlineKeyboardPagination($params['newPage'])) {
            $data['text']         = self::getPaginationContent($pagination['items']);
            $data['reply_markup'] = [
                'inline_keyboard' => [$pagination['keyboard']],
            ];
        }

        return Request::editMessageText($data);
    }

    public static function getFruits()
    {
        return DB::getPdo()->query('SELECT * FROM `fruits`')->fetchAll(PDO::FETCH_ASSOC);
    }

    public static function getPaginationContent(array $items)
    {
        $text = '';

        foreach ($items as $row) {
            $text .= "id: {$row['id']}\n";
            $text .= "name: {$row['name']}\n";
            $text .= "message: {$row['message']}\n";
        }

        return $text;
    }

    public static function getInlineKeyboardPagination($page = 1)
    {
        $fruits   = self::getFruits();

        if (empty($fruits)) {
            return null;
        }

        // Define inline keyboard pagination.
        $ikp = new InlineKeyboardPagination($fruits, 'fruits', $page, self::$per_page);

        // If item count changes, take wrong page clicks into account.
        try {
            $pagination = $ikp->getPagination();
        } catch (InlineKeyboardPaginationException $e) {
            $pagination = $ikp->getPagination(1);
        }

        return $pagination;
    }

    public function execute()
    {
        $data = [
            'chat_id' => $this->getMessage()->getChat()->getId(),
            'text'    => 'Empty',
        ];

        if ($pagination = self::getInlineKeyboardPagination(1)) {
            $data['text']         = self::getPaginationContent($pagination['items']);
            $data['reply_markup'] = [
                'inline_keyboard' => [$pagination['keyboard']],
            ];
        }

        return Request::sendMessage($data);
    }
}

how to configure InlineKeyboardPagination

❓ Support Question

MY custom comand

<?php

namespace Longman\TelegramBot\Commands\SystemCommand; 
use Longman\TelegramBot\Commands\SystemCommand;
use Longman\TelegramBot\Entities\ServerResponse;
use Longman\TelegramBot\Exception\TelegramException; 
use TelegramBot\InlineKeyboardPagination\InlineKeyboardPagination;

use Longman\TelegramBot\Entities\InlineKeyboard; 
class PostCommand extends SystemCommand  
{
    /**
     * @var string
     */
    protected $name = 'post';

    /**
     * @var string
     */
    protected $description = 'new command smf forum';

    /**
     * @var string
     */
    protected $usage = '/post <text>';

    /**
     * @var string
     */
    protected $version = '1.2.0';

    /**
     * Main command execution
     *
     * @return ServerResponse
     * @throws TelegramException
     */
    public function execute(): ServerResponse
    {
        $message = $this->getMessage();
        $text    = $message->getText(true);
         
        $items         = range(1, 100); // required. 
        $command       = 'post'; // optional. Default: pagination
        $selected_page = 10;            // optional. Default: 1
        $labels        = [              // optional. Change button labels (showing defaults)
            'default'  => '%d',
            'first'    => '« %d',
            'previous' => '‹ %d',
            'current'  => '· %d ·',
            'next'     => '%d ›',
            'last'     => '%d »',
        ];

        // optional. Change the callback_data format, adding placeholders for data (showing default)
        $callback_data_format = 'command={COMMAND}&oldPage={OLD_PAGE}&newPage={NEW_PAGE}';
         // Define inline keyboard pagination.
        $ikp = new InlineKeyboardPagination($items, $command);
        $ikp->setMaxButtons(7, true); // Second parameter set to always show 7 buttons if possible.
        $ikp->setLabels($labels);
        $ikp->setCallbackDataFormat($callback_data_format);
        $ikp->setSelectedPage($selected_page);
        $pagination = $ikp->getPagination(); 
 
        if (!empty($pagination['keyboard'])) {
             
            return $this->replyToChat('Inline Keyboard', [
                'reply_markup' =>[ 'inline_keyboard' => [
                    $pagination['keyboard'],
                ]],
            ]);
        } 
    }
}

resulto ok.
20210418014307646

but, when press buttom not change any value

20210418014447983

what happen ):

Pagination writes an error when new callbackHandler added

Hi!
I added my new callback handler in hook.php:

CallbackqueryCommand::addCallbackHandler(function (CallbackQuery $query) use ($telegram) {
        if ($query->getData() === 'gostart') {
            return Request::editMessageText([
                'chat_id' => $query->getMessage()->getChat()->getId(),
                'message_id' => $query->getMessage()->getMessageId(),
                'text' => $query->getData(),
                'reply_markup' => AboutCommand::getMyInlineKeyboard(),
            ]);
        }
    });

And! If I click on any InlineKeyboardButton in my bot (no matter in which command): I've get an error in log:

[:error] [pid 7455] [client 149.154.167.216:55200] PHP Notice:  Undefined index: command in /var/www/www-root/data/www/_/Commands/UserCommands/PortfolioCommand.php on line 39

Okay, look at my PortfolioCommand (thanks @noplanman )

class PortfolioCommand extends UserCommand
{
    ...

    public static function callbackHandler(CallbackQuery $query)
    {
        $params = InlineKeyboardPagination::getParametersFromCallbackData($query->getData());
        if ($params['command'] !== 'portfolio') { // 39 line. Error is here
            return null;
        }

        $data = [
            'chat_id'    => $query->getMessage()->getChat()->getId(),
            'message_id' => $query->getMessage()->getMessageId(),
            'text'       => 'Empty',
            'parse_mode' => 'Markdown',
        ];

        // Using pagination
        if ($pagination = self::getInlineKeyboardPagination($params['newPage'])) {
            $getData = DB::getPdo()->query("SELECT * FROM `portfolio` WHERE `id` = '" . intval($params['newPage']) . "' LIMIT 1")->fetch();
            $data['text']         = self::getPaginationContent($pagination['items']);
            $data['reply_markup'] = [
                'inline_keyboard' => [
                    [
                        ['text' => '🤖 Open', 'url' => 'https://google.com'],
                        ['text' => '📖 More...', 'url' => $getData['link']]
                    ],
                    $pagination['keyboard'],
                ],
            ];
        }

        return Request::editMessageText($data);
    }
}

I tried to fix that:

if (isset($params['command']) && $params['command'] !== 'portfolio') {
            return null;
}

And get 2 new errors:

[:error] [pid 7735] [client 149.154.167.216:56227] PHP Notice:  Undefined index: newPage in /var/www/www-root/data/www/_/Commands/UserCommands/PortfolioCommand.php on line 51

And

PHP Fatal error:  Uncaught TypeError: Argument 3 passed to TelegramBot\\InlineKeyboardPagination\\InlineKeyboardPagination::__construct() must be of the type integer, null given, called in /var/www/www-root/data/www/_/Commands/UserCommands/PortfolioCommand.php on line 97 and defined in /var/www/www-root/data/www/_/vendor/php-telegram-bot/inline-keyboard-pagination/src/InlineKeyboardPagination.php:211
Stack trace:
#0 /var/www/www-root/data/www/_/Commands/UserCommands/PortfolioCommand.php(97): TelegramBot\\InlineKeyboardPagination\\InlineKeyboardPagination->__construct(Array, 'portfolio', NULL, 1)
#1 /var/www/www-root/data/www/_/Commands/UserCommands/PortfolioCommand.php(51): Longman\\TelegramBot\\Commands\\UserCommands\\PortfolioCommand::getInlineKeyboardPagination(NULL)
#2 /var/www/www-root/data/www/_/vendor/longman/telegram-bot/src/Commands/SystemCommands/CallbackqueryCommand.php(56): Longman\\TelegramBot\\Commands\\UserCommands\\PortfolioCommand::callbackHandler(Object(Lon in /var/www/www-root/data/www/_/vendor/php-telegram-bot/inline-keyboard-pagination/src/InlineKeyboardPagination.php on line 211

How can I fix that? Thanks in advance!

Optimize code and get data from Item ID

Hi.

I have a two questions / problems:

  1. How can I list data from table with WHERE parameter? For example, I don't need to list all data from inventory table, but I need to list data from inventory table with user_id = $user_id.
    I tried to add a parameter $user_id to a getInventory() method, but it will not work...

  2. How can get an item ID (not a page ID). For example. at the page 1 user has item with ID: 150. I need to use that ID in a callback_data (look at my buttons :))
    But I really don't know, how can I make that.

Thanks a lot in advance.

<?php
...

class InventoryCommand extends UserCommand
{
    protected $name = 'inventory';
    protected $description = 'Inventory';
    protected $usage = '/inventory';
    protected $version = '1.0.0';
    protected $need_mysql = true;
    protected $private_only = true;
    protected static $per_page = 1;
    protected $conversation;

    public static function callbackHandler(CallbackQuery $query)
    {
        $params = InlineKeyboardPagination::getParametersFromCallbackData($query->getData());
        if (!isset($params['command']) || $params['command'] !== 'inventory') {
            return null;
        }

        $data = [
            'chat_id'    => $query->getMessage()->getChat()->getId(),
            'message_id' => $query->getMessage()->getMessageId(),
            'text'       => 'Empty',
            'parse_mode' => 'Markdown',
        ];

        // Using pagination
        if ($pagination = self::getInlineKeyboardPagination($params['newPage'])) {
            $data['text']         = self::getPaginationContent($pagination['items']);
            $data['reply_markup'] = [
                'inline_keyboard' => [
                    [
                        ['text' => '👕 Put on', 'callback_data' => 'putOn_<ITEM_ID>'],
                        ['text' => '❌ Drop', 'callback_data' => 'drop_<ITEM_ID>']
                    ],
                    $pagination['keyboard'],
                ],
            ];
        }

        return Request::editMessageText($data);
    }

    public static function getInventory($user_id)
    {
        return DB::getPdo()->query("SELECT * FROM `inventory` WHERE `user_id` = {$user_id}")->fetchAll(\PDO::FETCH_ASSOC);
    }

    public static function getPaginationContent(array $items)
    {
        $text = '';

        foreach ($items as $row) {
            $text .= "👕 Item: *{$row['name']}*\n\n";
            $text .= "Price: *{$row['price']}$*\n\n";
        }

        return $text;
    }

    public static function getInlineKeyboardPagination($page = 1)
    {
        $inventory   = self::getInventory($user_id);

        if (empty($inventory)) {
            return null;
        }

        // Define inline keyboard pagination.
        $ikp = new InlineKeyboardPagination($inventory, 'inventory', $page, self::$per_page);

        // If item count changes, take wrong page clicks into account.
        try {
            $pagination = $ikp->getPagination();
        } catch (InlineKeyboardPaginationException $e) {
            $pagination = $ikp->getPagination(1);
        }

        return $pagination;
    }

    /**
     * @return \Longman\TelegramBot\Entities\ServerResponse
     * @throws \Longman\TelegramBot\Exception\TelegramException
     */
    public function execute()
    {
        $message = $this->getMessage();
        $chat    = $message->getChat();
        $chat_id = $chat->getId();
        $user_id = $message->getFrom()->getId();
        $text    = trim($message->getText(true));

        $data = [
            'chat_id' => $chat_id,
            'parse_mode' => 'Markdown'
        ];

        $conversation = new Conversation($user_id, $chat_id, $this->getName());

        if ($text === '') {
            if ($pagination = self::getInlineKeyboardPagination(1)) {
                $data['text']         = self::getPaginationContent($pagination['items']);
                $data['reply_markup'] = [
                    'inline_keyboard' => [
                        [
                            ['text' => '👕 Put on', 'callback_data' => 'putOn_<ITEM_ID>'],
                            ['text' => '❌ Drop', 'callback_data' => 'drop_<ITEM_ID>']
                        ],
                        $pagination['keyboard'],
                    ],
                ];
            }

            return Request::sendMessage($data);
        }
    }
}

InlineKeyboard Buttons under the message

Hi!
How to make sure that each page has two buttons?
For example:

[MESSAGE: id: 1220, name: orange, count: 10]
[Contact]
[Review]
[PAGINATION]

When the user clicks on the button "Contact" or "Review", the handler starts. For contact: executeCommand() /contact and executeCommand() /review1220 for Review.

Is it hard to do this? :(

Thanks in advance!

Can't install & Run it !

Hello and thanks to code this prefect plugin !!

My php version is 7 and I installed and downloaded all contents from composer ( almost 12 Mb ! )

after that, I uploaded them in host and got them in my code with autoload.php

I write this in my code to do :

require_once('vendor/autoload.php');

$items         = range(1, 100);
$command       = 'testCommand'; 
$selected_page = 10;           
$labels        = [              
    'default'  => '%d',
    'first'    => '« %d',
    'previous' => '‹ %d',
    'current'  => '· %d ·',
    'next'     => '%d ›',
    'last'     => '%d »',
];

$callback_data_format = 'command={COMMAND}&oldPage={OLD_PAGE}&newPage={NEW_PAGE}'

$ikp = new InlineKeyboardPagination($items, $command);
$ikp->setMaxButtons(7, true);
$ikp->setLabels($labels);
$ikp->setCallbackDataFormat($callback_data_format);

$pagination = $ikp->getPagination($selected_page);
$ikp->setSelectedPage($selected_page);
$pagination = $ikp->getPagination();

if (!empty($pagination['keyboard'])) {
telegram_sendmessage( $USERID, 'TESTing ...', $pagination['keyboard'] );	
}

But action doesn't work and I get this error in error.log :

PHP Parse error: syntax error, unexpected '$ikp' (T_VARIABLE) in ...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.