Unverified Commit 84647ebc authored by Armando Lüscher's avatar Armando Lüscher

Merge remote-tracking branch 'upstream/develop' into 726-games

parents e80e551d 09f23ff9
dist: xenial
dist: trusty
sudo: required
language: php
......@@ -14,6 +14,7 @@ php:
- 5.6
- 7.0
- 7.1
- 7.2
- nightly
- hhvm
......@@ -42,7 +43,7 @@ before_script:
script:
- composer check-code
- if [ "$TRAVIS_PHP_VERSION" == "7.1" ] ; then composer test-cov; else composer test; fi
- if [ "$TRAVIS_PHP_VERSION" == "7.2" ] ; then composer test-cov; else composer test; fi
after_script:
- if [ "$TRAVIS_PHP_VERSION" == "7.1" ]; then composer test-cov-upload; fi
- if [ "$TRAVIS_PHP_VERSION" == "7.2" ]; then composer test-cov-upload; fi
......@@ -4,15 +4,29 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
Exclamation symbols (:exclamation:) note something of importance e.g. breaking changes. Click them to learn more.
## [Unreleased]
:exclamation: After updating to this version, you will need to execute the [SQL migration script][unreleased-sql-migration] on your database.
### Added
- Implemented new changes for Bot API 3.6 (streamable InputMediaVideo, connected website).
- `Telegram::getLastUpdateId()` method, returns ID of the last update that was processed.
- `Telegram::useGetUpdatesWithoutDatabase()` method, enables `Telegram::handleGetUpdates()` to run without a database.
- Telegram Games platform!
### Changed
- Updated Travis to use Trusty containers (for HHVM) and add PHP 7.2 to the tests.
- Add debug log entry instead of throwing an exception for duplicate updates.
- `Telegram::handleGetUpdates()` can now work without a database connection (not enabled by default).
### Deprecated
### Removed
### Fixed
- Entity relations and wrong types for payments.
- PHPCS fixes for updated CodeSniffer dependency.
- Send messages correctly via `/sendtochannel`.
### Security
## [0.52.0] - 2018-01-07
### Fixed
- Entity relations and wrong types for payments.
- Allow empty string for `switch_inline_query` and `switch_inline_query_current_chat` (InlineKeyboardButton).
- Fix empty date entry for User and Chat entities, using the current timestamp instead.
## [0.51.0] - 2017-12-05
:exclamation: After updating to this version, you will need to execute the [SQL migration script][0.51.0-sql-migration] on your database.
### Added
......@@ -185,6 +199,7 @@ Exclamation symbols (:exclamation:) note something of importance e.g. breaking c
### Deprecated
- Move `hideKeyboard` to `removeKeyboard`.
[unreleased-sql-migration]: https://github.com/php-telegram-bot/core/tree/develop/utils/db-schema-update/0.52.0-unreleased.sql
[0.51.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/develop/utils/db-schema-update/0.50.0-0.51.0.sql
[0.50.0-bc-messagegetcommand-return-value]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#messagegetcommand-return-value
[0.48.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/develop/utils/db-schema-update/0.47.1-0.48.0.sql
......@@ -199,6 +214,7 @@ Exclamation symbols (:exclamation:) note something of importance e.g. breaking c
[example-bot]: https://github.com/php-telegram-bot/example-bot
[Unreleased]: https://github.com/php-telegram-bot/core/compare/master...develop
[0.52.0]: https://github.com/php-telegram-bot/core/compare/0.51.0...0.52.0
[0.51.0]: https://github.com/php-telegram-bot/core/compare/0.50.0...0.51.0
[0.50.0]: https://github.com/php-telegram-bot/core/compare/0.49.0...0.50.0
[0.49.0]: https://github.com/php-telegram-bot/core/compare/0.48.0...0.49.0
......
......@@ -23,6 +23,5 @@ W: http://noplanman.ch
D: Maintainer and Collaborator
N: Jack'lul (alias)
E: jacklul@jacklul.com
W: http://jacklul.com
E: jacklulcat@gmail.com
D: Maintainer and Collaborator
......@@ -186,7 +186,7 @@ The bot can handle updates with **Webhook** or **getUpdates** method:
| ---- | :----: | :----: |
| Description | Telegram sends the updates directly to your host | You have to fetch Telegram updates manually |
| Host with https | Required | Not required |
| MySQL | Not required | Required |
| MySQL | Not required | ([Not](#getupdates-without-database)) Required |
## Webhook installation
......@@ -257,9 +257,9 @@ $result = $telegram->setWebhook($hook_url, ['certificate' => '/path/to/certifica
Edit [*unset.php*][unset.php] with your bot credentials and execute it.
### getUpdates installation
## getUpdates installation
The MySQL database must be enabled for the getUpdates method!
For best performance, the MySQL database should be enabled for the `getUpdates` method!
Create [*getUpdatesCLI.php*][getUpdatesCLI.php] with the following contents:
```php
......@@ -301,6 +301,13 @@ Lastly, run it!
$ ./getUpdatesCLI.php
```
### getUpdates without database
If you choose to / or are obliged to use the `getUpdates` method without a database, you can replace the `$telegram->useMySQL(...);` line above with:
```php
$telegram->useGetUpdatesWithoutDatabase();
```
## Support
### Types
......
......@@ -22,12 +22,12 @@
"ext-pdo": "*",
"ext-curl": "*",
"ext-mbstring": "*",
"monolog/monolog": "^1.22",
"guzzlehttp/guzzle": "^6.2"
"monolog/monolog": "^1.23",
"guzzlehttp/guzzle": "^6.3"
},
"require-dev": {
"phpunit/phpunit": "^4.8|^5.7|^6.1",
"squizlabs/php_codesniffer": "^2.8"
"phpunit/phpunit": "^4.8|^5.7|^6.5",
"squizlabs/php_codesniffer": "^3.2"
},
"autoload": {
"psr-4": {
......@@ -41,13 +41,13 @@
},
"scripts": {
"check-code": [
"./vendor/bin/phpcs --standard=phpcs.xml -snp --encoding=utf-8 src/ tests/ --report-width=150"
"\"vendor/bin/phpcs\" --standard=phpcs.xml -snp --encoding=utf-8 src/ tests/ --report-width=150"
],
"test": [
"./vendor/bin/phpunit"
"\"vendor/bin/phpunit\""
],
"test-cov": [
"./vendor/bin/phpunit --coverage-clover build/logs/clover.xml"
"\"vendor/bin/phpunit\" --coverage-clover build/logs/clover.xml"
],
"test-cov-upload": [
"wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml"
......
This diff is collapsed.
......@@ -290,13 +290,7 @@ class SendtochannelCommand extends AdminCommand
$data['longitude'] = $message->getLocation()->getLongitude();
}
$callback_path = 'Longman\TelegramBot\Request';
$callback_function = 'send' . ucfirst($type);
if (!method_exists($callback_path, $callback_function)) {
throw new TelegramException('Methods: ' . $callback_function . ' not found in class Request.');
}
return $callback_path::$callback_function($data);
return Request::send('send' . ucfirst($type), $data);
}
/**
......
......@@ -260,17 +260,13 @@ class DB
/**
* Convert from unix timestamp to timestamp
*
* @param int $time Unix timestamp (if null, current timestamp is used)
* @param int $time Unix timestamp (if empty, current timestamp is used)
*
* @return string
*/
protected static function getTimestamp($time = null)
{
if ($time === null) {
$time = time();
}
return date('Y-m-d H:i:s', $time);
return date('Y-m-d H:i:s', $time ?: time());
}
/**
......@@ -362,7 +358,7 @@ class DB
* @return bool If the insert was successful
* @throws TelegramException
*/
public static function insertUser(User $user, $date, Chat $chat = null)
public static function insertUser(User $user, $date = null, Chat $chat = null)
{
if (!self::isDbConnected()) {
return false;
......@@ -389,6 +385,7 @@ class DB
$sth->bindValue(':first_name', $user->getFirstName());
$sth->bindValue(':last_name', $user->getLastName());
$sth->bindValue(':language_code', $user->getLanguageCode());
$date = $date ?: self::getTimestamp();
$sth->bindValue(':created_at', $date);
$sth->bindValue(':updated_at', $date);
......@@ -429,7 +426,7 @@ class DB
* @return bool If the insert was successful
* @throws TelegramException
*/
public static function insertChat(Chat $chat, $date, $migrate_to_chat_id = null)
public static function insertChat(Chat $chat, $date = null, $migrate_to_chat_id = null)
{
if (!self::isDbConnected()) {
return false;
......@@ -466,6 +463,7 @@ class DB
$sth->bindValue(':title', $chat->getTitle());
$sth->bindValue(':username', $chat->getUsername());
$sth->bindValue(':all_members_are_administrators', $chat->getAllMembersAreAdministrators(), PDO::PARAM_INT);
$date = $date ?: self::getTimestamp();
$sth->bindValue(':created_at', $date);
$sth->bindValue(':updated_at', $date);
......@@ -494,10 +492,6 @@ class DB
$update_id = $update->getUpdateId();
$update_type = $update->getUpdateType();
if (count(self::selectTelegramUpdate(1, $update_id)) === 1) {
throw new TelegramException('Duplicate update received!');
}
// @todo Make this simpler: if ($message = $update->getMessage()) ...
if ($update_type === 'message') {
$message = $update->getMessage();
......@@ -835,7 +829,7 @@ class DB
`location`, `venue`, `new_chat_members`, `left_chat_member`,
`new_chat_title`,`new_chat_photo`, `delete_chat_photo`, `group_chat_created`,
`supergroup_chat_created`, `channel_chat_created`,
`migrate_from_chat_id`, `migrate_to_chat_id`, `pinned_message`
`migrate_from_chat_id`, `migrate_to_chat_id`, `pinned_message`, `connected_website`
) VALUES (
:message_id, :user_id, :chat_id, :date, :forward_from, :forward_from_chat, :forward_from_message_id,
:forward_date, :reply_to_chat, :reply_to_message, :media_group_id, :text, :entities, :audio, :document,
......@@ -843,7 +837,7 @@ class DB
:location, :venue, :new_chat_members, :left_chat_member,
:new_chat_title, :new_chat_photo, :delete_chat_photo, :group_chat_created,
:supergroup_chat_created, :channel_chat_created,
:migrate_from_chat_id, :migrate_to_chat_id, :pinned_message
:migrate_from_chat_id, :migrate_to_chat_id, :pinned_message, :connected_website
)
');
......@@ -904,6 +898,7 @@ class DB
$sth->bindValue(':migrate_from_chat_id', $message->getMigrateFromChatId());
$sth->bindValue(':migrate_to_chat_id', $message->getMigrateToChatId());
$sth->bindValue(':pinned_message', $message->getPinnedMessage());
$sth->bindValue(':connected_website', $message->getConnectedWebsite());
return $sth->execute();
} catch (PDOException $e) {
......
......@@ -67,12 +67,18 @@ class InlineKeyboardButton extends KeyboardButton
$num_params = 0;
foreach (['url', 'callback_data', 'switch_inline_query', 'switch_inline_query_current_chat', 'callback_game', 'pay'] as $param) {
foreach (['url', 'callback_data', 'callback_game', 'pay'] as $param) {
if ($this->getProperty($param, '') !== '') {
$num_params++;
}
}
foreach (['switch_inline_query', 'switch_inline_query_current_chat'] as $param) {
if ($this->getProperty($param) !== null) {
$num_params++;
}
}
if ($num_params !== 1) {
throw new TelegramException('You must use only one of these fields: url, callback_data, switch_inline_query, switch_inline_query_current_chat, callback_game, pay!');
}
......
......@@ -11,6 +11,7 @@
namespace Longman\TelegramBot\Entities;
use Longman\TelegramBot\Entities\InlineQuery\InlineQueryResult;
use Longman\TelegramBot\Request;
/**
* Class InlineQuery
......
......@@ -19,26 +19,29 @@ use Longman\TelegramBot\Entities\Entity;
*
* <code>
* $data = [
* 'media' => '123abc',
* 'caption' => 'Video caption',
* 'width' => 800,
* 'heidht' => 600,
* 'duration' => 42
* 'media' => '123abc',
* 'caption' => 'Video caption (streamable)',
* 'width' => 800,
* 'height' => 600,
* 'duration' => 42,
* 'supports_streaming' => true
* ];
* </code>
*
* @method string getType() Type of the result, must be video
* @method string getMedia() File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass "attach://<file_attach_name>" to upload a new one using multipart/form-data under <file_attach_name> name.
* @method string getCaption() Optional. Caption of the video to be sent, 0-200 characters
* @method int getWidth() Optional. Video width
* @method int getHeight() Optional. Video height
* @method int getDuration() Optional. Video duration
* @method string getType() Type of the result, must be video
* @method string getMedia() File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass "attach://<file_attach_name>" to upload a new one using multipart/form-data under <file_attach_name> name.
* @method string getCaption() Optional. Caption of the video to be sent, 0-200 characters
* @method int getWidth() Optional. Video width
* @method int getHeight() Optional. Video height
* @method int getDuration() Optional. Video duration
* @method bool getSupportsStreaming() Optional. Pass True, if the uploaded video is suitable for streaming
*
* @method $this setMedia(string $media) File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass "attach://<file_attach_name>" to upload a new one using multipart/form-data under <file_attach_name> name.
* @method $this setCaption(string $caption) Optional. Caption of the video to be sent, 0-200 characters
* @method $this setWidth(int $width) Optional. Video width
* @method $this setHeight(int $height) Optional. Video height
* @method $this setDuration(int $duration) Optional. Video duration
* @method $this setMedia(string $media) File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass "attach://<file_attach_name>" to upload a new one using multipart/form-data under <file_attach_name> name.
* @method $this setCaption(string $caption) Optional. Caption of the video to be sent, 0-200 characters
* @method $this setWidth(int $width) Optional. Video width
* @method $this setHeight(int $height) Optional. Video height
* @method $this setDuration(int $duration) Optional. Video duration
* @method $this setSupportsStreaming(bool $supports_streaming) Optional. Pass True, if the uploaded video is suitable for streaming
*/
class InputMediaVideo extends Entity implements InputMedia
{
......
......@@ -54,6 +54,7 @@ use Longman\TelegramBot\Entities\Payments\SuccessfulPayment;
* @method Message getPinnedMessage() Optional. Specified message was pinned. Note that the Message object in this field will not contain further reply_to_message fields even if it is itself a reply.
* @method Invoice getInvoice() Optional. Message is an invoice for a payment, information about the invoice.
* @method SuccessfulPayment getSuccessfulPayment() Optional. Message is a service message about a successful payment, information about the payment.
* @method string getConnectedWebsite() Optional. The domain name of the website on which the user has logged in.
*/
class Message extends Entity
{
......
......@@ -30,7 +30,7 @@ class Telegram
*
* @var string
*/
protected $version = '0.51.0';
protected $version = '0.52.0';
/**
* Telegram API key
......@@ -137,6 +137,21 @@ class Telegram
*/
protected $run_commands = false;
/**
* Is running getUpdates without DB enabled
*
* @var bool
*/
protected $getupdates_without_database = false;
/**
* Last update ID
* Only used when running getUpdates without a database
*
* @var integer
*/
protected $last_update_id = null;
/**
* Telegram constructor.
*
......@@ -320,26 +335,33 @@ class Telegram
throw new TelegramException('Bot Username is not defined!');
}
if (!DB::isDbConnected()) {
if (!DB::isDbConnected() && !$this->getupdates_without_database) {
return new ServerResponse(
[
'ok' => false,
'description' => 'getUpdates needs MySQL connection!',
'description' => 'getUpdates needs MySQL connection! (This can be overridden - see documentation)',
],
$this->bot_username
);
}
$offset = 0;
//Take custom input into account.
if ($custom_input = $this->getCustomInput()) {
$response = new ServerResponse(json_decode($custom_input, true), $this->bot_username);
} else {
//DB Query
$last_update = DB::selectTelegramUpdate(1);
$last_update = reset($last_update);
if (DB::isDbConnected()) {
//Get last update id from the database
$last_update = DB::selectTelegramUpdate(1);
$last_update = reset($last_update);
$this->last_update_id = isset($last_update['id']) ? $last_update['id'] : null;
}
//As explained in the telegram bot api documentation
$offset = isset($last_update['id']) ? $last_update['id'] + 1 : null;
if ($this->last_update_id !== null) {
$offset = $this->last_update_id + 1; //As explained in the telegram bot API documentation
}
$response = Request::getUpdates(
[
......@@ -351,11 +373,24 @@ class Telegram
}
if ($response->isOk()) {
$results = $response->getResult();
//Process all updates
/** @var Update $result */
foreach ((array) $response->getResult() as $result) {
foreach ($results as $result) {
$this->processUpdate($result);
}
if (!DB::isDbConnected() && !$custom_input && $this->last_update_id !== null && $offset === 0) {
//Mark update(s) as read after handling
Request::getUpdates(
[
'offset' => $this->last_update_id + 1,
'limit' => 1,
'timeout' => $timeout,
]
);
}
}
return $response;
......@@ -415,6 +450,7 @@ class Telegram
public function processUpdate(Update $update)
{
$this->update = $update;
$this->last_update_id = $update->getUpdateId();
//If all else fails, it's a generic message.
$command = 'genericmessage';
......@@ -457,6 +493,13 @@ class Telegram
//This is necessary to "require" all the necessary command files!
$this->getCommandsList();
//Make sure we don't try to process update that was already processed
$last_id = DB::selectTelegramUpdate(1, $this->update->getUpdateId());
if ($last_id && count($last_id) === 1) {
TelegramLog::debug('Duplicate update received, processing aborted!');
return Request::emptyResponse();
}
DB::insertRequest($this->update);
return $this->executeCommand($command);
......@@ -855,9 +898,8 @@ class Telegram
*/
protected function ucfirstUnicode($str, $encoding = 'UTF-8')
{
return
mb_strtoupper(mb_substr($str, 0, 1, $encoding), $encoding)
. mb_strtolower(mb_substr($str, 1, mb_strlen($str), $encoding), $encoding);
return mb_strtoupper(mb_substr($str, 0, 1, $encoding), $encoding)
. mb_strtolower(mb_substr($str, 1, mb_strlen($str), $encoding), $encoding);
}
/**
......@@ -959,4 +1001,24 @@ class Telegram
{
return $this->run_commands;
}
/**
* Switch to enable running getUpdates without a database
*
* @param bool $enable
*/
public function useGetUpdatesWithoutDatabase($enable = true)
{
$this->getupdates_without_database = $enable;
}
/**
* Return last update id
*
* @return int
*/
public function getLastUpdateId()
{
return $this->last_update_id;
}
}
......@@ -102,6 +102,7 @@ CREATE TABLE IF NOT EXISTS `message` (
`migrate_to_chat_id` bigint NULL DEFAULT NULL COMMENT 'Migrate to chat identifier. The group has been migrated to a supergroup with the specified identifier',
`migrate_from_chat_id` bigint NULL DEFAULT NULL COMMENT 'Migrate from chat identifier. The supergroup has been migrated from a group with the specified identifier',
`pinned_message` TEXT NULL COMMENT 'Message object. Specified message was pinned',
`connected_website` TEXT NULL COMMENT 'The domain name of the website on which the user has logged in.',
PRIMARY KEY (`chat_id`, `id`),
KEY `user_id` (`user_id`),
......
......@@ -99,7 +99,9 @@ class InlineKeyboardButtonTest extends TestCase
new InlineKeyboardButton(['text' => 'message', 'url' => 'url_value']);
new InlineKeyboardButton(['text' => 'message', 'callback_data' => 'callback_data_value']);
new InlineKeyboardButton(['text' => 'message', 'switch_inline_query' => 'switch_inline_query_value']);
new InlineKeyboardButton(['text' => 'message', 'switch_inline_query' => '']); // Allow empty string.
new InlineKeyboardButton(['text' => 'message', 'switch_inline_query_current_chat' => 'switch_inline_query_current_chat_value']);
new InlineKeyboardButton(['text' => 'message', 'switch_inline_query_current_chat' => '']); // Allow empty string.
new InlineKeyboardButton(['text' => 'message', 'callback_game' => new CallbackGame([])]);
new InlineKeyboardButton(['text' => 'message', 'pay' => true]);
}
......
ALTER TABLE `message` ADD COLUMN `connected_website` TEXT NULL COMMENT 'The domain name of the website on which the user has logged in.' AFTER `pinned_message`;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment