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 16:54:36 +02:00
|
|
|
/**
|
2019-09-02 17:08:36 +02:00
|
|
|
* Update loop.
|
2019-09-02 16:54:36 +02:00
|
|
|
*
|
|
|
|
* @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;
|
|
|
|
}
|
2019-06-23 12:32:23 +02:00
|
|
|
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}");
|
2019-05-30 15:25:56 +02:00
|
|
|
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
|
|
|
}
|