Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ Exclamation symbols (:exclamation:) note something of importance e.g. breaking c
: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.
### 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
Expand Down
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
72 changes: 64 additions & 8 deletions src/Telegram.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -320,26 +335,33 @@ public function handleGetUpdates($limit = null, $timeout = null)
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);

//As explained in the telegram bot api documentation
$offset = isset($last_update['id']) ? $last_update['id'] + 1 : null;
$this->last_update_id = isset($last_update['id']) ? $last_update['id'] : null;
}

if ($this->last_update_id !== null) {
$offset = $this->last_update_id + 1; //As explained in the telegram bot API documentation
}

$response = Request::getUpdates(
[
Expand All @@ -351,11 +373,24 @@ public function handleGetUpdates($limit = null, $timeout = null)
}

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;
Expand Down Expand Up @@ -415,6 +450,7 @@ protected function getCommandFromType($type)
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';
Expand Down Expand Up @@ -965,4 +1001,24 @@ public function isRunCommands()
{
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()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this for exactly? Why not just use the member variable directly?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, no idea why I put it there

Copy link
Collaborator Author

@jacklul jacklul Feb 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait! Probably to let user access last update id inside hook script, the purpose of that would be setting a signal handler (to prevent not marking updates as handled)
Couldn't find other way to access it...

        function signal_handler(Longman\TelegramBot\Telegram $telegram, $signo = null)
        {
            $last_update_id = $telegram->getLastUpdateId();
            if ($last_update_id > 0) {
                Longman\TelegramBot\Request::getUpdates(
                    [
                        'offset'  => $last_update_id + 1,
                        'limit'   => 1,
                    ]
                );
            }

            exit(PHP_EOL);
        }

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't fully understand what you meant there. Where would this signal_handler method be called?
Also, the $signo parameter isn't being used, what is that?

Copy link
Collaborator Author

@jacklul jacklul Feb 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, lets say the bot is heavily used and has to handle 40 updates at once, when bot crashes in the middle of processing they aren't marked as handled, next time script is run they are re-processed.

About $signo, for some reason that function refused to work without that parameter there... I tested it on linux subsystem since pcntl doesn't work on Windows.

Also that function is just an example, it can also be used to be called in catch block can also work after exceptions.

The point of that method is to returnid of the update that was processed (I should probably move the assignement in processUpdate somewhere after, will do later)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aaah, right! You're using the pcntl signal handling, thought it was just a custom function you call somewhere.

Ok. I'm just wondering if there is a cleaner way to do this. Also, we'll have to document this somehow with the extra code required to register the signal handler.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably a wiki article mentioning and example code will suffice.

{
return $this->last_update_id;
}
}