diff --git a/lua/madeline.php b/lua/madeline.php index a95a1bf9..b4ea2da8 100755 --- a/lua/madeline.php +++ b/lua/madeline.php @@ -55,7 +55,7 @@ if (!is_object($Lua)) { } $offset = 0; while (true) { - $updates = $Lua->MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout + $updates = $Lua->MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout foreach ($updates as $update) { $offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id $Lua->madeline_update_callback($update['update']); diff --git a/lua/td.php b/lua/td.php index 7b2244ed..95c53259 100755 --- a/lua/td.php +++ b/lua/td.php @@ -56,7 +56,7 @@ if (!is_object($Lua)) { $offset = 0; while (true) { - $updates = $Lua->MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout + $updates = $Lua->MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout foreach ($updates as $update) { $offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id $Lua->tdcli_update_callback($update['update']); diff --git a/phartesting.php b/phartesting.php index 3557397c..c3a81009 100755 --- a/phartesting.php +++ b/phartesting.php @@ -107,7 +107,7 @@ if (stripos(readline('Do you want to handle incoming calls? (y/n): '), 'y') !== $howmany = readline('How many calls would you like me to handle? '); $offset = 0; while ($howmany > 0) { - $updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout + $updates = $MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout foreach ($updates as $update) { \danog\MadelineProto\Logger::log($update); $offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id @@ -135,7 +135,7 @@ if (stripos(readline('Do you want to make the secret chat tests? (y/n): '), 'y') \danog\MadelineProto\Logger::log($sentMessage, \danog\MadelineProto\Logger::NOTICE); /* while (true) { - $updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout + $updates = $MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout //\danog\MadelineProto\Logger::log($updates); foreach ($updates as $update) { $offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id diff --git a/socket.php b/socket.php index fd349ea2..d79ba168 100644 --- a/socket.php +++ b/socket.php @@ -1,3 +1,6 @@ AF_INET, 'protocol' => 0, 'address' => 'localhost', 'port' => 8005]); +$handler->start(); diff --git a/src/Socket.php b/src/Socket.php index e72cf3a7..2e4603cd 100644 --- a/src/Socket.php +++ b/src/Socket.php @@ -120,13 +120,13 @@ If not, see . if (!extension_loaded('pthreads')) { if (extension_loaded('sockets')) { - class Socket + class SocketBase { private $sock; - public function __construct(int $domain, int $type, int $protocol) + public function __construct($sock) { - $this->sock = socket_create($domain, $type, $protocol); + $this->sock = $sock; } public function __destruct() @@ -170,7 +170,11 @@ if (!extension_loaded('pthreads')) { public function accept() { - return socket_accept($this->sock); + if ($socket = socket_accept($this->sock)) { + return new SocketBase($socket); + } else { + return $socket; + } } public function connect(string $address, int $port = 0) @@ -222,6 +226,13 @@ if (!extension_loaded('pthreads')) { return $port ? ['host' => $address, 'port' => $port] : ['host' => $address]; } } + class Socket extends SocketBase + { + public function __construct(int $domain, int $type, int $protocol) + { + parent::__construct(socket_create($domain, $type, $protocol)); + } + } } else { define('AF_INET', 0); define('AF_INET6', 1); diff --git a/src/danog/MadelineProto/API.php b/src/danog/MadelineProto/API.php index b23dffda..4010b290 100644 --- a/src/danog/MadelineProto/API.php +++ b/src/danog/MadelineProto/API.php @@ -22,7 +22,6 @@ class API extends APIFactory public function __magic_construct($params = []) { set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']); - set_exception_handler(['\\danog\\MadelineProto\\Serialization', 'serialize_all']); if (is_string($params)) { $realpaths = Serialization::realpaths($params); if (file_exists($realpaths['file'])) { diff --git a/src/danog/MadelineProto/Connection.php b/src/danog/MadelineProto/Connection.php index d66456e0..faf9b6a3 100644 --- a/src/danog/MadelineProto/Connection.php +++ b/src/danog/MadelineProto/Connection.php @@ -236,13 +236,10 @@ class Connection } return $wrote; - break; case 'udp': throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']); - break; default: throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_invalid']); - break; } } diff --git a/src/danog/MadelineProto/Server.php b/src/danog/MadelineProto/Server.php index 97241aae..19fd8d0c 100644 --- a/src/danog/MadelineProto/Server.php +++ b/src/danog/MadelineProto/Server.php @@ -23,7 +23,9 @@ class Server public function __construct($settings) { + set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']); $this->settings = $settings; + $this->main = getmypid(); } public function start() @@ -32,15 +34,24 @@ class Server pcntl_signal(SIGINT, [$this, 'sig_handler']); pcntl_signal(SIGCHLD, [$this, 'sig_handler']); - $this->sock = new Socket($this->settings['type'], SOCK_STREAM, $this->settings['protocol']); + $this->sock = new \Socket($this->settings['type'], SOCK_STREAM, $this->settings['protocol']); $this->sock->bind($this->settings['address'], $this->settings['port']); $this->sock->listen(); $this->sock->setBlocking(true); + + $timeout = 2; + $this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout); + $this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout); while (true) { - $this->handle($this->sock->accept()); + pcntl_signal_dispatch(); + try { + if ($sock = $this->sock->accept()) { + $this->handle($sock); + } + } catch (\danog\MadelineProto\Exception $e) { + } } } - private function handle($socket) { $pid = pcntl_fork(); @@ -49,16 +60,21 @@ class Server } elseif ($pid) { return $this->pids[] = $pid; } - $handler = new \danog\MadelineProto\Server\Handler($socket); + $handler = new \danog\MadelineProto\Server\Handler($socket, 'tcp_abridged', null, null, null, null, null); $handler->loop(); die; } public function __destruct() { - foreach ($this->pid as $pid) { - pcntl_wait($pid); + if (!\danog\MadelineProto\Logger::$is_fork) { + \danog\MadelineProto\Logger::log('Shutting main process down'); + foreach ($this->pids as $pid) { + pcntl_wait($pid); + } + return; } + \danog\MadelineProto\Logger::log('Shutting fork '.getmypid().' down'); } public function sig_handler($sig) @@ -66,6 +82,7 @@ class Server switch ($sig) { case SIGTERM: case SIGINT: + Logger::log('Got SIGTERM/SIGINT in '.getmypid()); exit(); case SIGCHLD: diff --git a/src/danog/MadelineProto/Server/Handler.php b/src/danog/MadelineProto/Server/Handler.php index 49dc20a7..9500641a 100644 --- a/src/danog/MadelineProto/Server/Handler.php +++ b/src/danog/MadelineProto/Server/Handler.php @@ -16,24 +16,123 @@ namespace danog\MadelineProto\Server; /* * Socket handler for server */ -class Handler +class Handler extends \danog\MadelineProto\Connection { - private $socket; + use \danog\MadelineProto\TL\TL; + use \danog\MadelineProto\Tools; + private $madeline; - public function __construct($socket) + public function __magic_construct($socket, $extra, $ip, $port, $protocol, $timeout, $ipv6) { - $this->socket = $socket; + $this->sock = $socket; + $this->sock->setBlocking(true); + $this->protocol = $protocol; + $this->construct_TL(['socket' => __DIR__.'/../TL_socket.tl']); + } + public function __destruct() { + unset($this->sock); + $this->destruct_madeline(); + exit(); + } + public function destruct_madeline() { + if ($this->madeline !== null) { + $this->madeline->settings['logger'] = ['logger' => 0]; + $this->madeline->serialize(); + unset($this->madeline); + return true; + } + return false; } - public function loop() { - } + while (true) { + $request_id = 0; + try { + $message = $this->read_message(); + } catch (\danog\MadelineProto\NothingInTheSocketException $e) { + continue; + } + if ($message === null) { + continue; + } + try { + $message = $this->deserialize($message, ['type' => '', 'datacenter' => '']); + if ($message['_'] !== 'socketMessageRequest') { + throw new \danog\MadelineProto\Exception('Invalid object received'); + } + $request_id = $message['request_id']; + $this->send_response($request_id, $this->on_request($request_id, $message['method'], $message['args'])); + } catch (\danog\MadelineProto\TL\Exception $e) { + $this->send_exception($request_id, $e); + continue; + } catch (\danog\MadelineProto\Exception $e) { + $this->send_exception($request_id, $e); + continue; + } catch (\danog\MadelineProto\RPCErrorException $e) { + $this->send_exception($request_id, $e); + continue; + } catch (\DOMException $e) { + $this->send_exception($request_id, $e); + continue; + } - public function read_payload() - { + } } + public function on_request($method, $args) { + if (count($method) === 0 || count($method) > 2) { + throw new \danog\MadelineProto\Exception('Invalid method called'); + } + if ($method[0] === '__construct') { + if (count($args) === 1 && is_array($args[0])) { + $args[0]['logger'] = ['logger' => 4, 'logger_param' => [$this, 'logger']]; + $args[0]['updates']['callback'] = [$this, 'update_handler']; + } else if (count($args) === 2 && is_array($args[1])) { + $args[1]['logger'] = ['logger' => 4, 'logger_param' => [$this, 'logger']]; + $args[1]['updates']['callback'] = [$this, 'update_handler']; + } + $this->madeline = new \danog\MadelineProto\API(...$args); + return true; + } + if ($method[0] === '__destruct') { + return $this->destruct_madeline(); + } + if ($this->madeline === null) { + throw new \danog\MadelineProto\Exception('__construct was not called'); + } + foreach ($args as &$arg) { + if (is_array($arg) && isset($arg['_'])){ + if ($arg['_'] === 'callback' && isset($arg['callback']) && !method_exists($this, $arg['callback'])) { + $arg = [$this, $arg['callback']]; + } + if ($arg['_'] === 'stream' && isset($arg['stream_id'])) { + $arg = fopen('madelineSocket://', 'r+b', false, Handler::getContext($this, $arg['stream_id'])); + } + } + } + if (count($method) === 1) { + return $this->madeline->{$method[0]}(...$args); + } + if (count($method) === 2) { + return $this->madeline->{$method[0]}->{$method[1]}(...$args); + } + } + public function send_exception($request_id, $e) { + echo $e; + //$this->send_message($this->serialize_object(['type' => 'socketMessageException'], ['request_id' => $request_id, 'exception' => $e])); + } + public function send_response($request_id, $response) { + $this->send_message($this->serialize_object(['type' => 'socketMessageResponse'], ['request_id' => $request_id, 'data' => $response])); + } + public function send_data($stream_id, $data) { + $this->send_message($this->serialize_object(['type' => 'socketMessageRawData'], ['stream_id' => $stream_id, 'data' => $data])); + } + public function logger($message, $level) { - public function write_payload($payload) - { + } + public function update_handler($update) { + $this->send_message($this->serialize_object(['type' => 'socketMessageUpdate'], ['data' => $update])); + } + public function __call($method, $args) { + $this->send_message($this->serialize_object(['type' => 'socketMessageRequest'], ['request_id' => 0, 'method' => $method, 'args' => $args])); } } diff --git a/src/danog/MadelineProto/Server/Stream.php b/src/danog/MadelineProto/Server/Stream.php new file mode 100644 index 00000000..a9baca3d --- /dev/null +++ b/src/danog/MadelineProto/Server/Stream.php @@ -0,0 +1,57 @@ +. +*/ + +namespace danog\MadelineProto\Server; + + +class Stream +{ + const WRAPPER_NAME = 'madelineSocket'; + + public $context; + private $_handler; + private $_stream_id; + + private static $_isRegistered = false; + + public static function getContext($handler, $stream_id) + { + if (!self::$_isRegistered) { + stream_wrapper_register(self::WRAPPER_NAME, get_class()); + self::$_isRegistered = true; + } + return stream_context_create(array(self::WRAPPER_NAME => ['handler' => $handler, $stream_id])); + } + + public function stream_open($path, $mode, $options, &$opened_path) + { + $opt = stream_context_get_options($this->context); + if (!is_array($opt[self::WRAPPER_NAME]) || + !isset($opt[self::WRAPPER_NAME]['handler']) || + !($opt[self::WRAPPER_NAME]['handler'] instanceof Handler) + !isset($opt[self::WRAPPER_NAME]['stream_id']) || + !is_integer($opt[self::WRAPPER_NAME]['stream_id'])) return false; + $this->_handler = $opt[self::WRAPPER_NAME]['handler']; + $this->_stream_id = $opt[self::WRAPPER_NAME]['stream_id']; + return true; + } + + public function stream_write($data) + { + $this->handler->send_data($this->_stream_id, $data); + } + + public function stream_lock($mode) { + + } +} \ No newline at end of file diff --git a/src/danog/MadelineProto/TL_socket.tl b/src/danog/MadelineProto/TL_socket.tl new file mode 100644 index 00000000..ad975cd4 --- /dev/null +++ b/src/danog/MadelineProto/TL_socket.tl @@ -0,0 +1,18 @@ + +dataJSON#7d748d04 data:string = DataJSON; + +socketMessageRequest request_id:int method:vector args:vector<%DataJSON> = SocketMessage; +socketMessageResponse request_id:int data:%DataJSON = SocketMessage; +socketMessageException request_id:int exception:SocketException = SocketMessage; +socketMessageUpdate data:%DataJSON = SocketMessage; +socketMessageLog flags:# thread:flags.0?true process:flags.1?true additional:flags.2?string file:flags.3?string level:int data:string = SocketMessage; +socketMessageRawData stream_id:int data:bytes = SocketMessage; + +socketException message:string code:int trace:%SocketTLTrace = SocketException; +socketRPCErrorException flags:# rpc_message:flags.0?string message:flags.1?string code:int trace:%SocketTLTrace = SocketException; +socketTLException message:string code:int trace:%SocketTLTrace = SocketException; +socketDOMException message:string code:int trace:%SocketTLTrace = SocketException; + +socketTLTrace frames:vector<%SocketTLFrame> = SocketTLTrace; + +socketTLFrame flags:# file:flags.0?string line:flags.1?string function:flags.2?string args:flags.3?string tl_param:flags.4?string = SocketTLFrame; \ No newline at end of file diff --git a/tests/testing.php b/tests/testing.php index d9db6b8c..e8cc11ba 100755 --- a/tests/testing.php +++ b/tests/testing.php @@ -113,7 +113,7 @@ if (stripos(readline('Do you want to handle incoming calls? (y/n): '), 'y') !== $howmany = readline('How many calls would you like me to handle? '); $offset = 0; while ($howmany > 0) { - $updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout + $updates = $MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout foreach ($updates as $update) { \danog\MadelineProto\Logger::log($update); $offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id @@ -141,7 +141,7 @@ if (stripos(readline('Do you want to make the secret chat tests? (y/n): '), 'y') \danog\MadelineProto\Logger::log($sentMessage, \danog\MadelineProto\Logger::NOTICE); /* while (true) { - $updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout + $updates = $MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout //\danog\MadelineProto\Logger::log($updates); foreach ($updates as $update) { $offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id diff --git a/userbots/MadelineProto_bot.php b/userbots/MadelineProto_bot.php index fd36fe10..de502c02 100755 --- a/userbots/MadelineProto_bot.php +++ b/userbots/MadelineProto_bot.php @@ -64,7 +64,7 @@ Created by [Daniil Gentili](mention:@danogentili) (@daniilgentili) using the [Ma echo 'Bot started.'.PHP_EOL; while (true) { - $updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout + $updates = $MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout foreach ($updates as $update) { $offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id switch ($update['update']['_']) { diff --git a/userbots/pipesbot.php b/userbots/pipesbot.php index 393d3ab8..b4e2e451 100755 --- a/userbots/pipesbot.php +++ b/userbots/pipesbot.php @@ -107,7 +107,7 @@ Note that the query must be terminated by a \$ Created by @danogentili (@daniilgentili) using the daniil.it/MadelineProto PHP MTProto client."; while (true) { - $updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout + $updates = $MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout foreach ($updates as $update) { $offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id try { diff --git a/userbots/pwrtelegram_debug_bot.php b/userbots/pwrtelegram_debug_bot.php index c8eb4f03..a16e34a9 100755 --- a/userbots/pwrtelegram_debug_bot.php +++ b/userbots/pwrtelegram_debug_bot.php @@ -109,7 +109,7 @@ function recurse($array, $prefix = '') } $offset = 0; while (true) { - $updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout + $updates = $MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout foreach ($updates as $update) { $offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id switch ($update['update']['_']) {