diff --git a/CHANGELOG.md b/CHANGELOG.md index 179439158..009cb71e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ## [0.41.0] - 2017-03-25 ### Added -- `$showInHelp` attribute for commands, to set if it should be displayed in the `/help` command. +- `$show_in_help` attribute for commands, to set if it should be displayed in the `/help` command. - Link to new Telegram group: `https://telegram.me/PHP_Telegram_Bot_Support` - Introduce change log. diff --git a/src/Entities/InlineKeyboard.php b/src/Entities/InlineKeyboard.php index 1981abb69..bad50175f 100644 --- a/src/Entities/InlineKeyboard.php +++ b/src/Entities/InlineKeyboard.php @@ -17,4 +17,100 @@ */ class InlineKeyboard extends Keyboard { + /** + * Get an inline pagination keyboard. + * + * - $callback_data is an ID for the CallbackqueryCommand, to know where the request comes from. + * The ID is automatically appended with '_page_%d' to filter out the selected page number. + * + * - $labels allows for custom button labels, using '%d' placeholders. + * Default: + * ``` + * [ + * 'first' => '« %d', + * 'previous' => '‹ %d', + * 'current' => '· %d ·', + * 'next' => '%d ›', + * 'last' => '%d »', + * ] + * `` + *` + * initial idea from: https://stackoverflow.com/a/42879866 + * + * @param string $callback_data + * @param int $current_page + * @param int $max_pages + * @param array $labels + * + * @return \Longman\TelegramBot\Entities\InlineKeyboard + */ + public static function getPagination($callback_data, $current_page, $max_pages, array $labels = []) + { + $callback_data .= '_page_%d'; + + // Merge labels with defaults. + $labels = array_merge([ + 'first' => '« %d', + 'previous' => '‹ %d', + 'current' => '· %d ·', + 'next' => '%d ›', + 'last' => '%d »', + ], $labels); + $pages = [ + 'first' => 1, + 'previous' => $current_page - 1, + 'current' => $current_page, + 'next' => $current_page + 1, + 'last' => $max_pages, + ]; + + // Set labels for keyboard, replacing placeholders with page numbers. + foreach ($labels as $key => &$label) { + if (strpos($label, '%d') !== false) { + $label = sprintf($label, $pages[$key]); + } elseif ($label === '') { + $label = null; + } + } + unset($label); + + $callbacks_data = []; + foreach ($pages as $key => $page) { + $callbacks_data[$key] = sprintf($callback_data, $page); + } + + $buttons = []; + + if ($current_page > 1 && $labels['first'] !== null) { + $buttons[] = new InlineKeyboardButton(['text' => $labels['first'], 'callback_data' => $callbacks_data['first']]); + } + if ($current_page > 2 && $labels['previous'] !== null) { + $buttons[] = new InlineKeyboardButton(['text' => $labels['previous'], 'callback_data' => $callbacks_data['previous']]); + } + + if ($labels['current'] !== null) { + $buttons[] = new InlineKeyboardButton(['text' => $labels['current'], 'callback_data' => $callbacks_data['current']]); + } + + if ($current_page < $max_pages - 1 && $labels['next'] !== null) { + $buttons[] = new InlineKeyboardButton(['text' => $labels['next'], 'callback_data' => $callbacks_data['next']]); + } + if ($current_page < $max_pages && $labels['last'] !== null) { + $buttons[] = new InlineKeyboardButton(['text' => $labels['last'], 'callback_data' => $callbacks_data['last']]); + } + + return new InlineKeyboard($buttons); + } + + /** + * Extract the page number from the passed callback data. + * + * @param string $callback_data + * + * @return int + */ + public static function getPageFromCallbackData($callback_data) + { + return (int) preg_replace('/.*_page_(\d+)$/', '$1', $callback_data); + } } diff --git a/src/Entities/InlineKeyboardPaginator.php b/src/Entities/InlineKeyboardPaginator.php new file mode 100644 index 000000000..628526c1a --- /dev/null +++ b/src/Entities/InlineKeyboardPaginator.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Longman\TelegramBot\Entities; + +interface InlineKeyboardPaginator +{ + /** + * A unique identifier for the callback query. + * + * @return string + */ + public static function getCallbackDataId(); + + /** + * Get the output for the currently selected page. + * + * @param int $current_page + * + * @return string + */ + public static function getOutput($current_page); + + /** + * Get the pagination for the current page. + * + * @param int $current_page + * + * @return InlineKeyboard + */ + public static function getPagination($current_page); +} diff --git a/src/Telegram.php b/src/Telegram.php index 0d3979d56..862fd9fbf 100644 --- a/src/Telegram.php +++ b/src/Telegram.php @@ -373,7 +373,7 @@ public function handle() * * @return string */ - private function getCommandFromType($type) + protected function getCommandFromType($type) { return $this->ucfirstUnicode(str_replace('_', '', $type)); } diff --git a/tests/unit/Entities/InlineKeyboardTest.php b/tests/unit/Entities/InlineKeyboardTest.php index 0f738355a..cc4c1196c 100644 --- a/tests/unit/Entities/InlineKeyboardTest.php +++ b/tests/unit/Entities/InlineKeyboardTest.php @@ -54,51 +54,60 @@ public function testInlineKeyboardDataMalformedSubfield() public function testInlineKeyboardSingleButtonSingleRow() { - $inline_keyboard = (new InlineKeyboard( + $keyboard = new InlineKeyboard( $this->getRandomButton('Button Text 1') - ))->getProperty('inline_keyboard'); - self::assertSame('Button Text 1', $inline_keyboard[0][0]->getText()); + ); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['Button Text 1'], + ], 'text', $keyboard); - $inline_keyboard = (new InlineKeyboard( + $keyboard = new InlineKeyboard( [$this->getRandomButton('Button Text 2')] - ))->getProperty('inline_keyboard'); - self::assertSame('Button Text 2', $inline_keyboard[0][0]->getText()); + ); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['Button Text 2'], + ], 'text', $keyboard); } public function testInlineKeyboardSingleButtonMultipleRows() { - $keyboard = (new InlineKeyboard( + $keyboard = new InlineKeyboard( $this->getRandomButton('Button Text 1'), $this->getRandomButton('Button Text 2'), $this->getRandomButton('Button Text 3') - ))->getProperty('inline_keyboard'); - self::assertSame('Button Text 1', $keyboard[0][0]->getText()); - self::assertSame('Button Text 2', $keyboard[1][0]->getText()); - self::assertSame('Button Text 3', $keyboard[2][0]->getText()); + ); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['Button Text 1'], + ['Button Text 2'], + ['Button Text 3'], + ], 'text', $keyboard); - $keyboard = (new InlineKeyboard( + $keyboard = new InlineKeyboard( [$this->getRandomButton('Button Text 4')], [$this->getRandomButton('Button Text 5')], [$this->getRandomButton('Button Text 6')] - ))->getProperty('inline_keyboard'); - self::assertSame('Button Text 4', $keyboard[0][0]->getText()); - self::assertSame('Button Text 5', $keyboard[1][0]->getText()); - self::assertSame('Button Text 6', $keyboard[2][0]->getText()); + ); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['Button Text 4'], + ['Button Text 5'], + ['Button Text 6'], + ], 'text', $keyboard); } public function testInlineKeyboardMultipleButtonsSingleRow() { - $keyboard = (new InlineKeyboard([ + $keyboard = new InlineKeyboard([ $this->getRandomButton('Button Text 1'), $this->getRandomButton('Button Text 2'), - ]))->getProperty('inline_keyboard'); - self::assertSame('Button Text 1', $keyboard[0][0]->getText()); - self::assertSame('Button Text 2', $keyboard[0][1]->getText()); + ]); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['Button Text 1', 'Button Text 2'], + ], 'text', $keyboard); } public function testInlineKeyboardMultipleButtonsMultipleRows() { - $keyboard = (new InlineKeyboard( + $keyboard = new InlineKeyboard( [ $this->getRandomButton('Button Text 1'), $this->getRandomButton('Button Text 2'), @@ -107,32 +116,104 @@ public function testInlineKeyboardMultipleButtonsMultipleRows() $this->getRandomButton('Button Text 3'), $this->getRandomButton('Button Text 4'), ] - ))->getProperty('inline_keyboard'); + ); - self::assertSame('Button Text 1', $keyboard[0][0]->getText()); - self::assertSame('Button Text 2', $keyboard[0][1]->getText()); - self::assertSame('Button Text 3', $keyboard[1][0]->getText()); - self::assertSame('Button Text 4', $keyboard[1][1]->getText()); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['Button Text 1', 'Button Text 2'], + ['Button Text 3', 'Button Text 4'], + ], 'text', $keyboard); } public function testInlineKeyboardAddRows() { - $keyboard_obj = new InlineKeyboard([]); + $keyboard = new InlineKeyboard([]); - $keyboard_obj->addRow($this->getRandomButton('Button Text 1')); - $keyboard = $keyboard_obj->getProperty('inline_keyboard'); - self::assertSame('Button Text 1', $keyboard[0][0]->getText()); + $keyboard->addRow($this->getRandomButton('Button Text 1')); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['Button Text 1'], + ], 'text', $keyboard); - $keyboard_obj->addRow( + $keyboard->addRow( $this->getRandomButton('Button Text 2'), $this->getRandomButton('Button Text 3') ); - $keyboard = $keyboard_obj->getProperty('inline_keyboard'); - self::assertSame('Button Text 2', $keyboard[1][0]->getText()); - self::assertSame('Button Text 3', $keyboard[1][1]->getText()); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['Button Text 1'], + ['Button Text 2', 'Button Text 3'], + ], 'text', $keyboard); + + $keyboard->addRow($this->getRandomButton('Button Text 4')); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['Button Text 1'], + ['Button Text 2', 'Button Text 3'], + ['Button Text 4'], + ], 'text', $keyboard); + } - $keyboard_obj->addRow($this->getRandomButton('Button Text 4')); - $keyboard = $keyboard_obj->getProperty('inline_keyboard'); - self::assertSame('Button Text 4', $keyboard[2][0]->getText()); + public function testInlineKeyboardPagination() + { + // Should get '_page_%d' appended to it. + $callback_data = 'cbdata'; + + // current + $keyboard = InlineKeyboard::getPagination($callback_data, 1, 1); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['· 1 ·'], + ], 'text', $keyboard); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['cbdata_page_1'], + ], 'callback_data', $keyboard); + + // current, next, last + $keyboard = InlineKeyboard::getPagination($callback_data, 1, 10); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['· 1 ·', '2 ›', '10 »'], + ], 'text', $keyboard); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['cbdata_page_1', 'cbdata_page_2', 'cbdata_page_10'], + ], 'callback_data', $keyboard); + + // first, previous, current, next, last + $keyboard = InlineKeyboard::getPagination($callback_data, 5, 10); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['« 1', '‹ 4', '· 5 ·', '6 ›', '10 »'], + ], 'text', $keyboard); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['cbdata_page_1', 'cbdata_page_4', 'cbdata_page_5', 'cbdata_page_6', 'cbdata_page_10'], + ], 'callback_data', $keyboard); + + // first, previous, current, last + $keyboard = InlineKeyboard::getPagination($callback_data, 9, 10); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['« 1', '‹ 8', '· 9 ·', '10 »'], + ], 'text', $keyboard); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['cbdata_page_1', 'cbdata_page_8', 'cbdata_page_9', 'cbdata_page_10'], + ], 'callback_data', $keyboard); + + // first, previous, current + $keyboard = InlineKeyboard::getPagination($callback_data, 10, 10); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['« 1', '‹ 9', '· 10 ·'], + ], 'text', $keyboard); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['cbdata_page_1', 'cbdata_page_9', 'cbdata_page_10'], + ], 'callback_data', $keyboard); + + // custom labels, skipping some buttons + // first, previous, current, next, last + $keyboard = InlineKeyboard::getPagination($callback_data, 5, 10, [ + 'first' => '', + 'previous' => 'previous %d', + 'current' => null, + 'next' => '%d next', + 'last' => '%d last', + ]); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['previous 4', '6 next', '10 last'], + ], 'text', $keyboard); + KeyboardTest::assertAllButtonPropertiesEqual([ + ['cbdata_page_4', 'cbdata_page_6', 'cbdata_page_10'], + ], 'callback_data', $keyboard); } } diff --git a/tests/unit/Entities/KeyboardTest.php b/tests/unit/Entities/KeyboardTest.php index bf92f6bdf..c2c95538e 100644 --- a/tests/unit/Entities/KeyboardTest.php +++ b/tests/unit/Entities/KeyboardTest.php @@ -22,6 +22,27 @@ */ class KeyboardTest extends TestCase { + public static function assertButtonPropertiesEqual($value, $property, $keyboard, $row, $column, $message = '') + { + self::assertSame($value, $keyboard->getProperty($keyboard->getKeyboardType())[$row][$column]->getProperty($property), $message); + } + + public static function assertRowButtonPropertiesEqual(array $values, $property, $keyboard, $row, $message = '') + { + $column = 0; + foreach ($values as $value) { + self::assertButtonPropertiesEqual($value, $property, $keyboard, $row, $column++, $message); + } + } + + public static function assertAllButtonPropertiesEqual(array $all_values, $property, $keyboard, $message = '') + { + $row = 0; + foreach ($all_values as $values) { + self::assertRowButtonPropertiesEqual($values, $property, $keyboard, $row++, $message); + } + } + /** * @expectedException \Longman\TelegramBot\Exception\TelegramException * @expectedExceptionMessage keyboard field is not an array! @@ -42,96 +63,109 @@ public function testKeyboardDataMalformedSubfield() public function testKeyboardSingleButtonSingleRow() { - $keyboard = (new Keyboard('Button Text 1'))->getProperty('keyboard'); - self::assertSame('Button Text 1', $keyboard[0][0]->getText()); - - $keyboard = (new Keyboard(['Button Text 2']))->getProperty('keyboard'); - self::assertSame('Button Text 2', $keyboard[0][0]->getText()); + $keyboard = new Keyboard('Button Text 1'); + self::assertAllButtonPropertiesEqual([ + ['Button Text 1'], + ], 'text', $keyboard); + + $keyboard = new Keyboard(['Button Text 2']); + self::assertAllButtonPropertiesEqual([ + ['Button Text 2'], + ], 'text', $keyboard); } public function testKeyboardSingleButtonMultipleRows() { - $keyboard = (new Keyboard( + $keyboard = new Keyboard( 'Button Text 1', 'Button Text 2', 'Button Text 3' - ))->getProperty('keyboard'); - self::assertSame('Button Text 1', $keyboard[0][0]->getText()); - self::assertSame('Button Text 2', $keyboard[1][0]->getText()); - self::assertSame('Button Text 3', $keyboard[2][0]->getText()); - - $keyboard = (new Keyboard( + ); + self::assertAllButtonPropertiesEqual([ + ['Button Text 1'], + ['Button Text 2'], + ['Button Text 3'], + ], 'text', $keyboard); + + $keyboard = new Keyboard( ['Button Text 4'], ['Button Text 5'], ['Button Text 6'] - ))->getProperty('keyboard'); - self::assertSame('Button Text 4', $keyboard[0][0]->getText()); - self::assertSame('Button Text 5', $keyboard[1][0]->getText()); - self::assertSame('Button Text 6', $keyboard[2][0]->getText()); + ); + self::assertAllButtonPropertiesEqual([ + ['Button Text 4'], + ['Button Text 5'], + ['Button Text 6'], + ], 'text', $keyboard); } public function testKeyboardMultipleButtonsSingleRow() { - $keyboard = (new Keyboard(['Button Text 1', 'Button Text 2']))->getProperty('keyboard'); - self::assertSame('Button Text 1', $keyboard[0][0]->getText()); - self::assertSame('Button Text 2', $keyboard[0][1]->getText()); + $keyboard = new Keyboard(['Button Text 1', 'Button Text 2']); + self::assertAllButtonPropertiesEqual([ + ['Button Text 1', 'Button Text 2'], + ], 'text', $keyboard); } public function testKeyboardMultipleButtonsMultipleRows() { - $keyboard = (new Keyboard( + $keyboard = new Keyboard( ['Button Text 1', 'Button Text 2'], ['Button Text 3', 'Button Text 4'] - ))->getProperty('keyboard'); - - self::assertSame('Button Text 1', $keyboard[0][0]->getText()); - self::assertSame('Button Text 2', $keyboard[0][1]->getText()); - self::assertSame('Button Text 3', $keyboard[1][0]->getText()); - self::assertSame('Button Text 4', $keyboard[1][1]->getText()); + ); + self::assertAllButtonPropertiesEqual([ + ['Button Text 1', 'Button Text 2'], + ['Button Text 3', 'Button Text 4'], + ], 'text', $keyboard); } public function testKeyboardWithButtonObjects() { - $keyboard = (new Keyboard( + $keyboard = new Keyboard( new KeyboardButton('Button Text 1') - ))->getProperty('keyboard'); - self::assertSame('Button Text 1', $keyboard[0][0]->getText()); + ); + self::assertAllButtonPropertiesEqual([ + ['Button Text 1'], + ], 'text', $keyboard); - $keyboard = (new Keyboard( + $keyboard = new Keyboard( new KeyboardButton('Button Text 2'), new KeyboardButton('Button Text 3') - ))->getProperty('keyboard'); - self::assertSame('Button Text 2', $keyboard[0][0]->getText()); - self::assertSame('Button Text 3', $keyboard[1][0]->getText()); + ); + self::assertAllButtonPropertiesEqual([ + ['Button Text 2'], + ['Button Text 3'], + ], 'text', $keyboard); - $keyboard = (new Keyboard( + $keyboard = new Keyboard( [new KeyboardButton('Button Text 4')], [new KeyboardButton('Button Text 5'), new KeyboardButton('Button Text 6')] - ))->getProperty('keyboard'); - self::assertSame('Button Text 4', $keyboard[0][0]->getText()); - self::assertSame('Button Text 5', $keyboard[1][0]->getText()); - self::assertSame('Button Text 6', $keyboard[1][1]->getText()); + ); + self::assertAllButtonPropertiesEqual([ + ['Button Text 4'], + ['Button Text 5', 'Button Text 6'], + ], 'text', $keyboard); } public function testKeyboardWithDataArray() { - $resize_keyboard = (bool)mt_rand(0, 1); - $one_time_keyboard = (bool)mt_rand(0, 1); - $selective = (bool)mt_rand(0, 1); + $resize_keyboard = (bool) mt_rand(0, 1); + $one_time_keyboard = (bool) mt_rand(0, 1); + $selective = (bool) mt_rand(0, 1); - $keyboard_obj = new Keyboard([ + $keyboard = new Keyboard([ 'resize_keyboard' => $resize_keyboard, 'one_time_keyboard' => $one_time_keyboard, 'selective' => $selective, 'keyboard' => [['Button Text 1']], ]); + self::assertAllButtonPropertiesEqual([ + ['Button Text 1'], + ], 'text', $keyboard); - $keyboard = $keyboard_obj->getProperty('keyboard'); - self::assertSame('Button Text 1', $keyboard[0][0]->getText()); - - self::assertSame($resize_keyboard, $keyboard_obj->getResizeKeyboard()); - self::assertSame($one_time_keyboard, $keyboard_obj->getOneTimeKeyboard()); - self::assertSame($selective, $keyboard_obj->getSelective()); + self::assertSame($resize_keyboard, $keyboard->getResizeKeyboard()); + self::assertSame($one_time_keyboard, $keyboard->getOneTimeKeyboard()); + self::assertSame($selective, $keyboard->getSelective()); } public function testPredefinedKeyboards() @@ -145,43 +179,48 @@ public function testPredefinedKeyboards() public function testKeyboardMethods() { - $keyboard_obj = new Keyboard([]); - - self::assertEmpty($keyboard_obj->getOneTimeKeyboard()); - self::assertEmpty($keyboard_obj->getResizeKeyboard()); - self::assertEmpty($keyboard_obj->getSelective()); - - $keyboard_obj->setOneTimeKeyboard(true); - self::assertTrue($keyboard_obj->getOneTimeKeyboard()); - $keyboard_obj->setOneTimeKeyboard(false); - self::assertFalse($keyboard_obj->getOneTimeKeyboard()); - - $keyboard_obj->setResizeKeyboard(true); - self::assertTrue($keyboard_obj->getResizeKeyboard()); - $keyboard_obj->setResizeKeyboard(false); - self::assertFalse($keyboard_obj->getResizeKeyboard()); - - $keyboard_obj->setSelective(true); - self::assertTrue($keyboard_obj->getSelective()); - $keyboard_obj->setSelective(false); - self::assertFalse($keyboard_obj->getSelective()); + $keyboard = new Keyboard([]); + + self::assertEmpty($keyboard->getOneTimeKeyboard()); + self::assertEmpty($keyboard->getResizeKeyboard()); + self::assertEmpty($keyboard->getSelective()); + + $keyboard->setOneTimeKeyboard(true); + self::assertTrue($keyboard->getOneTimeKeyboard()); + $keyboard->setOneTimeKeyboard(false); + self::assertFalse($keyboard->getOneTimeKeyboard()); + + $keyboard->setResizeKeyboard(true); + self::assertTrue($keyboard->getResizeKeyboard()); + $keyboard->setResizeKeyboard(false); + self::assertFalse($keyboard->getResizeKeyboard()); + + $keyboard->setSelective(true); + self::assertTrue($keyboard->getSelective()); + $keyboard->setSelective(false); + self::assertFalse($keyboard->getSelective()); } public function testKeyboardAddRows() { - $keyboard_obj = new Keyboard([]); - - $keyboard_obj->addRow('Button Text 1'); - $keyboard = $keyboard_obj->getProperty('keyboard'); - self::assertSame('Button Text 1', $keyboard[0][0]->getText()); - - $keyboard_obj->addRow('Button Text 2', 'Button Text 3'); - $keyboard = $keyboard_obj->getProperty('keyboard'); - self::assertSame('Button Text 2', $keyboard[1][0]->getText()); - self::assertSame('Button Text 3', $keyboard[1][1]->getText()); - - $keyboard_obj->addRow(['text' => 'Button Text 4']); - $keyboard = $keyboard_obj->getProperty('keyboard'); - self::assertSame('Button Text 4', $keyboard[2][0]->getText()); + $keyboard = new Keyboard([]); + + $keyboard->addRow('Button Text 1'); + self::assertAllButtonPropertiesEqual([ + ['Button Text 1'], + ], 'text', $keyboard); + + $keyboard->addRow('Button Text 2', 'Button Text 3'); + self::assertAllButtonPropertiesEqual([ + ['Button Text 1'], + ['Button Text 2', 'Button Text 3'], + ], 'text', $keyboard); + + $keyboard->addRow(['text' => 'Button Text 4']); + self::assertAllButtonPropertiesEqual([ + ['Button Text 1'], + ['Button Text 2', 'Button Text 3'], + ['Button Text 4'], + ], 'text', $keyboard); } }