1
0
mirror of https://github.com/danog/MadelineProto.git synced 2024-11-30 04:08:59 +01:00

Handle FLOOD_PREMIUM_WAIT during file operations

This commit is contained in:
Daniil Gentili 2024-06-16 16:41:51 +02:00
parent 30d542719f
commit afc082cc63
7 changed files with 117 additions and 37 deletions

View File

@ -31,7 +31,9 @@ use danog\MadelineProto\MTProto;
use danog\MadelineProto\MTProto\MTProtoIncomingMessage;
use danog\MadelineProto\MTProto\MTProtoOutgoingMessage;
use danog\MadelineProto\PTSException;
use danog\MadelineProto\RPCError\FloodPremiumWaitError;
use danog\MadelineProto\RPCError\FloodWaitError;
use danog\MadelineProto\RPCError\RateLimitError;
use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\SecretPeerNotInDbException;
use danog\MadelineProto\SecurityException;
@ -451,9 +453,9 @@ trait ResponseHandler
}
return static fn () => RPCErrorException::make($response['error_message'], $response['error_code'], $request->constructor);
case 420:
$seconds = preg_replace('/[^0-9]+/', '', $response['error_message']);
$seconds = (int) preg_replace('/[^0-9]+/', '', $response['error_message']);
$limit = $request->floodWaitLimit ?? $this->API->settings->getRPC()->getFloodTimeout();
if (is_numeric($seconds) && $seconds < $limit) {
if ($seconds < $limit) {
$this->API->logger("Flood, waiting $seconds seconds before repeating async call of $request...", Logger::NOTICE);
$this->gotResponseForOutgoingMessage($request);
$msgId = $request->getMsgId();
@ -466,9 +468,27 @@ trait ResponseHandler
return null;
}
if (str_starts_with($response['error_message'], 'FLOOD_WAIT_')) {
return static fn () => new FloodWaitError($response['error_message'], $response['error_code'], $request->constructor);
return static fn () => new FloodWaitError(
$response['error_message'],
$seconds,
$response['error_code'],
$request->constructor
);
}
// no break
if (str_starts_with($response['error_message'], 'FLOOD_PREMIUM_WAIT_')) {
return static fn () => new FloodPremiumWaitError(
$response['error_message'],
$seconds,
$response['error_code'],
$request->constructor
);
}
return static fn () => new RateLimitError(
$response['error_message'],
$seconds,
$response['error_code'],
$request->constructor
);
default:
return static fn () => RPCErrorException::make($response['error_message'], $response['error_code'], $request->constructor);
}

View File

@ -48,6 +48,7 @@ use danog\MadelineProto\FileRedirect;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProtoTools\Crypt\IGE;
use danog\MadelineProto\RPCError\FileTokenInvalidError;
use danog\MadelineProto\RPCError\FloodPremiumWaitError;
use danog\MadelineProto\RPCError\FloodWaitError;
use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\SecurityException;
@ -358,6 +359,12 @@ trait Files
}
$d->complete();
return;
} catch (FloodPremiumWaitError $e) {
$this->logger("Got {$e->rpc} while uploading $part_num: {$datacenter}, retrying...");
$writePromise = async(static function () use ($cancellation, $e, $writeCb): void {
$e->wait($cancellation);
$writeCb();
});
} catch (FileRedirect $e) {
$datacenter = $e->dc;
$this->logger("Got redirect while uploading $part_num: {$datacenter}");
@ -1210,8 +1217,10 @@ trait Files
break;
} catch (FileRedirect $e) {
$datacenter = $e->dc;
} catch (FloodWaitError $e) {
} catch (FloodWaitError) {
delay(1, cancellation: $cancellation);
} catch (FloodPremiumWaitError $e) {
$e->wait($cancellation);
} catch (FileTokenInvalidError) {
$cdn = false;
$datacenter = $this->authorized_dc;

View File

@ -0,0 +1,26 @@
<?php declare(strict_types=1);
/**
* RPCErrorException module.
*
* 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>
* @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
namespace danog\MadelineProto\RPCError;
/**
* Represents a FLOOD_PREMIUM_WAIT_ RPC error returned by telegram.
*/
final class FloodPremiumWaitError extends RateLimitError
{
}

View File

@ -18,40 +18,9 @@
namespace danog\MadelineProto\RPCError;
use danog\MadelineProto\RPCErrorException;
use Exception;
use Webmozart\Assert\Assert;
use function Amp\delay;
/**
* Represents a FLOOD_WAIT_ RPC error returned by telegram.
*/
final class FloodWaitError extends RPCErrorException
final class FloodWaitError extends RateLimitError
{
public readonly int $waitTime;
public function __construct(string $message, int $code, string $caller, ?Exception $previous = null)
{
Assert::true(str_starts_with($message, 'FLOOD_WAIT_'));
$seconds = substr($message, 11);
Assert::numeric($seconds);
$this->waitTime = (int) $seconds;
parent::__construct($message, "A rate limit was encountered, please repeat the method call after $seconds seconds", $code, $caller, $previous);
}
/**
* Returns the required waiting period in seconds before repeating the RPC call.
*/
public function getWaitTime(): int
{
return $this->waitTime;
}
/**
* Waits for the required waiting period.
*/
public function wait(): void
{
delay($this->waitTime);
}
}

View File

@ -0,0 +1,53 @@
<?php declare(strict_types=1);
/**
* RPCErrorException module.
*
* 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>
* @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
namespace danog\MadelineProto\RPCError;
use Amp\Cancellation;
use danog\MadelineProto\RPCErrorException;
use Exception;
use function Amp\delay;
/**
* Represents a rate limiting RPC error returned by telegram.
*/
class RateLimitError extends RPCErrorException
{
/** @internal */
public function __construct(string $message, public readonly int $waitTime, int $code, string $caller, ?Exception $previous = null)
{
parent::__construct($message, "A rate limit was encountered, please repeat the method call after $waitTime seconds", $code, $caller, $previous);
}
/**
* Returns the required waiting period in seconds before repeating the RPC call.
*/
public function getWaitTime(): int
{
return $this->waitTime;
}
/**
* Waits for the required waiting period.
*/
public function wait(?Cancellation $cancellation = null): void
{
delay($this->waitTime, cancellation: $cancellation);
}
}

View File

@ -130,6 +130,7 @@ class RPCErrorException extends \Exception
'VOLUME_LOC_NOT_FOUND' => true,
'FILE_WRITE_EMPTY' => true,
'Internal_Server_Error' => true,
'INVITE_HASH_UNSYNC' => true,
];
/** @internal */

View File

@ -57,6 +57,8 @@ require 'vendor/autoload.php';
`rm -r src/RPCError/*`;
`git checkout src/RPCError/FloodWaitError.php`;
`git checkout src/RPCError/FloodPremiumWaitError.php`;
`git checkout src/RPCError/RateLimitError.php`;
$map = [];