This commit is contained in:
Daniil Gentili 2019-07-06 17:48:34 +02:00
commit 92e8b58bd9
22 changed files with 296 additions and 190 deletions

View File

@ -39,7 +39,6 @@ class EventHandler extends \danog\MadelineProto\EventHandler
if (isset($update['message']['_']) && $update['message']['_'] === 'messageEmpty') {
return;
}
$res = json_encode($update, JSON_PRETTY_PRINT);
try {

2
docs

@ -1 +1 @@
Subproject commit 7414ae3e537b26a15b75d2a00ef6f93e702d2cd8
Subproject commit ddb2e4f76938b69ceac6e4615901c642accae1ef

View File

@ -199,7 +199,13 @@ class Connection
$this->disconnect();
yield $this->API->datacenter->dcConnectAsync($this->ctx->getDc());
if ($this->API->hasAllAuth() && !$this->hasPendingCalls()) {
$this->callFork($this->API->method_call_async_read('ping', ['ping_id' => $this->random_int()], ['datacenter' => $this->datacenter]));
$this->callFork((function () {
try {
$this->API->method_call_async_read('ping', ['ping_id' => $this->random_int()], ['datacenter' => $this->datacenter]);
} catch (\Throwable $e) {
$this->API->logger("Got an error while pinging on reconnect: $e", Logger::FATAL_ERROR);
}
})());
}
}

View File

@ -471,18 +471,10 @@ class DataCenter
}
break;
case 'wss':
if ($this->settings[$dc_config_number]['obfuscated']) {
$default = [[DefaultStream::getName(), []], [WssStream::getName(), []], [BufferedRawStream::getName(), []], [ObfuscatedStream::getName(), []], end($default)];
} else {
$default = [[DefaultStream::getName(), []], [WssStream::getName(), []], [BufferedRawStream::getName(), []], end($default)];
}
$default = [[DefaultStream::getName(), []], [WssStream::getName(), []], [BufferedRawStream::getName(), []], [ObfuscatedStream::getName(), []], end($default)];
break;
case 'ws':
if ($this->settings[$dc_config_number]['obfuscated']) {
$default = [[DefaultStream::getName(), []], [WsStream::getName(), []], [BufferedRawStream::getName(), []], [ObfuscatedStream::getName(), []], end($default)];
} else {
$default = [[DefaultStream::getName(), []], [WsStream::getName(), []], [BufferedRawStream::getName(), []], end($default)];
}
$default = [[DefaultStream::getName(), []], [WsStream::getName(), []], [BufferedRawStream::getName(), []], [ObfuscatedStream::getName(), []], end($default)];
break;
}
}

View File

@ -101,7 +101,9 @@ class Logger
Exception::$rollbar = false;
RPCErrorException::$rollbar = false;
}
if (!isset($settings['logger']['logger_param']) && isset($settings['logger']['param'])) {
$settings['logger']['logger_param'] = $settings['logger']['param'];
}
if (php_sapi_name() !== 'cli') {
if (isset($settings['logger']['logger_param']) && basename($settings['logger']['logger_param']) === 'MadelineProto.log') {
$settings['logger']['logger_param'] = Magic::$script_cwd.'/MadelineProto.log';
@ -179,6 +181,8 @@ class Logger
{
if (!is_null(self::$default)) {
self::$default->logger($param, $level, basename(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['file'], '.php'));
} else {
echo $param.PHP_EOL;
}
}

View File

@ -18,6 +18,8 @@
namespace danog\MadelineProto\Loop\Connection;
use Amp\ByteStream\PendingReadError;
use Amp\ByteStream\StreamException;
use Amp\Loop;
use Amp\Websocket\ClosedException;
use danog\MadelineProto\Logger;
@ -58,7 +60,7 @@ class ReadLoop extends SignalLoop
while (true) {
try {
$error = yield $this->waitSignal($this->readMessage());
} catch (NothingInTheSocketException|StreamException|PendingReadError $e) {
} catch (NothingInTheSocketException | StreamException | PendingReadError | \Error $e) {
if (isset($connection->old)) {
return;
}

View File

@ -287,9 +287,6 @@ class MTProto extends AsyncConstruct implements TLCallback
public function __wakeup_async($backtrace)
{
set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
//set_exception_handler(['\\danog\\MadelineProto\\Serialization', 'serialize_all']);
Magic::class_exists();
$this->setup_logger();
if (\danog\MadelineProto\Magic::$has_thread && is_object(\Thread::getCurrentThread())) {
return;

View File

@ -391,6 +391,8 @@ trait AuthKeyHandler
$req_pq = $req_pq === 'req_pq_multi' ? 'req_pq' : 'req_pq_multi';
} catch (\danog\MadelineProto\RPCErrorException $e) {
$this->logger->logger('An RPCErrorException occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...', \danog\MadelineProto\Logger::WARNING);
} catch (\Throwable $e) {
$this->logger->logger('An exception occurred while generating the authorization key: '.$e.PHP_EOL.' Retrying (try number '.$retry_id_total.')...', \danog\MadelineProto\Logger::WARNING);
}
}
if (strpos($datacenter, 'cdn') === false) {

View File

@ -41,6 +41,7 @@ use function Amp\File\exists;
use function Amp\File\open;
use function Amp\File\stat;
use function Amp\File\touch;
use danog\MadelineProto\Tools;
use function Amp\Promise\all;
use Amp\File\BlockingHandle;
use Amp\Artax\Client;
@ -566,7 +567,7 @@ trait Files
$res['mime'] = $this->get_mime_from_extension($res['ext'], 'image/jpeg');
}
if (!isset($res['name']) || $res['name'] === '') {
$res['name'] = $message_media['file']['access_hash'];
$res['name'] = Tools::unpack_signed_long_string($message_media['file']['access_hash']);
}
return $res;
@ -636,7 +637,7 @@ trait Files
$res['thumb_size'] = $message_media['type'];
if ($message_media['location']['_'] === 'fileLocationUnavailable') {
$res['name'] = $message_media['volume_id'].'_'.$message_media['local_id'];
$res['name'] = Tools::unpack_signed_long_string($message_media['volume_id']).'_'.$message_media['local_id'];
$res['mime'] = $this->get_mime_from_buffer($res['data']);
$res['ext'] = $this->get_extension_from_mime($res['mime']);
} else {
@ -658,7 +659,7 @@ trait Files
case 'fileLocationUnavailable':
throw new \danog\MadelineProto\Exception('File location unavailable');
case 'fileLocation':
$res['name'] = $message_media['volume_id'].'_'.$message_media['local_id'];
$res['name'] = Tools::unpack_signed_long_string($message_media['volume_id']).'_'.$message_media['local_id'];
$res['InputFileLocation'] = [
'_' => 'inputFileLocation',
'volume_id' => $message_media['volume_id'],
@ -675,7 +676,7 @@ trait Files
return $res;
case 'fileLocationToBeDeprecated':
$res['name'] = $message_media['volume_id'].'_'.$message_media['local_id'];
$res['name'] = Tools::unpack_signed_long_string($message_media['volume_id']).'_'.$message_media['local_id'];
$res['ext'] = '.jpg';
$res['mime'] = $this->get_mime_from_extension($res['ext'], 'image/jpeg');
$res['InputFileLocation'] = [
@ -730,7 +731,7 @@ trait Files
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], $this->get_extension_from_mime($message_media['document']['mime_type']));
}
if (!isset($res['name']) || $res['name'] === '') {
$res['name'] = $message_media['document']['access_hash'];
$res['name'] = Tools::unpack_signed_long_string($message_media['document']['access_hash']);
}
if (isset($message_media['document']['size'])) {
$res['size'] = $message_media['document']['size'];

View File

@ -25,24 +25,77 @@ use danog\MadelineProto\Tools;
use phpseclib\Math\BigInteger;
/**
* Manages password calculation.
* Manages SRP password calculation
*
* @author Daniil Gentili <daniil@daniil.it>
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
class PasswordCalculator
{
use AuthKeyHandler;
use Tools;
/**
* The algorithm to use for calculating the hash of new passwords (a PasswordKdfAlgo object)
*
* @var array
*/
private $new_algo;
/**
* A secure random string that can be used to compute the password
*
* @var string
*/
private $secure_random = '';
/**
* The algorithm to use for calculatuing the hash of the current password (a PasswordKdfAlgo object)
*
* @var array
*/
private $current_algo;
/**
* SRP b parameter
*
* @var BigInteger
*/
private $srp_B;
/**
* SRP b parameter for hashing
*
* @var BigInteger
*/
private $srp_BForHash;
/**
* SRP ID
*
* @var [type]
*/
private $srp_id;
/**
* Logger
*
* @var \danog\MadelineProto\Logger
*/
public $logger;
// This is needed do not remove this
public function __construct($logger) { $this->logger = $logger; }
/**
* Initialize logger
*
* @param \danog\MadelineProto\Logger $logger
*/
public function __construct($logger)
{
$this->logger = $logger;
}
/**
* Popupate 2FA configuration
*
* @param array $object 2FA configuration object obtained using account.getPassword
* @return void
*/
public function addInfo(array $object)
{
if ($object['_'] !== 'account.password') {
@ -101,16 +154,39 @@ class PasswordCalculator
$this->secure_random = $object['secure_random'];
}
/**
* Create a random string (eventually prefixed by the specified string)
*
* @param string $prefix Prefix
* @return string Salt
*/
public function createSalt(string $prefix = ''): string
{
return $prefix.$this->random(32);
}
/**
* Hash specified data using the salt with SHA256
*
* The result will be the SHA256 hash of the salt concatenated with the data concatenated with the salt
*
* @param string $data Data to hash
* @param string $salt Salt
* @return string Hash
*/
public function hashSha256(string $data, string $salt): string
{
return hash('sha256', $salt.$data.$salt, true);
}
/**
* Hashes the specified password
*
* @param string $password Password
* @param string $client_salt Client salt
* @param string $server_salt Server salt
* @return string Resulting hash
*/
public function hashPassword(string $password, string $client_salt, string $server_salt): string
{
$buf = $this->hashSha256($password, $client_salt);
@ -120,9 +196,15 @@ class PasswordCalculator
return $this->hashSha256($hash, $server_salt);
}
/**
* Get the InputCheckPassword object for checking the validity of a password using account.checkPassword
*
* @param string $password The password
* @return array InputCheckPassword object
*/
public function getCheckPassword(string $password): array
{
if ($password === '') {
if ($password === '' || !$this->current_algo) {
return ['_' => 'inputCheckPasswordEmpty'];
}
$client_salt = $this->current_algo['salt1'];
@ -131,6 +213,7 @@ class PasswordCalculator
$gForHash = $this->current_algo['gForHash'];
$p = $this->current_algo['p'];
$pForHash = $this->current_algo['pForHash'];
$B = $this->srp_B;
$BForHash = $this->srp_BForHash;
$id = $this->srp_id;
@ -166,9 +249,20 @@ class PasswordCalculator
return ['_' => 'inputCheckPasswordSRP', 'srp_id' => $id, 'A' => $AForHash, 'M1' => $M1];
}
/**
* Get parameters to be passed to the account.updatePasswordSettings to update/set a 2FA password
*
* The input params array can contain password, new_password, email and hint params.
*
* @param array $params Input params
* @return array account.updatePasswordSettings parameters
*/
public function getPassword(array $params): array
{
$return = ['password' => $this->getCheckPassword(isset($params['password']) ? $params['password'] : ''), 'new_settings' => ['_' => 'account.passwordInputSettings', 'new_algo' => ['_' => 'passwordKdfAlgoUnknown'], 'new_password_hash' => '', 'hint' => '']];
$oldPassword = $this->getCheckPassword($params['password'] ?? '');
$return = ['password' => $oldPassword, 'new_settings' => ['_' => 'account.passwordInputSettings', 'new_algo' => ['_' => 'passwordKdfAlgoUnknown'], 'new_password_hash' => '', 'hint' => '']];
$new_settings = &$return['new_settings'];
if (isset($params['new_password']) && $params['new_password'] !== '') {
@ -183,11 +277,11 @@ class PasswordCalculator
$vForHash = str_pad($v->toBytes(), 256, chr(0), \STR_PAD_LEFT);
$new_settings['new_algo'] = [
'_' => 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow',
'_' => 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow',
'salt1' => $client_salt,
'salt2' => $server_salt,
'g' => (int) $g->toString(),
'p' => $pForHash,
'g' => (int) $g->toString(),
'p' => $pForHash,
];
$new_settings['new_password_hash'] = $vForHash;
$new_settings['hint'] = $params['hint'];

View File

@ -312,7 +312,15 @@ trait ResponseHandler
if (isset($request['promise'])) {
$promise = $request['promise'];
unset($request['promise']);
$promise->fail($data);
try {
$promise->fail($data);
} catch (\Error $e) {
if (strpos($e->getMessage(), "Promise has already been resolved") !== 0) {
throw $e;
}
$this->logger->logger("Got promise already resolved error", \danog\MadelineProto\Logger::FATAL_ERROR);
}
} else {
$this->logger->logger('Rejecting: already got response for '.(isset($request['_']) ? $request['_'] : '-'));
$this->logger->logger("Rejecting: $data");
@ -580,7 +588,14 @@ trait ResponseHandler
if (isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$request_id]['promise'])) { // This should not happen but happens, should debug
$promise = $this->datacenter->sockets[$datacenter]->outgoing_messages[$request_id]['promise'];
unset($this->datacenter->sockets[$datacenter]->outgoing_messages[$request_id]['promise']);
$promise->resolve($response);
try {
$promise->resolve($response);
} catch (\Error $e) {
if (strpos($e->getMessage(), "Promise has already been resolved") !== 0) {
throw $e;
}
$this->logger->logger("Got promise already resolved error", \danog\MadelineProto\Logger::FATAL_ERROR);
}
}
}
)());

View File

@ -66,7 +66,7 @@ class Magic
public static function class_exists()
{
set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
//set_exception_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionHandler']);
set_exception_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionHandler']);
if (!self::$inited) {
if (!defined('\\phpseclib\\Crypt\\Common\\SymmetricKey::MODE_IGE') || \phpseclib\Crypt\Common\SymmetricKey::MODE_IGE !== 7) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['phpseclib_fork']);
@ -183,6 +183,12 @@ class Magic
//$this->logger->logger('Could not enable PHP logging');
}
}
$res = json_decode(@file_get_contents('https://rpc.pwrtelegram.xyz/?allv3'), true);
if (isset($res['ok']) && $res['ok']) {
RPCErrorException::$errorMethodMap = $res['result'];
RPCErrorException::$descriptions += $res['human_result'];
}
self::$inited = true;
}
}

View File

@ -25,21 +25,86 @@ class RPCErrorException extends \Exception
private $fetched = false;
public static $rollbar = true;
public function getMess()
public static $descriptions = [
'RPC_MCGET_FAIL' => 'Telegram is having internal issues, please try again later.',
'RPC_CALL_FAIL' => 'Telegram is having internal issues, please try again later.',
'USER_PRIVACY_RESTRICTED' => "The user's privacy settings do not allow you to do this",
'CHANNEL_PRIVATE' => "You haven't joined this channel/supergroup",
'USER_IS_BOT' => "Bots can't send messages to other bots",
'BOT_METHOD_INVALID' => 'This method cannot be run by a bot',
'PHONE_CODE_EXPIRED' => 'The phone code you provided has expired, this may happen if it was sent to any chat on telegram (if the code is sent through a telegram chat (not the official account) to avoid it append or prepend to the code some chars)',
'USERNAME_INVALID' => 'The provided username is not valid',
'ACCESS_TOKEN_INVALID' => 'The provided token is not valid',
'ACTIVE_USER_REQUIRED' => 'The method is only available to already activated users',
'FIRSTNAME_INVALID' => 'The first name is invalid',
'LASTNAME_INVALID' => 'The last name is invalid',
'PHONE_NUMBER_INVALID' => 'The phone number is invalid',
'PHONE_CODE_HASH_EMPTY' => 'phone_code_hash is missing',
'PHONE_CODE_EMPTY' => 'phone_code is missing',
'PHONE_CODE_EXPIRED' => 'The confirmation code has expired',
'API_ID_INVALID' => 'The api_id/api_hash combination is invalid',
'PHONE_NUMBER_OCCUPIED' => 'The phone number is already in use',
'PHONE_NUMBER_UNOCCUPIED' => 'The phone number is not yet being used',
'USERS_TOO_FEW' => 'Not enough users (to create a chat, for example)',
'USERS_TOO_MUCH' => 'The maximum number of users has been exceeded (to create a chat, for example)',
'TYPE_CONSTRUCTOR_INVALID' => 'The type constructor is invalid',
'FILE_PART_INVALID' => 'The file part number is invalid',
'FILE_PARTS_INVALID' => 'The number of file parts is invalid',
'MD5_CHECKSUM_INVALID' => 'The MD5 checksums do not match',
'PHOTO_INVALID_DIMENSIONS' => 'The photo dimensions are invalid',
'FIELD_NAME_INVALID' => 'The field with the name FIELD_NAME is invalid',
'FIELD_NAME_EMPTY' => 'The field with the name FIELD_NAME is missing',
'MSG_WAIT_FAILED' => 'A waiting call returned an error',
'USERNAME_NOT_OCCUPIED' => 'The provided username is not occupied',
'PHONE_NUMBER_BANNED' => 'The provided phone number is banned from telegram',
'AUTH_KEY_UNREGISTERED' => 'The authorization key has expired',
'INVITE_HASH_EXPIRED' => 'The invite link has expired',
'USER_DEACTIVATED' => 'The user was deactivated',
'USER_ALREADY_PARTICIPANT' => 'The user is already in the group',
'MESSAGE_ID_INVALID' => 'The provided message id is invalid',
'PEER_ID_INVALID' => 'The provided peer id is invalid',
'CHAT_ID_INVALID' => 'The provided chat id is invalid',
'MESSAGE_DELETE_FORBIDDEN' => "You can't delete one of the messages you tried to delete, most likely because it is a service message.",
'CHAT_ADMIN_REQUIRED' => 'You must be an admin in this chat to do this',
-429 => 'Too many requests',
'PEER_FLOOD' => "You are spamreported, you can't do this",
];
public static $errorMethodMap = [];
private $caller = '';
public static function localizeMessage($method, $code, $error)
{
if ($this->fetched === false) {
$res = json_decode(@file_get_contents('https://rpc.pwrtelegram.xyz/?method='.$additional[0].'&code='.$code.'&error='.$this->rpc), true);
if (!$method || !$code || !$error) {
return $error;
}
$error = preg_replace('/\d+$/', "X", $error);
$description = self::$descriptions[$error] ?? '';
if (!isset(self::$errorMethodMap[$code][$method][$error])
|| !isset(self::$descriptions[$error])
|| $code === 500
) {
$res = json_decode(@file_get_contents('https://rpc.pwrtelegram.xyz/?method='.$method.'&code='.$code.'&error='.$error), true);
if (isset($res['ok']) && $res['ok']) {
$this->message = $res['result'];
$description = $res['result'];
self::$descriptions[$error] = $description;
self::$errorMethodMap[$code][$method][$error] = $error;
}
}
return $this->message;
if (!$description) {
return $error;
}
return $description;
}
public function __toString()
{
$result = sprintf(\danog\MadelineProto\Lang::$current_lang['rpc_tg_error'], $this->getMess()." ({$this->code})", $this->rpc, $this->file, $this->line.PHP_EOL, \danog\MadelineProto\Magic::$revision.PHP_EOL.PHP_EOL).PHP_EOL.$this->getTLTrace().PHP_EOL;
$result = sprintf(\danog\MadelineProto\Lang::$current_lang['rpc_tg_error'], self::localizeMessage($this->caller, $this->code, $this->message)." ({$this->code})", $this->rpc, $this->file, $this->line.PHP_EOL, \danog\MadelineProto\Magic::$revision.PHP_EOL.PHP_EOL).PHP_EOL.$this->getTLTrace().PHP_EOL;
if (php_sapi_name() !== 'cli') {
$result = str_replace(PHP_EOL, '<br>'.PHP_EOL, $result);
}
@ -50,147 +115,19 @@ class RPCErrorException extends \Exception
public function __construct($message = null, $code = 0, $caller = '', Exception $previous = null)
{
$this->rpc = $message;
switch ($message) {
case 'RPC_MCGET_FAIL':
case 'RPC_CALL_FAIL':
$message = 'Telegram is having internal issues, please try again later.';
break;
case 'USER_PRIVACY_RESTRICTED':
$message = "The user's privacy settings do not allow you to do this";
break;
case 'CHANNEL_PRIVATE':
$message = "You haven't joined this channel/supergroup";
break;
case 'FLOOD_WAIT_666':
$message = 'Spooky af m8';
break;
case 'USER_IS_BOT':
$message = "Bots can't send messages to other bots";
break;
case 'BOT_METHOD_INVALID':
$message = 'This method cannot be run by a bot';
break;
case 'PHONE_CODE_EXPIRED':
$message = 'The phone code you provided has expired, this may happen if it was sent to any chat on telegram (if the code is sent through a telegram chat (not the official account) to avoid it append or prepend to the code some chars)';
break;
case 'USERNAME_INVALID':
$message = 'The provided username is not valid';
break;
case 'ACCESS_TOKEN_INVALID':
$message = 'The provided token is not valid';
break;
case 'ACTIVE_USER_REQUIRED':
$message = 'The method is only available to already activated users';
break;
case 'FIRSTNAME_INVALID':
$message = 'The first name is invalid';
break;
case 'LASTNAME_INVALID':
$message = 'The last name is invalid';
break;
case 'PHONE_NUMBER_INVALID':
$message = 'The phone number is invalid';
break;
case 'PHONE_CODE_HASH_EMPTY':
$message = 'phone_code_hash is missing';
break;
case 'PHONE_CODE_EMPTY':
$message = 'phone_code is missing';
break;
case 'PHONE_CODE_EXPIRED':
$message = 'The confirmation code has expired';
break;
case 'API_ID_INVALID':
$message = 'The api_id/api_hash combination is invalid';
break;
case 'PHONE_NUMBER_OCCUPIED':
$message = 'The phone number is already in use';
break;
case 'PHONE_NUMBER_UNOCCUPIED':
$message = 'The phone number is not yet being used';
break;
case 'USERS_TOO_FEW':
$message = 'Not enough users (to create a chat, for example)';
break;
case 'USERS_TOO_MUCH':
$message = 'The maximum number of users has been exceeded (to create a chat, for example)';
break;
case 'TYPE_CONSTRUCTOR_INVALID':
$message = 'The type constructor is invalid';
break;
case 'FILE_PART_INVALID':
$message = 'The file part number is invalid';
break;
case 'FILE_PARTS_INVALID':
$message = 'The number of file parts is invalid';
break;
case 'MD5_CHECKSUM_INVALID':
$message = 'The MD5 checksums do not match';
break;
case 'PHOTO_INVALID_DIMENSIONS':
$message = 'The photo dimensions are invalid';
break;
case 'FIELD_NAME_INVALID':
$message = 'The field with the name FIELD_NAME is invalid';
break;
case 'FIELD_NAME_EMPTY':
$message = 'The field with the name FIELD_NAME is missing';
break;
case 'MSG_WAIT_FAILED':
$message = 'A waiting call returned an error';
break;
case 'USERNAME_NOT_OCCUPIED':
$message = 'The provided username is not occupied';
break;
case 'PHONE_NUMBER_BANNED':
$message = 'The provided phone number is banned from telegram';
break;
case 'AUTH_KEY_UNREGISTERED':
$message = 'The authorization key has expired';
break;
case 'INVITE_HASH_EXPIRED':
$message = 'The invite link has expired';
break;
case 'USER_DEACTIVATED':
$message = 'The user was deactivated';
break;
case 'USER_ALREADY_PARTICIPANT':
$message = 'The user is already in the group';
break;
case 'MESSAGE_ID_INVALID':
$message = 'The provided message id is invalid';
break;
case 'PEER_ID_INVALID':
$message = 'The provided peer id is invalid';
break;
case 'CHAT_ID_INVALID':
$message = 'The provided chat id is invalid';
break;
case 'MESSAGE_DELETE_FORBIDDEN':
$message = "You can't delete one of the messages you tried to delete, most likely because it is a service message.";
break;
case 'CHAT_ADMIN_REQUIRED':
$message = 'You must be an admin in this chat to do this';
break;
case -429:
case 'PEER_FLOOD':
$message = 'Too many requests';
break;
}
parent::__construct($message, $code, $previous);
$this->prettify_tl($caller);
$this->caller = $caller;
$additional = [];
foreach ($this->getTrace() as $level) {
if (isset($level['function']) && $level['function'] === 'method_call') {
$this->line = $level['line'];
$this->file = $level['file'];
$additional = $level['args'];
break;
}
}
if ($this->rpc !== $message) {
$this->fetched = true;
}
if (!self::$rollbar || !class_exists('\\Rollbar\\Rollbar')) {
return;
}

View File

@ -24,11 +24,6 @@ namespace danog\MadelineProto;
*/
class Serialization
{
public static function serialize_all($exception)
{
echo $exception.PHP_EOL;
}
public static function realpaths($file)
{
$file = Absolute::absolute($file);

View File

@ -194,6 +194,7 @@ class ObfuscatedStream implements BufferedProxyStreamInterface
$extra['secret'] = substr($extra['secret'], 1, 16);
}
}
$this->extra = $extra;
}

View File

@ -84,6 +84,7 @@ class WsStream implements RawStreamInterface
break;
}
}
if (!$this->stream) {
throw new ConnectionException('Failed to read response from server');
}

View File

@ -1050,17 +1050,20 @@ invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
auth.signUp#1b067634 phone_number:string phone_code_hash:string phone_code:string first_name:string last_name:string = auth.Authorization;
auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization;
auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode;
auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool;
auth.logOut#5717da40 = Bool;
auth.resetAuthorizations#9fab0d1a = Bool;
auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization;
auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization;
auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool;
auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization;
auth.checkPassword#d18b4d16 password:InputCheckPasswordSRP = auth.Authorization;
auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery;
auth.recoverPassword#4ea56e92 code:string = auth.Authorization;
auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode;
auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool;
auth.dropTempAuthKeys#8e48a188 except_auth_keys:Vector<long> = Bool;
account.registerDevice#5cbea590 token_type:int token:string app_sandbox:Bool secret:bytes other_uids:Vector<int> = Bool;

View File

@ -23,6 +23,9 @@ use Amp\Failure;
use Amp\Loop;
use Amp\Promise;
use Amp\Success;
use function Amp\ByteStream\getOutputBufferStream;
use function Amp\ByteStream\getStdin;
use function Amp\ByteStream\getStdout;
use function Amp\Promise\all;
use function Amp\Promise\any;
use function Amp\Promise\first;
@ -35,6 +38,7 @@ use function Amp\ByteStream\getOutputBufferStream;
use function Amp\File\exists;
use function Amp\File\touch;
use Amp\File\StatCache;
use phpseclib\Math\BigInteger;
/**
* Some tools.
@ -122,6 +126,16 @@ trait Tools
return unpack('q', \danog\MadelineProto\Magic::$BIG_ENDIAN ? strrev($value) : $value)[1];
}
public static function unpack_signed_long_string($value)
{
if (strlen($value) !== 8) {
throw new TL\Exception(\danog\MadelineProto\Lang::$current_lang['length_not_8']);
}
$big = new BigInteger($value, -256);
return (string) $big;
}
public static function pack_signed_int($value)
{
if ($value > 2147483647) {
@ -194,6 +208,7 @@ trait Tools
try {
Loop::run(function () use (&$resolved, &$value, &$exception, $promise) {
$promise->onResolve(function ($e, $v) use (&$resolved, &$value, &$exception) {
Loop::stop();
$resolved = true;
$exception = $e;
@ -308,12 +323,21 @@ trait Tools
if ($file) {
$file = " started @ $file";
}
if ($logger) $logger->logger("Got the following exception within a forked strand$file, trying to rethrow");
if ($logger) {
$logger->logger("Got the following exception within a forked strand$file, trying to rethrow");
}
if ($e->getMessage() === "Cannot get return value of a generator that hasn't returned") {
$logger->logger("Well you know, this might actually not be the actual exception, scroll up in the logs to see the actual exception");
if (!$zis || !$zis->destructing) Promise\rethrow(new Failure($e));
if (!$zis || !$zis->destructing) {
Promise\rethrow(new Failure($e));
}
} else {
if ($logger) $logger->logger($e);
if ($logger) {
$logger->logger($e);
}
Promise\rethrow(new Failure($e));
}
}
@ -332,7 +356,7 @@ trait Tools
return;
}
$b = self::call($b());
$b->onResolve(static function ($e, $res) use ($deferred) {
$b->onResolve(function ($e, $res) use ($deferred) {
if ($e) {
if (isset($this)) {
$this->rethrow($e, $file);
@ -415,8 +439,7 @@ trait Tools
return array_shift($lines);
}
public static function echo($string)
{
public static function echo ($string) {
return getOutputBufferStream()->write($string);
}
public static function is_array_or_alike($var)

View File

@ -205,7 +205,7 @@ trait Login
/**
* Update the 2FA password
*
* The params can contain password, new_password, email and hint params.
* The params array can contain password, new_password, email and hint params.
*
* @param array $params The params
* @return void

View File

@ -68,6 +68,9 @@ trait Loop
} catch (\danog\MadelineProto\Exception $e) {
$needs_restart = true;
}
if (isset($_REQUEST['MadelineSelfRestart'])) {
$this->logger->logger("Self-restarted, restart token ".$_REQUEST['MadelineSelfRestart']);
}
$this->logger->logger($needs_restart ? 'Will self-restart' : 'Will not self-restart');
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
@ -105,9 +108,31 @@ trait Loop
if ($needs_restart) {
$logger = &$this->logger;
Shutdown::addCallback(static function () use (&$logger) {
$a = fsockopen((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] ? 'tls' : 'tcp').'://'.$_SERVER['SERVER_NAME'], $_SERVER['SERVER_PORT']);
fwrite($a, $_SERVER['REQUEST_METHOD'].' '.$_SERVER['REQUEST_URI'].' '.$_SERVER['SERVER_PROTOCOL']."\r\n".'Host: '.$_SERVER['SERVER_NAME']."\r\n\r\n");
$logger->logger('Self-restarted');
$address = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] ? 'tls' : 'tcp').'://'.$_SERVER['SERVER_NAME'];
$port = $_SERVER['SERVER_PORT'];
$uri = $_SERVER['REQUEST_URI'];
$params = $_GET;
$params['MadelineSelfRestart'] = $this->random_int();
list($url, $query) = explode($uri, '?', 2);
$query = http_build_query($params);
$uri = implode('?', [$url, $query]);
$payload = $_SERVER['REQUEST_METHOD'].' '.$uri.' '.$_SERVER['SERVER_PROTOCOL']."\r\n".'Host: '.$_SERVER['SERVER_NAME']."\r\n\r\n";
$logger->logger("Connecting to $address:$port");
$a = fsockopen($address, $port);
$logger->logger("Sending self-restart payload");
$logger->logger($payload);
fwrite($a, $payload);
$logger->logger("Payload sent with token {$params['MadelineSelfRestart']}, waiting for self-restart");
sleep(10);
fclose($a);
}, 'restarter');
}
@ -164,5 +189,8 @@ trait Loop
ob_end_flush();
flush();
$GLOBALS['exited'] = true;
if (function_exists('fastcgi_finish_request')) {
\fastcgi_finish_request();
}
}
}

View File

@ -52,7 +52,7 @@ cd ..
sudo apt-get update -q
sudo apt-get install php7.3-cli php7.3-json php7.3-mbstring php7.3-curl php7.3-xml php7.3-json -y
composer global require spatie/7to5 dev-master#5c65f68
composer global require spatie/7to5 dev-master#d4be6d0
[ -f $HOME/.composer/vendor/bin/php7to5 ] && php7to5=$HOME/.composer/vendor/bin/php7to5
[ -f $HOME/.config/composer/vendor/bin/php7to5 ] && php7to5=$HOME/.config/composer/vendor/bin/php7to5

View File

@ -37,7 +37,7 @@ if (file_exists('.env')) {
$dotenv->load();
}
if (getenv('TEST_SECRET_CHAT') == '') {
echo('TEST_SECRET_CHAT is not defined in .env, please define it (copy .env.example).'.PHP_EOL);
echo ('TEST_SECRET_CHAT is not defined in .env, please define it (copy .env.example).'.PHP_EOL);
die(1);
}
echo 'Loading settings...'.PHP_EOL;