MadelineProto/src/danog/MadelineProto/Loop/Update/FeedLoop.php

249 lines
11 KiB
PHP
Raw Normal View History

2019-05-29 15:17:14 +02:00
<?php
2020-01-31 19:29:43 +01:00
2019-05-29 15:17:14 +02:00
/**
* Update feeder loop.
*
* This file is part of MadelineProto.
* MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
* You should have received a copy of the GNU General Public License along with MadelineProto.
* If not, see <http://www.gnu.org/licenses/>.
*
* @author Daniil Gentili <daniil@daniil.it>
2020-02-17 14:13:46 +01:00
* @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
2019-05-29 15:17:14 +02:00
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
*
2019-10-31 15:07:35 +01:00
* @link https://docs.madelineproto.xyz MadelineProto documentation
2019-05-29 15:17:14 +02:00
*/
2019-05-29 17:19:42 +02:00
namespace danog\MadelineProto\Loop\Update;
2019-05-29 15:17:14 +02:00
2019-05-30 13:28:50 +02:00
use Amp\Loop;
2019-05-29 15:17:14 +02:00
use danog\MadelineProto\Loop\Impl\ResumableSignalLoop;
/**
* update feed loop.
*
* @author Daniil Gentili <daniil@daniil.it>
*/
class FeedLoop extends ResumableSignalLoop
{
private $incomingUpdates = [];
private $parsedUpdates = [];
private $channelId;
/**
2019-09-02 17:08:36 +02:00
* Update loop.
*
* @var UpdateLoop
*/
2019-05-29 15:17:14 +02:00
private $updater;
public function __construct($API, $channelId = false)
{
$this->API = $API;
$this->channelId = $channelId;
}
2020-01-31 19:29:43 +01:00
public function loop(): \Generator
2019-05-29 15:17:14 +02:00
{
$API = $this->API;
2019-06-01 21:16:52 +02:00
$this->updater = $API->updaters[$this->channelId];
2019-10-29 22:51:14 +01:00
if (!$this->API->settings['updates']['handle_updates']) {
2019-05-29 15:17:14 +02:00
return false;
}
2019-10-29 22:51:14 +01:00
while (!$this->API->settings['updates']['handle_updates'] || !$API->hasAllAuth()) {
2019-05-29 15:17:14 +02:00
if (yield $this->waitSignal($this->pause())) {
return;
}
}
2020-01-31 19:49:58 +01:00
$this->state = $this->channelId === false ? yield from $API->loadUpdateState() : $API->loadChannelState($this->channelId);
2019-05-29 15:17:14 +02:00
while (true) {
2019-10-29 22:51:14 +01:00
while (!$this->API->settings['updates']['handle_updates'] || !$API->hasAllAuth()) {
2019-05-29 15:17:14 +02:00
if (yield $this->waitSignal($this->pause())) {
return;
}
}
if (yield $this->waitSignal($this->pause())) {
return;
}
2019-10-29 22:51:14 +01:00
if (!$this->API->settings['updates']['handle_updates']) {
2019-05-29 15:17:14 +02:00
return;
}
2020-01-31 19:29:43 +01:00
$API->logger->logger("Resumed {$this}");
2019-05-29 15:17:14 +02:00
while ($this->incomingUpdates) {
$updates = $this->incomingUpdates;
2019-05-29 22:00:51 +02:00
$this->incomingUpdates = [];
2020-01-31 19:29:43 +01:00
yield from $this->parse($updates);
2019-05-29 15:17:14 +02:00
$updates = null;
}
2019-06-05 21:41:42 +02:00
while ($this->parsedUpdates) {
$parsedUpdates = $this->parsedUpdates;
$this->parsedUpdates = [];
foreach ($parsedUpdates as $update) {
2020-01-31 19:49:58 +01:00
yield from $API->saveUpdate($update);
2019-05-29 18:28:43 +02:00
}
2019-06-07 02:48:25 +02:00
$parsedUpdates = null;
2019-05-30 17:22:28 +02:00
$this->API->signalUpdate();
2019-05-30 13:28:50 +02:00
}
2019-05-29 15:17:14 +02:00
}
}
2020-01-31 19:29:43 +01:00
public function parse($updates): \Generator
2019-05-29 15:17:14 +02:00
{
2019-09-02 17:08:36 +02:00
\reset($updates);
2019-05-29 15:17:14 +02:00
while ($updates) {
2019-09-02 17:08:36 +02:00
$key = \key($updates);
2019-05-29 15:17:14 +02:00
$update = $updates[$key];
unset($updates[$key]);
2019-05-29 22:36:16 +02:00
if ($update['_'] === 'updateChannelTooLong') {
$this->API->logger->logger('Got channel too long update, getting difference...', \danog\MadelineProto\Logger::VERBOSE);
2019-05-31 00:33:32 +02:00
yield $this->updater->resume();
2019-05-29 22:36:16 +02:00
continue;
}
if (isset($update['pts'], $update['pts_count'])) {
2019-05-29 15:17:14 +02:00
$logger = function ($msg) use ($update) {
2019-05-30 15:17:26 +02:00
$pts_count = $update['pts_count'];
2019-05-29 15:17:14 +02:00
$mid = isset($update['message']['id']) ? $update['message']['id'] : '-';
$mypts = $this->state->pts();
2019-05-30 15:17:26 +02:00
$computed = $mypts + $pts_count;
2020-02-12 18:51:53 +01:00
$this->API->logger->logger("{$msg}. My pts: {$mypts}, remote pts: {$update['pts']}, computed pts: {$computed}, msg id: {$mid}, channel id: {$this->channelId}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
2019-05-29 15:17:14 +02:00
};
$result = $this->state->checkPts($update);
if ($result < 0) {
2019-06-04 14:55:58 +02:00
$logger('PTS duplicate');
2019-05-29 15:17:14 +02:00
continue;
}
if ($result > 0) {
2019-06-04 14:55:58 +02:00
$logger('PTS hole');
2019-05-29 22:36:16 +02:00
$this->updater->setLimit($this->state->pts() + $result);
2019-05-29 15:17:14 +02:00
yield $this->updater->resume();
2019-09-02 17:08:36 +02:00
$updates = \array_merge($this->incomingUpdates, $updates);
2019-05-30 14:19:38 +02:00
$this->incomingUpdates = [];
2019-05-29 15:17:14 +02:00
continue;
}
2019-09-02 17:08:36 +02:00
if (isset($update['message']['id'], $update['message']['to_id']) && !\in_array($update['_'], ['updateEditMessage', 'updateEditChannelMessage', 'updateMessageID'])) {
2019-10-29 21:33:23 +01:00
if (!$this->API->checkMsgId($update['message'])) {
2019-06-04 14:55:58 +02:00
$logger('MSGID duplicate');
2019-05-29 15:17:14 +02:00
continue;
}
}
2019-06-04 14:55:58 +02:00
$logger('PTS OK');
2019-05-29 15:17:14 +02:00
$this->state->pts($update['pts']);
}
$this->save($update);
}
}
2020-04-05 22:22:47 +02:00
public function feed(array $updates): \Generator
2019-05-29 15:17:14 +02:00
{
2019-05-30 13:28:50 +02:00
$result = [];
foreach ($updates as $update) {
$res = $this->feedSingle($update);
if ($res instanceof \Generator) {
2020-02-28 16:02:19 +01:00
$res = yield from $res;
2019-05-30 13:28:50 +02:00
}
$result[$res] = true;
}
return $result;
2019-05-29 15:17:14 +02:00
}
2020-04-05 22:22:47 +02:00
public function feedSingle(array $update): \Generator
2019-05-29 15:17:14 +02:00
{
2019-05-30 14:09:15 +02:00
$channelId = false;
switch ($update['_']) {
case 'updateNewChannelMessage':
case 'updateEditChannelMessage':
2019-06-01 15:42:17 +02:00
$channelId = isset($update['message']['to_id']['channel_id']) ? $update['message']['to_id']['channel_id'] : false;
2019-06-25 14:09:39 +02:00
if (!$channelId) {
return false;
}
2019-05-30 14:09:15 +02:00
break;
2019-05-30 15:17:26 +02:00
case 'updateChannelWebPage':
2019-05-30 14:09:15 +02:00
case 'updateDeleteChannelMessages':
$channelId = $update['channel_id'];
break;
case 'updateChannelTooLong':
2019-06-02 16:23:49 +02:00
$channelId = isset($update['channel_id']) ? $update['channel_id'] : false;
2019-05-30 14:09:15 +02:00
if (!isset($update['pts'])) {
$update['pts'] = 1;
2019-05-30 13:28:50 +02:00
}
2019-05-30 14:09:15 +02:00
break;
}
if ($channelId && !$this->API->getChannelStates()->has($channelId)) {
$this->API->loadChannelState($channelId, $update);
if (!isset($this->API->feeders[$channelId])) {
2019-06-04 14:55:58 +02:00
$this->API->feeders[$channelId] = new self($this->API, $channelId);
2019-05-30 14:09:15 +02:00
}
if (!isset($this->API->updaters[$channelId])) {
2019-05-30 15:39:51 +02:00
$this->API->updaters[$channelId] = new UpdateLoop($this->API, $channelId);
2019-05-30 14:09:15 +02:00
}
$this->API->feeders[$channelId]->start();
$this->API->updaters[$channelId]->start();
}
switch ($update['_']) {
case 'updateNewMessage':
case 'updateEditMessage':
case 'updateNewChannelMessage':
case 'updateEditChannelMessage':
$to = false;
$from = false;
$via_bot = false;
$entities = false;
2020-01-31 19:49:58 +01:00
if ($update['message']['_'] !== 'messageEmpty' && (($from = isset($update['message']['from_id']) && !(yield from $this->API->peerIsset($update['message']['from_id']))) || ($to = !(yield from $this->API->peerIsset($update['message']['to_id']))) || ($via_bot = isset($update['message']['via_bot_id']) && !(yield from $this->API->peerIsset($update['message']['via_bot_id']))) || ($entities = isset($update['message']['entities']) && !(yield from $this->API->entitiesPeerIsset($update['message']['entities']))))) {
2019-05-30 14:09:15 +02:00
$log = '';
if ($from) {
$log .= "from_id {$update['message']['from_id']}, ";
}
if ($to) {
2020-04-05 22:22:47 +02:00
$log .= 'to_id '.\json_encode($update['message']['to_id']).', ';
2019-05-30 14:09:15 +02:00
}
if ($via_bot) {
$log .= "via_bot {$update['message']['via_bot_id']}, ";
2019-05-30 13:28:50 +02:00
}
2019-05-30 14:09:15 +02:00
if ($entities) {
2020-04-05 22:22:47 +02:00
$log .= 'entities '.\json_encode($update['message']['entities']).', ';
2019-05-30 13:28:50 +02:00
}
2020-01-31 19:29:43 +01:00
$this->API->logger->logger("Not enough data: for message update {$log}, getting difference...", \danog\MadelineProto\Logger::VERBOSE);
2019-05-30 14:09:15 +02:00
$update = ['_' => 'updateChannelTooLong'];
2019-06-13 19:46:58 +02:00
if ($channelId && $to) {
$channelId = false;
}
2019-05-30 14:09:15 +02:00
}
break;
default:
2020-01-31 19:49:58 +01:00
if ($channelId && !(yield from $this->API->peerIsset($this->API->toSupergroup($channelId)))) {
2020-04-05 22:22:47 +02:00
$this->API->logger->logger('Skipping update, I do not have the channel id '.$channelId, \danog\MadelineProto\Logger::ERROR);
2019-06-22 18:39:40 +02:00
return false;
2019-05-30 14:09:15 +02:00
}
break;
}
if ($channelId !== $this->channelId) {
2019-06-08 21:01:57 +02:00
if (isset($this->API->feeders[$channelId])) {
2020-01-31 19:29:43 +01:00
return yield from $this->API->feeders[$channelId]->feedSingle($update);
2019-09-02 17:08:36 +02:00
} elseif ($this->channelId) {
2020-01-31 19:29:43 +01:00
return yield from $this->API->feeders[false]->feedSingle($update);
2019-06-08 21:01:57 +02:00
}
2019-05-30 13:28:50 +02:00
}
2020-04-05 22:22:47 +02:00
$this->API->logger->logger('Was fed an update of type '.$update['_']." in {$this}...", \danog\MadelineProto\Logger::VERBOSE);
2019-05-30 13:28:50 +02:00
$this->incomingUpdates[] = $update;
return $this->channelId;
2019-05-29 15:17:14 +02:00
}
public function save($update)
{
2019-05-30 13:28:50 +02:00
$this->parsedUpdates[] = $update;
2019-05-29 15:17:14 +02:00
}
2019-05-29 22:00:51 +02:00
public function saveMessages($messages)
{
foreach ($messages as $message) {
2019-10-29 21:33:23 +01:00
if (!$this->API->checkMsgId($message)) {
2020-01-31 19:29:43 +01:00
$this->API->logger->logger("MSGID duplicate ({$message['id']}) in {$this}");
continue;
}
2019-09-14 15:31:52 +02:00
if ($message['_'] !== 'messageEmpty') {
2020-04-05 22:22:47 +02:00
$this->API->logger->logger('Getdiff fed me message of type '.$message['_']." in {$this}...", \danog\MadelineProto\Logger::VERBOSE);
2019-09-14 15:31:52 +02:00
}
2019-05-30 13:28:50 +02:00
$this->parsedUpdates[] = ['_' => $this->channelId === false ? 'updateNewMessage' : 'updateNewChannelMessage', 'message' => $message, 'pts' => -1, 'pts_count' => -1];
2019-05-29 22:00:51 +02:00
}
}
2019-05-30 14:09:15 +02:00
public function __toString(): string
{
2019-06-04 14:55:58 +02:00
return !$this->channelId ? 'update feed loop generic' : "update feed loop channel {$this->channelId}";
2019-05-30 14:09:15 +02:00
}
2019-05-29 15:17:14 +02:00
}