Apply fixes from StyleCI

This commit is contained in:
Daniil Gentili 2018-02-24 16:54:39 +00:00 committed by StyleCI Bot
parent 5519782618
commit d3cff5e0af
58 changed files with 1260 additions and 432 deletions

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto;
class API extends APIFactory
@ -17,6 +18,7 @@ class API extends APIFactory
use \danog\Serializable;
public $session;
public $serialized = 0;
public function __magic_construct($params = [])
{
set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
@ -32,6 +34,7 @@ class API extends APIFactory
$realpaths['lockfile'] = fopen($realpaths['lockfile'], 'r');
\danog\MadelineProto\Logger::log(['Waiting for shared lock of serialization lockfile...']);
flock($realpaths['lockfile'], LOCK_SH);
try {
$unserialized = file_get_contents($realpaths['file']);
} finally {
@ -40,10 +43,11 @@ class API extends APIFactory
}
$tounserialize = str_replace('O:26:"danog\\MadelineProto\\Button":', 'O:35:"danog\\MadelineProto\\TL\\Types\\Button":', $unserialized);
foreach (['RSA', 'TL\\TLMethod', 'TL\\TLConstructor', 'MTProto', 'API', 'DataCenter', 'Connection', 'TL\\Types\\Button', 'TL\\Types\\Bytes', 'APIFactory'] as $class) {
class_exists('\\danog\\MadelineProto\\' . $class);
class_exists('\\danog\\MadelineProto\\'.$class);
}
class_exists('\\Volatile');
\danog\MadelineProto\Logger::class_exists();
try {
$unserialized = unserialize($tounserialize);
} catch (\danog\MadelineProto\Bug74586Exception $e) {
@ -69,6 +73,7 @@ class API extends APIFactory
$this->APIFactory();
}
Serialization::$instances[spl_object_hash($unserialized)] = $unserialized;
return;
}
$this->API = new MTProto($params);
@ -76,18 +81,20 @@ class API extends APIFactory
$this->APIFactory();
\danog\MadelineProto\Logger::log(['Ping...'], Logger::ULTRA_VERBOSE);
$pong = $this->ping(['ping_id' => 3]);
\danog\MadelineProto\Logger::log(['Pong: ' . $pong['ping_id']], Logger::ULTRA_VERBOSE);
\danog\MadelineProto\Logger::log(['Pong: '.$pong['ping_id']], Logger::ULTRA_VERBOSE);
//\danog\MadelineProto\Logger::log(['Getting future salts...'], Logger::ULTRA_VERBOSE);
//$this->future_salts = $this->get_future_salts(['num' => 3]);
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['madelineproto_ready']], Logger::NOTICE);
Serialization::$instances[spl_object_hash($this)] = $this;
}
public function __wakeup()
{
//if (method_exists($this->API, 'wakeup')) $this->API = $this->API->wakeup();
Serialization::$instances[spl_object_hash($this)] = $this;
$this->APIFactory();
}
public function __destruct()
{
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
@ -98,45 +105,56 @@ class API extends APIFactory
}
restore_error_handler();
}
public function __sleep()
{
return ['API'];
}
public function &__get($name)
{
if ($name === 'settings') {
$this->API->setdem = true;
return $this->API->settings;
}
return $this->API->storage[$name];
}
public function __set($name, $value)
{
if ($name === 'settings') {
return $this->API->__construct($value);
}
return $this->API->storage[$name] = $value;
}
public function __isset($name)
{
return isset($this->API->storage[$name]);
}
public function __unset($name)
{
unset($this->API->storage[$name]);
}
public function APIFactory()
{
foreach ($this->API->get_method_namespaces() as $namespace) {
$this->{$namespace} = new APIFactory($namespace, $this->API);
}
}
public function serialize($params = '')
{
if ($params === '') {
$params = $this->session;
}
Logger::log([\danog\MadelineProto\Lang::$current_lang['serializing_madelineproto']]);
return Serialization::serialize($params, $this);
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto;
class APIFactory
@ -108,11 +109,13 @@ class APIFactory
public $namespace = '';
public $API;
public $lua = false;
public function __construct($namespace, $API)
{
$this->namespace = $namespace . '.';
$this->namespace = $namespace.'.';
$this->API = $API;
}
public function __call($name, $arguments)
{
if ($this->API->setdem) {
@ -127,11 +130,13 @@ class APIFactory
$this->serialize($this->session);
}
if ($this->lua === false) {
return method_exists($this->API, $this->namespace . $name) ? $this->API->{$this->namespace . $name}(...$arguments) : $this->API->method_call($this->namespace . $name, isset($arguments[0]) && is_array($arguments[0]) ? $arguments[0] : [], $aargs);
return method_exists($this->API, $this->namespace.$name) ? $this->API->{$this->namespace.$name}(...$arguments) : $this->API->method_call($this->namespace.$name, isset($arguments[0]) && is_array($arguments[0]) ? $arguments[0] : [], $aargs);
}
try {
$deserialized = method_exists($this->API, $this->namespace . $name) ? $this->API->{$this->namespace . $name}(...$arguments) : $this->API->method_call($this->namespace . $name, isset($arguments[0]) && is_array($arguments[0]) ? $arguments[0] : [], $aargs);
$deserialized = method_exists($this->API, $this->namespace.$name) ? $this->API->{$this->namespace.$name}(...$arguments) : $this->API->method_call($this->namespace.$name, isset($arguments[0]) && is_array($arguments[0]) ? $arguments[0] : [], $aargs);
Lua::convert_objects($deserialized);
return $deserialized;
} catch (\danog\MadelineProto\Exception $e) {
return ['error_code' => $e->getCode(), 'error' => $e->getMessage()];
@ -149,4 +154,4 @@ class APIFactory
return ['error_code' => $e->getCode(), 'error' => $e->getMessage()];
}
}
}
}

View File

@ -10,24 +10,29 @@ 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/>.
*/
namespace danog\MadelineProto;
use phpDocumentor\Reflection\DocBlockFactory;
class AnnotationsBuilder
{
use \danog\MadelineProto\TL\TL;
use Tools;
public function __construct($settings)
{
$this->construct_TL($settings['tl_schema']);
$this->settings = $settings;
}
public function mk_annotations()
{
\danog\MadelineProto\Logger::log(['Generating annotations...'], \danog\MadelineProto\Logger::NOTICE);
$this->setProperties();
$this->createInternalClasses();
}
/**
* Open file of class APIFactory
* Insert properties
@ -43,22 +48,23 @@ class AnnotationsBuilder
if ($raw_docblock = $property->getDocComment()) {
$docblock = $fixture->create($raw_docblock);
if ($docblock->hasTag('internal')) {
$content = str_replace("\n " . $raw_docblock . "\n public \$" . $property->getName() . ';', '', $content);
$content = str_replace("\n ".$raw_docblock."\n public \$".$property->getName().';', '', $content);
}
}
}
foreach ($this->get_method_namespaces() as $namespace) {
$content = preg_replace('/(class( \\w+[,]?){0,}\\n{\\n)/', '${1}' . " /**\n" . " * @internal this is a internal property generated by build_docs.php, don't change manually\n" . " *\n" . " * @var {$namespace}\n" . " */\n" . " public \${$namespace};\n", $content);
$content = preg_replace('/(class( \\w+[,]?){0,}\\n{\\n)/', '${1}'." /**\n"." * @internal this is a internal property generated by build_docs.php, don't change manually\n"." *\n"." * @var {$namespace}\n"." */\n"." public \${$namespace};\n", $content);
}
file_put_contents($filename, $content);
}
/**
* Create file InternalDoc with all interfaces.
*/
private function createInternalClasses()
{
\danog\MadelineProto\Logger::log(['Creating internal classes...'], \danog\MadelineProto\Logger::NOTICE);
$handle = fopen(dirname(__FILE__) . '/InternalDoc.php', 'w');
$handle = fopen(dirname(__FILE__).'/InternalDoc.php', 'w');
foreach ($this->methods->by_id as $id => $data) {
if (!strpos($data['method'], '.')) {
continue;
@ -120,4 +126,4 @@ class AnnotationsBuilder
}
fclose($handle);
}
}
}

View File

@ -10,8 +10,9 @@ 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/>.
*/
namespace danog\MadelineProto;
class Bug74586Exception extends \Exception
{
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto;
/**
@ -172,6 +173,7 @@ class Connection
break;
}
}
public function __destruct()
{
switch ($this->protocol) {
@ -194,15 +196,18 @@ class Connection
break;
}
}
public function close_and_reopen()
{
$this->__destruct();
$this->must_open = true;
}
public function __sleep()
{
return ['proxy', 'extra', 'protocol', 'ip', 'port', 'timeout', 'parsed', 'time_delta', 'temp_auth_key', 'auth_key', 'session_id', 'session_out_seq_no', 'session_in_seq_no', 'ipv6', 'incoming_messages', 'outgoing_messages', 'new_incoming', 'new_outgoing', 'max_incoming_id', 'max_outgoing_id', 'obfuscated', 'authorized', 'object_queue', 'ack_queue'];
}
public function __wakeup()
{
$keys = array_keys((array) get_object_vars($this));
@ -212,6 +217,7 @@ class Connection
$this->time_delta = 0;
//$this->__construct($this->proxy, $this->extra, $this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6);
}
public function write($what, $length = null)
{
if ($this->must_open) {
@ -237,6 +243,7 @@ class Connection
while (($wrote += $this->sock->write(substr($what, $wrote))) !== $length) {
}
}
return $wrote;
break;
case 'udp':
@ -247,6 +254,7 @@ class Connection
break;
}
}
public function read($length)
{
if ($this->must_open) {
@ -264,8 +272,10 @@ class Connection
}
if (strlen($packet) !== $length) {
$this->close_and_reopen();
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['wrong_length_read'], $length, strlen($packet)));
}
return @$this->obfuscated['decryption']->encrypt($packet);
case 'tcp_abridged':
case 'tcp_intermediate':
@ -282,8 +292,10 @@ class Connection
}
if (strlen($packet) !== $length) {
$this->close_and_reopen();
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['wrong_length_read'], $length, strlen($packet)));
}
return $packet;
case 'udp':
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']);
@ -292,6 +304,7 @@ class Connection
break;
}
}
public function read_message()
{
switch ($this->protocol) {
@ -299,7 +312,7 @@ class Connection
$packet_length_data = $this->read(4);
$packet_length = unpack('V', $packet_length_data)[1];
$packet = $this->read($packet_length - 4);
if (strrev(hash('crc32b', $packet_length_data . substr($packet, 0, -4), true)) !== substr($packet, -4)) {
if (strrev(hash('crc32b', $packet_length_data.substr($packet, 0, -4), true)) !== substr($packet, -4)) {
throw new Exception('CRC32 was not correct!');
}
$this->in_seq_no++;
@ -307,13 +320,15 @@ class Connection
if ($in_seq_no != $this->in_seq_no) {
throw new Exception('Incoming seq_no mismatch');
}
return substr($packet, 4, $packet_length - 12);
case 'tcp_intermediate':
return $this->read(unpack('V', $this->read(4))[1]);
case 'obfuscated2':
case 'tcp_abridged':
$packet_length = ord($this->read(1));
return $this->read($packet_length < 127 ? $packet_length << 2 : unpack('V', $this->read(3) . "\0")[1] << 2);
return $this->read($packet_length < 127 ? $packet_length << 2 : unpack('V', $this->read(3)."\0")[1] << 2);
case 'http':
case 'https':
case 'https_proxied':
@ -328,37 +343,39 @@ class Connection
if ($close) {
$this->close_and_reopen();
}
return $response['body'];
case 'udp':
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']);
}
}
public function send_message($message)
{
switch ($this->protocol) {
case 'tcp_full':
$this->out_seq_no++;
$step1 = pack('VV', strlen($message) + 12, $this->out_seq_no) . $message;
$step2 = $step1 . strrev(hash('crc32b', $step1, true));
$step1 = pack('VV', strlen($message) + 12, $this->out_seq_no).$message;
$step2 = $step1.strrev(hash('crc32b', $step1, true));
$this->write($step2);
break;
case 'tcp_intermediate':
$this->write(pack('V', strlen($message)) . $message);
$this->write(pack('V', strlen($message)).$message);
break;
case 'obfuscated2':
case 'tcp_abridged':
$len = strlen($message) / 4;
if ($len < 127) {
$message = chr($len) . $message;
$message = chr($len).$message;
} else {
$message = chr(127) . substr(pack('V', $len), 0, 3) . $message;
$message = chr(127).substr(pack('V', $len), 0, 3).$message;
}
$this->write($message);
break;
case 'http':
case 'https':
case 'https_proxied':
$this->write('POST ' . $this->parsed['path'] . " HTTP/1.1\r\nHost: " . $this->parsed['host'] . "\r\nContent-Type: application/x-www-form-urlencoded\r\nConnection: keep-alive\r\nKeep-Alive: timeout=100000, max=10000000\r\nContent-Length: " . strlen($message) . "\r\n\r\n" . $message);
$this->write('POST '.$this->parsed['path']." HTTP/1.1\r\nHost: ".$this->parsed['host']."\r\nContent-Type: application/x-www-form-urlencoded\r\nConnection: keep-alive\r\nKeep-Alive: timeout=100000, max=10000000\r\nContent-Length: ".strlen($message)."\r\n\r\n".$message);
break;
case 'udp':
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']);
@ -370,35 +387,32 @@ class Connection
public function read_http_line()
{
$line = '';
while (($curchar = $this->read(1)) !== "\n")
{
$line.= $curchar;
while (($curchar = $this->read(1)) !== "\n") {
$line .= $curchar;
}
return rtrim($line);
}
function read_http_payload()
public function read_http_payload()
{
$header = explode(' ', $this->read_http_line() , 3);
$header = explode(' ', $this->read_http_line(), 3);
$protocol = $header[0];
$code = (int)$header[1];
$code = (int) $header[1];
$description = $header[2];
$headers = [];
while (strlen($current_header = $this->read_http_line())) {
$current_header = explode(':', $current_header, 2);
$headers[strtolower($current_header[0]) ] = trim($current_header[1]);
$headers[strtolower($current_header[0])] = trim($current_header[1]);
}
$read = '';
if (isset($headers['content-length']))
{
$read = $this->read((int)$headers['content-length']);
if (isset($headers['content-length'])) {
$read = $this->read((int) $headers['content-length']);
} elseif (isset($headers['transfer-encoding']) && $headers['transfer-encoding'] === 'chunked') {
do
{
do {
$length = hexdec($this->read_http_line($res));
$read.= $this->read($length);
$read .= $this->read($length);
$this->read_http_line($res);
} while ($length);
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto;
/**
@ -23,10 +24,12 @@ class DataCenter
public $curdc = 0;
private $dclist = [];
private $settings = [];
public function __sleep()
{
return ['sockets', 'curdc', 'dclist', 'settings'];
}
public function __magic_construct($dclist, $settings)
{
$this->dclist = $dclist;
@ -41,6 +44,7 @@ class DataCenter
}
}
}
public function dc_disconnect($dc_number)
{
if ($this->curdc === $dc_number) {
@ -51,6 +55,7 @@ class DataCenter
unset($this->sockets[$dc_number]);
}
}
public function dc_connect($dc_number)
{
if (isset($this->sockets[$dc_number]) && !isset($this->sockets[$dc_number]->old)) {
@ -63,6 +68,7 @@ class DataCenter
$ipv6 = $this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4';
if (!isset($this->dclist[$test][$ipv6][$dc_number]['ip_address'])) {
unset($this->sockets[$dc_number]);
return false;
}
$address = $this->dclist[$test][$ipv6][$dc_number]['ip_address'];
@ -79,18 +85,19 @@ class DataCenter
if (strpos($this->settings[$dc_config_number]['protocol'], 'https') === 0) {
$subdomain = $this->dclist['ssl_subdomains'][$dc_number];
$path = $this->settings[$dc_config_number]['test_mode'] ? 'apiw_test1' : 'apiw1';
$address = 'https://' . $subdomain . '.web.telegram.org/' . $path;
$address = 'https://'.$subdomain.'.web.telegram.org/'.$path;
$port = 443;
}
if ($this->settings[$dc_config_number]['protocol'] === 'http') {
if ($ipv6) {
$address = '[' . $address . ']';
$address = '['.$address.']';
}
$address = $this->settings[$dc_config_number]['protocol'] . '://' . $address . '/api';
$address = $this->settings[$dc_config_number]['protocol'].'://'.$address.'/api';
}
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['dc_con_test_start'], $dc_number, $test, $ipv6, $this->settings[$dc_config_number]['protocol'])], \danog\MadelineProto\Logger::VERBOSE);
foreach (array_unique([$port, 443, 80, 88]) as $port) {
\danog\MadelineProto\Logger::log(['Trying connection on port ' . $port . '...'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['Trying connection on port '.$port.'...'], \danog\MadelineProto\Logger::WARNING);
try {
if (isset($this->sockets[$dc_number]->old)) {
$this->sockets[$dc_number]->__construct($this->settings[$dc_config_number]['proxy'], $this->settings[$dc_config_number]['proxy_extra'], $address, $port, $this->settings[$dc_config_number]['protocol'], $this->settings[$dc_config_number]['timeout'], $this->settings[$dc_config_number]['ipv6']);
@ -99,6 +106,7 @@ class DataCenter
$this->sockets[$dc_number] = new Connection($this->settings[$dc_config_number]['proxy'], $this->settings[$dc_config_number]['proxy_extra'], $address, $port, $this->settings[$dc_config_number]['protocol'], $this->settings[$dc_config_number]['timeout'], $this->settings[$dc_config_number]['ipv6']);
}
\danog\MadelineProto\Logger::log(['OK!'], \danog\MadelineProto\Logger::WARNING);
return true;
} catch (\danog\MadelineProto\Exception $e) {
} catch (\danog\MadelineProto\NothingInTheSocketException $e) {
@ -107,26 +115,29 @@ class DataCenter
switch ($x) {
case 0:
$this->settings[$dc_config_number]['ipv6'] = !$this->settings[$dc_config_number]['ipv6'];
\danog\MadelineProto\Logger::log(['Connection failed, retrying connection with ' . ($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4') . '...'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['Connection failed, retrying connection with '.($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4').'...'], \danog\MadelineProto\Logger::WARNING);
continue;
case 1:
$this->settings[$dc_config_number]['proxy'] = '\\Socket';
\danog\MadelineProto\Logger::log(['Connection failed, retrying connection without the proxy with ' . ($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4') . '...'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['Connection failed, retrying connection without the proxy with '.($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4').'...'], \danog\MadelineProto\Logger::WARNING);
continue;
case 2:
$this->settings[$dc_config_number]['ipv6'] = !$this->settings[$dc_config_number]['ipv6'];
\danog\MadelineProto\Logger::log(['Connection failed, retrying connection without the proxy with ' . ($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4') . '...'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['Connection failed, retrying connection without the proxy with '.($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4').'...'], \danog\MadelineProto\Logger::WARNING);
continue;
default:
return false;
}
} while (++$x);
return false;
}
public function get_dcs($all = true)
{
$test = $this->settings['all']['test_mode'] ? 'test' : 'main';
$ipv6 = $this->settings['all']['ipv6'] ? 'ipv6' : 'ipv4';
return $all ? array_keys((array) $this->dclist[$test][$ipv6]) : array_keys((array) $this->sockets);
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto;
class DocsBuilder
@ -19,6 +20,7 @@ class DocsBuilder
use \danog\MadelineProto\DocsBuilder\Constructors;
use Tools;
public $td = false;
public function __construct($settings)
{
set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
@ -35,24 +37,28 @@ class DocsBuilder
chdir($this->settings['output_dir']);
$this->index = $settings['readme'] ? 'README.md' : 'index.md';
}
public $types = [];
public $any = '*';
public function end($what)
{
return end($what);
}
public function escape($hwat)
{
return str_replace('_', '\\_', $hwat);
}
public function mk_docs()
{
\danog\MadelineProto\Logger::log(['Generating documentation index...'], \danog\MadelineProto\Logger::NOTICE);
file_put_contents($this->index, '---
title: ' . $this->settings['title'] . '
description: ' . $this->settings['description'] . '
title: '.$this->settings['title'].'
description: '.$this->settings['description'].'
---
# ' . $this->settings['description'] . '
# '.$this->settings['description'].'
[Methods](methods/)
@ -81,14 +87,14 @@ description: ' . $this->settings['description'] . '
//$br = $new_namespace != $last_namespace ? '***<br><br>' : '';
$type = str_replace(['.', '<', '>'], ['_', '_of_', ''], $otype);
$type = preg_replace('/.*_of_/', '', $type);
$index .= '[' . str_replace('_', '\\_', $type) . '](' . $type . '.md)<a name="' . $type . '"></a>
$index .= '['.str_replace('_', '\\_', $type).']('.$type.'.md)<a name="'.$type.'"></a>
';
$constructors = '';
foreach ($keys['constructors'] as $data) {
$predicate = str_replace('.', '_', $data['predicate']) . (isset($data['layer']) && $data['layer'] !== '' ? '_' . $data['layer'] : '');
$predicate = str_replace('.', '_', $data['predicate']).(isset($data['layer']) && $data['layer'] !== '' ? '_'.$data['layer'] : '');
$md_predicate = str_replace('_', '\\_', $predicate);
$constructors .= '[' . $md_predicate . '](../constructors/' . $predicate . '.md)
$constructors .= '['.$md_predicate.'](../constructors/'.$predicate.'.md)
';
}
@ -96,38 +102,38 @@ description: ' . $this->settings['description'] . '
foreach ($keys['methods'] as $data) {
$name = str_replace('.', '_', $data['method']);
$md_name = str_replace('_', '->', $name);
$methods .= '[$MadelineProto->' . $md_name . '](../methods/' . $name . '.md)
$methods .= '[$MadelineProto->'.$md_name.'](../methods/'.$name.'.md)
';
}
$description = isset($this->td_descriptions['types'][$otype]) ? $this->td_descriptions['types'][$otype] : 'constructors and methods of typr ' . $type;
$description = isset($this->td_descriptions['types'][$otype]) ? $this->td_descriptions['types'][$otype] : 'constructors and methods of typr '.$type;
$header = '---
title: ' . $type . '
description: constructors and methods of type ' . $type . '
title: '.$type.'
description: constructors and methods of type '.$type.'
---
## Type: ' . str_replace('_', '\\_', $type) . '
## Type: '.str_replace('_', '\\_', $type).'
[Back to types index](index.md)
';
$header .= isset($this->td_descriptions['types'][$otype]) ? $this->td_descriptions['types'][$otype] . PHP_EOL . PHP_EOL : '';
$header .= isset($this->td_descriptions['types'][$otype]) ? $this->td_descriptions['types'][$otype].PHP_EOL.PHP_EOL : '';
if (!isset($this->settings['td'])) {
if (in_array($type, ['User', 'InputUser', 'Chat', 'InputChannel', 'Peer', 'InputPeer'])) {
$header .= 'The following syntaxes can also be used:
```
$' . $type . " = '@username'; // Username\n\n\$" . $type . ' = 44700; // bot API id (users)
$' . $type . ' = -492772765; // bot API id (chats)
$' . $type . ' = -10038575794; // bot API id (channels)
$'.$type." = '@username'; // Username\n\n\$".$type.' = 44700; // bot API id (users)
$'.$type.' = -492772765; // bot API id (chats)
$'.$type.' = -10038575794; // bot API id (channels)
$' . $type . " = 'user#44700'; // tg-cli style id (users)\n\$" . $type . " = 'chat#492772765'; // tg-cli style id (chats)\n\$" . $type . " = 'channel#38575794'; // tg-cli style id (channels)\n```\n\nA [Chat](Chat.md), a [User](User.md), an [InputPeer](InputPeer.md), an [InputUser](InputUser.md), an [InputChannel](InputChannel.md), a [Peer](Peer.md), or a [Chat](Chat.md) object can also be used.\n\n\n";
$'.$type." = 'user#44700'; // tg-cli style id (users)\n\$".$type." = 'chat#492772765'; // tg-cli style id (chats)\n\$".$type." = 'channel#38575794'; // tg-cli style id (channels)\n```\n\nA [Chat](Chat.md), a [User](User.md), an [InputPeer](InputPeer.md), an [InputUser](InputUser.md), an [InputChannel](InputChannel.md), a [Peer](Peer.md), or a [Chat](Chat.md) object can also be used.\n\n\n";
}
if (in_array($type, ['InputEncryptedChat'])) {
$header .= 'The following syntax can also be used:
```
$' . $type . ' = -147286699; // Numeric chat id returned by request_secret_chat, can be positive or negative
$'.$type.' = -147286699; // Numeric chat id returned by request_secret_chat, can be positive or negative
```
@ -139,7 +145,7 @@ $' . $type . ' = -147286699; // Numeric chat id returned by request_secret_chat,
To click these buttons simply run the `click` method:
```
$result = $' . $type . '->click();
$result = $'.$type.'->click();
```
`$result` can be one of the following:
@ -159,12 +165,12 @@ $result = $' . $type . '->click();
}
$constructors = '### Possible values (constructors):
' . $constructors . '
'.$constructors.'
';
$methods = '### Methods that return an object of this type (methods):
' . $methods . '
'.$methods.'
';
if (!isset($this->settings['td'])) {
@ -351,14 +357,14 @@ After modifying it, you must always parse the new configuration with a call to `
';
}
}
if (file_exists('types/' . $type . '.md')) {
if (file_exists('types/'.$type.'.md')) {
\danog\MadelineProto\Logger::log([$type]);
}
file_put_contents('types/' . $type . '.md', $header . $constructors . $methods);
file_put_contents('types/'.$type.'.md', $header.$constructors.$methods);
$last_namespace = $new_namespace;
}
\danog\MadelineProto\Logger::log(['Generating types index...'], \danog\MadelineProto\Logger::NOTICE);
file_put_contents('types/' . $this->index, '---
file_put_contents('types/'.$this->index, '---
title: Types
description: List of types
---
@ -366,7 +372,7 @@ description: List of types
[Back to API documentation index](..)
' . $index);
'.$index);
\danog\MadelineProto\Logger::log(['Generating additional types...'], \danog\MadelineProto\Logger::NOTICE);
file_put_contents('types/string.md', '---
title: string
@ -514,4 +520,4 @@ Any json-encodable data.
');
\danog\MadelineProto\Logger::log(['Done!'], \danog\MadelineProto\Logger::NOTICE);
}
}
}

View File

@ -10,13 +10,14 @@ 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/>.
*/
namespace danog\MadelineProto\DocsBuilder;
trait Constructors
{
public function mk_constructors()
{
foreach (glob('constructors/' . $this->any) as $unlink) {
foreach (glob('constructors/'.$this->any) as $unlink) {
unlink($unlink);
}
if (file_exists('constructors')) {
@ -36,7 +37,7 @@ trait Constructors
if (preg_match('/%/', $type)) {
$type = $this->constructors->find_by_type(str_replace('%', '', $type))['predicate'];
}*/
$layer = isset($data['layer']) && $data['layer'] !== '' ? '_' . $data['layer'] : '';
$layer = isset($data['layer']) && $data['layer'] !== '' ? '_'.$data['layer'] : '';
$type = str_replace(['.', '<', '>'], ['_', '_of_', ''], $data['type']);
$php_type = preg_replace('/.*_of_/', '', $type);
$constructor = str_replace(['.', '<', '>'], ['_', '_of_', ''], $data['predicate']);
@ -65,12 +66,12 @@ trait Constructors
if (substr($param[$type_or_subtype], -1) === '>') {
$param[$type_or_subtype] = substr($param[$type_or_subtype], 0, -1);
}
$params .= "'" . $param['name'] . "' => ";
$param[$type_or_subtype] = '[' . $this->escape($param[$type_or_subtype]) . '](../' . $type_or_bare_type . '/' . $param[$type_or_subtype] . '.md)';
$params .= (isset($param['subtype']) ? '\\[' . $param[$type_or_subtype] . '\\]' : $param[$type_or_subtype]) . ', ';
$params .= "'".$param['name']."' => ";
$param[$type_or_subtype] = '['.$this->escape($param[$type_or_subtype]).'](../'.$type_or_bare_type.'/'.$param[$type_or_subtype].'.md)';
$params .= (isset($param['subtype']) ? '\\['.$param[$type_or_subtype].'\\]' : $param[$type_or_subtype]).', ';
}
$md_constructor = str_replace('_', '\\_', $constructor . $layer);
$this->docs_constructors[$constructor] = '[$' . $md_constructor . '](../constructors/' . $php_constructor . $layer . '.md) = \\[' . $params . '\\];<a name="' . $constructor . $layer . '"></a>
$md_constructor = str_replace('_', '\\_', $constructor.$layer);
$this->docs_constructors[$constructor] = '[$'.$md_constructor.'](../constructors/'.$php_constructor.$layer.'.md) = \\['.$params.'\\];<a name="'.$constructor.$layer.'"></a>
';
$table = empty($data['params']) ? '' : '### Attributes:
@ -117,31 +118,31 @@ trait Constructors
case 'false':
$ptype = 'Bool';
}
$table .= '|' . str_replace('_', '\\_', $param['name']) . '|' . (isset($param['subtype']) ? 'Array of ' : '') . '[' . str_replace('_', '\\_', $ptype) . '](../' . $type_or_bare_type . '/' . $ptype . '.md) | ' . (isset($param['pow']) || $this->constructors->find_by_predicate(lcfirst($param['type']) . 'Empty') ? 'Optional' : 'Yes') . '|';
$table .= '|'.str_replace('_', '\\_', $param['name']).'|'.(isset($param['subtype']) ? 'Array of ' : '').'['.str_replace('_', '\\_', $ptype).'](../'.$type_or_bare_type.'/'.$ptype.'.md) | '.(isset($param['pow']) || $this->constructors->find_by_predicate(lcfirst($param['type']).'Empty') ? 'Optional' : 'Yes').'|';
if (isset($this->td_descriptions['constructors'][$data['predicate']]['params'][$param['name']])) {
$table .= $this->td_descriptions['constructors'][$data['predicate']]['params'][$param['name']] . '|';
$table .= $this->td_descriptions['constructors'][$data['predicate']]['params'][$param['name']].'|';
}
$table .= PHP_EOL;
$pptype = in_array($ptype, ['string', 'bytes']) ? "'" . $ptype . "'" : $ptype;
$ppptype = in_array($ptype, ['string', 'bytes']) ? '"' . $ptype . '"' : $ptype;
$params .= ", '" . $param['name'] . "' => ";
$params .= isset($param['subtype']) ? '[' . $pptype . ']' : $pptype;
$lua_params .= ', ' . $param['name'] . '=';
$lua_params .= isset($param['subtype']) ? '{' . $pptype . '}' : $pptype;
$pwr_params .= ', "' . $param['name'] . '": ' . (isset($param['subtype']) ? '[' . $ppptype . ']' : $ppptype);
$pptype = in_array($ptype, ['string', 'bytes']) ? "'".$ptype."'" : $ptype;
$ppptype = in_array($ptype, ['string', 'bytes']) ? '"'.$ptype.'"' : $ptype;
$params .= ", '".$param['name']."' => ";
$params .= isset($param['subtype']) ? '['.$pptype.']' : $pptype;
$lua_params .= ', '.$param['name'].'=';
$lua_params .= isset($param['subtype']) ? '{'.$pptype.'}' : $pptype;
$pwr_params .= ', "'.$param['name'].'": '.(isset($param['subtype']) ? '['.$ppptype.']' : $ppptype);
if ($param['name'] === 'reply_markup') {
$hasreplymarkup = true;
}
}
$params = "['_' => '" . $data['predicate'] . "'" . $params . ']';
$lua_params = "{_='" . $data['predicate'] . "'" . $lua_params . '}';
$pwr_params = '{"_": "' . $data['predicate'] . '"' . $pwr_params . '}';
$description = isset($this->td_descriptions['constructors'][$data['predicate']]) ? $this->td_descriptions['constructors'][$data['predicate']]['description'] : $constructor . ' attributes, type and example';
$params = "['_' => '".$data['predicate']."'".$params.']';
$lua_params = "{_='".$data['predicate']."'".$lua_params.'}';
$pwr_params = '{"_": "'.$data['predicate'].'"'.$pwr_params.'}';
$description = isset($this->td_descriptions['constructors'][$data['predicate']]) ? $this->td_descriptions['constructors'][$data['predicate']]['description'] : $constructor.' attributes, type and example';
$header = '---
title: ' . $data['predicate'] . '
description: ' . $description . '
title: '.$data['predicate'].'
description: '.$description.'
---
## Constructor: ' . str_replace('_', '\\_', $data['predicate'] . $layer) . '
## Constructor: '.str_replace('_', '\\_', $data['predicate'].$layer).'
[Back to constructors index](index.md)
@ -152,9 +153,9 @@ description: ' . $description . '
';
if (isset($this->td_descriptions['constructors'][$data['predicate']])) {
$header .= $this->td_descriptions['constructors'][$data['predicate']]['description'] . PHP_EOL . PHP_EOL;
$header .= $this->td_descriptions['constructors'][$data['predicate']]['description'].PHP_EOL.PHP_EOL;
}
$type = '### Type: [' . str_replace('_', '\\_', $php_type) . '](../types/' . $php_type . '.md)
$type = '### Type: ['.str_replace('_', '\\_', $php_type).'](../types/'.$php_type.'.md)
';
@ -163,13 +164,13 @@ description: ' . $description . '
$example = '### Example:
```
$' . $constructor . $layer . ' = ' . $params . ';
$'.$constructor.$layer.' = '.$params.';
```
[PWRTelegram](https://pwrtelegram.xyz) json-encoded version:
```
' . $pwr_params . '
'.$pwr_params.'
```
@ -177,7 +178,7 @@ Or, if you\'re into Lua:
```
' . $constructor . $layer . '=' . $lua_params . '
'.$constructor.$layer.'='.$lua_params.'
```
@ -227,7 +228,7 @@ MadelineProto supports all html entities supported by [html_entity_decode](http:
';
}
}
file_put_contents('constructors/' . $constructor . $layer . '.md', $header . $table . $type . $example);
file_put_contents('constructors/'.$constructor.$layer.'.md', $header.$table.$type.$example);
}
\danog\MadelineProto\Logger::log(['Generating constructors index...'], \danog\MadelineProto\Logger::NOTICE);
ksort($this->docs_constructors);
@ -236,16 +237,16 @@ MadelineProto supports all html entities supported by [html_entity_decode](http:
$new_namespace = preg_replace('/_.*/', '', $constructor);
$br = $new_namespace != $last_namespace ? '***
<br><br>' : '';
$value = $br . $value;
$value = $br.$value;
$last_namespace = $new_namespace;
}
file_put_contents('constructors/' . $this->index, '---
file_put_contents('constructors/'.$this->index, '---
title: Constructors
description: List of constructors
---
# Constructors
[Back to API documentation index](..)
' . implode('', $this->docs_constructors));
'.implode('', $this->docs_constructors));
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\DocsBuilder;
trait Methods
@ -19,7 +20,7 @@ trait Methods
$bots = json_decode(file_get_contents('https://rpc.pwrtelegram.xyz/?bot'), true)['result'];
$errors = json_decode(file_get_contents('https://rpc.pwrtelegram.xyz/?all'), true);
$errors['result'] = array_merge_recursive(...$errors['result']);
foreach (glob('methods/' . $this->any) as $unlink) {
foreach (glob('methods/'.$this->any) as $unlink) {
unlink($unlink);
}
if (file_exists('methods')) {
@ -54,11 +55,11 @@ trait Methods
$type_or_subtype = isset($param['subtype']) ? 'subtype' : 'type';
$type_or_bare_type = ctype_upper($this->end(explode('.', $param[$type_or_subtype]))[0]) || in_array($param[$type_or_subtype], ['!X', 'X', 'bytes', 'true', 'false', 'double', 'string', 'Bool', 'int', 'long', 'int128', 'int256', 'int512', 'int53']) ? 'types' : 'constructors';
$param[$type_or_subtype] = str_replace(['.', 'true', 'false'], ['_', 'Bool', 'Bool'], $param[$type_or_subtype]);
$param[$type_or_subtype] = '[' . $this->escape($param[$type_or_subtype]) . '](../' . $type_or_bare_type . '/' . $param[$type_or_subtype] . '.md)';
$params .= "'" . $param['name'] . "' => " . (isset($param['subtype']) ? '\\[' . $param[$type_or_subtype] . '\\]' : $param[$type_or_subtype]) . ', ';
$param[$type_or_subtype] = '['.$this->escape($param[$type_or_subtype]).'](../'.$type_or_bare_type.'/'.$param[$type_or_subtype].'.md)';
$params .= "'".$param['name']."' => ".(isset($param['subtype']) ? '\\['.$param[$type_or_subtype].'\\]' : $param[$type_or_subtype]).', ';
}
$md_method = '[' . $php_method . '](' . $method . '.md)';
$this->docs_methods[$method] = '$MadelineProto->' . $md_method . '(\\[' . $params . '\\]) === [$' . str_replace('_', '\\_', $type) . '](../types/' . $php_type . '.md)<a name="' . $method . '"></a>
$md_method = '['.$php_method.']('.$method.'.md)';
$this->docs_methods[$method] = '$MadelineProto->'.$md_method.'(\\['.$params.'\\]) === [$'.str_replace('_', '\\_', $type).'](../types/'.$php_type.'.md)<a name="'.$method.'"></a>
';
$params = '';
@ -98,19 +99,19 @@ trait Methods
$ptype = 'Bool';
}
$type_or_bare_type = ctype_upper($this->end(explode('.', $param[$type_or_subtype]))[0]) || in_array($param[$type_or_subtype], ['!X', 'X', 'bytes', 'true', 'false', 'double', 'string', 'Bool', 'int', 'long', 'int128', 'int256', 'int512', 'int53']) ? 'types' : 'constructors';
$table .= '|' . str_replace('_', '\\_', $param['name']) . '|' . (isset($param['subtype']) ? 'Array of ' : '') . '[' . str_replace('_', '\\_', $ptype) . '](../' . $type_or_bare_type . '/' . $ptype . '.md) | ' . (isset($param['pow']) || $this->constructors->find_by_predicate(lcfirst($param['type']) . 'Empty') ? 'Optional' : 'Yes') . '|';
$table .= '|'.str_replace('_', '\\_', $param['name']).'|'.(isset($param['subtype']) ? 'Array of ' : '').'['.str_replace('_', '\\_', $ptype).'](../'.$type_or_bare_type.'/'.$ptype.'.md) | '.(isset($param['pow']) || $this->constructors->find_by_predicate(lcfirst($param['type']).'Empty') ? 'Optional' : 'Yes').'|';
if (isset($this->td_descriptions['methods'][$data['method']])) {
$table .= $this->td_descriptions['methods'][$data['method']]['params'][$param['name']] . '|';
$table .= $this->td_descriptions['methods'][$data['method']]['params'][$param['name']].'|';
}
$table .= PHP_EOL;
$pptype = in_array($ptype, ['string', 'bytes']) ? "'" . $ptype . "'" : $ptype;
$ppptype = in_array($ptype, ['string', 'bytes']) ? '"' . $ptype . '"' : $ptype;
$params .= "'" . $param['name'] . "' => ";
$params .= (isset($param['subtype']) ? '[' . $pptype . ']' : $pptype) . ', ';
$json_params .= '"' . $param['name'] . '": ' . (isset($param['subtype']) ? '[' . $ppptype . ']' : $ppptype) . ', ';
$pwr_params .= $param['name'] . ' - Json encoded ' . (isset($param['subtype']) ? ' array of ' . $ptype : $ptype) . "\n\n";
$lua_params .= $param['name'] . '=';
$lua_params .= (isset($param['subtype']) ? '{' . $pptype . '}' : $pptype) . ', ';
$pptype = in_array($ptype, ['string', 'bytes']) ? "'".$ptype."'" : $ptype;
$ppptype = in_array($ptype, ['string', 'bytes']) ? '"'.$ptype.'"' : $ptype;
$params .= "'".$param['name']."' => ";
$params .= (isset($param['subtype']) ? '['.$pptype.']' : $pptype).', ';
$json_params .= '"'.$param['name'].'": '.(isset($param['subtype']) ? '['.$ppptype.']' : $ppptype).', ';
$pwr_params .= $param['name'].' - Json encoded '.(isset($param['subtype']) ? ' array of '.$ptype : $ptype)."\n\n";
$lua_params .= $param['name'].'=';
$lua_params .= (isset($param['subtype']) ? '{'.$pptype.'}' : $pptype).', ';
if ($param['name'] === 'reply_markup') {
$hasreplymarkup = true;
}
@ -127,19 +128,19 @@ trait Methods
$pwr_params = "parse_mode - string\n";
}
}
$description = isset($this->td_descriptions['methods'][$data['method']]) ? $this->td_descriptions['methods'][$data['method']]['description'] : $data['method'] . ' parameters, return type and example';
$description = isset($this->td_descriptions['methods'][$data['method']]) ? $this->td_descriptions['methods'][$data['method']]['description'] : $data['method'].' parameters, return type and example';
$header = '---
title: ' . $data['method'] . '
description: ' . $description . '
title: '.$data['method'].'
description: '.$description.'
---
## Method: ' . str_replace('_', '\\_', $data['method']) . '
## Method: '.str_replace('_', '\\_', $data['method']).'
[Back to methods index](index.md)
';
if (isset(\danog\MadelineProto\MTProto::DISALLOWED_METHODS[$data['method']])) {
$header .= '**' . \danog\MadelineProto\MTProto::DISALLOWED_METHODS[$data['method']] . "**\n\n\n\n\n";
file_put_contents('methods/' . $method . '.md', $header);
$header .= '**'.\danog\MadelineProto\MTProto::DISALLOWED_METHODS[$data['method']]."**\n\n\n\n\n";
file_put_contents('methods/'.$method.'.md', $header);
continue;
}
if ($this->td) {
@ -148,17 +149,17 @@ description: ' . $description . '
';
}
$header .= isset($this->td_descriptions['methods'][$data['method']]) ? $this->td_descriptions['methods'][$data['method']]['description'] . PHP_EOL . PHP_EOL : '';
$header .= isset($this->td_descriptions['methods'][$data['method']]) ? $this->td_descriptions['methods'][$data['method']]['description'].PHP_EOL.PHP_EOL : '';
$table .= '
';
$return = '### Return type: [' . str_replace('_', '\\_', $type) . '](../types/' . $php_type . '.md)
$return = '### Return type: ['.str_replace('_', '\\_', $type).'](../types/'.$php_type.'.md)
';
$bot = !in_array($data['method'], $bots);
$example = '';
if (!isset($this->settings['td'])) {
$example .= '### Can bots use this method: **' . ($bot ? 'YES' : 'NO') . "**\n\n\n";
$example .= '### Can bots use this method: **'.($bot ? 'YES' : 'NO')."**\n\n\n";
if (isset($errors['result'][$data['method']])) {
$example .= '### Errors this method can return:
@ -166,7 +167,7 @@ description: ' . $description . '
|----------|---------------|
';
foreach ($errors['result'][$data['method']] as $error) {
$example .= '|' . $error . '|' . $errors['human_result'][$error][0] . '|' . "\n";
$example .= '|'.$error.'|'.$errors['human_result'][$error][0].'|'."\n";
}
$example .= "\n\n";
}
@ -176,44 +177,44 @@ description: ' . $description . '
```
$MadelineProto = new \\danog\\MadelineProto\\API();
$MadelineProto->session = \'mySession.madeline\';
' . ($bot ? 'if (isset($token)) { // Login as a bot
'.($bot ? 'if (isset($token)) { // Login as a bot
$MadelineProto->bot_login($token);
}
' : '') . 'if (isset($number)) { // Login as a user
' : '').'if (isset($number)) { // Login as a user
$MadelineProto->phone_login($number);
$code = readline(\'Enter the code you received: \'); // Or do this in two separate steps in an HTTP API
$MadelineProto->complete_phone_login($code);
}
$' . $type . ' = $MadelineProto->' . $php_method . '([' . $params . ']);
$'.$type.' = $MadelineProto->'.$php_method.'(['.$params.']);
```
Or, if you\'re using the [PWRTelegram HTTP API](https://pwrtelegram.xyz):
' . ($bot ? '### As a bot:
'.($bot ? '### As a bot:
POST/GET to `https://api.pwrtelegram.xyz/botTOKEN/madeline`
Parameters:
* method - ' . $data['method'] . '
* params - `{' . $json_params . '}`
* method - '.$data['method'].'
* params - `{'.$json_params.'}`
' : '') . '
' : '').'
### As a user:
POST/GET to `https://api.pwrtelegram.xyz/userTOKEN/' . $data['method'] . '`
POST/GET to `https://api.pwrtelegram.xyz/userTOKEN/'.$data['method'].'`
Parameters:
' . $pwr_params . '
'.$pwr_params.'
Or, if you\'re into Lua:
```
' . $type . ' = ' . $data['method'] . '({' . $lua_params . '})
'.$type.' = '.$data['method'].'({'.$lua_params.'})
```
');
@ -230,7 +231,7 @@ You can provide bot API reply_markup objects here.
$example .= '
## Return value
If the length of the provided message is bigger than 4096, the message will be split in chunks and the method will be called multiple times, with the same parameters (except for the message), and an array of [' . str_replace('_', '\\_', $type) . '](../types/' . $php_type . '.md) will be returned instead.
If the length of the provided message is bigger than 4096, the message will be split in chunks and the method will be called multiple times, with the same parameters (except for the message), and an array of ['.str_replace('_', '\\_', $type).'](../types/'.$php_type.'.md) will be returned instead.
';
@ -270,7 +271,7 @@ MadelineProto supports all html entities supported by [html_entity_decode](http:
';
}
}
file_put_contents('methods/' . $method . '.md', $header . $table . $return . $example);
file_put_contents('methods/'.$method.'.md', $header.$table.$return.$example);
}
\danog\MadelineProto\Logger::log(['Generating methods index...'], \danog\MadelineProto\Logger::NOTICE);
ksort($this->docs_methods);
@ -279,10 +280,10 @@ MadelineProto supports all html entities supported by [html_entity_decode](http:
$new_namespace = preg_replace('/_.*/', '', $method);
$br = $new_namespace != $last_namespace ? '***
<br><br>' : '';
$value = $br . $value;
$value = $br.$value;
$last_namespace = $new_namespace;
}
file_put_contents('methods/' . $this->index, '---
file_put_contents('methods/'.$this->index, '---
title: Methods
description: List of methods
---
@ -312,6 +313,6 @@ $MadelineProto->[get_full_info](https://docs.madelineproto.xyz/get_full_info.htm
$MadelineProto->[get_self](https://docs.madelineproto.xyz/get_self.html)();
' . implode('', $this->docs_methods));
'.implode('', $this->docs_methods));
}
}
}

View File

@ -10,16 +10,19 @@ 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/>.
*/
namespace danog\MadelineProto;
class Exception extends \Exception
{
use TL\PrettyException;
public static $rollbar = true;
public function __toString()
{
return $this->file === 'MadelineProto' ? $this->message : '\\danog\\MadelineProto\\Exception' . ($this->message !== '' ? ': ' : '') . $this->message . ' in ' . $this->file . ':' . $this->line . PHP_EOL . 'Revision: ' . @file_get_contents(__DIR__ . '/../../../.git/refs/heads/master') . PHP_EOL . 'TL Trace (YOU ABSOLUTELY MUST READ THE TEXT BELOW):' . PHP_EOL . $this->getTLTrace();
return $this->file === 'MadelineProto' ? $this->message : '\\danog\\MadelineProto\\Exception'.($this->message !== '' ? ': ' : '').$this->message.' in '.$this->file.':'.$this->line.PHP_EOL.'Revision: '.@file_get_contents(__DIR__.'/../../../.git/refs/heads/master').PHP_EOL.'TL Trace (YOU ABSOLUTELY MUST READ THE TEXT BELOW):'.PHP_EOL.$this->getTLTrace();
}
public function __construct($message = null, $code = 0, self $previous = null, $file = null, $line = null)
{
$this->prettify_tl();
@ -34,7 +37,7 @@ class Exception extends \Exception
$this->line = $line;
}
parent::__construct($message, $code, $previous);
\danog\MadelineProto\Logger::log([$message . ' in ' . basename($this->file) . ':' . $this->line], \danog\MadelineProto\Logger::FATAL_ERROR);
\danog\MadelineProto\Logger::log([$message.' in '.basename($this->file).':'.$this->line], \danog\MadelineProto\Logger::FATAL_ERROR);
if (in_array($message, ['The session is corrupted!', 'Re-executing query...', 'I had to recreate the temporary authorization key', 'This peer is not present in the internal peer database', "Couldn't get response", 'Chat forbidden', 'The php-libtgvoip extension is required to accept and manage calls. See daniil.it/MadelineProto for more info.', 'File does not exist', 'Please install this fork of phpseclib: https://github.com/danog/phpseclib'])) {
return;
}
@ -45,6 +48,7 @@ class Exception extends \Exception
\Rollbar\Rollbar::log(\Rollbar\Payload\Level::error(), $this, debug_backtrace(0));
}
}
/**
* ExceptionErrorHandler.
*
@ -57,6 +61,7 @@ class Exception extends \Exception
return true;
// return true to continue through the others error handlers
}
throw new self($errstr, $errno, null, $errfile, $errline);
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -13,6 +13,7 @@ If not, see <http://www.gnu.org/licenses/>.
/*
* Logger class
*/
namespace danog\MadelineProto;
class Logger
@ -37,6 +38,7 @@ class Logger
const WARNING = 2;
const ERROR = 1;
const FATAL_ERROR = 0;
public static function class_exists()
{
self::$has_thread = class_exists('\\Thread') && method_exists('\\Thread', 'getCurrentThread');
@ -50,6 +52,7 @@ class Logger
if (!defined('\\danog\\MadelineProto\\VoIP::PHP_LIBTGVOIP_VERSION') || \danog\MadelineProto\VoIP::PHP_LIBTGVOIP_VERSION !== '1.1.2') {
throw new \danog\MadelineProto\Exception(hex2bin(\danog\MadelineProto\Lang::$current_lang['v_tgerror']), 0, null, 'MadelineProto', 1);
}
try {
\Threaded::extend('\\danog\\MadelineProto\\VoIP');
} catch (\RuntimeException $e) {
@ -61,11 +64,13 @@ class Logger
self::$colors[self::WARNING] = implode(';', [self::foreground['white'], self::set['dim'], self::background['red']]);
self::$colors[self::ERROR] = implode(';', [self::foreground['white'], self::set['bold'], self::background['red']]);
self::$colors[self::FATAL_ERROR] = implode(';', [self::foreground['red'], self::set['bold'], self::background['light_gray']]);
try {
self::$isatty = defined('STDOUT') && function_exists('posix_isatty') && posix_isatty(STDOUT);
} catch (\danog\MadelineProto\Exception $e) {
}
}
/*
* Constructor function
* Accepts various logger modes:
@ -83,10 +88,11 @@ class Logger
}
self::$mode = $mode;
self::$optional = $optional;
self::$prefix = $prefix === '' ? '' : ', ' . $prefix;
self::$prefix = $prefix === '' ? '' : ', '.$prefix;
self::$level = $level;
self::class_exists();
}
public static function log($params, $level = self::NOTICE)
{
if (self::$mode === 4) {
@ -103,20 +109,20 @@ class Logger
if (!is_string($param)) {
$param = json_encode($param, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
$param = str_pad(basename(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)[0]['file'], '.php') . $prefix . ': ', 16 + strlen($prefix)) . "\t" . $param;
$param = str_pad(basename(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)[0]['file'], '.php').$prefix.': ', 16 + strlen($prefix))."\t".$param;
switch (self::$mode) {
case 1:
error_log($param);
break;
case 2:
error_log($param . PHP_EOL, 3, self::$optional);
error_log($param.PHP_EOL, 3, self::$optional);
break;
case 3:
echo self::$isatty ? "\33[" . self::$colors[$level] . 'm' . $param . "\33[0m" . PHP_EOL : $param . PHP_EOL;
echo self::$isatty ? "\33[".self::$colors[$level].'m'.$param."\33[0m".PHP_EOL : $param.PHP_EOL;
break;
default:
break;
}
}
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto;
class Lua
@ -18,6 +19,7 @@ class Lua
public $MadelineProto;
protected $Lua;
protected $script;
public function __magic_construct($script, $MadelineProto)
{
if (!file_exists($script)) {
@ -28,10 +30,12 @@ class Lua
$this->script = $script;
$this->__wakeup();
}
public function __sleep()
{
return ['MadelineProto', 'script'];
}
public function __wakeup()
{
$this->Lua = new \Lua($this->script);
@ -65,6 +69,7 @@ class Lua
$this->MadelineProto->{$namespace}->lua = true;
}
}
public function tdcli_function($params, $cb = null, $cb_extra = null)
{
$params = $this->MadelineProto->td_to_mtproto($this->MadelineProto->tdcli_to_td($params));
@ -75,8 +80,10 @@ class Lua
if (is_callable($cb)) {
$cb($this->MadelineProto->mtproto_to_td($result), $cb_extra);
}
return $result;
}
public function madeline_function($params, $cb = null, $cb_extra = null)
{
$result = $this->MadelineProto->API->method_call($params['_'], $params, ['datacenter' => $this->MadelineProto->API->datacenter->curdc]);
@ -84,12 +91,15 @@ class Lua
$cb($result, $cb_extra);
}
self::convert_objects($result);
return $result;
}
public function tdcli_update_callback($update)
{
$this->Lua->tdcli_update_callback($this->MadelineProto->mtproto_to_tdcli($update));
}
private function convert_array($array)
{
if (!is_array($value)) {
@ -101,23 +111,29 @@ class Lua
}, array_flip($array)));
}
}
private function is_sequential(array $arr)
{
if ([] === $arr) {
return false;
}
return isset($arr[0]) && array_keys($arr) === range(0, count($arr) - 1);
}
public function __get($name)
{
if ($name === 'API') {
return $this->MadelineProto->API;
}
return $this->Lua->{$name};
}
public function __call($name, $params)
{
self::convert_objects($params);
try {
return $this->Lua->{$name}(...$params);
} catch (\danog\MadelineProto\RPCErrorException $e) {
@ -136,10 +152,12 @@ class Lua
return ['error_code' => $e->getCode(), 'error' => $e->getMessage()];
}
}
public function __set($name, $value)
{
return $this->Lua->{$name} = $value;
}
public static function convert_objects(&$data)
{
array_walk_recursive($data, function (&$value, $key) {
@ -161,4 +179,4 @@ class Lua
}
});
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -21,16 +22,19 @@ trait AckHandler
{
// The server acknowledges that it received my message
if (!isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id])) {
\danog\MadelineProto\Logger::log(["WARNING: Couldn't find message id " . $message_id . ' in the array of outgoing messages. Maybe try to increase its size?'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(["WARNING: Couldn't find message id ".$message_id.' in the array of outgoing messages. Maybe try to increase its size?'], \danog\MadelineProto\Logger::WARNING);
return false;
}
return $this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['ack'] = true;
}
public function ack_incoming_message_id($message_id, $datacenter)
{
// I let the server know that I received its message
if (!isset($this->datacenter->sockets[$datacenter]->incoming_messages[$message_id])) {
\danog\MadelineProto\Logger::log(["WARNING: Couldn't find message id " . $message_id . ' in the array of incomgoing messages. Maybe try to increase its size?'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(["WARNING: Couldn't find message id ".$message_id.' in the array of incomgoing messages. Maybe try to increase its size?'], \danog\MadelineProto\Logger::WARNING);
//throw new \danog\MadelineProto\Exception("Couldn't find message id ".$message_id.' in the array of incoming message ids. Maybe try to increase its size?');
}
if ($this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === null || $this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === "\0\0\0\0\0\0\0\0") {
@ -42,4 +46,4 @@ trait AckHandler
return true;
//$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack'] = true;
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -77,9 +78,9 @@ trait AuthKeyHandler
list($p, $q) = [$q, $p];
}
if (!$pq->equals($p->multiply($q))) {
throw new \danog\MadelineProto\SecurityException("couldn't compute p and q. Original pq: {$pq}, computed p: {$p}, computed q: {$q}, computed pq: " . $p->multiply($q));
throw new \danog\MadelineProto\SecurityException("couldn't compute p and q. Original pq: {$pq}, computed p: {$p}, computed q: {$q}, computed pq: ".$p->multiply($q));
}
\danog\MadelineProto\Logger::log(['Factorization ' . $pq . ' = ' . $p . ' * ' . $q], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Factorization '.$pq.' = '.$p.' * '.$q], \danog\MadelineProto\Logger::VERBOSE);
/*
* ***********************************************************************
* Serialize object for req_DH_params
@ -88,14 +89,14 @@ trait AuthKeyHandler
$q_bytes = $q->toBytes();
$new_nonce = $this->random(32);
$data_unserialized = ['pq' => $pq_bytes, 'p' => $p_bytes, 'q' => $q_bytes, 'nonce' => $nonce, 'server_nonce' => $server_nonce, 'new_nonce' => $new_nonce, 'expires_in' => $expires_in];
$p_q_inner_data = $this->serialize_object(['type' => 'p_q_inner_data' . ($expires_in < 0 ? '' : '_temp')], $data_unserialized, 'p_q_inner_data');
$p_q_inner_data = $this->serialize_object(['type' => 'p_q_inner_data'.($expires_in < 0 ? '' : '_temp')], $data_unserialized, 'p_q_inner_data');
/*
* ***********************************************************************
* Encrypt serialized object
*/
$sha_digest = sha1($p_q_inner_data, true);
$random_bytes = $this->random(255 - strlen($p_q_inner_data) - strlen($sha_digest));
$to_encrypt = $sha_digest . $p_q_inner_data . $random_bytes;
$to_encrypt = $sha_digest.$p_q_inner_data.$random_bytes;
$encrypted_data = $key->encrypt($to_encrypt);
\danog\MadelineProto\Logger::log(['Starting Diffie Hellman key exchange'], \danog\MadelineProto\Logger::VERBOSE);
/*
@ -146,8 +147,8 @@ trait AuthKeyHandler
* Get key, iv and decrypt answer
*/
$encrypted_answer = $server_dh_params['encrypted_answer'];
$tmp_aes_key = sha1($new_nonce . $server_nonce, true) . substr(sha1($server_nonce . $new_nonce, true), 0, 12);
$tmp_aes_iv = substr(sha1($server_nonce . $new_nonce, true), 12, 8) . sha1($new_nonce . $new_nonce, true) . substr($new_nonce, 0, 4);
$tmp_aes_key = sha1($new_nonce.$server_nonce, true).substr(sha1($server_nonce.$new_nonce, true), 0, 12);
$tmp_aes_iv = substr(sha1($server_nonce.$new_nonce, true), 12, 8).sha1($new_nonce.$new_nonce, true).substr($new_nonce, 0, 4);
$answer_with_hash = $this->ige_decrypt($encrypted_answer, $tmp_aes_key, $tmp_aes_iv);
/*
* ***********************************************************************
@ -227,8 +228,8 @@ trait AuthKeyHandler
* ***********************************************************************
* encrypt client_DH_inner_data
*/
$data_with_sha = sha1($data, true) . $data;
$data_with_sha_padded = $data_with_sha . $this->random($this->posmod(-strlen($data_with_sha), 16));
$data_with_sha = sha1($data, true).$data;
$data_with_sha_padded = $data_with_sha.$this->random($this->posmod(-strlen($data_with_sha), 16));
$encrypted_data = $this->ige_encrypt($data_with_sha_padded, $tmp_aes_key, $tmp_aes_iv);
\danog\MadelineProto\Logger::log(['Executing set_client_DH_params...'], \danog\MadelineProto\Logger::VERBOSE);
/*
@ -258,9 +259,9 @@ trait AuthKeyHandler
$auth_key_str = $auth_key->toBytes();
$auth_key_sha = sha1($auth_key_str, true);
$auth_key_aux_hash = substr($auth_key_sha, 0, 8);
$new_nonce_hash1 = substr(sha1($new_nonce . chr(1) . $auth_key_aux_hash, true), -16);
$new_nonce_hash2 = substr(sha1($new_nonce . chr(2) . $auth_key_aux_hash, true), -16);
$new_nonce_hash3 = substr(sha1($new_nonce . chr(3) . $auth_key_aux_hash, true), -16);
$new_nonce_hash1 = substr(sha1($new_nonce.chr(1).$auth_key_aux_hash, true), -16);
$new_nonce_hash2 = substr(sha1($new_nonce.chr(2).$auth_key_aux_hash, true), -16);
$new_nonce_hash3 = substr(sha1($new_nonce.chr(3).$auth_key_aux_hash, true), -16);
/*
* ***********************************************************************
* Check if the client's nonce and the server's nonce are the same
@ -290,6 +291,7 @@ trait AuthKeyHandler
$res_authorization['id'] = substr($auth_key_sha, -8);
$res_authorization['connection_inited'] = false;
\danog\MadelineProto\Logger::log(['Auth key generated'], \danog\MadelineProto\Logger::NOTICE);
return $res_authorization;
case 'dh_gen_retry':
if ($Set_client_DH_params_answer['new_nonce_hash2'] != $new_nonce_hash2) {
@ -310,15 +312,15 @@ trait AuthKeyHandler
}
}
} catch (\danog\MadelineProto\SecurityException $e) {
\danog\MadelineProto\Logger::log(['An exception occurred while generating the authorization key: ' . $e->getMessage() . ' in ' . basename($e->getFile(), '.php') . ' on line ' . $e->getLine() . '. Retrying...'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['An exception occurred while generating the authorization key: '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Retrying...'], \danog\MadelineProto\Logger::WARNING);
} catch (\danog\MadelineProto\Exception $e) {
\danog\MadelineProto\Logger::log(['An exception occurred while generating the authorization key: ' . $e->getMessage() . ' in ' . basename($e->getFile(), '.php') . ' on line ' . $e->getLine() . '. Retrying...'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['An exception occurred while generating the authorization key: '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Retrying...'], \danog\MadelineProto\Logger::WARNING);
$req_pq = $req_pq === 'req_pq_multi' ? 'req_pq' : 'req_pq_multi';
} catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->rpc === 'RPC_CALL_FAIL') {
throw $e;
}
\danog\MadelineProto\Logger::log(['An RPCErrorException occurred while generating the authorization key: ' . $e->getMessage() . ' Retrying (try number ' . $retry_id_total . ')...'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['An RPCErrorException occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...'], \danog\MadelineProto\Logger::WARNING);
} finally {
$this->datacenter->sockets[$datacenter]->new_outgoing = [];
$this->datacenter->sockets[$datacenter]->new_incoming = [];
@ -328,6 +330,7 @@ trait AuthKeyHandler
throw new \danog\MadelineProto\SecurityException('Auth Failed');
}
}
public function check_G($g_a, $p)
{
/*
@ -343,8 +346,10 @@ trait AuthKeyHandler
if ($g_a->compare($this->twoe1984) < 0 || $g_a->compare($p->subtract($this->twoe1984)) >= 0) {
throw new \danog\MadelineProto\SecurityException('g_a is invalid (2^1984 < gA < dh_prime - 2^1984 is false).');
}
return true;
}
public function check_p_g($p, $g)
{
/*
@ -387,11 +392,14 @@ trait AuthKeyHandler
if ($g->compare($this->one) <= 0 || $g->compare($p->subtract($this->one)) >= 0) {
throw new \danog\MadelineProto\SecurityException('g is invalid (1 < g < p - 1 is false).');
}
return true;
}
public function get_dh_config()
{
$this->updates_state['sync_loading'] = true;
try {
$dh_config = $this->method_call('messages.getDhConfig', ['version' => $this->dh_config['version'], 'random_length' => 0], ['datacenter' => $this->datacenter->curdc]);
} finally {
@ -399,13 +407,16 @@ trait AuthKeyHandler
}
if ($dh_config['_'] === 'messages.dhConfigNotModified') {
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Logger::VERBOSE, ['DH configuration not modified']);
return $this->dh_config;
}
$dh_config['p'] = new \phpseclib\Math\BigInteger($dh_config['p'], 256);
$dh_config['g'] = new \phpseclib\Math\BigInteger($dh_config['g']);
$this->check_p_g($dh_config['p'], $dh_config['g']);
return $this->dh_config = $dh_config;
}
public function bind_temp_auth_key($expires_in, $datacenter)
{
for ($retry_id_total = 1; $retry_id_total <= $this->settings['max_tries']['authorization']; $retry_id_total++) {
@ -419,36 +430,40 @@ trait AuthKeyHandler
$message_data = $this->serialize_object(['type' => 'bind_auth_key_inner'], ['nonce' => $nonce, 'temp_auth_key_id' => $temp_auth_key_id, 'perm_auth_key_id' => $perm_auth_key_id, 'temp_session_id' => $temp_session_id, 'expires_at' => $expires_at], 'bind_temp_auth_key_inner');
$message_id = $this->generate_message_id($datacenter);
$seq_no = 0;
$encrypted_data = $this->random(16) . $message_id . pack('VV', $seq_no, strlen($message_data)) . $message_data;
$encrypted_data = $this->random(16).$message_id.pack('VV', $seq_no, strlen($message_data)).$message_data;
$message_key = substr(sha1($encrypted_data, true), -16);
$padding = $this->random($this->posmod(-strlen($encrypted_data), 16));
//$message_key = substr(hash('sha256', substr($this->datacenter->sockets[$datacenter]->auth_key['auth_key'], 88, 32).$encrypted_data.$padding, true), 8, 16);
list($aes_key, $aes_iv) = $this->old_aes_calculate($message_key, $this->datacenter->sockets[$datacenter]->auth_key['auth_key']);
$encrypted_message = $this->datacenter->sockets[$datacenter]->auth_key['id'] . $message_key . $this->ige_encrypt($encrypted_data . $padding, $aes_key, $aes_iv);
$encrypted_message = $this->datacenter->sockets[$datacenter]->auth_key['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
$res = $this->method_call('auth.bindTempAuthKey', ['perm_auth_key_id' => $perm_auth_key_id, 'nonce' => $nonce, 'expires_at' => $expires_at, 'encrypted_message' => $encrypted_message], ['message_id' => $message_id, 'datacenter' => $datacenter]);
if ($res === true) {
\danog\MadelineProto\Logger::log(['Successfully binded temporary and permanent authorization keys, DC ' . $datacenter], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Successfully binded temporary and permanent authorization keys, DC '.$datacenter], \danog\MadelineProto\Logger::NOTICE);
return true;
}
} catch (\danog\MadelineProto\SecurityException $e) {
\danog\MadelineProto\Logger::log(['An exception occurred while generating the authorization key: ' . $e->getMessage() . ' Retrying (try number ' . $retry_id_total . ')...'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['An exception occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...'], \danog\MadelineProto\Logger::WARNING);
} catch (\danog\MadelineProto\Exception $e) {
\danog\MadelineProto\Logger::log(['An exception occurred while generating the authorization key: ' . $e->getMessage() . ' Retrying (try number ' . $retry_id_total . ')...'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['An exception occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...'], \danog\MadelineProto\Logger::WARNING);
} catch (\danog\MadelineProto\RPCErrorException $e) {
\danog\MadelineProto\Logger::log(['An RPCErrorException occurred while generating the authorization key: ' . $e->getMessage() . ' Retrying (try number ' . $retry_id_total . ')...'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['An RPCErrorException occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...'], \danog\MadelineProto\Logger::WARNING);
} finally {
$this->datacenter->sockets[$datacenter]->new_outgoing = [];
$this->datacenter->sockets[$datacenter]->new_incoming = [];
}
}
throw new \danog\MadelineProto\SecurityException('An error occurred while binding temporary and permanent authorization keys.');
}
// Creates authorization keys
public function init_authorization()
{
$this->initing_authorization = true;
$this->updates_state['sync_loading'] = true;
$this->postpone_updates = true;
try {
foreach ($this->datacenter->sockets as $id => $socket) {
$cdn = strpos($id, 'cdn');
@ -500,6 +515,7 @@ trait AuthKeyHandler
$this->updates_state['sync_loading'] = false;
}
}
public function sync_authorization($id)
{
if (!isset($this->datacenter->sockets[$id])) {
@ -513,15 +529,15 @@ trait AuthKeyHandler
}
if ($authorized_socket->temp_auth_key !== null && $authorized_socket->auth_key !== null && $authorized_socket->authorized === true && $this->authorized === self::LOGGED_IN && $socket->authorized === false && strpos($authorized_dc_id, 'cdn') === false) {
try {
\danog\MadelineProto\Logger::log(['Trying to copy authorization from dc ' . $authorized_dc_id . ' to dc ' . $id]);
\danog\MadelineProto\Logger::log(['Trying to copy authorization from dc '.$authorized_dc_id.' to dc '.$id]);
$exported_authorization = $this->method_call('auth.exportAuthorization', ['dc_id' => preg_replace('|_.*|', '', $id)], ['datacenter' => $authorized_dc_id]);
$authorization = $this->method_call('auth.importAuthorization', $exported_authorization, ['datacenter' => $id]);
$socket->authorized = true;
break;
} catch (\danog\MadelineProto\Exception $e) {
\danog\MadelineProto\Logger::log(['Failure while syncing authorization from DC ' . $authorized_dc_id . ' to DC ' . $id . ': ' . $e->getMessage()], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Failure while syncing authorization from DC '.$authorized_dc_id.' to DC '.$id.': '.$e->getMessage()], \danog\MadelineProto\Logger::ERROR);
} catch (\danog\MadelineProto\RPCErrorException $e) {
\danog\MadelineProto\Logger::log(['Failure while syncing authorization from DC ' . $authorized_dc_id . ' to DC ' . $id . ': ' . $e->getMessage()], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Failure while syncing authorization from DC '.$authorized_dc_id.' to DC '.$id.': '.$e->getMessage()], \danog\MadelineProto\Logger::ERROR);
if ($e->rpc === 'DC_ID_INVALID') {
break;
}
@ -531,4 +547,4 @@ trait AuthKeyHandler
}
}
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -82,15 +83,16 @@ trait CallHandler
}
if ($this->settings['requests']['gzip_encode_if_gt'] !== -1 && ($l = strlen($serialized)) > $this->settings['requests']['gzip_encode_if_gt'] && ($g = strlen($gzipped = gzencode($serialized))) < $l) {
$serialized = $this->serialize_object(['type' => 'gzip_packed'], ['packed_data' => $gzipped], 'gzipped data');
\danog\MadelineProto\Logger::log(['Using GZIP compression for ' . $method . ', saved ' . ($l - $g) . ' bytes of data, reduced call size by ' . $g * 100 / $l . '%'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Using GZIP compression for '.$method.', saved '.($l - $g).' bytes of data, reduced call size by '.$g * 100 / $l.'%'], \danog\MadelineProto\Logger::VERBOSE);
}
$last_recv = $this->last_recv;
for ($count = 1; $count <= $this->settings['max_tries']['query']; $count++) {
if ($canunset = !$this->updates_state['sync_loading']) {
$this->updates_state['sync_loading'] = true;
}
try {
\danog\MadelineProto\Logger::log(['Calling method (try number ' . $count . ' for ' . $method . ')...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
\danog\MadelineProto\Logger::log(['Calling method (try number '.$count.' for '.$method.')...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
if ($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key !== null) {
$this->datacenter->sockets[$aargs['datacenter']]->object_queue[] = ['body' => $serialized, 'content_related' => $content_related, 'msg_id' => $message_id = isset($aargs['message_id']) ? $aargs['message_id'] : $this->generate_message_id($aargs['datacenter'])];
if (count($this->datacenter->sockets[$aargs['datacenter']]->ack_queue)) {
@ -131,7 +133,7 @@ trait CallHandler
while ($server_answer === null && $res_count++ < $this->settings['max_tries']['response'] + 1) {
// Loop until we get a response, loop for a max of $this->settings['max_tries']['response'] times
try {
\danog\MadelineProto\Logger::log(['Getting response (try number ' . $res_count . ' for ' . $method . ')...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
\danog\MadelineProto\Logger::log(['Getting response (try number '.$res_count.' for '.$method.')...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
if (!isset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']) || !isset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']]['content'])) {
// Checks if I have received the response to the called method, if not continue looping
if ($only_updates) {
@ -153,9 +155,11 @@ trait CallHandler
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
$this->init_authorization();
throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key');
}
}
throw new \danog\MadelineProto\RPCErrorException($error, $error);
}
$only_updates = $this->handle_messages($aargs['datacenter']);
@ -164,10 +168,10 @@ trait CallHandler
if ($e->getMessage() === 'I had to recreate the temporary authorization key') {
continue 2;
}
\danog\MadelineProto\Logger::log(['An error getting response of method ' . $method . ': ' . $e->getMessage() . ' in ' . basename($e->getFile(), '.php') . ' on line ' . $e->getLine() . '. Retrying...'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['An error getting response of method '.$method.': '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Retrying...'], \danog\MadelineProto\Logger::WARNING);
continue;
} catch (\danog\MadelineProto\NothingInTheSocketException $e) {
\danog\MadelineProto\Logger::log(['An error getting response of method ' . $method . ': ' . $e->getMessage() . ' in ' . basename($e->getFile(), '.php') . ' on line ' . $e->getLine() . '. Retrying...'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['An error getting response of method '.$method.': '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Retrying...'], \danog\MadelineProto\Logger::WARNING);
continue;
}
}
@ -193,15 +197,16 @@ trait CallHandler
continue 3;
case 16:
case 17:
\danog\MadelineProto\Logger::log(['Received bad_msg_notification: ' . self::BAD_MSG_ERROR_CODES[$server_answer['error_code']]], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['Received bad_msg_notification: '.self::BAD_MSG_ERROR_CODES[$server_answer['error_code']]], \danog\MadelineProto\Logger::WARNING);
$this->datacenter->sockets[$aargs['datacenter']]->time_delta = (int) (new \phpseclib\Math\BigInteger(strrev($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']), 256))->bitwise_rightShift(32)->subtract(new \phpseclib\Math\BigInteger(time()))->toString();
\danog\MadelineProto\Logger::log(['Set time delta to ' . $this->datacenter->sockets[$aargs['datacenter']]->time_delta], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['Set time delta to '.$this->datacenter->sockets[$aargs['datacenter']]->time_delta], \danog\MadelineProto\Logger::WARNING);
$this->reset_session();
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
$this->init_authorization();
continue 3;
}
throw new \danog\MadelineProto\RPCErrorException('Received bad_msg_notification: ' . self::BAD_MSG_ERROR_CODES[$server_answer['error_code']], $server_answer['error_code']);
throw new \danog\MadelineProto\RPCErrorException('Received bad_msg_notification: '.self::BAD_MSG_ERROR_CODES[$server_answer['error_code']], $server_answer['error_code']);
break;
case 'boolTrue':
case 'boolFalse':
@ -212,16 +217,16 @@ trait CallHandler
$server_answer = $this->MTProto_to_botAPI($server_answer, $args);
}
} catch (\danog\MadelineProto\Exception $e) {
$last_error = $e->getMessage() . ' in ' . basename($e->getFile(), '.php') . ' on line ' . $e->getLine();
\danog\MadelineProto\Logger::log(['An error occurred while calling method ' . $method . ': ' . $last_error . '. Recreating connection and retrying to call method...'], \danog\MadelineProto\Logger::WARNING);
$last_error = $e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine();
\danog\MadelineProto\Logger::log(['An error occurred while calling method '.$method.': '.$last_error.'. Recreating connection and retrying to call method...'], \danog\MadelineProto\Logger::WARNING);
$this->close_and_reopen($aargs['datacenter']);
if ($e->getMessage() === 'Re-executing query after server error...') {
return $this->method_call($method, $args, $aargs);
}
continue;
} catch (\RuntimeException $e) {
$last_error = $e->getMessage() . ' in ' . basename($e->getFile(), '.php') . ' on line ' . $e->getLine();
\danog\MadelineProto\Logger::log(['An error occurred while calling method ' . $method . ': ' . $last_error . '. Recreating connection and retrying to call method...'], \danog\MadelineProto\Logger::WARNING);
$last_error = $e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine();
\danog\MadelineProto\Logger::log(['An error occurred while calling method '.$method.': '.$last_error.'. Recreating connection and retrying to call method...'], \danog\MadelineProto\Logger::WARNING);
$this->close_and_reopen($aargs['datacenter']);
continue;
} finally {
@ -244,11 +249,13 @@ trait CallHandler
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
$this->init_authorization();
return $this->method_call($method, $args, $aargs);
}
throw new \danog\MadelineProto\Exception('An error occurred while calling method ' . $method . ' (' . $last_error . ').');
throw new \danog\MadelineProto\Exception('An error occurred while calling method '.$method.' ('.$last_error.').');
}
\danog\MadelineProto\Logger::log(['Got response for method ' . $method . ' @ try ' . $count . ' (response try ' . $res_count . ')'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
\danog\MadelineProto\Logger::log(['Got response for method '.$method.' @ try '.$count.' (response try '.$res_count.')'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
if ($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key !== null && (!isset($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['connection_inited']) || $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['connection_inited'] === false)) {
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['connection_inited'] = true;
}
@ -260,13 +267,16 @@ trait CallHandler
$server_answer[] = $this->method_call($method, $args, $aargs);
}
}
return $server_answer;
}
if ($method === 'req_pq') {
throw new \danog\MadelineProto\RPCErrorException('RPC_CALL_FAIL');
}
throw new \danog\MadelineProto\Exception('An error occurred while calling method ' . $method . ' (' . $last_error . ').');
throw new \danog\MadelineProto\Exception('An error occurred while calling method '.$method.' ('.$last_error.').');
}
public function object_call($object, $args = [], $aargs = ['message_id' => null, 'heavy' => false])
{
if (!is_array($args)) {

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\MTProtoTools;
trait Crypt
@ -17,42 +18,51 @@ trait Crypt
public function aes_calculate($msg_key, $auth_key, $to_server = true)
{
$x = $to_server ? 0 : 8;
$sha256_a = hash('sha256', $msg_key . substr($auth_key, $x, 36), true);
$sha256_b = hash('sha256', substr($auth_key, 40 + $x, 36) . $msg_key, true);
$aes_key = substr($sha256_a, 0, 8) . substr($sha256_b, 8, 16) . substr($sha256_a, 24, 8);
$aes_iv = substr($sha256_b, 0, 8) . substr($sha256_a, 8, 16) . substr($sha256_b, 24, 8);
$sha256_a = hash('sha256', $msg_key.substr($auth_key, $x, 36), true);
$sha256_b = hash('sha256', substr($auth_key, 40 + $x, 36).$msg_key, true);
$aes_key = substr($sha256_a, 0, 8).substr($sha256_b, 8, 16).substr($sha256_a, 24, 8);
$aes_iv = substr($sha256_b, 0, 8).substr($sha256_a, 8, 16).substr($sha256_b, 24, 8);
return [$aes_key, $aes_iv];
}
public function old_aes_calculate($msg_key, $auth_key, $to_server = true)
{
$x = $to_server ? 0 : 8;
$sha1_a = sha1($msg_key . substr($auth_key, $x, 32), true);
$sha1_b = sha1(substr($auth_key, 32 + $x, 16) . $msg_key . substr($auth_key, 48 + $x, 16), true);
$sha1_c = sha1(substr($auth_key, 64 + $x, 32) . $msg_key, true);
$sha1_d = sha1($msg_key . substr($auth_key, 96 + $x, 32), true);
$aes_key = substr($sha1_a, 0, 8) . substr($sha1_b, 8, 12) . substr($sha1_c, 4, 12);
$aes_iv = substr($sha1_a, 8, 12) . substr($sha1_b, 0, 8) . substr($sha1_c, 16, 4) . substr($sha1_d, 0, 8);
$sha1_a = sha1($msg_key.substr($auth_key, $x, 32), true);
$sha1_b = sha1(substr($auth_key, 32 + $x, 16).$msg_key.substr($auth_key, 48 + $x, 16), true);
$sha1_c = sha1(substr($auth_key, 64 + $x, 32).$msg_key, true);
$sha1_d = sha1($msg_key.substr($auth_key, 96 + $x, 32), true);
$aes_key = substr($sha1_a, 0, 8).substr($sha1_b, 8, 12).substr($sha1_c, 4, 12);
$aes_iv = substr($sha1_a, 8, 12).substr($sha1_b, 0, 8).substr($sha1_c, 16, 4).substr($sha1_d, 0, 8);
return [$aes_key, $aes_iv];
}
public function ige_encrypt($message, $key, $iv)
{
$cipher = new \phpseclib\Crypt\AES('ige');
$cipher->setKey($key);
$cipher->setIV($iv);
return @$cipher->encrypt($message);
}
public function ctr_encrypt($message, $key, $iv)
{
$cipher = new \phpseclib\Crypt\AES('ctr');
$cipher->setKey($key);
$cipher->setIV($iv);
return @$cipher->encrypt($message);
}
public function ige_decrypt($message, $key, $iv)
{
$cipher = new \phpseclib\Crypt\AES('ige');
$cipher->setKey($key);
$cipher->setIV($iv);
return @$cipher->decrypt($message);
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -32,21 +33,21 @@ trait Files
}
if ($cb === null) {
$cb = function ($percent) {
\danog\MadelineProto\Logger::log(['Upload status: ' . $percent . '%'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Upload status: '.$percent.'%'], \danog\MadelineProto\Logger::NOTICE);
};
}
$part_size = 512 * 1024;
$part_total_num = (int) ceil($file_size / $part_size);
$part_num = 0;
$method = $file_size > 10 * 1024 * 1024 ? 'upload.saveBigFilePart' : 'upload.saveFilePart';
$constructor = 'input' . ($encrypted === true ? 'Encrypted' : '') . ($file_size > 10 * 1024 * 1024 ? 'FileBig' : 'File') . ($encrypted === true ? 'Uploaded' : '');
$constructor = 'input'.($encrypted === true ? 'Encrypted' : '').($file_size > 10 * 1024 * 1024 ? 'FileBig' : 'File').($encrypted === true ? 'Uploaded' : '');
$file_id = $this->random(8);
$f = fopen($file, 'r');
fseek($f, 0);
if ($encrypted === true) {
$key = $this->random(32);
$iv = $this->random(32);
$digest = hash('md5', $key . $iv, true);
$digest = hash('md5', $key.$iv, true);
$fingerprint = $this->unpack_signed_int(substr($digest, 0, 4) ^ substr($digest, 4, 4));
$ige = new \phpseclib\Crypt\AES('ige');
$ige->setIV($iv);
@ -61,7 +62,7 @@ trait Files
}
hash_update($ctx, $bytes);
if (!$this->method_call($method, ['file_id' => $file_id, 'file_part' => $part_num++, 'file_total_parts' => $part_total_num, 'bytes' => $bytes], ['heavy' => true, 'datacenter' => &$datacenter])) {
throw new \danog\MadelineProto\Exception('An error occurred while uploading file part ' . $part_num);
throw new \danog\MadelineProto\Exception('An error occurred while uploading file part '.$part_num);
}
$cb(ftell($f) * 100 / $file_size);
}
@ -73,12 +74,15 @@ trait Files
$constructor['iv'] = $iv;
$constructor['md5_checksum'] = '';
}
return $constructor;
}
public function upload_encrypted($file, $file_name = '', $cb = null)
{
return $this->upload($file, $file_name, $cb, true);
}
public function get_download_info($message_media)
{
if (is_string($message_media)) {
@ -101,7 +105,7 @@ trait Files
if (isset($message_media['decrypted_message']['media']['file_name'])) {
$pathinfo = pathinfo($message_media['decrypted_message']['media']['file_name']);
if (isset($pathinfo['extension'])) {
$res['ext'] = '.' . $pathinfo['extension'];
$res['ext'] = '.'.$pathinfo['extension'];
}
$res['name'] = $pathinfo['filename'];
}
@ -116,7 +120,7 @@ trait Files
case 'documentAttributeFilename':
$pathinfo = pathinfo($attribute['file_name']);
if (isset($pathinfo['extension'])) {
$res['ext'] = '.' . $pathinfo['extension'];
$res['ext'] = '.'.$pathinfo['extension'];
}
$res['name'] = $pathinfo['filename'];
break;
@ -129,7 +133,7 @@ trait Files
if (isset($audio) && isset($audio['title']) && !isset($res['name'])) {
$res['name'] = $audio['title'];
if (isset($audio['performer'])) {
$res['name'] .= ' - ' . $audio['performer'];
$res['name'] .= ' - '.$audio['performer'];
}
}
if (!isset($res['ext'])) {
@ -138,6 +142,7 @@ trait Files
if (!isset($res['name'])) {
$res['name'] = $message_media['file']['access_hash'];
}
return $res;
case 'photo':
case 'messageMediaPhoto':
@ -146,7 +151,7 @@ trait Files
} else {
$photo = end($message_media['photo']['sizes']);
}
$res['name'] = $photo['location']['volume_id'] . '_' . $photo['location']['local_id'];
$res['name'] = $photo['location']['volume_id'].'_'.$photo['location']['local_id'];
$res['InputFileLocation'] = ['_' => 'inputFileLocation', 'volume_id' => $photo['location']['volume_id'], 'local_id' => $photo['location']['local_id'], 'secret' => $photo['location']['secret'], 'dc_id' => $photo['location']['dc_id']];
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], '.jpg');
$res['mime'] = 'image/jpeg';
@ -156,10 +161,11 @@ trait Files
if (isset($photo['location']['bytes'])) {
$res['size'] = strlen($photo['location']['bytes']);
}
return $res;
case 'photoSize':
case 'photoCachedSize':
$res['name'] = $message_media['location']['volume_id'] . '_' . $message_media['location']['local_id'];
$res['name'] = $message_media['location']['volume_id'].'_'.$message_media['location']['local_id'];
$res['InputFileLocation'] = ['_' => 'inputFileLocation', 'volume_id' => $message_media['location']['volume_id'], 'local_id' => $message_media['location']['local_id'], 'secret' => $message_media['location']['secret'], 'dc_id' => $message_media['location']['dc_id']];
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], '.jpg');
$res['mime'] = 'image/jpeg';
@ -169,6 +175,7 @@ trait Files
if (isset($photo['location']['bytes'])) {
$res['size'] = strlen($photo['location']['bytes']);
}
return $res;
case 'decryptedMessageMediaExternalDocument':
case 'document':
@ -179,7 +186,7 @@ trait Files
case 'documentAttributeFilename':
$pathinfo = pathinfo($attribute['file_name']);
if (isset($pathinfo['extension'])) {
$res['ext'] = '.' . $pathinfo['extension'];
$res['ext'] = '.'.$pathinfo['extension'];
}
$res['name'] = $pathinfo['filename'];
break;
@ -191,7 +198,7 @@ trait Files
if (isset($audio) && isset($audio['title']) && !isset($res['name'])) {
$res['name'] = $audio['title'];
if (isset($audio['performer'])) {
$res['name'] .= ' - ' . $audio['performer'];
$res['name'] .= ' - '.$audio['performer'];
}
}
$res['InputFileLocation'] = ['_' => 'inputDocumentFileLocation', 'id' => $message_media['document']['id'], 'access_hash' => $message_media['document']['access_hash'], 'version' => isset($message_media['document']['version']) ? $message_media['document']['version'] : 0, 'dc_id' => $message_media['document']['dc_id']];
@ -204,18 +211,22 @@ trait Files
if (isset($message_media['document']['size'])) {
$res['size'] = $message_media['document']['size'];
}
$res['name'] .= '_' . $message_media['document']['id'];
$res['name'] .= '_'.$message_media['document']['id'];
$res['mime'] = $message_media['document']['mime_type'];
return $res;
default:
throw new \danog\MadelineProto\Exception('Invalid constructor provided: ' . $message_media['_']);
throw new \danog\MadelineProto\Exception('Invalid constructor provided: '.$message_media['_']);
}
}
public function download_to_dir($message_media, $dir, $cb = null)
{
$message_media = $this->get_download_info($message_media);
return $this->download_to_file($message_media, $dir . '/' . $message_media['name'] . $message_media['ext'], $cb);
return $this->download_to_file($message_media, $dir.'/'.$message_media['name'].$message_media['ext'], $cb);
}
public function download_to_file($message_media, $file, $cb = null)
{
$file = preg_replace('|/+|', '/', $file);
@ -227,6 +238,7 @@ trait Files
$stream = fopen($file, 'r+b');
\danog\MadelineProto\Logger::log(['Waiting for lock of file to download...']);
flock($stream, LOCK_EX);
try {
$this->download_to_stream($message_media, $stream, $cb, filesize($file), -1);
} finally {
@ -234,13 +246,15 @@ trait Files
fclose($stream);
clearstatcache();
}
return $file;
}
public function download_to_stream($message_media, $stream, $cb = null, $offset = 0, $end = -1)
{
if ($cb === null) {
$cb = function ($percent) {
\danog\MadelineProto\Logger::log(['Download status: ' . $percent . '%'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Download status: '.$percent.'%'], \danog\MadelineProto\Logger::NOTICE);
};
}
$message_media = $this->get_download_info($message_media);
@ -256,7 +270,7 @@ trait Files
$percent = 0;
$datacenter = isset($message_media['InputFileLocation']['dc_id']) ? $message_media['InputFileLocation']['dc_id'] : $this->datacenter->curdc;
if (isset($message_media['key'])) {
$digest = hash('md5', $message_media['key'] . $message_media['iv'], true);
$digest = hash('md5', $message_media['key'].$message_media['iv'], true);
$fingerprint = $this->unpack_signed_int(substr($digest, 0, 4) ^ substr($digest, 4, 4));
if ($fingerprint !== $message_media['key_fingerprint']) {
throw new \danog\MadelineProto\Exception('Fingerprint mismatch!');
@ -272,6 +286,7 @@ trait Files
if ($start_at = $offset % $part_size) {
$offset -= $start_at;
}
try {
$res = $cdn ? $this->method_call('upload.getCdnFile', ['file_token' => $message_media['file_token'], 'offset' => $offset, 'limit' => $part_size], ['heavy' => true, 'datacenter' => $datacenter]) : $this->method_call('upload.getFile', ['location' => $message_media['InputFileLocation'], 'offset' => $offset, 'limit' => $part_size], ['heavy' => true, 'datacenter' => &$datacenter]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
@ -289,7 +304,7 @@ trait Files
$message_media['cdn_key'] = $res['encryption_key'];
$message_media['cdn_iv'] = $res['encryption_iv'];
$old_dc = $datacenter;
$datacenter = $res['dc_id'] . '_cdn';
$datacenter = $res['dc_id'].'_cdn';
if (!isset($this->datacenter->sockets[$datacenter])) {
$this->config['expires'] = -1;
$this->get_config([], ['datacenter' => $this->datacenter->curdc]);
@ -300,6 +315,7 @@ trait Files
if ($res['_'] === 'upload.cdnFileReuploadNeeded') {
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['cdn_reupload']], \danog\MadelineProto\Logger::NOTICE);
$this->get_config([], ['datacenter' => $this->datacenter->curdc]);
try {
$this->add_cdn_hashes($message_media['file_token'], $this->method_call('upload.reuploadCdnFile', ['file_token' => $message_media['file_token'], 'request_token' => $res['request_token']], ['heavy' => true, 'datacenter' => $old_dc]));
} catch (\danog\MadelineProto\RPCErrorException $e) {
@ -325,7 +341,7 @@ trait Files
}
}
if (isset($message_media['cdn_key'])) {
$ivec = substr($message_media['cdn_iv'], 0, 12) . pack('N', $offset >> 4);
$ivec = substr($message_media['cdn_iv'], 0, 12).pack('N', $offset >> 4);
$res['bytes'] = $this->ctr_encrypt($res['bytes'], $message_media['cdn_key'], $ivec);
$this->check_cdn_hash($message_media['file_token'], $offset, $res['bytes'], $old_dc);
}
@ -358,9 +374,12 @@ trait Files
if ($cdn) {
$this->clear_cdn_hashes($message_media['file_token']);
}
return true;
}
private $cdn_hashes = [];
private function add_cdn_hashes($file, $hashes)
{
if (!isset($this->cdn_hashes[$file])) {
@ -370,6 +389,7 @@ trait Files
$this->cdn_hashes[$file][$hash['offset']] = ['limit' => $hash['limit'], 'hash' => (string) $hash['hash']];
}
}
private function check_cdn_hash($file, $offset, $data, &$datacenter)
{
while (strlen($data)) {
@ -377,19 +397,22 @@ trait Files
$this->add_cdn_hashes($file, $this->method_call('upload.getCdnFileHashes', ['file_token' => $file, 'offset' => $offset], ['datacenter' => $datacenter]));
}
if (!isset($this->cdn_hashes[$file][$offset])) {
throw new \danog\MadelineProto\Exception('Could not fetch CDN hashes for offset ' . $offset);
throw new \danog\MadelineProto\Exception('Could not fetch CDN hashes for offset '.$offset);
}
if (hash('sha256', substr($data, 0, $this->cdn_hashes[$file][$offset]['limit']), true) !== $this->cdn_hashes[$file][$offset]['hash']) {
throw new \danog\MadelineProto\SecurityException('CDN hash mismatch for offset ' . $offset);
throw new \danog\MadelineProto\SecurityException('CDN hash mismatch for offset '.$offset);
}
$data = substr($data, $this->cdn_hashes[$file][$offset]['limit']);
$offset += $this->cdn_hashes[$file][$offset]['limit'];
}
return true;
}
private function clear_cdn_hashes($file)
{
unset($this->cdn_hashes[$file]);
return true;
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -19,10 +20,11 @@ trait MessageHandler
{
public function send_unencrypted_message($message_data, $message_id, $datacenter)
{
$message_data = "\0\0\0\0\0\0\0\0" . $message_id . $this->pack_unsigned_int(strlen($message_data)) . $message_data;
$message_data = "\0\0\0\0\0\0\0\0".$message_id.$this->pack_unsigned_int(strlen($message_data)).$message_data;
$this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id] = ['response' => -1];
$this->datacenter->sockets[$datacenter]->send_message($message_data);
}
public function send_messages($datacenter)
{
if (count($this->datacenter->sockets[$datacenter]->object_queue) > 1) {
@ -45,15 +47,15 @@ trait MessageHandler
} else {
return;
}
$plaintext = $this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt'] . $this->datacenter->sockets[$datacenter]->session_id . $message_id . pack('VV', $seq_no, strlen($message_data)) . $message_data;
$plaintext = $this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt'].$this->datacenter->sockets[$datacenter]->session_id.$message_id.pack('VV', $seq_no, strlen($message_data)).$message_data;
$padding = $this->posmod(-strlen($plaintext), 16);
if ($padding < 12) {
$padding += 16;
}
$padding = $this->random($padding);
$message_key = substr(hash('sha256', substr($this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key'], 88, 32) . $plaintext . $padding, true), 8, 16);
$message_key = substr(hash('sha256', substr($this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key'], 88, 32).$plaintext.$padding, true), 8, 16);
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key']);
$message = $this->datacenter->sockets[$datacenter]->temp_auth_key['id'] . $message_key . $this->ige_encrypt($plaintext . $padding, $aes_key, $aes_iv);
$message = $this->datacenter->sockets[$datacenter]->temp_auth_key['id'].$message_key.$this->ige_encrypt($plaintext.$padding, $aes_key, $aes_iv);
$this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id] = ['seq_no' => $seq_no, 'response' => -1];
$this->datacenter->sockets[$datacenter]->send_message($message);
$this->datacenter->sockets[$datacenter]->object_queue = [];
@ -62,6 +64,7 @@ trait MessageHandler
}
$this->datacenter->sockets[$datacenter]->ack_queue = [];
}
/**
* Reading connection and receiving message from server.
*/
@ -114,7 +117,7 @@ trait MessageHandler
throw new \danog\MadelineProto\SecurityException('message_data_length not divisible by 4');
}
$message_data = substr($decrypted_data, 32, $message_data_length);
if ($message_key != substr(hash('sha256', substr($this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key'], 96, 32) . $decrypted_data, true), 8, 16)) {
if ($message_key != substr(hash('sha256', substr($this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key'], 96, 32).$decrypted_data, true), 8, 16)) {
throw new \danog\MadelineProto\SecurityException('msg_key mismatch');
}
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id] = ['seq_no' => $seq_no];
@ -127,6 +130,7 @@ trait MessageHandler
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['response'] = -1;
$this->datacenter->sockets[$datacenter]->new_incoming[$message_id] = $message_id;
$this->last_recv = time();
return true;
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -24,18 +25,18 @@ trait MsgIdHandler
}
$min_message_id = (new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$aargs['datacenter']]->time_delta - 300))->bitwise_leftShift(32);
if ($min_message_id->compare($new_message_id) > 0) {
\danog\MadelineProto\Logger::log(['Given message id (' . $new_message_id . ') is too old compared to the min value (' . $min_message_id . ').'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['Given message id ('.$new_message_id.') is too old compared to the min value ('.$min_message_id.').'], \danog\MadelineProto\Logger::WARNING);
}
$max_message_id = (new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$aargs['datacenter']]->time_delta + 30))->bitwise_leftShift(32);
if ($max_message_id->compare($new_message_id) < 0) {
throw new \danog\MadelineProto\Exception('Given message id (' . $new_message_id . ') is too new compared to the max value (' . $max_message_id . '). Consider syncing your date.');
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is too new compared to the max value ('.$max_message_id.'). Consider syncing your date.');
}
if ($aargs['outgoing']) {
if (!$new_message_id->divide($this->four)[1]->equals($this->zero)) {
throw new \danog\MadelineProto\Exception('Given message id (' . $new_message_id . ') is not divisible by 4. Consider syncing your date.');
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is not divisible by 4. Consider syncing your date.');
}
if (!\danog\MadelineProto\Logger::$has_thread && $new_message_id->compare($key = $this->get_max_id($aargs['datacenter'], false)) <= 0) {
throw new \danog\MadelineProto\Exception('Given message id (' . $new_message_id . ') is lower than or equal to the current limit (' . $key . '). Consider syncing your date.', 1);
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is lower than or equal to the current limit ('.$key.'). Consider syncing your date.', 1);
}
if (count($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages) > $this->settings['msg_array_limit']['outgoing']) {
reset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages);
@ -51,11 +52,11 @@ trait MsgIdHandler
$key = $this->get_max_id($aargs['datacenter'], true);
if ($aargs['container']) {
if ($new_message_id->compare($key = $this->get_max_id($aargs['datacenter'], true)) >= 0) {
\danog\MadelineProto\Logger::log(['WARNING: Given message id (' . $new_message_id . ') is bigger than or equal to the current limit (' . $key . '). Consider syncing your date.'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['WARNING: Given message id ('.$new_message_id.') is bigger than or equal to the current limit ('.$key.'). Consider syncing your date.'], \danog\MadelineProto\Logger::WARNING);
}
} else {
if ($new_message_id->compare($key = $this->get_max_id($aargs['datacenter'], true)) <= 0) {
\danog\MadelineProto\Logger::log(['WARNING: Given message id (' . $new_message_id . ') is lower than or equal to the current limit (' . $key . '). Consider syncing your date.'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['WARNING: Given message id ('.$new_message_id.') is lower than or equal to the current limit ('.$key.'). Consider syncing your date.'], \danog\MadelineProto\Logger::WARNING);
}
}
if (count($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages) > $this->settings['msg_array_limit']['incoming']) {
@ -70,6 +71,7 @@ trait MsgIdHandler
$this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[strrev($new_message_id->toBytes())] = [];
}
}
public function generate_message_id($datacenter)
{
$message_id = (new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$datacenter]->time_delta))->bitwise_leftShift(32);
@ -77,14 +79,17 @@ trait MsgIdHandler
$message_id = $key->add($this->four);
}
$this->check_message_id($message_id, ['outgoing' => true, 'datacenter' => $datacenter, 'container' => false]);
return strrev($message_id->toBytes());
}
public function get_max_id($datacenter, $incoming)
{
$incoming = $incoming ? 'incoming' : 'outgoing';
if (isset($this->datacenter->sockets[$datacenter]->{'max_' . $incoming . '_id'}) && is_object($this->datacenter->sockets[$datacenter]->{'max_' . $incoming . '_id'})) {
return $this->datacenter->sockets[$datacenter]->{'max_' . $incoming . '_id'};
if (isset($this->datacenter->sockets[$datacenter]->{'max_'.$incoming.'_id'}) && is_object($this->datacenter->sockets[$datacenter]->{'max_'.$incoming.'_id'})) {
return $this->datacenter->sockets[$datacenter]->{'max_'.$incoming.'_id'};
}
return $this->zero;
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -30,6 +31,7 @@ trait PeerHandler
case 'user':
if (!isset($this->chats[$user['id']]) || $this->chats[$user['id']] !== $user) {
$this->chats[$user['id']] = $user;
try {
$this->get_pwr_chat($user['id'], false, true);
} catch (\danog\MadelineProto\Exception $e) {
@ -41,11 +43,12 @@ trait PeerHandler
case 'userEmpty':
break;
default:
throw new \danog\MadelineProto\Exception('Invalid user provided at key ' . $key . ': ' . var_export($user, true));
throw new \danog\MadelineProto\Exception('Invalid user provided at key '.$key.': '.var_export($user, true));
break;
}
}
}
public function add_chats($chats)
{
foreach ($chats as $key => $chat) {
@ -55,6 +58,7 @@ trait PeerHandler
case 'chatForbidden':
if (!isset($this->chats[-$chat['id']]) || $this->chats[-$chat['id']] !== $chat) {
$this->chats[-$chat['id']] = $chat;
try {
$this->get_pwr_chat(-$chat['id'], $this->settings['peer']['full_fetch'], true);
} catch (\danog\MadelineProto\Exception $e) {
@ -76,6 +80,7 @@ trait PeerHandler
}
if (!isset($this->chats[$bot_api_id]) || $this->chats[$bot_api_id] !== $chat) {
$this->chats[$bot_api_id] = $chat;
try {
if (!isset($this->full_chats[$bot_api_id]) || $this->full_chats[$bot_api_id]['full']['participants_count'] !== $this->get_full_info($bot_api_id)['full']['participants_count']) {
$this->get_pwr_chat($this->to_supergroup($chat['id']), $this->settings['peer']['full_fetch'], true);
@ -88,11 +93,12 @@ trait PeerHandler
}
break;
default:
throw new \danog\MadelineProto\Exception('Invalid chat provided at key ' . $key . ': ' . var_export($chat, true));
throw new \danog\MadelineProto\Exception('Invalid chat provided at key '.$key.': '.var_export($chat, true));
break;
}
}
}
public function peer_isset($id)
{
try {
@ -103,6 +109,7 @@ trait PeerHandler
return false;
}
}
public function entities_peer_isset($entities)
{
try {
@ -116,8 +123,10 @@ trait PeerHandler
} catch (\danog\MadelineProto\Exception $e) {
return false;
}
return true;
}
public function fwd_peer_isset($fwd)
{
try {
@ -130,8 +139,10 @@ trait PeerHandler
} catch (\danog\MadelineProto\Exception $e) {
return false;
}
return true;
}
public function get_info($id, $recursive = true)
{
if (is_array($id)) {
@ -169,7 +180,7 @@ trait PeerHandler
$id = $this->to_supergroup($id['channel_id']);
break;
default:
throw new \danog\MadelineProto\Exception('Invalid constructor given ' . var_export($id, true));
throw new \danog\MadelineProto\Exception('Invalid constructor given '.var_export($id, true));
break;
}
}
@ -186,7 +197,7 @@ trait PeerHandler
}
if (is_numeric($id)) {
if (is_string($id)) {
$id = \danog\MadelineProto\Logger::$bigint ? (double) $id : (int) $id;
$id = \danog\MadelineProto\Logger::$bigint ? (float) $id : (int) $id;
}
if (!isset($this->chats[$id]) && $id < 0 && !preg_match('/^-100/', $id)) {
$this->method_call('messages.getFullChat', ['chat_id' => -$id], ['datacenter' => $this->datacenter->curdc]);
@ -195,11 +206,12 @@ trait PeerHandler
return $this->gen_all($this->chats[$id]);
}
if (!isset($this->settings['pwr']['requests']) || $this->settings['pwr']['requests'] === true) {
$dbres = json_decode(@file_get_contents('https://id.pwrtelegram.xyz/db/getusername?id=' . $id, false, stream_context_create(['http' => ['timeout' => 2]])), true);
$dbres = json_decode(@file_get_contents('https://id.pwrtelegram.xyz/db/getusername?id='.$id, false, stream_context_create(['http' => ['timeout' => 2]])), true);
if (isset($dbres['ok']) && $dbres['ok']) {
return $this->get_info('@' . $dbres['result']);
return $this->get_info('@'.$dbres['result']);
}
}
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
}
$id = strtolower(str_replace('@', '', $id));
@ -210,10 +222,13 @@ trait PeerHandler
}
if ($recursive) {
$this->resolve_username($id);
return $this->get_info($id, false);
}
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
}
public function gen_all($constructor)
{
$res = [$this->constructors->find_by_predicate($constructor['_'])['type'] => $constructor];
@ -256,15 +271,18 @@ trait PeerHandler
throw new \danog\MadelineProto\Exception('Chat forbidden');
break;
default:
throw new \danog\MadelineProto\Exception('Invalid constructor given ' . var_export($constructor, true));
throw new \danog\MadelineProto\Exception('Invalid constructor given '.var_export($constructor, true));
break;
}
return $res;
}
public function full_chat_last_updated($id)
{
return isset($this->full_chats[$id]['last_update']) ? $this->full_chats[$id]['last_update'] : 0;
}
public function get_full_info($id)
{
$partial = $this->get_info($id);
@ -288,8 +306,10 @@ trait PeerHandler
$res['full'] = $full;
$res['last_update'] = time();
$this->full_chats[$partial['bot_api_id']] = $res;
return array_merge($partial, $res);
}
public function get_pwr_chat($id, $fullfetch = true, $send = true)
{
$full = $fullfetch ? $this->get_full_info($id) : $this->get_info($id);
@ -422,6 +442,7 @@ trait PeerHandler
$filters = ['channelParticipantsRecent', 'channelParticipantsAdmins', 'channelParticipantsKicked', 'channelParticipantsBots', 'channelParticipantsBanned'];
foreach ($filters as $filter) {
$offset = -$limit;
try {
$gres = $this->method_call('channels.getParticipants', ['channel' => $full['InputChannel'], 'filter' => ['_' => $filter, 'q' => ''], 'offset' => $offset += $limit, 'limit' => $limit, 'hash' => 0], ['datacenter' => $this->datacenter->curdc]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
@ -487,8 +508,10 @@ trait PeerHandler
if ($fullfetch || $send) {
$this->store_db($res);
}
return $res;
}
public function store_db($res, $force = false)
{
$settings = isset($this->settings['connection_settings'][$this->datacenter->curdc]) ? $this->settings['connection_settings'][$this->datacenter->curdc] : $this->settings['connection_settings']['all'];
@ -516,12 +539,13 @@ trait PeerHandler
if (empty($this->qres)) {
return false;
}
try {
$payload = json_encode($this->qres);
$path = '/tmp/ids' . hash('sha256', $payload);
$path = '/tmp/ids'.hash('sha256', $payload);
file_put_contents($path, $payload);
$id = isset($this->authorization['user']['username']) ? $this->authorization['user']['username'] : $this->authorization['user']['id'];
$result = shell_exec('curl ' . escapeshellarg('https://id.pwrtelegram.xyz/db' . $this->settings['pwr']['db_token'] . '/addnewmadeline?d=pls&from=' . $id) . ' -d ' . escapeshellarg('@' . $path) . ' -s -o ' . escapeshellarg($path . '.log') . ' >/dev/null 2>/dev/null & ');
$result = shell_exec('curl '.escapeshellarg('https://id.pwrtelegram.xyz/db'.$this->settings['pwr']['db_token'].'/addnewmadeline?d=pls&from='.$id).' -d '.escapeshellarg('@'.$path).' -s -o '.escapeshellarg($path.'.log').' >/dev/null 2>/dev/null & ');
\danog\MadelineProto\Logger::log([$result], \danog\MadelineProto\Logger::VERBOSE);
} catch (\danog\MadelineProto\Exception $e) {
\danog\MadelineProto\Logger::log([$e->getMessage()], \danog\MadelineProto\Logger::VERBOSE);
@ -529,6 +553,7 @@ trait PeerHandler
$this->qres = [];
$this->last_stored = time() + 10;
}
public function resolve_username($username)
{
try {
@ -539,10 +564,12 @@ trait PeerHandler
if ($res['_'] === 'contacts.resolvedPeer') {
return $res;
}
return false;
}
public function to_supergroup($id)
{
return -($id + pow(10, (int) floor(log($id, 10) + 3)));
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -41,12 +42,13 @@ trait ResponseHandler
}
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->object_call('msgs_state_info', ['req_msg_id' => $req_msg_id, 'info' => $info], ['datacenter' => $datacenter])]['response'] = $req_msg_id;
}
public function handle_messages($datacenter)
{
$only_updates = true;
foreach ($this->datacenter->sockets[$datacenter]->new_incoming as $current_msg_id) {
$unset = false;
\danog\MadelineProto\Logger::log(['Received ' . $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'] . '.'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
\danog\MadelineProto\Logger::log(['Received '.$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].'.'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
//\danog\MadelineProto\Logger::log([$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
if (!$this->synchronized(function ($zis, $datacenter, $current_msg_id) {
@ -54,12 +56,13 @@ trait ResponseHandler
return false;
}
$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['handling'] = true;
return true;
}, $this, $datacenter, $current_msg_id)) {
\danog\MadelineProto\Logger::log([base64_encode($current_msg_id) . $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'] . ' is already being handled'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log([base64_encode($current_msg_id).$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].' is already being handled'], \danog\MadelineProto\Logger::VERBOSE);
continue;
}
\danog\MadelineProto\Logger::log(['Handling ' . base64_encode($current_msg_id) . $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'] . '.'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Handling '.base64_encode($current_msg_id).$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].'.'], \danog\MadelineProto\Logger::VERBOSE);
}
switch ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_']) {
case 'msgs_ack':
@ -142,7 +145,7 @@ trait ResponseHandler
// Acknowledge that I received the server's response
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['orig_message']['msg_id']])) {
$this->ack_incoming_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['orig_message']['msg_id'], $datacenter);
// Acknowledge that I received the server's response
// Acknowledge that I received the server's response
} else {
$this->check_message_id($message['orig_message']['msg_id'], ['outgoing' => false, 'datacenter' => $datacenter, 'container' => true]);
$this->datacenter->sockets[$datacenter]->incoming_messages[$message['orig_message']['msg_id']] = ['content' => $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['orig_message']];
@ -179,7 +182,7 @@ trait ResponseHandler
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $key => $msg_id) {
$msg_id = new \phpseclib\Math\BigInteger(strrev($msg_id), 256);
$status = 'Status for message id ' . $msg_id . ': ';
$status = 'Status for message id '.$msg_id.': ';
if (($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['info'][$key] & 4) !== 0) {
$this->ack_outgoing_message_id($msg_id, $datacenter);
}
@ -253,9 +256,9 @@ trait ResponseHandler
break;
default:
$only_updates = false;
\danog\MadelineProto\Logger::log(['Trying to assign a response of type ' . $response_type . ' to its request...'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Trying to assign a response of type '.$response_type.' to its request...'], \danog\MadelineProto\Logger::VERBOSE);
foreach ($this->datacenter->sockets[$datacenter]->new_outgoing as $key => $expecting) {
\danog\MadelineProto\Logger::log(['Does the request of return type ' . $expecting['type'] . ' match?'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Does the request of return type '.$expecting['type'].' match?'], \danog\MadelineProto\Logger::VERBOSE);
if ($response_type === $expecting['type']) {
\danog\MadelineProto\Logger::log(['Yes'], \danog\MadelineProto\Logger::VERBOSE);
$this->datacenter->sockets[$datacenter]->outgoing_messages[$expecting['msg_id']]['response'] = $current_msg_id;
@ -265,7 +268,8 @@ trait ResponseHandler
}
\danog\MadelineProto\Logger::log(['No'], \danog\MadelineProto\Logger::VERBOSE);
}
throw new \danog\MadelineProto\ResponseException('Dunno how to handle ' . PHP_EOL . var_export($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'], true));
throw new \danog\MadelineProto\ResponseException('Dunno how to handle '.PHP_EOL.var_export($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'], true));
break;
}
break;
@ -293,11 +297,14 @@ trait ResponseHandler
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]);
}
}
return $only_updates;
}
public function handle_messages_threaded()
{
}
public function handle_rpc_error($server_answer, &$aargs)
{
if (in_array($server_answer['error_message'], ['PERSISTENT_TIMESTAMP_EMPTY', 'PERSISTENT_TIMESTAMP_OUTDATED', 'PERSISTENT_TIMESTAMP_INVALID'])) {
@ -306,7 +313,8 @@ trait ResponseHandler
switch ($server_answer['error_code']) {
case 303:
$this->datacenter->curdc = $aargs['datacenter'] = (int) preg_replace('/[^0-9]+/', '', $server_answer['error_message']);
throw new \danog\MadelineProto\Exception('Received request to switch to DC ' . $this->datacenter->curdc);
throw new \danog\MadelineProto\Exception('Received request to switch to DC '.$this->datacenter->curdc);
case 401:
switch ($server_answer['error_message']) {
case 'USER_DEACTIVATED':
@ -346,8 +354,9 @@ trait ResponseHandler
$seconds = preg_replace('/[^0-9]+/', '', $server_answer['error_message']);
$limit = isset($aargs['FloodWaitLimit']) ? $aargs['FloodWaitLimit'] : $this->settings['flood_timeout']['wait_if_lt'];
if (is_numeric($seconds) && $seconds < $limit) {
\danog\MadelineProto\Logger::log(['Flood, waiting ' . $seconds . ' seconds...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Flood, waiting '.$seconds.' seconds...'], \danog\MadelineProto\Logger::NOTICE);
sleep($seconds);
throw new \danog\MadelineProto\Exception('Re-executing query...');
}
case 500:
@ -356,6 +365,7 @@ trait ResponseHandler
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
}
}
public function handle_pending_updates()
{
if ($this->postpone_updates) {
@ -369,6 +379,7 @@ trait ResponseHandler
}
}
}
public function handle_updates($updates)
{
if (!$this->settings['updates']['handle_updates']) {
@ -377,6 +388,7 @@ trait ResponseHandler
if ($this->postpone_updates) {
\danog\MadelineProto\Logger::log(['Postpone update handling'], \danog\MadelineProto\Logger::VERBOSE);
$this->pending_updates[] = $updates;
return false;
}
$this->handle_pending_updates();
@ -419,7 +431,7 @@ trait ResponseHandler
$this->get_updates_difference();
break;
default:
throw new \danog\MadelineProto\ResponseException('Unrecognized update received: ' . var_export($updates, true));
throw new \danog\MadelineProto\ResponseException('Unrecognized update received: '.var_export($updates, true));
break;
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -23,14 +24,16 @@ trait SaltHandler
$this->add_salt($salt['valid_since'], $salt['valid_until'], $salt['salt']);
}
}
public function add_salt($valid_since, $valid_until, $salt)
{
if (!isset($this->datacenter->sockets[$datacenter]->temp_auth_key['salts'][$salt])) {
$this->datacenter->sockets[$datacenter]->temp_auth_key['salts'][$salt] = ['valid_since' => $valid_since, 'valid_until' => $valid_until];
}
}
public function handle_future_salts($salt)
{
$this->method_call('messages.sendMessage', ['peer' => $salt, 'message' => base64_decode('UG93ZXJlZCBieSBATWFkZWxpbmVQcm90bw==')], ['datacenter' => $this->datacenter->curdc]);
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -25,12 +26,14 @@ trait SeqNoHandler
//var_dump("OUT $datacenter: $value + $in = ".$this->datacenter->sockets[$datacenter]->session_out_seq_no);
return $value * 2 + $in;
}
public function check_in_seq_no($datacenter, $current_msg_id)
{
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['seq_no']) && ($seq_no = $this->generate_in_seq_no($datacenter, $this->content_related($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']))) !== $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['seq_no']) {
//\danog\MadelineProto\Logger::log(['SECURITY WARNING: Seqno mismatch (should be '.$seq_no.', is '.$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['seq_no'].', '.$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].')'], \danog\MadelineProto\Logger::ERROR);
}
}
public function generate_in_seq_no($datacenter, $content_related)
{
$in = $content_related ? 1 : 0;
@ -39,6 +42,7 @@ trait SeqNoHandler
//var_dump("IN $datacenter: $value + $in = ".$this->datacenter->sockets[$datacenter]->session_in_seq_no);
return $value * 2 + $in;
}
public function content_related($method)
{
return isset($method['_']) ? !in_array($method['_'], [
@ -74,4 +78,4 @@ trait SeqNoHandler
'msg_resend_ans_req',
]) : true;
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -23,6 +24,7 @@ trait UpdateHandler
private $channels_state = [];
public $updates = [];
public $updates_key = 0;
public function pwr_update_handler($update)
{
/*
@ -55,6 +57,7 @@ trait UpdateHandler
in_array($this->settings['pwr']['update_handler'], [['danog\\MadelineProto\\API', 'get_updates_update_handler'], 'get_updates_update_handler']) ? $this->get_updates_update_handler($update) : $this->settings['pwr']['update_handler']($update);
}
}
public function get_updates_update_handler($update)
{
if (!$this->settings['updates']['handle_updates']) {
@ -63,6 +66,7 @@ trait UpdateHandler
$this->updates[$this->updates_key++] = $update;
//\danog\MadelineProto\Logger::log(['Stored ', $update);
}
public function get_updates($params = [])
{
if (!$this->settings['updates']['handle_updates']) {
@ -74,6 +78,7 @@ trait UpdateHandler
}
});
$time = microtime(true);
try {
try {
if (($error = $this->recv_message($this->datacenter->curdc)) !== true) {
@ -82,9 +87,11 @@ trait UpdateHandler
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
$this->datacenter->sockets[$this->datacenter->curdc]->temp_auth_key = null;
$this->init_authorization();
throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key');
}
}
throw new \danog\MadelineProto\RPCErrorException($error, $error);
}
$only_updates = $this->handle_messages($this->datacenter->curdc);
@ -127,21 +134,26 @@ trait UpdateHandler
$updates[] = ['update_id' => $key, 'update' => $value];
}
}
return $updates;
}
public function &load_channel_state($channel, $pts = 0)
{
if (!isset($this->channels_state[$channel])) {
$this->channels_state[$channel] = ['pts' => $pts, 'sync_loading' => false];
}
return $this->channels_state[$channel];
}
public function set_channel_state($channel, $data)
{
if (isset($data['pts']) && $data['pts'] !== 0) {
$this->load_channel_state($channel)['pts'] = $data['pts'];
}
}
public function check_msg_id($message)
{
try {
@ -152,22 +164,27 @@ trait UpdateHandler
$message_id = $message['id'];
if (!isset($this->msg_ids[$peer_id]) || $message_id > $this->msg_ids[$peer_id]) {
$this->msg_ids[$peer_id] = $message_id;
return true;
}
return false;
}
public function get_channel_difference($channel)
{
if (!$this->settings['updates']['handle_updates']) {
return;
}
if ($this->load_channel_state($channel)['sync_loading']) {
\danog\MadelineProto\Logger::log(['Not fetching ' . $channel . ' difference, I am already fetching it']);
\danog\MadelineProto\Logger::log(['Not fetching '.$channel.' difference, I am already fetching it']);
return;
}
$this->load_channel_state($channel)['sync_loading'] = true;
try {
$input = $this->get_info('channel#' . $channel);
$input = $this->get_info('channel#'.$channel);
if (!isset($input['InputChannel'])) {
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
}
@ -180,17 +197,20 @@ trait UpdateHandler
$this->load_channel_state($channel)['sync_loading'] = false;
}
$this->load_channel_state($channel)['sync_loading'] = true;
\danog\MadelineProto\Logger::log(['Fetching ' . $channel . ' difference...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
\danog\MadelineProto\Logger::log(['Fetching '.$channel.' difference...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
try {
$difference = $this->method_call('updates.getChannelDifference', ['channel' => $input, 'filter' => ['_' => 'channelMessagesFilterEmpty'], 'pts' => $this->load_channel_state($channel)['pts'], 'limit' => 30], ['datacenter' => $this->datacenter->curdc]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->getMessage() === "You haven't joined this channel/supergroup") {
return false;
}
throw $e;
} catch (\danog\MadelineProto\PTSException $e) {
unset($this->channels_state[$channel]);
$this->load_channel_state($channel)['sync_loading'] = false;
return $this->get_channel_difference($channel);
} finally {
$this->load_channel_state($channel)['sync_loading'] = false;
@ -202,6 +222,7 @@ trait UpdateHandler
break;
case 'updates.channelDifference':
$this->load_channel_state($channel)['sync_loading'] = true;
try {
$this->set_channel_state($channel, $difference);
$this->handle_update_messages($difference['new_messages'], $channel);
@ -215,8 +236,9 @@ trait UpdateHandler
}
break;
case 'updates.channelDifferenceTooLong':
\danog\MadelineProto\Logger::log(['Got ' . $difference['_']], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::VERBOSE);
$this->load_channel_state($channel)['sync_loading'] = true;
try {
$this->set_channel_state($channel, $difference);
$this->handle_update_messages($difference['messages'], $channel);
@ -227,10 +249,11 @@ trait UpdateHandler
$this->get_channel_difference($channel);
break;
default:
throw new \danog\MadelineProto\Exception('Unrecognized update difference received: ' . var_export($difference, true));
throw new \danog\MadelineProto\Exception('Unrecognized update difference received: '.var_export($difference, true));
break;
}
}
public function set_update_state($data)
{
if (isset($data['pts']) && $data['pts'] !== 0) {
@ -246,6 +269,7 @@ trait UpdateHandler
$this->load_update_state()['date'] = $data['date'];
}
}
public function &load_update_state()
{
if (!isset($this->updates_state['qts'])) {
@ -255,8 +279,10 @@ trait UpdateHandler
$this->got_state = true;
$this->set_update_state($this->get_updates_state());
}
return $this->updates_state;
}
public function get_updates_difference()
{
if (!$this->settings['updates']['handle_updates']) {
@ -264,6 +290,7 @@ trait UpdateHandler
}
if ($this->updates_state['sync_loading']) {
\danog\MadelineProto\Logger::log(['Not fetching normal difference, I am already fetching it']);
return false;
}
$this->updates_state['sync_loading'] = true;
@ -278,7 +305,8 @@ trait UpdateHandler
$this->updates_state['sync_loading'] = false;
}
}
\danog\MadelineProto\Logger::log(['Got ' . $difference['_']], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
\danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
try {
switch ($difference['_']) {
case 'updates.differenceEmpty':
@ -303,37 +331,42 @@ trait UpdateHandler
$this->get_updates_difference();
break;
default:
throw new \danog\MadelineProto\Exception('Unrecognized update difference received: ' . var_export($difference, true));
throw new \danog\MadelineProto\Exception('Unrecognized update difference received: '.var_export($difference, true));
break;
}
} finally {
$this->updates_state['sync_loading'] = false;
}
}
public function get_updates_state()
{
$last = $this->updates_state['sync_loading'];
$this->updates_state['sync_loading'] = true;
try {
$data = $this->method_call('updates.getState', [], ['datacenter' => $this->datacenter->curdc]);
$this->get_cdn_config($this->datacenter->curdc);
} finally {
$this->updates_state['sync_loading'] = $last;
}
return $data;
}
public function handle_update($update, $options = [])
{
if (!$this->settings['updates']['handle_updates']) {
return;
}
\danog\MadelineProto\Logger::log(['Handling an update of type ' . $update['_'] . '...'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Handling an update of type '.$update['_'].'...'], \danog\MadelineProto\Logger::VERBOSE);
$channel_id = false;
switch ($update['_']) {
case 'updateNewChannelMessage':
case 'updateEditChannelMessage':
if ($update['message']['_'] === 'messageEmpty') {
\danog\MadelineProto\Logger::log(['Got message empty, not saving'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
return false;
}
$channel_id = $update['message']['to_id']['channel_id'];
@ -346,24 +379,26 @@ trait UpdateHandler
\danog\MadelineProto\Logger::log(['Got channel too long update, getting difference...'], \danog\MadelineProto\Logger::VERBOSE);
if (!isset($this->channels_state[$channel_id]) && !isset($update['pts'])) {
\danog\MadelineProto\Logger::log(['I do not have the channel in the states and the pts is not set.'], \danog\MadelineProto\Logger::ERROR);
return;
}
break;
}
if ($channel_id === false) {
$cur_state =& $this->load_update_state();
$cur_state = &$this->load_update_state();
} else {
$cur_state =& $this->load_channel_state($channel_id, (isset($update['pts']) ? $update['pts'] : 0) - (isset($update['pts_count']) ? $update['pts_count'] : 0));
$cur_state = &$this->load_channel_state($channel_id, (isset($update['pts']) ? $update['pts'] : 0) - (isset($update['pts_count']) ? $update['pts_count'] : 0));
}
/*
if ($cur_state['sync_loading'] && in_array($update['_'], ['updateNewMessage', 'updateEditMessage', 'updateNewChannelMessage', 'updateEditChannelMessage'])) {
\danog\MadelineProto\Logger::log(['Sync loading, not handling update'], \danog\MadelineProto\Logger::NOTICE);
return false;
}*/
switch ($update['_']) {
case 'updateChannelTooLong':
$this->get_channel_difference($channel_id);
return false;
case 'updateNewMessage':
case 'updateEditMessage':
@ -376,37 +411,42 @@ trait UpdateHandler
} else {
$this->get_updates_difference();
}
return false;
}
break;
default:
if ($channel_id !== false && !$this->peer_isset($this->to_supergroup($channel_id))) {
\danog\MadelineProto\Logger::log(['Skipping update, I do not have the channel id ' . $channel_id], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Skipping update, I do not have the channel id '.$channel_id], \danog\MadelineProto\Logger::ERROR);
return false;
}
break;
}
if (isset($update['pts'])) {
if ($update['pts'] < $cur_state['pts']) {
\danog\MadelineProto\Logger::log(['Duplicate update, channel id: ' . $channel_id], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Duplicate update, channel id: '.$channel_id], \danog\MadelineProto\Logger::ERROR);
return false;
}
if ($cur_state['pts'] + (isset($update['pts_count']) ? $update['pts_count'] : 0) !== $update['pts']) {
\danog\MadelineProto\Logger::log(['Pts hole. current pts: ' . $cur_state['pts'] . ', pts count: ' . (isset($update['pts_count']) ? $update['pts_count'] : 0) . ', pts: ' . $update['pts'] . ', channel id: ' . $channel_id], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Pts hole. current pts: '.$cur_state['pts'].', pts count: '.(isset($update['pts_count']) ? $update['pts_count'] : 0).', pts: '.$update['pts'].', channel id: '.$channel_id], \danog\MadelineProto\Logger::ERROR);
if ($channel_id !== false && $this->peer_isset($this->to_supergroup($channel_id))) {
$this->get_channel_difference($channel_id);
} else {
$this->get_updates_difference();
}
return false;
}
if (isset($update['message']['id'], $update['message']['to_id'])) {
if (!$this->check_msg_id($update['message'])) {
\danog\MadelineProto\Logger::log(['Duplicate update by message id, channel id: ' . $channel_id], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Duplicate update by message id, channel id: '.$channel_id], \danog\MadelineProto\Logger::ERROR);
return false;
}
}
\danog\MadelineProto\Logger::log(['Applying pts. current pts: ' . $cur_state['pts'] . ', new pts: ' . $update['pts'] . ', channel id: ' . $channel_id], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Applying pts. current pts: '.$cur_state['pts'].', new pts: '.$update['pts'].', channel id: '.$channel_id], \danog\MadelineProto\Logger::VERBOSE);
$cur_state['pts'] = $update['pts'];
if ($channel_id === false && isset($options['date']) && $cur_state['date'] < $options['date']) {
$cur_state['date'] = $options['date'];
@ -416,8 +456,9 @@ trait UpdateHandler
$seq = $options['seq'];
$seq_start = isset($options['seq_start']) ? $options['seq_start'] : $options['seq'];
if ($seq_start != $cur_state['seq'] + 1 && $seq_start > $cur_state['seq']) {
\danog\MadelineProto\Logger::log(['Seq hole. seq_start: ' . $seq_start . ' != cur seq: ' . $cur_state['seq'] . ' + 1'], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Seq hole. seq_start: '.$seq_start.' != cur seq: '.$cur_state['seq'].' + 1'], \danog\MadelineProto\Logger::ERROR);
$this->get_updates_difference();
return false;
}
if ($cur_state['seq'] !== $seq) {
@ -429,6 +470,7 @@ trait UpdateHandler
}
$this->save_update($update);
}
public function handle_multiple_update($updates, $options = [], $channel = false)
{
if (!$this->settings['updates']['handle_updates']) {
@ -444,6 +486,7 @@ trait UpdateHandler
}
}
}
public function handle_update_messages($messages, $channel = false)
{
if (!$this->settings['updates']['handle_updates']) {
@ -453,6 +496,7 @@ trait UpdateHandler
$this->handle_update(['_' => $channel === false ? 'updateNewMessage' : 'updateNewChannelMessage', 'message' => $message, 'pts' => $channel === false ? $this->load_update_state()['pts'] : $this->load_channel_state($channel)['pts'], 'pts_count' => 0]);
}
}
public function save_update($update)
{
array_walk($this->calls, function ($controller, $id) {
@ -463,11 +507,13 @@ trait UpdateHandler
if ($update['_'] === 'updateDcOptions') {
\danog\MadelineProto\Logger::log(['Got new dc options'], \danog\MadelineProto\Logger::VERBOSE);
$this->parse_dc_options($update['dc_options']);
return;
}
if ($update['_'] === 'updatePhoneCall') {
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
\danog\MadelineProto\Logger::log(['The php-libtgvoip extension is required to accept and manage calls. See daniil.it/MadelineProto for more info.'], \danog\MadelineProto\Logger::WARNING);
return;
}
switch ($update['phone_call']['_']) {
@ -495,6 +541,7 @@ trait UpdateHandler
if (!isset($this->calls[$update['phone_call']['id']])) {
return;
}
return $this->calls[$update['phone_call']['id']]->discard(['_' => 'phoneCallDiscardReasonHangup'], [], $update['phone_call']['need_debug']);
}
}
@ -504,17 +551,20 @@ trait UpdateHandler
$cur_state['qts'] = $update['qts'];
}
if ($update['qts'] < $cur_state['qts']) {
\danog\MadelineProto\Logger::log(['Duplicate update. update qts: ' . $update['qts'] . ' <= current qts ' . $cur_state['qts'] . ', chat id: ' . $update['message']['chat_id']], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Duplicate update. update qts: '.$update['qts'].' <= current qts '.$cur_state['qts'].', chat id: '.$update['message']['chat_id']], \danog\MadelineProto\Logger::ERROR);
return false;
}
if ($update['qts'] > $cur_state['qts'] + 1) {
\danog\MadelineProto\Logger::log(['Qts hole. Fetching updates manually: update qts: ' . $update['qts'] . ' > current qts ' . $cur_state['qts'] . '+1, chat id: ' . $update['message']['chat_id']], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Qts hole. Fetching updates manually: update qts: '.$update['qts'].' > current qts '.$cur_state['qts'].'+1, chat id: '.$update['message']['chat_id']], \danog\MadelineProto\Logger::ERROR);
$this->get_updates_difference();
return false;
}
\danog\MadelineProto\Logger::log(['Applying qts: ' . $update['qts'] . ' over current qts ' . $cur_state['qts'] . ', chat id: ' . $update['message']['chat_id']], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Applying qts: '.$update['qts'].' over current qts '.$cur_state['qts'].', chat id: '.$update['message']['chat_id']], \danog\MadelineProto\Logger::VERBOSE);
$this->method_call('messages.receivedQueue', ['max_qts' => $cur_state['qts'] = $update['qts']], ['datacenter' => $this->datacenter->curdc]);
$this->handle_encrypted_update($update);
return;
}
/*
@ -528,11 +578,11 @@ trait UpdateHandler
if ($this->settings['secret_chats']['accept_chats'] === false || is_array($this->settings['secret_chats']['accept_chats']) && !in_array($update['chat']['admin_id'], $this->settings['secret_chats']['accept_chats'])) {
return;
}
\danog\MadelineProto\Logger::log(['Accepting secret chat ' . $update['chat']['id']], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Accepting secret chat '.$update['chat']['id']], \danog\MadelineProto\Logger::NOTICE);
$this->accept_secret_chat($update['chat']);
break;
case 'encryptedChatDiscarded':
\danog\MadelineProto\Logger::log(['Deleting secret chat ' . $update['chat']['id'] . ' because it was revoked by the other user'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Deleting secret chat '.$update['chat']['id'].' because it was revoked by the other user'], \danog\MadelineProto\Logger::NOTICE);
if (isset($this->secret_chats[$update['chat']['id']])) {
unset($this->secret_chats[$update['chat']['id']]);
}
@ -541,7 +591,7 @@ trait UpdateHandler
}
break;
case 'encryptedChat':
\danog\MadelineProto\Logger::log(['Completing creation of secret chat ' . $update['chat']['id']], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Completing creation of secret chat '.$update['chat']['id']], \danog\MadelineProto\Logger::NOTICE);
$this->complete_secret_chat($update['chat']);
break;
}
@ -556,19 +606,21 @@ trait UpdateHandler
if (isset($update['message']['from_id']) && $update['message']['from_id'] === $this->authorization['user']['id']) {
$update['message']['out'] = true;
}
\danog\MadelineProto\Logger::log(['Saving an update of type ' . $update['_'] . '...'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Saving an update of type '.$update['_'].'...'], \danog\MadelineProto\Logger::VERBOSE);
if (isset($this->settings['pwr']['strict']) && $this->settings['pwr']['strict'] && isset($this->settings['pwr']['update_handler'])) {
$this->pwr_update_handler($update);
} else {
in_array($this->settings['updates']['callback'], [['danog\\MadelineProto\\API', 'get_updates_update_handler'], 'get_updates_update_handler']) ? $this->get_updates_update_handler($update) : $this->settings['updates']['callback']($update);
}
}
public function pwr_webhook($update)
{
$payload = json_encode($update);
\danog\MadelineProto\Logger::log([$update, $payload, json_last_error()]);
if ($payload === '') {
\danog\MadelineProto\Logger::log(['EMPTY UPDATE']);
return false;
}
$ch = curl_init();
@ -588,7 +640,7 @@ trait UpdateHandler
}
$result = curl_exec($ch);
curl_close($ch);
\danog\MadelineProto\Logger::log(['Result of webhook query is ' . $result], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Result of webhook query is '.$result], \danog\MadelineProto\Logger::NOTICE);
$result = json_decode($result, true);
if (is_array($result) && isset($result['method']) && $result['method'] != '' && is_string($result['method'])) {
try {
@ -600,4 +652,4 @@ trait UpdateHandler
}
}
}
}
}

View File

@ -10,8 +10,9 @@ 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/>.
*/
namespace danog\MadelineProto;
class NothingInTheSocketException extends \Exception
{
}
}

View File

@ -10,18 +10,21 @@ 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/>.
*/
namespace danog\MadelineProto;
class PTSException extends \Exception
{
use TL\PrettyException;
public function __toString()
{
return get_class($this) . ($this->message !== '' ? ': ' : '') . $this->message . PHP_EOL . 'TL Trace:' . PHP_EOL . PHP_EOL . $this->getTLTrace() . PHP_EOL;
return get_class($this).($this->message !== '' ? ': ' : '').$this->message.PHP_EOL.'TL Trace:'.PHP_EOL.PHP_EOL.$this->getTLTrace().PHP_EOL;
}
public function __construct($message, $file = '')
{
parent::__construct($message);
$this->prettify_tl($file);
}
}
}

View File

@ -10,23 +10,38 @@ 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/>.
*/
namespace danog\MadelineProto;
interface Proxy
{
public function __construct($domain, $type, $protocol);
public function setOption($level, $name, $value);
public function getOption($level, $name);
public function setBlocking($blocking);
public function bind($address, $port = 0);
public function listen($backlog = 0);
public function accept();
public function connect($address, $port = 0);
public function select(array &$read, array &$write, array &$except, $tv_sec, $tv_usec = 0);
public function read($length, $flags = 0);
public function write($buffer, $length = -1);
public function send($data, $length, $flags);
public function close();
public function getPeerName($port = true);
public function getSockName($port = true);
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto;
class RPCErrorException extends \Exception
@ -17,20 +18,24 @@ class RPCErrorException extends \Exception
use TL\PrettyException;
private $fetched = false;
public static $rollbar = true;
public function getMess()
{
if ($this->fetched === false) {
$res = json_decode(@file_get_contents('https://rpc.pwrtelegram.xyz/?method=' . $additional[0] . '&code=' . $code . '&error=' . $this->rpc), true);
$res = json_decode(@file_get_contents('https://rpc.pwrtelegram.xyz/?method='.$additional[0].'&code='.$code.'&error='.$this->rpc), true);
if (isset($res['ok']) && $res['ok']) {
$this->message = $res['result'];
}
}
return $this->message;
}
public function __toString()
{
return sprintf(\danog\MadelineProto\Lang::$current_lang['rpc_tg_error'], $this->getMess(), $this->rpc, $this->file, $this->line . PHP_EOL . PHP_EOL) . PHP_EOL . 'Revision: ' . @file_get_contents(__DIR__ . '/../../../.git/refs/heads/master') . PHP_EOL . $this->getTLTrace() . PHP_EOL;
return sprintf(\danog\MadelineProto\Lang::$current_lang['rpc_tg_error'], $this->getMess(), $this->rpc, $this->file, $this->line.PHP_EOL.PHP_EOL).PHP_EOL.'Revision: '.@file_get_contents(__DIR__.'/../../../.git/refs/heads/master').PHP_EOL.$this->getTLTrace().PHP_EOL;
}
public function __construct($message = null, $code = 0, Exception $previous = null)
{
$this->rpc = $message;
@ -186,4 +191,4 @@ class RPCErrorException extends \Exception
}
$message === 'Telegram is having internal issues, please try again later.' ? \Rollbar\Rollbar::log(\Rollbar\Payload\Level::critical(), $message) : \Rollbar\Rollbar::log(\Rollbar\Payload\Level::error(), $this, $additional);
}
}
}

View File

@ -10,6 +10,7 @@ See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with the MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto;
class RSA
@ -20,6 +21,7 @@ class RSA
public $e;
public $n;
public $fp;
public function __magic_construct($rsa_key)
{
//if ($this->unserialized($rsa_key)) return true;
@ -30,16 +32,20 @@ class RSA
$this->n = \phpseclib\Common\Functions\Objects::getVar($key, 'modulus');
$this->e = \phpseclib\Common\Functions\Objects::getVar($key, 'exponent');
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['computing_fingerprint']], Logger::ULTRA_VERBOSE);
$this->fp = substr(sha1($this->serialize_object(['type' => 'bytes'], $this->n->toBytes(), 'key') . $this->serialize_object(['type' => 'bytes'], $this->e->toBytes(), 'key'), true), -8);
$this->fp = substr(sha1($this->serialize_object(['type' => 'bytes'], $this->n->toBytes(), 'key').$this->serialize_object(['type' => 'bytes'], $this->e->toBytes(), 'key'), true), -8);
return true;
}
public function __sleep()
{
return ['e', 'n', 'fp'];
}
public function encrypt($data)
{
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['rsa_encrypting']], Logger::VERBOSE);
return (new \phpseclib\Math\BigInteger($data, 256))->powMod($this->e, $this->n)->toBytes();
}
}
}

View File

@ -10,8 +10,9 @@ 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/>.
*/
namespace danog\MadelineProto;
class ResponseException extends \Exception
{
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\SecretChats;
/**
@ -21,12 +22,14 @@ trait AuthKeyHandler
{
protected $temp_requested_secret_chats = [];
protected $secret_chats = [];
public function accept_secret_chat($params)
{
//var_dump($params['id'],$this->secret_chat_status($params['id']));
if ($this->secret_chat_status($params['id']) !== 0) {
//var_dump($this->secret_chat_status($params['id']));
\danog\MadelineProto\Logger::log(["I've already accepted secret chat " . $params['id']]);
\danog\MadelineProto\Logger::log(["I've already accepted secret chat ".$params['id']]);
return false;
}
$dh_config = $this->get_dh_config();
@ -45,8 +48,9 @@ trait AuthKeyHandler
$this->method_call('messages.acceptEncryption', ['peer' => $params['id'], 'g_b' => $g_b->toBytes(), 'key_fingerprint' => $key['fingerprint']], ['datacenter' => $this->datacenter->curdc]);
$this->notify_layer($params['id']);
$this->handle_pending_updates();
\danog\MadelineProto\Logger::log(['Secret chat ' . $params['id'] . ' accepted successfully!'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Secret chat '.$params['id'].' accepted successfully!'], \danog\MadelineProto\Logger::NOTICE);
}
public function request_secret_chat($user)
{
$user = $this->get_info($user);
@ -54,7 +58,7 @@ trait AuthKeyHandler
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
}
$user = $user['InputUser'];
\danog\MadelineProto\Logger::log(['Creating secret chat with ' . $user['user_id'] . '...'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Creating secret chat with '.$user['user_id'].'...'], \danog\MadelineProto\Logger::VERBOSE);
$dh_config = $this->get_dh_config();
\danog\MadelineProto\Logger::log(['Generating a...'], \danog\MadelineProto\Logger::VERBOSE);
$a = new \phpseclib\Math\BigInteger($this->random(256), 256);
@ -65,14 +69,17 @@ trait AuthKeyHandler
$this->temp_requested_secret_chats[$res['id']] = $a;
$this->handle_pending_updates();
$this->get_updates_difference();
\danog\MadelineProto\Logger::log(['Secret chat ' . $res['id'] . ' requested successfully!'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Secret chat '.$res['id'].' requested successfully!'], \danog\MadelineProto\Logger::NOTICE);
return $res['id'];
}
public function complete_secret_chat($params)
{
if ($this->secret_chat_status($params['id']) !== 1) {
//var_dump($this->secret_chat_status($params['id']));
\danog\MadelineProto\Logger::log(['Could not find and complete secret chat ' . $params['id']]);
\danog\MadelineProto\Logger::log(['Could not find and complete secret chat '.$params['id']]);
return false;
}
$dh_config = $this->get_dh_config();
@ -84,6 +91,7 @@ trait AuthKeyHandler
//var_dump($key);
if ($key['fingerprint'] !== $params['key_fingerprint']) {
$this->discard_secret_chat($params['id']);
throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!');
}
$key['visualization_orig'] = substr(sha1($key['auth_key'], true), 16);
@ -91,19 +99,22 @@ trait AuthKeyHandler
$this->secret_chats[$params['id']] = ['key' => $key, 'admin' => true, 'user_id' => $params['participant_id'], 'InputEncryptedChat' => ['chat_id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputEncryptedChat'], 'in_seq_no_x' => 0, 'out_seq_no_x' => 1, 'in_seq_no' => 0, 'out_seq_no' => 0, 'layer' => 8, 'ttl' => 0, 'ttr' => 100, 'updated' => time(), 'incoming' => [], 'outgoing' => [], 'created' => time(), 'rekeying' => [0], 'key_x' => 'to server', 'mtproto' => 1];
$this->notify_layer($params['id']);
$this->handle_pending_updates();
\danog\MadelineProto\Logger::log(['Secret chat ' . $params['id'] . ' completed successfully!'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Secret chat '.$params['id'].' completed successfully!'], \danog\MadelineProto\Logger::NOTICE);
}
public function notify_layer($chat)
{
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNotifyLayer', 'layer' => $this->encrypted_layer]]], ['datacenter' => $this->datacenter->curdc]);
}
protected $temp_rekeyed_secret_chats = [];
public function rekey($chat)
{
if ($this->secret_chats[$chat]['rekeying'][0] !== 0) {
return;
}
\danog\MadelineProto\Logger::log(['Rekeying secret chat ' . $chat . '...'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Rekeying secret chat '.$chat.'...'], \danog\MadelineProto\Logger::VERBOSE);
$dh_config = $this->get_dh_config();
\danog\MadelineProto\Logger::log(['Generating a...'], \danog\MadelineProto\Logger::VERBOSE);
$a = new \phpseclib\Math\BigInteger($this->random(256), 256);
@ -116,8 +127,10 @@ trait AuthKeyHandler
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionRequestKey', 'g_a' => $g_a->toBytes(), 'exchange_id' => $e]]], ['datacenter' => $this->datacenter->curdc]);
$this->handle_pending_updates();
$this->get_updates_difference();
return $e;
}
public function accept_rekey($chat, $params)
{
if ($this->secret_chats[$chat]['rekeying'][0] !== 0) {
@ -129,10 +142,11 @@ trait AuthKeyHandler
}
if ($my_exchange_id === $other_exchange_id) {
$this->secret_chats[$chat]['rekeying'] = [0];
return;
}
}
\danog\MadelineProto\Logger::log(['Accepting rekeying of secret chat ' . $chat . '...'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Accepting rekeying of secret chat '.$chat.'...'], \danog\MadelineProto\Logger::VERBOSE);
$dh_config = $this->get_dh_config();
\danog\MadelineProto\Logger::log(['Generating b...'], \danog\MadelineProto\Logger::VERBOSE);
$b = new \phpseclib\Math\BigInteger($this->random(256), 256);
@ -150,13 +164,15 @@ trait AuthKeyHandler
$this->handle_pending_updates();
$this->get_updates_difference();
}
public function commit_rekey($chat, $params)
{
if ($this->secret_chats[$chat]['rekeying'][0] !== 1 || !isset($this->temp_rekeyed_secret_chats[$params['exchange_id']])) {
$this->secret_chats[$chat]['rekeying'] = [0];
return;
}
\danog\MadelineProto\Logger::log(['Committing rekeying of secret chat ' . $chat . '...'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Committing rekeying of secret chat '.$chat.'...'], \danog\MadelineProto\Logger::VERBOSE);
$dh_config = $this->get_dh_config();
$params['g_b'] = new \phpseclib\Math\BigInteger($params['g_b'], 256);
$this->check_G($params['g_b'], $dh_config['p']);
@ -166,6 +182,7 @@ trait AuthKeyHandler
$key['visualization_46'] = substr(hash('sha256', $key['auth_key'], true), 20);
if ($key['fingerprint'] !== $params['key_fingerprint']) {
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionAbortKey', 'exchange_id' => $params['exchange_id']]]], ['datacenter' => $this->datacenter->curdc]);
throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!');
}
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionCommitKey', 'exchange_id' => $params['exchange_id'], 'key_fingerprint' => $key['fingerprint']]]], ['datacenter' => $this->datacenter->curdc]);
@ -178,6 +195,7 @@ trait AuthKeyHandler
$this->handle_pending_updates();
$this->get_updates_difference();
}
public function complete_rekey($chat, $params)
{
if ($this->secret_chats[$chat]['rekeying'][0] !== 2 || !isset($this->temp_rekeyed_secret_chats['fingerprint'])) {
@ -185,9 +203,10 @@ trait AuthKeyHandler
}
if ($this->temp_rekeyed_secret_chats['fingerprint'] !== $params['key_fingerprint']) {
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionAbortKey', 'exchange_id' => $params['exchange_id']]]], ['datacenter' => $this->datacenter->curdc]);
throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!');
}
\danog\MadelineProto\Logger::log(['Completing rekeying of secret chat ' . $chat . '...'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Completing rekeying of secret chat '.$chat.'...'], \danog\MadelineProto\Logger::VERBOSE);
$this->secret_chats[$chat]['rekeying'] = [0];
$this->secret_chats[$chat]['old_key'] = $this->secret_chats[$chat]['key'];
$this->secret_chats[$chat]['key'] = $this->temp_rekeyed_secret_chats[$chat];
@ -195,9 +214,11 @@ trait AuthKeyHandler
$this->secret_chats[$chat]['updated'] = time();
unset($this->temp_rekeyed_secret_chats[$params['exchange_id']]);
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNoop']]], ['datacenter' => $this->datacenter->curdc]);
\danog\MadelineProto\Logger::log(['Secret chat ' . $chat . ' rekeyed successfully!'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Secret chat '.$chat.' rekeyed successfully!'], \danog\MadelineProto\Logger::VERBOSE);
return true;
}
public function secret_chat_status($chat)
{
if (isset($this->secret_chats[$chat])) {
@ -206,15 +227,18 @@ trait AuthKeyHandler
if (isset($this->temp_requested_secret_chats[$chat])) {
return 1;
}
return 0;
}
public function get_secret_chat($chat)
{
return $this->secret_chats[$chat];
}
public function discard_secret_chat($chat)
{
\danog\MadelineProto\Logger::log(['Discarding secret chat ' . $chat . '...'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Discarding secret chat '.$chat.'...'], \danog\MadelineProto\Logger::VERBOSE);
//var_dump(debug_backtrace(0)[0]);
if (isset($this->secret_chats[$chat])) {
unset($this->secret_chats[$chat]);
@ -225,6 +249,7 @@ trait AuthKeyHandler
if (isset($this->temp_rekeyed_secret_chats[$chat])) {
unset($this->temp_rekeyed_secret_chats[$chat]);
}
try {
$this->method_call('messages.discardEncryption', ['chat_id' => $chat], ['datacenter' => $this->datacenter->curdc]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
@ -233,4 +258,4 @@ trait AuthKeyHandler
}
}
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\SecretChats;
/**
@ -21,6 +22,7 @@ trait MessageHandler
{
if (!isset($this->secret_chats[$chat_id])) {
\danog\MadelineProto\Logger::log(sprintf(\danog\MadelineProto\Lang::$current_lang['secret_chat_skipping'], $chat_id));
return false;
}
$this->secret_chats[$chat_id]['ttr']--;
@ -33,27 +35,30 @@ trait MessageHandler
}
$this->secret_chats[$chat_id]['outgoing'][$this->secret_chats[$chat_id]['out_seq_no']] = $message;
$message = $this->serialize_object(['type' => $constructor = $this->secret_chats[$chat_id]['layer'] === 8 ? 'DecryptedMessage' : 'DecryptedMessageLayer'], $message, $constructor, $this->secret_chats[$chat_id]['layer']);
$message = $this->pack_unsigned_int(strlen($message)) . $message;
$message = $this->pack_unsigned_int(strlen($message)).$message;
if ($this->secret_chats[$chat_id]['mtproto'] === 2) {
$padding = $this->posmod(-strlen($message), 16);
if ($padding < 12) {
$padding += 16;
}
$message .= $this->random($padding);
$message_key = substr(hash('sha256', substr($this->secret_chats[$chat_id]['key']['auth_key'], 88 + ($this->secret_chats[$chat_id]['admin'] ? 0 : 8), 32) . $message, true), 8, 16);
$message_key = substr(hash('sha256', substr($this->secret_chats[$chat_id]['key']['auth_key'], 88 + ($this->secret_chats[$chat_id]['admin'] ? 0 : 8), 32).$message, true), 8, 16);
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->secret_chats[$chat_id]['key']['auth_key'], $this->secret_chats[$chat_id]['admin']);
} else {
$message_key = substr(sha1($message, true), -16);
list($aes_key, $aes_iv) = $this->old_aes_calculate($message_key, $this->secret_chats[$chat_id]['key']['auth_key'], true);
$message .= $this->random($this->posmod(-strlen($message), 16));
}
$message = $this->secret_chats[$chat_id]['key']['fingerprint'] . $message_key . $this->ige_encrypt($message, $aes_key, $aes_iv);
$message = $this->secret_chats[$chat_id]['key']['fingerprint'].$message_key.$this->ige_encrypt($message, $aes_key, $aes_iv);
return $message;
}
public function handle_encrypted_update($message, $test = false)
{
if (!isset($this->secret_chats[$message['message']['chat_id']])) {
\danog\MadelineProto\Logger::log(sprintf(\danog\MadelineProto\Lang::$current_lang['secret_chat_skipping'], $message['message']['chat_id']));
return false;
}
$auth_key_id = substr($message['message']['bytes'], 0, 8);
@ -62,36 +67,40 @@ trait MessageHandler
if (isset($this->secret_chats[$message['message']['chat_id']]['old_key']['fingerprint'])) {
if ($auth_key_id !== $this->secret_chats[$message['message']['chat_id']]['old_key']['fingerprint']) {
$this->discard_secret_chat($message['message']['chat_id']);
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['fingerprint_mismatch']);
}
$old = true;
} else {
$this->discard_secret_chat($message['message']['chat_id']);
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['fingerprint_mismatch']);
}
}
$message_key = substr($message['message']['bytes'], 8, 16);
$encrypted_data = substr($message['message']['bytes'], 24);
if ($this->secret_chats[$message['message']['chat_id']]['mtproto'] === 2) {
\danog\MadelineProto\Logger::log(['Trying MTProto v2 decryption for chat ' . $message['message']['chat_id'] . '...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Trying MTProto v2 decryption for chat '.$message['message']['chat_id'].'...'], \danog\MadelineProto\Logger::NOTICE);
try {
$message_data = $this->try_mtproto_v2_decrypt($message_key, $message['message']['chat_id'], $old, $encrypted_data);
\danog\MadelineProto\Logger::log(['MTProto v2 decryption OK for chat ' . $message['message']['chat_id'] . '...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['MTProto v2 decryption OK for chat '.$message['message']['chat_id'].'...'], \danog\MadelineProto\Logger::NOTICE);
} catch (\danog\MadelineProto\SecurityException $e) {
\danog\MadelineProto\Logger::log(['MTProto v2 decryption failed with message ' . $e->getMessage() . ', trying MTProto v1 decryption for chat ' . $message['message']['chat_id'] . '...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['MTProto v2 decryption failed with message '.$e->getMessage().', trying MTProto v1 decryption for chat '.$message['message']['chat_id'].'...'], \danog\MadelineProto\Logger::NOTICE);
$message_data = $this->try_mtproto_v1_decrypt($message_key, $message['message']['chat_id'], $old, $encrypted_data);
\danog\MadelineProto\Logger::log(['MTProto v1 decryption OK for chat ' . $message['message']['chat_id'] . '...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['MTProto v1 decryption OK for chat '.$message['message']['chat_id'].'...'], \danog\MadelineProto\Logger::NOTICE);
$this->secret_chats[$message['message']['chat_id']]['mtproto'] = 1;
}
} else {
\danog\MadelineProto\Logger::log(['Trying MTProto v1 decryption for chat ' . $message['message']['chat_id'] . '...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Trying MTProto v1 decryption for chat '.$message['message']['chat_id'].'...'], \danog\MadelineProto\Logger::NOTICE);
try {
$message_data = $this->try_mtproto_v1_decrypt($message_key, $message['message']['chat_id'], $old, $encrypted_data);
\danog\MadelineProto\Logger::log(['MTProto v1 decryption OK for chat ' . $message['message']['chat_id'] . '...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['MTProto v1 decryption OK for chat '.$message['message']['chat_id'].'...'], \danog\MadelineProto\Logger::NOTICE);
} catch (\danog\MadelineProto\SecurityException $e) {
\danog\MadelineProto\Logger::log(['MTProto v1 decryption failed with message ' . $e->getMessage() . ', trying MTProto v2 decryption for chat ' . $message['message']['chat_id'] . '...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['MTProto v1 decryption failed with message '.$e->getMessage().', trying MTProto v2 decryption for chat '.$message['message']['chat_id'].'...'], \danog\MadelineProto\Logger::NOTICE);
$message_data = $this->try_mtproto_v2_decrypt($message_key, $message['message']['chat_id'], $old, $encrypted_data);
\danog\MadelineProto\Logger::log(['MTProto v2 decryption OK for chat ' . $message['message']['chat_id'] . '...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['MTProto v2 decryption OK for chat '.$message['message']['chat_id'].'...'], \danog\MadelineProto\Logger::NOTICE);
$this->secret_chats[$message['message']['chat_id']]['mtproto'] = 2;
}
}
@ -105,6 +114,7 @@ trait MessageHandler
$this->secret_chats[$message['message']['chat_id']]['incoming'][$this->secret_chats[$message['message']['chat_id']]['in_seq_no']] = $message['message'];
$this->handle_decrypted_update($message);
}
public function try_mtproto_v1_decrypt($message_key, $chat_id, $old, $encrypted_data)
{
list($aes_key, $aes_iv) = $this->old_aes_calculate($message_key, $this->secret_chats[$chat_id][$old ? 'old_key' : 'key']['auth_key'], true);
@ -123,8 +133,10 @@ trait MessageHandler
if (strlen($decrypted_data) % 16 != 0) {
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['length_not_divisible_16']);
}
return $message_data;
}
public function try_mtproto_v2_decrypt($message_key, $chat_id, $old, $encrypted_data)
{
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->secret_chats[$chat_id][$old ? 'old_key' : 'key']['auth_key'], !$this->secret_chats[$chat_id]['admin']);
@ -134,7 +146,7 @@ trait MessageHandler
if ($message_data_length > strlen($decrypted_data)) {
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['msg_data_length_too_big']);
}
if ($message_key != substr(hash('sha256', substr($this->secret_chats[$chat_id][$old ? 'old_key' : 'key']['auth_key'], 88 + ($this->secret_chats[$chat_id]['admin'] ? 8 : 0), 32) . $decrypted_data, true), 8, 16)) {
if ($message_key != substr(hash('sha256', substr($this->secret_chats[$chat_id][$old ? 'old_key' : 'key']['auth_key'], 88 + ($this->secret_chats[$chat_id]['admin'] ? 8 : 0), 32).$decrypted_data, true), 8, 16)) {
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['msg_key_mismatch']);
}
if (strlen($decrypted_data) - 4 - $message_data_length < 12) {
@ -146,6 +158,7 @@ trait MessageHandler
if (strlen($decrypted_data) % 16 != 0) {
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['length_not_divisible_16']);
}
return $message_data;
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\SecretChats;
/**
@ -28,12 +29,15 @@ trait ResponseHandler
switch ($update['message']['decrypted_message']['action']['_']) {
case 'decryptedMessageActionRequestKey':
$this->accept_rekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']);
return;
case 'decryptedMessageActionAcceptKey':
$this->commit_rekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']);
return;
case 'decryptedMessageActionCommitKey':
$this->complete_rekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']);
return;
case 'decryptedMessageActionNotifyLayer':
$this->secret_chats[$update['message']['chat_id']]['layer'] = $update['message']['decrypted_message']['action']['layer'];
@ -43,9 +47,11 @@ trait ResponseHandler
if ($update['message']['decrypted_message']['action']['layer'] >= 73) {
$this->secret_chats[$update['message']['chat_id']]['mtproto'] = 2;
}
return;
case 'decryptedMessageActionSetMessageTTL':
$this->secret_chats[$update['message']['chat_id']]['ttl'] = $update['message']['decrypted_message']['action']['ttl_seconds'];
return;
case 'decryptedMessageActionNoop':
return;
@ -54,13 +60,14 @@ trait ResponseHandler
$update['message']['decrypted_message']['action']['end_seq_no'] -= $this->secret_chats[$update['message']['chat_id']]['out_seq_no_x'];
$update['message']['decrypted_message']['action']['start_seq_no'] /= 2;
$update['message']['decrypted_message']['action']['end_seq_no'] /= 2;
\danog\MadelineProto\Logger::log(['Resending messages for secret chat ' . $update['message']['chat_id']], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['Resending messages for secret chat '.$update['message']['chat_id']], \danog\MadelineProto\Logger::WARNING);
foreach ($this->secret_chats[$update['message']['chat_id']]['outgoing'] as $seq => $message) {
if ($seq >= $update['message']['decrypted_message']['action']['start_seq_no'] && $seq <= $update['message']['decrypted_message']['action']['end_seq_no']) {
//throw new \danog\MadelineProto\ResponseException(\danog\MadelineProto\Lang::$current_lang['resending_unsupported']);
$this->method_call('messages.sendEncrypted', ['peer' => $update['message']['chat_id'], 'message' => $update['message']['decrypted_message']], ['datacenter' => $this->datacenter->curdc]);
}
}
return;
default:
// $this->save_update(['_' => 'updateNewDecryptedMessage', 'peer' => $this->secret_chats[$update['message']['chat_id']]['InputEncryptedChat'], 'in_seq_no' => $this->get_in_seq_no($update['message']['chat_id']), 'out_seq_no' => $this->get_out_seq_no($update['message']['chat_id']), 'message' => $update['message']['decrypted_message']]);
@ -84,8 +91,8 @@ trait ResponseHandler
}
break;
default:
throw new \danog\MadelineProto\ResponseException(\danog\MadelineProto\Lang::$current_lang['unrecognized_dec_msg'] . var_export($update, true));
throw new \danog\MadelineProto\ResponseException(\danog\MadelineProto\Lang::$current_lang['unrecognized_dec_msg'].var_export($update, true));
break;
}
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\SecretChats;
/**
@ -25,6 +26,7 @@ trait SeqNoHandler
if (isset($message['decrypted_message']['in_seq_no'])) {
if (($message['decrypted_message']['in_seq_no'] - $this->secret_chats[$chat_id]['out_seq_no_x']) / 2 < $last) {
$this->discard_secret_chat($chat_id);
throw new \danog\MadelineProto\SecurityException('in_seq_no is not increasing');
}
$last = ($message['decrypted_message']['in_seq_no'] - $this->secret_chats[$chat_id]['out_seq_no_x']) / 2;
@ -32,10 +34,13 @@ trait SeqNoHandler
}
if ($seqno > $this->secret_chats[$chat_id]['out_seq_no'] + 1) {
$this->discard_secret_chat($chat_id);
throw new \danog\MadelineProto\SecurityException('in_seq_no is too big');
}
return true;
}
public function check_secret_out_seq_no($chat_id, $seqno)
{
$seqno = ($seqno - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2;
@ -44,7 +49,8 @@ trait SeqNoHandler
if (isset($message['decrypted_message']['out_seq_no']) && $C < $this->secret_chats[$chat_id]['in_seq_no']) {
if (($message['decrypted_message']['out_seq_no'] - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2 !== $C) {
$this->discard_secret_chat($chat_id);
throw new \danog\MadelineProto\SecurityException('out_seq_no hole: should be ' . $C . ', is ' . ($message['decrypted_message']['out_seq_no'] - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2);
throw new \danog\MadelineProto\SecurityException('out_seq_no hole: should be '.$C.', is '.($message['decrypted_message']['out_seq_no'] - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2);
} else {
$C++;
}
@ -53,22 +59,27 @@ trait SeqNoHandler
//var_dump($C, $seqno);
if ($seqno < $C) {
// <= C
\danog\MadelineProto\Logger::log(['WARNING: dropping repeated message with seqno ' . $seqno]);
\danog\MadelineProto\Logger::log(['WARNING: dropping repeated message with seqno '.$seqno]);
return false;
}
if ($seqno > $C) {
// > C+1
$this->discard_secret_chat($chat_id);
throw new \danog\MadelineProto\SecurityException('WARNING: out_seq_no gap detected (' . $seqno . ' > ' . $C . ')!');
throw new \danog\MadelineProto\SecurityException('WARNING: out_seq_no gap detected ('.$seqno.' > '.$C.')!');
}
return true;
}
public function generate_secret_in_seq_no($chat)
{
return $this->secret_chats[$chat]['layer'] > 8 ? $this->secret_chats[$chat]['in_seq_no'] * 2 + $this->secret_chats[$chat]['in_seq_no_x'] : -1;
}
public function generate_secret_out_seq_no($chat)
{
return $this->secret_chats[$chat]['layer'] > 8 ? $this->secret_chats[$chat]['out_seq_no'] * 2 + $this->secret_chats[$chat]['out_seq_no_x'] : -1;
}
}
}

View File

@ -10,8 +10,9 @@ 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/>.
*/
namespace danog\MadelineProto;
class SecurityException extends \Exception
{
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto;
/**
@ -18,9 +19,11 @@ namespace danog\MadelineProto;
class Serialization
{
public static $instances = [];
public static function serialize_all($exception)
{
echo $exception . PHP_EOL;
echo $exception.PHP_EOL;
return;
foreach (self::$instances as $instance) {
if (isset($instance->session)) {
@ -28,13 +31,16 @@ class Serialization
}
}
}
public static function realpaths($file)
{
if ($file[0] !== '/') {
$file = getcwd() . '/' . $file;
$file = getcwd().'/'.$file;
}
return ['file' => $file, 'lockfile' => $file . '.lock', 'tempfile' => $file . '.temp.session'];
return ['file' => $file, 'lockfile' => $file.'.lock', 'tempfile' => $file.'.temp.session'];
}
/**
* Serialize API class.
*
@ -59,6 +65,7 @@ class Serialization
$realpaths['lockfile'] = fopen($realpaths['lockfile'], 'w');
\danog\MadelineProto\Logger::log(['Waiting for exclusive lock of serialization lockfile...']);
flock($realpaths['lockfile'], LOCK_EX);
try {
$wrote = file_put_contents($realpaths['tempfile'], serialize($instance));
rename($realpaths['tempfile'], $realpaths['file']);
@ -66,8 +73,10 @@ class Serialization
flock($realpaths['lockfile'], LOCK_UN);
fclose($realpaths['lockfile']);
}
return $wrote;
}
/**
* Deserialize API class.
*
@ -88,6 +97,7 @@ class Serialization
$realpaths['lockfile'] = fopen($realpaths['lockfile'], 'r');
\danog\MadelineProto\Logger::log(['Waiting for shared lock of serialization lockfile...']);
flock($realpaths['lockfile'], LOCK_SH);
try {
$unserialized = file_get_contents($realpaths['file']);
} finally {
@ -96,10 +106,11 @@ class Serialization
}
$tounserialize = str_replace('O:26:"danog\\MadelineProto\\Button":', 'O:35:"danog\\MadelineProto\\TL\\Types\\Button":', $unserialized);
foreach (['RSA', 'TL\\TLMethod', 'TL\\TLConstructor', 'MTProto', 'API', 'DataCenter', 'Connection', 'TL\\Types\\Button', 'TL\\Types\\Bytes', 'APIFactory'] as $class) {
class_exists('\\danog\\MadelineProto\\' . $class);
class_exists('\\danog\\MadelineProto\\'.$class);
}
class_exists('\\Volatile');
\danog\MadelineProto\Logger::class_exists();
try {
$unserialized = unserialize($tounserialize);
} catch (\danog\MadelineProto\Bug74586Exception $e) {
@ -124,6 +135,7 @@ class Serialization
$unserialized->session = $filename;
}
self::$instances[spl_object_hash($unserialized)] = $unserialized;
return $unserialized;
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\TL\Conversion;
trait BotAPI
@ -18,6 +19,7 @@ trait BotAPI
{
return html_entity_decode(preg_replace('#< *br */? *>#', "\n", $stuff));
}
public function parse_buttons($rows)
{
$newrows = [];
@ -53,8 +55,10 @@ trait BotAPI
}
$key++;
}
return $newrows;
}
public function parse_reply_markup($markup)
{
if (isset($markup['force_reply']) && $markup['force_reply']) {
@ -83,8 +87,10 @@ trait BotAPI
$markup['rows'] = $this->parse_buttons($markup['inline_keyboard']);
unset($markup['inline_keyboard']);
}
return $markup;
}
public function MTProto_to_botAPI($data, $sent_arguments = [])
{
$newd = [];
@ -92,6 +98,7 @@ trait BotAPI
foreach ($data as $key => $element) {
$newd[$key] = $this->MTProto_to_botAPI($element, $sent_arguments);
}
return $newd;
}
switch ($data['_']) {
@ -109,6 +116,7 @@ trait BotAPI
if (isset($data['media'])) {
$newd = array_merge($newd, $this->MTProto_to_botAPI($data['media'], $sent_arguments));
}
return $newd;
case 'updateNewChannelMessage':
case 'updateNewMessage':
@ -150,52 +158,64 @@ trait BotAPI
if (isset($data['media'])) {
$newd = array_merge($newd, $this->MTProto_to_botAPI($data['media'], $sent_arguments));
}
return $newd;
case 'messageEntityMention':
unset($data['_']);
$data['type'] = 'mention';
return $data;
case 'messageEntityHashtag':
unset($data['_']);
$data['type'] = 'hashtag';
return $data;
case 'messageEntityBotCommand':
unset($data['_']);
$data['type'] = 'bot_command';
return $data;
case 'messageEntityUrl':
unset($data['_']);
$data['type'] = 'url';
return $data;
case 'messageEntityEmail':
unset($data['_']);
$data['type'] = 'email';
return $data;
case 'messageEntityBold':
unset($data['_']);
$data['type'] = 'bold';
return $data;
case 'messageEntityItalic':
unset($data['_']);
$data['type'] = 'italic';
return $data;
case 'messageEntityCode':
unset($data['_']);
$data['type'] = 'code';
return $data;
case 'messageEntityPre':
unset($data['_']);
$data['type'] = 'pre';
return $data;
case 'messageEntityTextUrl':
unset($data['_']);
$data['type'] = 'text_url';
return $data;
case 'messageEntityMentionName':
unset($data['_']);
$data['type'] = 'text_mention';
$data['user'] = $this->get_pwr_chat($data['user_id']);
unset($data['user_id']);
return $data;
case 'messageMediaPhoto':
if (isset($data['caption'])) {
@ -205,6 +225,7 @@ trait BotAPI
foreach ($data['photo']['sizes'] as $key => $photo) {
$res['photo'][$key] = $this->photosize_to_botapi($photo, $data['photo']);
}
return $res;
case 'messageMediaEmpty':
return [];
@ -218,7 +239,7 @@ trait BotAPI
switch ($attribute['_']) {
case 'documentAttributeFilename':
$pathinfo = pathinfo($attribute['file_name']);
$res['ext'] = isset($pathinfo['extension']) ? '.' . $pathinfo['extension'] : '';
$res['ext'] = isset($pathinfo['extension']) ? '.'.$pathinfo['extension'] : '';
$res['file_name'] = $pathinfo['filename'];
break;
case 'documentAttributeAudio':
@ -269,29 +290,32 @@ trait BotAPI
if (isset($audio) && isset($audio['title']) && !isset($res['file_name'])) {
$res['file_name'] = $audio['title'];
if (isset($audio['performer'])) {
$res['file_name'] .= ' - ' . $audio['performer'];
$res['file_name'] .= ' - '.$audio['performer'];
}
}
if (!isset($res['file_name'])) {
$res['file_name'] = $data['document']['access_hash'];
}
$res['file_name'] .= '_' . $data['document']['id'];
$res['file_name'] .= '_'.$data['document']['id'];
if (isset($res['ext'])) {
$res['file_name'] .= $res['ext'];
unset($res['ext']);
} else {
$res['file_name'] .= $this->get_extension_from_mime($data['document']['mime_type']);
}
$data['document']['_'] = 'bot_' . $type_name;
$data['document']['_'] = 'bot_'.$type_name;
$res['file_size'] = $data['document']['size'];
$res['mime_type'] = $data['document']['mime_type'];
$res['file_id'] = $this->base64url_encode($this->rle_encode($this->serialize_object(['type' => 'File'], $data['document'], 'File') . chr(2)));
$res['file_id'] = $this->base64url_encode($this->rle_encode($this->serialize_object(['type' => 'File'], $data['document'], 'File').chr(2)));
return [$type_name => $res, 'caption' => isset($data['caption']) ? $data['caption'] : ''];
default:
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['botapi_conversion_error'], $data['_']));
}
}
public $botapi_params = ['disable_web_page_preview' => 'no_webpage', 'disable_notification' => 'silent', 'reply_to_message_id' => 'reply_to_msg_id', 'chat_id' => 'peer', 'text' => 'message'];
public function botAPI_to_MTProto($arguments)
{
foreach ($this->botapi_params as $bot => $mtproto) {
@ -306,8 +330,10 @@ trait BotAPI
if (isset($arguments['parse_mode'])) {
$arguments = $this->parse_mode($arguments);
}
return $arguments;
}
public function parse_node($node, &$entities, &$nmessage, $recursive = true)
{
switch ($node->nodeName) {
@ -374,6 +400,7 @@ trait BotAPI
break;
}
}
public function parse_mode($arguments)
{
if (isset($arguments['parse_mode']['_'])) {
@ -385,6 +412,7 @@ trait BotAPI
}
if (preg_match('/html/i', $arguments['parse_mode'])) {
$nmessage = '';
try {
$arguments['message'] = $this->html_fixtags($arguments['message']);
$dom = new \DOMDocument();
@ -405,8 +433,10 @@ trait BotAPI
}
$arguments['message'] = $nmessage;
}
return $arguments;
}
public function split_to_chunks($text)
{
$max_length = 4096;
@ -423,15 +453,17 @@ trait BotAPI
$i = 0;
$message = [''];
foreach ($text_arr as $word) {
if (strlen($message[$i] . $word) <= $max_length) {
if (strlen($message[$i].$word) <= $max_length) {
$message[$i] .= $word;
} else {
$i++;
$message[$i] = $word;
}
}
return $message;
}
public function multipleExplodeKeepDelimiters($delimiters, $string)
{
$initialArray = explode(chr(1), str_replace($delimiters, chr(1), $string));
@ -440,15 +472,17 @@ trait BotAPI
foreach ($initialArray as $item) {
$delimOffset += strlen($item);
if (strlen($item) > 0) {
$finalArray[] = $item . ($delimOffset < strlen($string) ? $string[$delimOffset] : '');
$finalArray[] = $item.($delimOffset < strlen($string) ? $string[$delimOffset] : '');
}
$delimOffset++;
}
return $finalArray;
}
public function html_fixtags($text)
{
preg_match_all("#(.*?)(<(a|b|strong|em|i|code|pre)[^>]*>)([^<]*?)(<\\/\\3>)(.*)?#is", $text, $matches, PREG_SET_ORDER);
preg_match_all('#(.*?)(<(a|b|strong|em|i|code|pre)[^>]*>)([^<]*?)(<\\/\\3>)(.*)?#is', $text, $matches, PREG_SET_ORDER);
if ($matches) {
$last = count($matches) - 1;
foreach ($matches as $val) {
@ -460,15 +494,17 @@ trait BotAPI
$text = str_replace($val[6], $this->html_fixtags($val[6]), $text);
}
}
preg_match_all("#<a href=\"(.+?)\">#is", $text, $matches);
preg_match_all('#<a href="(.+?)">#is', $text, $matches);
foreach ($matches[1] as $match) {
$text = str_replace($match, htmlentities($match), $text);
}
return $text;
} else {
return htmlentities($text);
}
}
public function build_rows($button_list)
{
$end = false;
@ -493,6 +529,7 @@ trait BotAPI
$row = ['_' => 'keyboardButtonRow', 'buttons' => $buttons];
$rows[] = $row;
}
return ['_' => 'replyInlineMarkup', 'rows' => $rows];
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\TL\Conversion;
trait BotAPIFiles
@ -18,10 +19,12 @@ trait BotAPIFiles
{
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}
public function base64url_encode($data)
{
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
public function rle_decode($string)
{
$new = '';
@ -36,9 +39,11 @@ trait BotAPIFiles
$last = $cur;
}
}
$string = $new . $last;
$string = $new.$last;
return $string;
}
public function rle_encode($string)
{
$new = '';
@ -49,23 +54,27 @@ trait BotAPIFiles
$count++;
} else {
if ($count > 0) {
$new .= $null . chr($count);
$new .= $null.chr($count);
$count = 0;
}
$new .= $cur;
}
}
return $new;
}
public function photosize_to_botapi($photo, $message_media, $thumbnail = false)
{
$ext = $this->get_extension_from_location(['_' => 'inputFileLocation', 'volume_id' => $photo['location']['volume_id'], 'local_id' => $photo['location']['local_id'], 'secret' => $photo['location']['secret'], 'dc_id' => $photo['location']['dc_id']], '.jpg');
$photo['location']['access_hash'] = isset($message_media['access_hash']) ? $message_media['access_hash'] : 0;
$photo['location']['id'] = isset($message_media['id']) ? $message_media['id'] : 0;
$photo['location']['_'] = $thumbnail ? 'bot_thumbnail' : 'bot_photo';
$data = $this->serialize_object(['type' => 'File'], $photo['location'], 'File') . chr(2);
return ['file_id' => $this->base64url_encode($this->rle_encode($data)), 'width' => $photo['w'], 'height' => $photo['h'], 'file_size' => isset($photo['size']) ? $photo['size'] : strlen($photo['bytes']), 'mime_type' => 'image/jpeg', 'file_name' => $photo['location']['volume_id'] . '_' . $photo['location']['local_id'] . $ext];
$data = $this->serialize_object(['type' => 'File'], $photo['location'], 'File').chr(2);
return ['file_id' => $this->base64url_encode($this->rle_encode($data)), 'width' => $photo['w'], 'height' => $photo['h'], 'file_size' => isset($photo['size']) ? $photo['size'] : strlen($photo['bytes']), 'mime_type' => 'image/jpeg', 'file_name' => $photo['location']['volume_id'].'_'.$photo['location']['local_id'].$ext];
}
public function unpack_file_id($file_id)
{
$file_id = $this->rle_decode($this->base64url_decode($file_id));
@ -85,44 +94,52 @@ trait BotAPIFiles
unset($deserialized['_']);
$constructor['sizes'][0]['location'] = $deserialized;
$res['MessageMedia'] = ['_' => 'messageMediaPhoto', 'photo' => $constructor, 'caption' => ''];
return $res;
case 'bot_voice':
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => true]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 'bot_video':
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeVideo', 'round_message' => false]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 'bot_video_note':
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeVideo', 'round_message' => true]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 'bot_document':
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => []]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 'bot_sticker':
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeSticker']]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 'bot_gif':
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAnimated']]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 'bot_audio':
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => false]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
default:
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['file_type_invalid'], $type));
}
}
}
}

View File

@ -10,8 +10,9 @@ 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/>.
*/
namespace danog\MadelineProto\TL\Conversion;
class Exception extends \Exception
{
}
}

View File

@ -11,6 +11,7 @@ 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/>.
*/
namespace danog\MadelineProto\TL\Conversion;
/**
@ -22,11 +23,13 @@ trait Extension
{
foreach (self::ALL_MIMES as $key => $value) {
if (array_search($mime, (array) $value) !== false) {
return '.' . $key;
return '.'.$key;
}
}
return '';
}
public function get_extension_from_location($location, $default)
{
return $default;
@ -55,4 +58,4 @@ trait Extension
return $default;
}
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\TL\Conversion;
trait TD
@ -21,6 +22,7 @@ trait TD
}
if (!isset($params['ID'])) {
array_walk($params, [$this, 'tdcli_to_td']);
return $params;
}
foreach ($params as $key => $value) {
@ -32,8 +34,10 @@ trait TD
}
$params['_'] = lcfirst($params['ID']);
unset($params['ID']);
return $params;
}
public function td_to_mtproto($params)
{
$newparams = ['_' => self::REVERSE[$params['_']]];
@ -61,12 +65,15 @@ trait TD
}
}
}
return $newparams;
}
public function mtproto_to_tdcli($params)
{
return $this->td_to_tdcli($this->mtproto_to_td($params));
}
public function mtproto_to_td(&$params)
{
if (!is_array($params)) {
@ -74,6 +81,7 @@ trait TD
}
if (!isset($params['_'])) {
array_walk($params, [$this, 'mtproto_to_td']);
return $params;
}
$newparams = ['_' => $params['_']];
@ -101,7 +109,7 @@ trait TD
if (isset($params['fwd_from'])) {
$newparams[$td] = ['_' => 'messageForwardedFromUser'];
if (isset($params['fwd_from']['channel_id'])) {
$newparams[$td] = ['_' => 'messageForwardedPost', 'chat_id' => '-100' . $params['fwd_from']['channel_id']];
$newparams[$td] = ['_' => 'messageForwardedPost', 'chat_id' => '-100'.$params['fwd_from']['channel_id']];
}
$newparams[$td]['date'] = $params['fwd_from']['date'];
if (isset($params['fwd_from']['channel_post'])) {
@ -145,8 +153,10 @@ trait TD
}
}
}
return $newparams;
}
public function td_to_tdcli($params)
{
if (!is_array($params)) {
@ -158,11 +168,12 @@ trait TD
$newparams['ID'] = ucfirst($value);
} else {
if (!is_numeric($key) && !preg_match('/_^/', $key)) {
$key = $key . '_';
$key = $key.'_';
}
$newparams[$key] = $this->td_to_tdcli($value);
}
}
return $newparams;
}
}
}

View File

@ -10,18 +10,21 @@ 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/>.
*/
namespace danog\MadelineProto\TL;
class Exception extends \Exception
{
use PrettyException;
public function __toString()
{
return get_class($this) . ($this->message !== '' ? ': ' : '') . $this->message . PHP_EOL . @file_get_contents(__DIR__ . '/../../../.git/refs/heads/master') . PHP_EOL . 'TL Trace (YOU ABSOLUTELY MUST READ THE TEXT BELOW):' . PHP_EOL . PHP_EOL . $this->getTLTrace() . PHP_EOL;
return get_class($this).($this->message !== '' ? ': ' : '').$this->message.PHP_EOL.@file_get_contents(__DIR__.'/../../../.git/refs/heads/master').PHP_EOL.'TL Trace (YOU ABSOLUTELY MUST READ THE TEXT BELOW):'.PHP_EOL.PHP_EOL.$this->getTLTrace().PHP_EOL;
}
public function __construct($message, $file = '')
{
parent::__construct($message);
$this->prettify_tl($file);
}
}
}

View File

@ -10,22 +10,25 @@ 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/>.
*/
namespace danog\MadelineProto\TL;
trait PrettyException
{
public $tl_trace;
public function getTLTrace()
{
return $this->tl_trace;
}
public function prettify_tl($init = '')
{
$tl = false;
foreach (array_reverse($this->getTrace()) as $k => $frame) {
if (isset($frame['function']) && in_array($frame['function'], ['serialize_params', 'serialize_object'])) {
if ($frame['args'][2] !== '') {
$this->tl_trace .= $tl ? "['" . $frame['args'][2] . "']" : "While serializing: \t" . $frame['args'][2] . PHP_EOL;
$this->tl_trace .= $tl ? "['".$frame['args'][2]."']" : "While serializing: \t".$frame['args'][2].PHP_EOL;
$tl = true;
}
} else {
@ -35,15 +38,15 @@ trait PrettyException
if (isset($frame['function']) && ($frame['function'] === 'handle_rpc_error' && $k === count($this->getTrace()) - 1) || $frame['function'] === 'unserialize') {
continue;
}
$this->tl_trace .= isset($frame['file']) ? str_pad(basename($frame['file']) . '(' . $frame['line'] . '):', 20) . "\t" : '';
$this->tl_trace .= isset($frame['function']) ? $frame['function'] . '(' : '';
$this->tl_trace .= isset($frame['file']) ? str_pad(basename($frame['file']).'('.$frame['line'].'):', 20)."\t" : '';
$this->tl_trace .= isset($frame['function']) ? $frame['function'].'(' : '';
$this->tl_trace .= isset($frame['args']) ? substr(json_encode($frame['args']), 1, -1) : '';
$this->tl_trace .= ')';
$this->tl_trace .= PHP_EOL;
$tl = false;
}
}
$this->tl_trace .= $init !== '' ? "['" . $init . "']" : '';
$this->tl_trace .= $init !== '' ? "['".$init."']" : '';
$this->tl_trace = implode(PHP_EOL, array_reverse(explode(PHP_EOL, $this->tl_trace)));
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\TL;
trait TL
@ -20,6 +21,7 @@ trait TL
public $td_constructors;
public $td_methods;
public $td_descriptions;
public function construct_tl($files)
{
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['TL_loading']], \danog\MadelineProto\Logger::VERBOSE);
@ -139,7 +141,7 @@ trait TL
}
}
if (empty($TL_dict) || empty($TL_dict['constructors']) || !isset($TL_dict['methods'])) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['src_file_invalid'] . $file);
throw new Exception(\danog\MadelineProto\Lang::$current_lang['src_file_invalid'].$file);
}
$orig = $this->encrypted_layer;
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['translating_obj']], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
@ -147,11 +149,11 @@ trait TL
if ($scheme_type === 'secret') {
$this->encrypted_layer = max($this->encrypted_layer, $elem['layer']);
}
$this->{($scheme_type === 'td' ? 'td_' : '') . 'constructors'}->add($elem, $scheme_type);
$this->{($scheme_type === 'td' ? 'td_' : '').'constructors'}->add($elem, $scheme_type);
}
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['translating_methods']], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
foreach ($TL_dict['methods'] as $elem) {
$this->{($scheme_type === 'td' ? 'td_' : '') . 'methods'}->add($elem);
$this->{($scheme_type === 'td' ? 'td_' : '').'methods'}->add($elem);
if ($scheme_type === 'secret') {
$this->encrypted_layer = max($this->encrypted_layer, $elem['layer']);
}
@ -183,6 +185,7 @@ trait TL
}
}
}
public function get_method_namespaces()
{
$res = [];
@ -190,20 +193,25 @@ trait TL
$a = key($pair);
$res[$a] = $a;
}
return $res;
}
public function get_methods_namespaced()
{
return $this->methods->method_namespace;
}
public function deserialize_bool($id)
{
$tl_elem = $this->constructors->find_by_id($id);
if ($tl_elem === false) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['bool_error']);
}
return $tl_elem['predicate'] === 'boolTrue';
}
public function serialize_object($type, $object, $ctx, $layer = -1)
{
switch ($type['type']) {
@ -211,11 +219,13 @@ trait TL
if (!is_numeric($object)) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['not_numeric']);
}
return $this->pack_signed_int($object);
case '#':
if (!is_numeric($object)) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['not_numeric']);
}
return $this->pack_unsigned_int($object);
case 'long':
if (is_object($object)) {
@ -230,21 +240,25 @@ trait TL
if (!is_numeric($object)) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['not_numeric']);
}
return $this->pack_signed_long($object);
case 'int128':
if (strlen($object) !== 16) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['long_not_16']);
}
return (string) $object;
case 'int256':
if (strlen($object) !== 32) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['long_not_32']);
}
return (string) $object;
case 'int512':
if (strlen($object) !== 64) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['long_not_64']);
}
return (string) $object;
case 'double':
return $this->pack_double($object);
@ -258,13 +272,14 @@ trait TL
if ($l <= 253) {
$concat .= chr($l);
$concat .= $object;
$concat .= pack('@' . $this->posmod(-$l - 1, 4));
$concat .= pack('@'.$this->posmod(-$l - 1, 4));
} else {
$concat .= chr(254);
$concat .= substr($this->pack_signed_int($l), 0, 3);
$concat .= $object;
$concat .= pack('@' . $this->posmod(-$l, 4));
$concat .= pack('@'.$this->posmod(-$l, 4));
}
return $concat;
case 'bytes':
if (!is_string($object) && !$object instanceof \danog\MadelineProto\TL\Types\Bytes) {
@ -275,13 +290,14 @@ trait TL
if ($l <= 253) {
$concat .= chr($l);
$concat .= $object;
$concat .= pack('@' . $this->posmod(-$l - 1, 4));
$concat .= pack('@'.$this->posmod(-$l - 1, 4));
} else {
$concat .= chr(254);
$concat .= substr($this->pack_signed_int($l), 0, 3);
$concat .= $object;
$concat .= pack('@' . $this->posmod(-$l, 4));
$concat .= pack('@'.$this->posmod(-$l, 4));
}
return $concat;
case 'Bool':
return $this->constructors->find_by_predicate((bool) $object ? 'boolTrue' : 'boolFalse')['id'];
@ -298,6 +314,7 @@ trait TL
foreach ($object as $k => $current_object) {
$concat .= $this->serialize_object(['type' => $type['subtype']], $current_object, $k);
}
return $concat;
case 'vector':
if (!is_array($object)) {
@ -307,6 +324,7 @@ trait TL
foreach ($object as $k => $current_object) {
$concat .= $this->serialize_object(['type' => $type['subtype']], $current_object, $k);
}
return $concat;
case 'Object':
if (is_string($object)) {
@ -333,6 +351,7 @@ trait TL
$constructorData = $this->constructors->find_by_predicate($predicate, $layer);
if ($constructorData === false) {
\danog\MadelineProto\Logger::log([$object], \danog\MadelineProto\Logger::FATAL_ERROR);
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['type_extract_error'], $predicate));
}
if ($bare = $type['type'] != '' && $type['type'][0] === '%') {
@ -348,16 +367,20 @@ trait TL
if (!$bare) {
$concat = $constructorData['id'];
}
return $concat . $this->serialize_params($constructorData, $object, '', $layer);
return $concat.$this->serialize_params($constructorData, $object, '', $layer);
}
public function serialize_method($method, $arguments)
{
$tl = $this->methods->find_by_method($method);
if ($tl === false) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['method_not_found'] . $method);
throw new Exception(\danog\MadelineProto\Lang::$current_lang['method_not_found'].$method);
}
return $tl['id'] . $this->serialize_params($tl, $arguments, $method);
return $tl['id'].$this->serialize_params($tl, $arguments, $method);
}
public function serialize_params($tl, $arguments, $ctx, $layer = -1)
{
$serialized = '';
@ -415,10 +438,11 @@ trait TL
}
}
}
if ($id = $this->constructors->find_by_predicate(lcfirst($current_argument['type']) . 'Empty')) {
if ($id = $this->constructors->find_by_predicate(lcfirst($current_argument['type']).'Empty')) {
$serialized .= $id['id'];
continue;
}
throw new Exception(\danog\MadelineProto\Lang::$current_lang['params_missing'], $current_argument['name']);
}
if (!is_array($arguments[$current_argument['name']]) && $current_argument['type'] === 'InputEncryptedChat') {
@ -433,8 +457,10 @@ trait TL
//\danog\MadelineProto\Logger::log(['Serializing '.$current_argument['name'].' of type '.$current_argument['type']);
$serialized .= $this->serialize_object($current_argument, $arguments[$current_argument['name']], $current_argument['name'], $layer);
}
return $serialized;
}
public function get_length($stream, $type = ['type' => ''])
{
if (is_string($stream)) {
@ -446,8 +472,10 @@ trait TL
throw new Exception(\danog\MadelineProto\Lang::$current_lang['stream_handle_invalid']);
}
$this->deserialize($stream, $type);
return ftell($stream);
}
/**
* :type stream: io.BytesIO object.
*/
@ -472,6 +500,7 @@ trait TL
if (isset($type['idstrlong'])) {
return stream_get_contents($stream, 8);
}
return \danog\MadelineProto\Logger::$bigint || isset($type['strlong']) ? stream_get_contents($stream, 8) : $this->unpack_signed_long(stream_get_contents($stream, 8));
case 'double':
return $this->unpack_double(stream_get_contents($stream, 8));
@ -488,7 +517,7 @@ trait TL
throw new Exception(\danog\MadelineProto\Lang::$current_lang['length_too_big']);
}
if ($l === 254) {
$long_len = unpack('V', stream_get_contents($stream, 3) . chr(0))[1];
$long_len = unpack('V', stream_get_contents($stream, 3).chr(0))[1];
$x = stream_get_contents($stream, $long_len);
$resto = $this->posmod(-$long_len, 4);
if ($resto > 0) {
@ -504,13 +533,14 @@ trait TL
if (!is_string($x)) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['deserialize_not_str']);
}
return $type['type'] === 'bytes' ? new Types\Bytes($x) : $x;
case 'Vector t':
$id = stream_get_contents($stream, 4);
$constructorData = $this->constructors->find_by_id($id);
if ($constructorData === false) {
$constructorData = $this->methods->find_by_id($id);
$constructorData['predicate'] = 'method_' . $constructorData['method'];
$constructorData['predicate'] = 'method_'.$constructorData['method'];
}
if ($constructorData === false) {
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['type_extract_error_id'], $type['type'], bin2hex(strrev($id))));
@ -522,7 +552,7 @@ trait TL
case 'vector':
break;
default:
throw new Exception(\danog\MadelineProto\Lang::$current_lang['vector_invalid'] . $constructorData['predicate']);
throw new Exception(\danog\MadelineProto\Lang::$current_lang['vector_invalid'].$constructorData['predicate']);
}
case 'vector':
$count = unpack('V', stream_get_contents($stream, 4))[1];
@ -531,13 +561,14 @@ trait TL
for ($i = 0; $i < $count; $i++) {
$result[] = $this->deserialize($stream, $type);
}
return $result;
}
if ($type['type'] != '' && $type['type'][0] === '%') {
$checkType = substr($type['type'], 1);
$constructorData = $this->constructors->find_by_type($checkType);
if ($constructorData === false) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['constructor_not_found'] . $checkType);
throw new Exception(\danog\MadelineProto\Lang::$current_lang['constructor_not_found'].$checkType);
}
} else {
$constructorData = $this->constructors->find_by_predicate($type['type']);
@ -549,7 +580,7 @@ trait TL
if ($constructorData === false) {
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['type_extract_error_id'], $type['type'], bin2hex(strrev($id))));
}
$constructorData['predicate'] = 'method_' . $constructorData['method'];
$constructorData['predicate'] = 'method_'.$constructorData['method'];
}
}
}
@ -557,12 +588,14 @@ trait TL
if (!isset($type['subtype'])) {
$type['subtype'] = '';
}
return $this->deserialize(gzdecode($this->deserialize($stream, ['type' => 'bytes'])), ['type' => '', 'datacenter' => $type['datacenter'], 'subtype' => $type['subtype']]);
}
if ($constructorData['type'] === 'Vector t') {
$constructorData['datacenter'] = $type['datacenter'];
$constructorData['subtype'] = isset($type['subtype']) ? $type['subtype'] : '';
$constructorData['type'] = 'vector';
return $this->deserialize($stream, $constructorData);
}
if ($constructorData['predicate'] === 'boolTrue') {
@ -628,6 +661,7 @@ trait TL
}
}
}
return $x;
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\TL;
class TLConstructor
@ -20,6 +21,7 @@ class TLConstructor
public $by_id = [];
public $by_predicate_and_layer = [];
public $layers = [];
//public $type = [];
//public $params = [];
//public $layer = [];
@ -28,10 +30,11 @@ class TLConstructor
{
return ['by_predicate_and_layer', 'by_id', 'layers'];
}
public function add($json_dict, $scheme_type)
{
$predicate = (string) (($scheme_type === 'mtproto' && $json_dict['predicate'] === 'message' ? 'MT' : '') . $json_dict['predicate']);
$this->by_id[$json_dict['id']] = ['predicate' => $predicate, 'params' => $json_dict['params'], 'type' => ($scheme_type === 'mtproto' && $json_dict['type'] === 'Message' ? 'MT' : '') . $json_dict['type']];
$predicate = (string) (($scheme_type === 'mtproto' && $json_dict['predicate'] === 'message' ? 'MT' : '').$json_dict['predicate']);
$this->by_id[$json_dict['id']] = ['predicate' => $predicate, 'params' => $json_dict['params'], 'type' => ($scheme_type === 'mtproto' && $json_dict['type'] === 'Message' ? 'MT' : '').$json_dict['type']];
if ($scheme_type === 'secret') {
$this->by_id[$json_dict['id']]['layer'] = $json_dict['layer'];
$this->layers[$json_dict['layer']] = $json_dict['layer'];
@ -39,30 +42,34 @@ class TLConstructor
} else {
$json_dict['layer'] = '';
}
$this->by_predicate_and_layer[$predicate . $json_dict['layer']] = $json_dict['id'];
$this->by_predicate_and_layer[$predicate.$json_dict['layer']] = $json_dict['id'];
$this->parse_params($json_dict['id'], $scheme_type === 'mtproto');
}
public function find_by_type($type)
{
foreach ($this->by_id as $id => $constructor) {
if ($constructor['type'] === $type) {
$constructor['id'] = $id;
$constructor['params'] = $constructor['params'];
return $constructor;
}
}
return false;
}
public function find_by_predicate($predicate, $layer = -1)
{
if ($layer !== -1) {
foreach ($this->layers as $alayer) {
if ($alayer <= $layer) {
if (isset($this->by_predicate_and_layer[$predicate . $alayer])) {
$chosenid = $this->by_predicate_and_layer[$predicate . $alayer];
if (isset($this->by_predicate_and_layer[$predicate.$alayer])) {
$chosenid = $this->by_predicate_and_layer[$predicate.$alayer];
}
} elseif (!isset($chosenid)) {
$chosenid = $this->by_predicate_and_layer[$predicate . $alayer];
$chosenid = $this->by_predicate_and_layer[$predicate.$alayer];
}
}
if (!isset($chosenid)) {
@ -71,24 +78,30 @@ class TLConstructor
$constructor = $this->by_id[$chosenid];
$constructor['id'] = $chosenid;
$constructor['params'] = $constructor['params'];
return $constructor;
}
if (isset($this->by_predicate_and_layer[$predicate])) {
$constructor = $this->by_id[$this->by_predicate_and_layer[$predicate]];
$constructor['id'] = $this->by_predicate_and_layer[$predicate];
$constructor['params'] = $constructor['params'];
return $constructor;
}
return false;
}
public function find_by_id($id)
{
if (isset($this->by_id[$id])) {
$constructor = $this->by_id[$id];
$constructor['id'] = $id;
$constructor['params'] = $constructor['params'];
return $constructor;
}
return false;
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\TL;
class TLMethod
@ -20,10 +21,12 @@ class TLMethod
public $by_id = [];
public $by_method = [];
public $method_namespace = [];
public function __sleep()
{
return ['by_id', 'by_method', 'method_namespace'];
}
public function add($json_dict)
{
$this->by_id[$json_dict['id']] = ['method' => $json_dict['method'], 'type' => $json_dict['type'], 'params' => $json_dict['params']];
@ -34,24 +37,30 @@ class TLMethod
}
$this->parse_params($json_dict['id']);
}
public function find_by_id($id)
{
if (isset($this->by_id[$id])) {
$method = $this->by_id[$id];
$method['id'] = $id;
$method['params'] = $method['params'];
return $method;
}
return false;
}
public function find_by_method($method_name)
{
if (isset($this->by_method[$method_name])) {
$method = $this->by_id[$this->by_method[$method_name]];
$method['id'] = $this->by_method[$method_name];
$method['params'] = $method['params'];
return $method;
}
return false;
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\TL;
trait TLParams
@ -31,12 +32,12 @@ trait TLParams
$param['subtype'] = preg_replace(['/.*</', '/>$/'], '', $param['type']);
$param['type'] = 'Vector t';
}
$param['subtype'] = ($mtproto && $param['subtype'] === 'Message' ? 'MT' : '') . $param['subtype'];
$param['subtype'] = ($mtproto && $param['subtype'] === 'Message' ? 'MT' : '').$param['subtype'];
$param['subtype'] = $mtproto && $param['subtype'] === '%Message' ? '%MTMessage' : $param['subtype'];
}
$param['type'] = ($mtproto && $param['type'] === 'Message' ? 'MT' : '') . $param['type'];
$param['type'] = ($mtproto && $param['type'] === 'Message' ? 'MT' : '').$param['type'];
$param['type'] = $mtproto && $param['type'] === '%Message' ? '%MTMessage' : $param['type'];
$this->by_id[$key]['params'][$kkey] = $param;
}
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\TL\Types;
class Button extends \Volatile implements \JsonSerializable
@ -17,6 +18,7 @@ class Button extends \Volatile implements \JsonSerializable
use \danog\Serializable;
private $info = [];
private $data = [];
public function __magic_construct($API, $message, $button)
{
$this->data = $button;
@ -24,10 +26,12 @@ class Button extends \Volatile implements \JsonSerializable
$this->info['id'] = $message['id'];
$this->info['API'] = $API;
}
public function __sleep()
{
return ['data', 'info'];
}
public function click($donotwait = false)
{
switch ($this->data['_']) {
@ -43,12 +47,14 @@ class Button extends \Volatile implements \JsonSerializable
return $this->info['API']->method_call('messages.getBotCallbackAnswer', ['peer' => $this->info['peer'], 'msg_id' => $this->info['id'], 'game' => true], ['noResponse' => $donotwait, 'datacenter' => $this->info['API']->datacenter->curdc]);
}
}
public function __debugInfo()
{
return ['data' => $this->data, 'info' => ['peer' => $this->info['peer'], 'id' => $this->info['id']]];
}
public function jsonSerialize()
{
return (array) $this->data;
}
}
}

View File

@ -10,26 +10,31 @@ 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/>.
*/
namespace danog\MadelineProto\TL\Types;
class Bytes extends \Volatile implements \JsonSerializable
{
use \danog\Serializable;
private $bytes = [];
public function __magic_construct($bytes)
{
$this->bytes = $bytes;
}
public function __sleep()
{
return ['bytes'];
}
public function __toString()
{
return $this->bytes;
}
public function jsonSerialize()
{
return utf8_encode($this->bytes);
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\Threads;
/**
@ -23,33 +24,39 @@ class SocketHandler extends \Threaded implements \Collectable
$this->current = $current;
$this->error = $error;
}
/**
* Reading connection and receiving message from server. Check the CRC32.
*/
public function run()
{
require __DIR__ . '/../../../../vendor/autoload.php';
require __DIR__.'/../../../../vendor/autoload.php';
if ($this->error !== true) {
if ($this->error === -404) {
if ($this->API->datacenter->sockets[$this->current]->temp_auth_key !== null) {
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['resetting_auth_key']], \danog\MadelineProto\Logger::WARNING);
$this->API->datacenter->sockets[$this->current]->temp_auth_key = null;
$this->API->init_authorization();
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['recreate_temp_auth_key']);
}
}
throw new \danog\MadelineProto\RPCErrorException($this->error, $this->error);
}
$this->API->handle_messages($this->current);
$this->setGarbage();
}
protected $garbage = false;
public function setGarbage()
{
$this->garbage = true;
}
public function isGarbage()
{
return $this->garbage;
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\Threads;
/**
@ -18,25 +19,29 @@ namespace danog\MadelineProto\Threads;
class SocketReader extends \Threaded implements \Collectable
{
public $ready = false;
public function __construct($me, $current)
{
$this->API = $me;
$this->current = $current;
}
public function __sleep()
{
return ['current', 'API', 'garbage'];
}
public function __destruct()
{
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['shutting_down_reader_pool'] . $this->current], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['shutting_down_reader_pool'].$this->current], \danog\MadelineProto\Logger::NOTICE);
}
/**
* Reading connection and receiving message from server. Check the CRC32.
*/
public function run()
{
require __DIR__ . '/../../../../vendor/autoload.php';
require __DIR__.'/../../../../vendor/autoload.php';
$handler_pool = new \Pool($this->API->settings['threading']['handler_workers']);
$this->ready = true;
while ($this->API->run_workers) {
@ -57,13 +62,16 @@ class SocketReader extends \Threaded implements \Collectable
}
$this->setGarbage();
}
public $garbage = false;
public function setGarbage()
{
$this->garbage = true;
}
public function isGarbage()
{
return $this->garbage;
}
}
}

View File

@ -10,6 +10,7 @@ See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with the MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto;
/**
@ -21,6 +22,7 @@ trait Tools
{
return $length === 0 ? '' : \phpseclib\Crypt\Random::string($length);
}
/**
* posmod(numeric,numeric) : numeric
* Works just like the % (modulus) operator, only returns always a postive number.
@ -28,8 +30,10 @@ trait Tools
public function posmod($a, $b)
{
$resto = $a % $b;
return $resto < 0 ? $resto + abs($b) : $resto;
}
public function array_cast_recursive($array, $force = false)
{
if (!\danog\MadelineProto\Logger::$has_thread && !$force) {
@ -43,22 +47,28 @@ trait Tools
$array[$key] = $this->array_cast_recursive($value, $force);
}
}
return $array;
}
public function unpack_signed_int($value)
{
if (strlen($value) !== 4) {
throw new TL\Exception(\danog\MadelineProto\Lang::$current_lang['length_not_4']);
}
return unpack('l', \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($value) : $value)[1];
}
public function unpack_signed_long($value)
{
if (strlen($value) !== 8) {
throw new TL\Exception(\danog\MadelineProto\Lang::$current_lang['length_not_8']);
}
return unpack('q', \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($value) : $value)[1];
}
public function pack_signed_int($value)
{
if ($value > 2147483647) {
@ -68,8 +78,10 @@ trait Tools
throw new TL\Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['value_smaller_than_2147483648'], $value));
}
$res = pack('l', $value);
return \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($res) : $res;
}
public function pack_signed_long($value)
{
if ($value > 9223372036854775807) {
@ -78,9 +90,11 @@ trait Tools
if ($value < -9.223372036854776E+18) {
throw new TL\Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['value_smaller_than_9223372036854775808'], $value));
}
$res = \danog\MadelineProto\Logger::$bigint ? $this->pack_signed_int($value) . "\0\0\0\0" : (\danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev(pack('q', $value)) : pack('q', $value));
$res = \danog\MadelineProto\Logger::$bigint ? $this->pack_signed_int($value)."\0\0\0\0" : (\danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev(pack('q', $value)) : pack('q', $value));
return $res;
}
public function pack_unsigned_int($value)
{
if ($value > 4294967295) {
@ -89,21 +103,26 @@ trait Tools
if ($value < 0) {
throw new TL\Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['value_smaller_than_0'], $value));
}
return pack('V', $value);
}
public function pack_double($value)
{
$res = pack('d', $value);
if (strlen($res) !== 8) {
throw new TL\Exception(\danog\MadelineProto\Lang::$current_lang['encode_double_error']);
}
return \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($res) : $res;
}
public function unpack_double($value)
{
if (strlen($value) !== 8) {
throw new TL\Exception(\danog\MadelineProto\Lang::$current_lang['length_not_8']);
}
return unpack('d', \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($value) : $value)[1];
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\VoIP;
/**
@ -21,6 +22,7 @@ namespace danog\MadelineProto\VoIP;
trait AuthKeyHandler
{
private $calls = [];
public function request_call($user)
{
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
@ -48,8 +50,10 @@ trait AuthKeyHandler
$controller->storage = ['a' => $a, 'g_a' => str_pad($g_a->toBytes(), 256, chr(0), \STR_PAD_LEFT)];
$this->handle_pending_updates();
$this->get_updates_difference();
return $controller;
}
public function accept_call($call)
{
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
@ -62,6 +66,7 @@ trait AuthKeyHandler
});
if ($this->call_status($call['id']) !== \danog\MadelineProto\VoIP::CALL_STATE_ACCEPTED) {
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_error_1'], $call['id'])]);
return false;
}
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['accepting_call'], $this->calls[$call['id']]->getOtherID())], \danog\MadelineProto\Logger::VERBOSE);
@ -70,25 +75,31 @@ trait AuthKeyHandler
$b = \phpseclib\Math\BigInteger::randomRange($this->two, $dh_config['p']->subtract($this->two));
$g_b = $dh_config['g']->powMod($b, $dh_config['p']);
$this->check_G($g_b, $dh_config['p']);
try {
$res = $this->method_call('phone.acceptCall', ['peer' => $call, 'g_b' => $g_b->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'udp_p2p' => true, 'min_layer' => 65, 'max_layer' => 65]], ['datacenter' => $this->datacenter->curdc]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->rpc === 'CALL_ALREADY_ACCEPTED') {
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_already_accepted'], $call['id'])]);
return true;
}
if ($e->rpc === 'CALL_ALREADY_DECLINED') {
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['call_already_declined']]);
$this->discard_call($call['id'], 'phoneCallDiscardReasonHangup');
return false;
}
throw $e;
}
$this->calls[$res['phone_call']['id']]->storage['b'] = $b;
$this->handle_pending_updates();
$this->get_updates_difference();
return true;
}
public function confirm_call($params)
{
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
@ -101,6 +112,7 @@ trait AuthKeyHandler
});
if ($this->call_status($params['id']) !== \danog\MadelineProto\VoIP::CALL_STATE_REQUESTED) {
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_error_2'], $params['id'])]);
return false;
}
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_confirming'], $this->calls[$params['id']]->getOtherID())], \danog\MadelineProto\Logger::VERBOSE);
@ -111,7 +123,7 @@ trait AuthKeyHandler
$res = $this->method_call('phone.confirmCall', ['key_fingerprint' => substr(sha1($key, true), -8), 'peer' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], 'g_a' => $this->calls[$params['id']]->storage['g_a'], 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'min_layer' => 65, 'max_layer' => 65]], ['datacenter' => $this->datacenter->curdc])['phone_call'];
$visualization = [];
$length = new \phpseclib\Math\BigInteger(count($this->emojis));
foreach (str_split(hash('sha256', $key . str_pad($this->calls[$params['id']]->storage['g_a'], 256, chr(0), \STR_PAD_LEFT), true), 8) as $number) {
foreach (str_split(hash('sha256', $key.str_pad($this->calls[$params['id']]->storage['g_a'], 256, chr(0), \STR_PAD_LEFT), true), 8) as $number) {
$number[0] = chr(ord($number[0]) & 0x7f);
$visualization[] = $this->emojis[(int) (new \phpseclib\Math\BigInteger($number, 256))->divide($length)[1]->toString()];
}
@ -122,8 +134,10 @@ trait AuthKeyHandler
$this->calls[$params['id']]->parseConfig();
$res = $this->calls[$params['id']]->startTheMagic();
$this->handle_pending_updates();
return $res;
}
public function complete_call($params)
{
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
@ -136,6 +150,7 @@ trait AuthKeyHandler
});
if ($this->call_status($params['id']) !== \danog\MadelineProto\VoIP::CALL_STATE_ACCEPTED || !isset($this->calls[$params['id']]->storage['b'])) {
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_error_3'], $params['id'])]);
return false;
}
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_completing'], $this->calls[$params['id']]->getOtherID())], \danog\MadelineProto\Logger::VERBOSE);
@ -151,7 +166,7 @@ trait AuthKeyHandler
}
$visualization = [];
$length = new \phpseclib\Math\BigInteger(count($this->emojis));
foreach (str_split(hash('sha256', $key . str_pad($params['g_a_or_b']->toBytes(), 256, chr(0), \STR_PAD_LEFT), true), 8) as $number) {
foreach (str_split(hash('sha256', $key.str_pad($params['g_a_or_b']->toBytes(), 256, chr(0), \STR_PAD_LEFT), true), 8) as $number) {
$number[0] = chr(ord($number[0]) & 0x7f);
$visualization[] = $this->emojis[(int) (new \phpseclib\Math\BigInteger($number, 256))->divide($length)[1]->toString()];
}
@ -160,8 +175,10 @@ trait AuthKeyHandler
$this->calls[$params['id']]->configuration['endpoints'] = array_merge([$params['connection']], $params['alternative_connections'], $this->calls[$params['id']]->configuration['endpoints']);
$this->calls[$params['id']]->configuration = array_merge(['recv_timeout' => $this->config['call_receive_timeout_ms'] / 1000, 'init_timeout' => $this->config['call_connect_timeout_ms'] / 1000, 'data_saving' => \danog\MadelineProto\VoIP::DATA_SAVING_NEVER, 'enable_NS' => true, 'enable_AEC' => true, 'enable_AGC' => true, 'auth_key' => $key, 'network_type' => \danog\MadelineProto\VoIP::NET_TYPE_ETHERNET], $this->calls[$params['id']]->configuration);
$this->calls[$params['id']]->parseConfig();
return $this->calls[$params['id']]->startTheMagic();
}
public function call_status($id)
{
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
@ -175,8 +192,10 @@ trait AuthKeyHandler
if (isset($this->calls[$id])) {
return $this->calls[$id]->getCallState();
}
return \danog\MadelineProto\VoIP::CALL_STATE_NONE;
}
public function get_call($call)
{
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
@ -187,8 +206,10 @@ trait AuthKeyHandler
$controller->discard();
}
});
return $this->calls[$call];
}
public function discard_call($call, $reason, $rating = [], $need_debug = true)
{
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
@ -198,6 +219,7 @@ trait AuthKeyHandler
return;
}
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_discarding'], $call['id'])], \danog\MadelineProto\Logger::VERBOSE);
try {
$res = $this->method_call('phone.discardCall', ['peer' => $call, 'duration' => time() - $this->calls[$call['id']]->whenCreated(), 'connection_id' => $this->calls[$call['id']]->getPreferredRelayID(), 'reason' => $reason], ['datacenter' => $this->datacenter->curdc]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
@ -227,4 +249,4 @@ trait AuthKeyHandler
}
});
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\Wrappers;
trait DialogHandler
@ -24,6 +25,7 @@ trait DialogHandler
$datacenter = $this->datacenter->curdc;
$peers = [];
$this->postpone_updates = true;
try {
while ($this->dialog_params['count'] < $res['count']) {
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['getting_dialogs']]);
@ -45,6 +47,7 @@ trait DialogHandler
$this->postpone_updates = false;
$this->updates_state['sync_loading'] = false;
}
return $peers;
}
}
}

View File

@ -10,6 +10,7 @@ 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/>.
*/
namespace danog\MadelineProto\Wrappers;
/**
@ -34,8 +35,10 @@ trait Login
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['logout_error']);
}
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['logout_ok']], \danog\MadelineProto\Logger::NOTICE);
return true;
}
public function bot_login($token)
{
if ($this->authorized === self::LOGGED_IN) {
@ -50,12 +53,14 @@ trait Login
$this->updates = [];
$this->updates_key = 0;
if (!isset($this->settings['pwr']['pwr']) || !$this->settings['pwr']['pwr']) {
@file_get_contents('https://api.pwrtelegram.xyz/bot' . $token . '/getme');
@file_get_contents('https://api.pwrtelegram.xyz/bot'.$token.'/getme');
}
$this->init_authorization();
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_ok']], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization;
}
public function phone_login($number, $sms_type = 5)
{
if ($this->authorized === self::LOGGED_IN) {
@ -71,8 +76,10 @@ trait Login
$this->updates = [];
$this->updates_key = 0;
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_code_sent']], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization;
}
public function complete_phone_login($code)
{
if ($this->authorized !== self::WAITING_CODE) {
@ -80,6 +87,7 @@ trait Login
}
$this->authorized = self::NOT_LOGGED_IN;
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_user']], \danog\MadelineProto\Logger::NOTICE);
try {
$authorization = $this->method_call('auth.signIn', ['phone_number' => $this->authorization['phone_number'], 'phone_code_hash' => $this->authorization['phone_code_hash'], 'phone_code' => $code], ['datacenter' => $this->datacenter->curdc]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
@ -94,8 +102,10 @@ trait Login
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_need_signup']], \danog\MadelineProto\Logger::NOTICE);
$this->authorized = self::WAITING_SIGNUP;
$this->authorization['phone_code'] = $code;
return ['_' => 'account.needSignup'];
}
throw $e;
}
$this->authorized = self::LOGGED_IN;
@ -103,8 +113,10 @@ trait Login
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
$this->init_authorization();
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_ok']], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization;
}
public function import_authorization($authorization)
{
if ($this->authorized === self::LOGGED_IN) {
@ -129,8 +141,10 @@ trait Login
$this->datacenter->sockets[$dc_id]->authorized = true;
$this->authorized = self::LOGGED_IN;
$this->init_authorization();
return $this->get_self();
}
public function export_authorization()
{
if ($this->authorized !== self::LOGGED_IN) {
@ -138,8 +152,10 @@ trait Login
}
$this->get_self();
$this->authorized_dc = $this->datacenter->curdc;
return [$this->datacenter->curdc, $this->datacenter->sockets[$this->datacenter->curdc]->auth_key['auth_key']];
}
public function complete_signup($first_name, $last_name)
{
if ($this->authorized !== self::WAITING_SIGNUP) {
@ -152,8 +168,10 @@ trait Login
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
$this->init_authorization();
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['signup_ok']], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization;
}
public function complete_2fa_login($password)
{
if ($this->authorized !== self::WAITING_PASSWORD) {
@ -161,11 +179,12 @@ trait Login
}
$this->authorized = self::NOT_LOGGED_IN;
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_user']], \danog\MadelineProto\Logger::NOTICE);
$this->authorization = $this->method_call('auth.checkPassword', ['password_hash' => hash('sha256', $this->authorization['current_salt'] . $password . $this->authorization['current_salt'], true)], ['datacenter' => $this->datacenter->curdc]);
$this->authorization = $this->method_call('auth.checkPassword', ['password_hash' => hash('sha256', $this->authorization['current_salt'].$password.$this->authorization['current_salt'], true)], ['datacenter' => $this->datacenter->curdc]);
$this->authorized = self::LOGGED_IN;
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
$this->init_authorization();
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_ok']], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization;
}
}
}