Skip to content

Commit 8b18496

Browse files
authored
Merge pull request #1066 from noplanman/1049_bot_api_4.6
Bot API 4.6
2 parents 229d884 + 265fe50 commit 8b18496

File tree

14 files changed

+246
-42
lines changed

14 files changed

+246
-42
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Exclamation symbols (:exclamation:) note something of importance e.g. breaking c
1010
- Bot API 4.5 (Unique file IDs, MarkdownV2). (#1046)
1111
- Chain the exception thrown when getting commands from path. (#1030)
1212
- Support `language_code` in `DB::selectChats()` for filtering the chats selection.
13+
- Bot API 4.6 (Polls 2.0).
1314
### Changed
1415
- Save notes an unescaped JSON, to allow easy DB reading and editing of values. (#1005)
1516
- `Request::setClient()` now accepts more flexible `ClientInterface`. (#1068)

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
A Telegram Bot based on the official [Telegram Bot API]
99

10-
[![API Version](https://img.shields.io/badge/Bot%20API-4.5%20%28December%202019%29-32a2da.svg)](https://core.telegram.org/bots/api#december-31-2019)
10+
[![API Version](https://img.shields.io/badge/Bot%20API-4.6%20%28January%202020%29-32a2da.svg)](https://core.telegram.org/bots/api#december-31-2019)
1111
[![Join the bot support group on Telegram](https://img.shields.io/badge/telegram-@PHP__Telegram__Bot__Support-64659d.svg)](https://telegram.me/PHP_Telegram_Bot_Support)
1212
[![Donate](https://img.shields.io/badge/%F0%9F%92%99-Donate%20%2F%20Support%20Us-blue.svg)](#donate)
1313

@@ -76,7 +76,7 @@ This Bot aims to provide a platform where one can simply write a bot and have in
7676

7777
The Bot can:
7878
- Retrieve updates with [webhook](#webhook-installation) and [getUpdates](#getupdates-installation) methods.
79-
- Supports all types and methods according to Telegram API 4.4 (July 2019).
79+
- Supports all types and methods according to Telegram API 4.6 (January 2020).
8080
- Supports supergroups.
8181
- Handle commands in chat with other bots.
8282
- Manage Channel from the bot admin interface.

src/DB.php

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Longman\TelegramBot\Entities\Payments\PreCheckoutQuery;
2121
use Longman\TelegramBot\Entities\Payments\ShippingQuery;
2222
use Longman\TelegramBot\Entities\Poll;
23+
use Longman\TelegramBot\Entities\PollAnswer;
2324
use Longman\TelegramBot\Entities\Update;
2425
use Longman\TelegramBot\Entities\User;
2526
use Longman\TelegramBot\Exception\TelegramException;
@@ -145,6 +146,7 @@ protected static function defineTables()
145146
'message',
146147
'pre_checkout_query',
147148
'poll',
149+
'poll_answer',
148150
'request_limiter',
149151
'shipping_query',
150152
'telegram_update',
@@ -317,6 +319,7 @@ public static function entitiesArrayToJson($entities, $default = null)
317319
* @param string|null $shipping_query_id
318320
* @param string|null $pre_checkout_query_id
319321
* @param string|null $poll_id
322+
* @param string|null $poll_answer_poll_id
320323
*
321324
* @return bool If the insert was successful
322325
* @throws TelegramException
@@ -333,10 +336,11 @@ protected static function insertTelegramUpdate(
333336
$callback_query_id = null,
334337
$shipping_query_id = null,
335338
$pre_checkout_query_id = null,
336-
$poll_id = null
339+
$poll_id = null,
340+
$poll_answer_poll_id = null
337341
) {
338-
if ($message_id === null && $edited_message_id === null && $channel_post_id === null && $edited_channel_post_id === null && $inline_query_id === null && $chosen_inline_result_id === null && $callback_query_id === null && $shipping_query_id === null && $pre_checkout_query_id === null && $poll_id === null) {
339-
throw new TelegramException('message_id, edited_message_id, channel_post_id, edited_channel_post_id, inline_query_id, chosen_inline_result_id, callback_query_id, shipping_query_id, pre_checkout_query_id, poll_id are all null');
342+
if ($message_id === null && $edited_message_id === null && $channel_post_id === null && $edited_channel_post_id === null && $inline_query_id === null && $chosen_inline_result_id === null && $callback_query_id === null && $shipping_query_id === null && $pre_checkout_query_id === null && $poll_id === null && $poll_answer_poll_id === null) {
343+
throw new TelegramException('message_id, edited_message_id, channel_post_id, edited_channel_post_id, inline_query_id, chosen_inline_result_id, callback_query_id, shipping_query_id, pre_checkout_query_id, poll_id, poll_answer_poll_id are all null');
340344
}
341345

342346
if (!self::isDbConnected()) {
@@ -346,9 +350,9 @@ protected static function insertTelegramUpdate(
346350
try {
347351
$sth = self::$pdo->prepare('
348352
INSERT IGNORE INTO `' . TB_TELEGRAM_UPDATE . '`
349-
(`id`, `chat_id`, `message_id`, `edited_message_id`, `channel_post_id`, `edited_channel_post_id`, `inline_query_id`, `chosen_inline_result_id`, `callback_query_id`, `shipping_query_id`, `pre_checkout_query_id`, `poll_id`)
353+
(`id`, `chat_id`, `message_id`, `edited_message_id`, `channel_post_id`, `edited_channel_post_id`, `inline_query_id`, `chosen_inline_result_id`, `callback_query_id`, `shipping_query_id`, `pre_checkout_query_id`, `poll_id`, `poll_answer_poll_id`)
350354
VALUES
351-
(:id, :chat_id, :message_id, :edited_message_id, :channel_post_id, :edited_channel_post_id, :inline_query_id, :chosen_inline_result_id, :callback_query_id, :shipping_query_id, :pre_checkout_query_id, :poll_id)
355+
(:id, :chat_id, :message_id, :edited_message_id, :channel_post_id, :edited_channel_post_id, :inline_query_id, :chosen_inline_result_id, :callback_query_id, :shipping_query_id, :pre_checkout_query_id, :poll_id, :poll_answer_poll_id)
352356
');
353357

354358
$sth->bindValue(':id', $update_id);
@@ -363,6 +367,7 @@ protected static function insertTelegramUpdate(
363367
$sth->bindValue(':shipping_query_id', $shipping_query_id);
364368
$sth->bindValue(':pre_checkout_query_id', $pre_checkout_query_id);
365369
$sth->bindValue(':poll_id', $poll_id);
370+
$sth->bindValue(':poll_answer_poll_id', $poll_answer_poll_id);
366371

367372
return $sth->execute();
368373
} catch (PDOException $e) {
@@ -526,6 +531,7 @@ public static function insertRequest(Update $update)
526531
$shipping_query_id = null;
527532
$pre_checkout_query_id = null;
528533
$poll_id = null;
534+
$poll_answer_poll_id = null;
529535

530536
if (($message = $update->getMessage()) && self::insertMessageRequest($message)) {
531537
$chat_id = $message->getChat()->getId();
@@ -551,6 +557,8 @@ public static function insertRequest(Update $update)
551557
$pre_checkout_query_id = $pre_checkout_query->getId();
552558
} elseif (($poll = $update->getPoll()) && self::insertPollRequest($poll)) {
553559
$poll_id = $poll->getId();
560+
} elseif (($poll_answer = $update->getPollAnswer()) && self::insertPollAnswerRequest($poll_answer)) {
561+
$poll_answer_poll_id = $poll_answer->getPollId();
554562
} else {
555563
return false;
556564
}
@@ -567,7 +575,8 @@ public static function insertRequest(Update $update)
567575
$callback_query_id,
568576
$shipping_query_id,
569577
$pre_checkout_query_id,
570-
$poll_id
578+
$poll_id,
579+
$poll_answer_poll_id
571580
);
572581
}
573582

@@ -828,18 +837,28 @@ public static function insertPollRequest(Poll $poll)
828837
try {
829838
$sth = self::$pdo->prepare('
830839
INSERT INTO `' . TB_POLL . '`
831-
(`id`, `question`, `options`, `is_closed`, `created_at`)
840+
(`id`, `question`, `options`, `total_voter_count`, `is_closed`, `is_anonymous`, `type`, `allows_multiple_answers`, `correct_option_id`, `created_at`)
832841
VALUES
833-
(:id, :question, :options, :is_closed, :created_at)
842+
(:id, :question, :options, :total_voter_count, :is_closed, :is_anonymous, :type, :allows_multiple_answers, :correct_option_id, :created_at)
834843
ON DUPLICATE KEY UPDATE
835-
`options` = VALUES(`options`),
836-
`is_closed` = VALUES(`is_closed`)
844+
`options` = VALUES(`options`),
845+
`total_voter_count` = VALUES(`total_voter_count`),
846+
`is_closed` = VALUES(`is_closed`),
847+
`is_anonymous` = VALUES(`is_anonymous`),
848+
`type` = VALUES(`type`),
849+
`allows_multiple_answers` = VALUES(`allows_multiple_answers`),
850+
`correct_option_id` = VALUES(`correct_option_id`)
837851
');
838852

839853
$sth->bindValue(':id', $poll->getId());
840854
$sth->bindValue(':question', $poll->getQuestion());
841855
$sth->bindValue(':options', self::entitiesArrayToJson($poll->getOptions() ?: null));
856+
$sth->bindValue(':total_voter_count', $poll->getTotalVoterCount());
842857
$sth->bindValue(':is_closed', $poll->getIsClosed(), PDO::PARAM_INT);
858+
$sth->bindValue(':is_anonymous', $poll->getIsAnonymous(), PDO::PARAM_INT);
859+
$sth->bindValue(':type', $poll->getType());
860+
$sth->bindValue(':allows_multiple_answers', $poll->getAllowsMultipleAnswers(), PDO::PARAM_INT);
861+
$sth->bindValue(':correct_option_id', $poll->getCorrectOptionId());
843862
$sth->bindValue(':created_at', self::getTimestamp());
844863

845864
return $sth->execute();
@@ -848,6 +867,49 @@ public static function insertPollRequest(Poll $poll)
848867
}
849868
}
850869

870+
/**
871+
* Insert poll answer request into database
872+
*
873+
* @param PollAnswer $poll_answer
874+
*
875+
* @return bool If the insert was successful
876+
* @throws TelegramException
877+
*/
878+
public static function insertPollAnswerRequest(PollAnswer $poll_answer)
879+
{
880+
if (!self::isDbConnected()) {
881+
return false;
882+
}
883+
884+
try {
885+
$sth = self::$pdo->prepare('
886+
INSERT INTO `' . TB_POLL_ANSWER . '`
887+
(`poll_id`, `user_id`, `option_ids`, `created_at`)
888+
VALUES
889+
(:poll_id, :user_id, :option_ids, :created_at)
890+
ON DUPLICATE KEY UPDATE
891+
`option_ids` = VALUES(`option_ids`)
892+
');
893+
894+
$date = self::getTimestamp();
895+
$user_id = null;
896+
897+
if ($user = $poll_answer->getUser()) {
898+
$user_id = $user->getId();
899+
self::insertUser($user, $date);
900+
}
901+
902+
$sth->bindValue(':poll_id', $poll_answer->getPollId());
903+
$sth->bindValue(':user_id', $user_id);
904+
$sth->bindValue(':option_ids', json_encode($poll_answer->getOptionIds()));
905+
$sth->bindValue(':created_at', $date);
906+
907+
return $sth->execute();
908+
} catch (PDOException $e) {
909+
throw new TelegramException($e->getMessage());
910+
}
911+
}
912+
851913
/**
852914
* Insert Message request in db
853915
*

src/Entities/KeyboardButton.php

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,23 @@
1616
/**
1717
* Class KeyboardButton
1818
*
19+
* This object represents one button of the reply keyboard. For simple text buttons String can be used instead of this object to specify text of the button. Optional fields request_contact, request_location, and request_poll are mutually exclusive.
20+
*
1921
* @link https://core.telegram.org/bots/api#keyboardbutton
2022
*
21-
* @property bool $request_contact
22-
* @property bool $request_location
23+
* @property bool $request_contact
24+
* @property bool $request_location
25+
* @property KeyboardButtonPollType $request_poll
2326
*
24-
* @method string getText() Text of the button. If none of the optional fields are used, it will be sent to the bot as a message when the button is pressed
25-
* @method bool getRequestContact() Optional. If True, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only
26-
* @method bool getRequestLocation() Optional. If True, the user's current location will be sent when the button is pressed. Available in private chats only
27+
* @method string getText() Text of the button. If none of the optional fields are used, it will be sent to the bot as a message when the button is pressed
28+
* @method bool getRequestContact() Optional. If True, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only
29+
* @method bool getRequestLocation() Optional. If True, the user's current location will be sent when the button is pressed. Available in private chats only
30+
* @method KeyboardButtonPollType getRequestPoll() Optional. If specified, the user will be asked to create a poll and send it to the bot when the button is pressed. Available in private chats only
2731
*
28-
* @method $this setText(string $text) Text of the button. If none of the optional fields are used, it will be sent to the bot as a message when the button is pressed
29-
* @method $this setRequestContact(bool $request_contact) Optional. If True, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only
30-
* @method $this setRequestLocation(bool $request_location) Optional. If True, the user's current location will be sent when the button is pressed. Available in private chats only
32+
* @method $this setText(string $text) Text of the button. If none of the optional fields are used, it will be sent to the bot as a message when the button is pressed
33+
* @method $this setRequestContact(bool $request_contact) Optional. If True, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only
34+
* @method $this setRequestLocation(bool $request_location) Optional. If True, the user's current location will be sent when the button is pressed. Available in private chats only
35+
* @method $this setRequestPoll(KeyboardButtonPollType $request_poll) Optional. If specified, the user will be asked to create a poll and send it to the bot when the button is pressed. Available in private chats only
3136
*/
3237
class KeyboardButton extends Entity
3338
{
@@ -63,8 +68,14 @@ protected function validate()
6368
throw new TelegramException('You must add some text to the button!');
6469
}
6570

66-
if ($this->getRequestContact() && $this->getRequestLocation()) {
67-
throw new TelegramException('You must use only one of these fields: request_contact, request_location!');
71+
// Make sure only 1 of the optional request fields is set.
72+
$field_count = array_filter([
73+
$this->getRequestContact(),
74+
$this->getRequestLocation(),
75+
$this->getRequestPoll(),
76+
]);
77+
if (count($field_count) > 1) {
78+
throw new TelegramException('You must use only one of these fields: request_contact, request_location, request_poll!');
6879
}
6980
}
7081

@@ -74,8 +85,8 @@ protected function validate()
7485
public function __call($method, $args)
7586
{
7687
// Only 1 of these can be set, so clear the others when setting a new one.
77-
if (in_array($method, ['setRequestContact', 'setRequestLocation'], true)) {
78-
unset($this->request_contact, $this->request_location);
88+
if (in_array($method, ['setRequestContact', 'setRequestLocation', 'setRequestPoll'], true)) {
89+
unset($this->request_contact, $this->request_location, $this->request_poll);
7990
}
8091

8192
return parent::__call($method, $args);
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the TelegramBot package.
5+
*
6+
* (c) Avtandil Kikabidze aka LONGMAN <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Longman\TelegramBot\Entities;
13+
14+
/**
15+
* Class KeyboardButtonPollType
16+
*
17+
* This entity represents type of a poll, which is allowed to be created and sent when the corresponding button is pressed.
18+
*
19+
* @link https://core.telegram.org/bots/api#keyboardbutton
20+
*
21+
* @method string getType() Optional. If 'quiz' is passed, the user will be allowed to create only polls in the quiz mode. If 'regular' is passed, only regular polls will be allowed. Otherwise, the user will be allowed to create a poll of any type.
22+
*
23+
* @method $this setType(string $type) Optional. If 'quiz' is passed, the user will be allowed to create only polls in the quiz mode. If 'regular' is passed, only regular polls will be allowed. Otherwise, the user will be allowed to create a poll of any type.
24+
*/
25+
class KeyboardButtonPollType extends Entity
26+
{
27+
28+
}

src/Entities/MessageEntity.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616
*
1717
* @link https://core.telegram.org/bots/api#messageentity
1818
*
19-
* @method string getType() Type of the entity. Can be 'mention' (@username), 'hashtag' (#hashtag), 'cashtag' ($USD), 'bot_command' (/start@jobs_bot), 'url' (https://telegram.org), 'email' ([email protected]), 'phone_number' (+1-212-555-0123), 'bold' (bold text), 'italic' (italic text), 'underline' (underlined text), 'strikethrough' (strikethrough text), 'code' (monowidth string), 'pre' (monowidth block), 'text_link' (for clickable text URLs), 'text_mention' (for users without usernames)
20-
* @method int getOffset() Offset in UTF-16 code units to the start of the entity
21-
* @method int getLength() Length of the entity in UTF-16 code units
22-
* @method string getUrl() Optional. For "text_link" only, url that will be opened after user taps on the text
23-
* @method User getUser() Optional. For "text_mention" only, the mentioned user
19+
* @method string getType() Type of the entity. Can be 'mention' (@username), 'hashtag' (#hashtag), 'cashtag' ($USD), 'bot_command' (/start@jobs_bot), 'url' (https://telegram.org), 'email' ([email protected]), 'phone_number' (+1-212-555-0123), 'bold' (bold text), 'italic' (italic text), 'underline' (underlined text), 'strikethrough' (strikethrough text), 'code' (monowidth string), 'pre' (monowidth block), 'text_link' (for clickable text URLs), 'text_mention' (for users without usernames)
20+
* @method int getOffset() Offset in UTF-16 code units to the start of the entity
21+
* @method int getLength() Length of the entity in UTF-16 code units
22+
* @method string getUrl() Optional. For "text_link" only, url that will be opened after user taps on the text
23+
* @method User getUser() Optional. For "text_mention" only, the mentioned user
24+
* @method string getLanguage() Optional. For "pre" only, the programming language of the entity text
2425
*/
2526
class MessageEntity extends Entity
2627
{

src/Entities/Poll.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,16 @@
1818
*
1919
* @link https://core.telegram.org/bots/api#poll
2020
*
21-
* @method string getId() Unique poll identifier
22-
* @method string getQuestion() Poll question, 1-255 characters
23-
* @method PollOption[] getOptions() List of poll options
24-
* @method bool getIsClosed() True, if the poll is closed
21+
* @method string getId() Unique poll identifier
22+
* @method string getQuestion() Poll question, 1-255 characters
23+
* @method PollOption[] getOptions() List of poll options
24+
* @method int getTotalVoterCount() Total number of users that voted in the poll
25+
* @method bool getIsClosed() True, if the poll is closed
26+
* @method bool getIsAnonymous() True, if the poll is anonymous
27+
* @method string getType() Poll type, currently can be “regular” or “quiz”
28+
* @method bool getAllowsMultipleAnswers() True, if the poll allows multiple answers
29+
* @method int getCorrectOptionId() Optional. 0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot.
30+
2531
*/
2632
class Poll extends Entity
2733
{

0 commit comments

Comments
 (0)