diff --git a/src/danog/MadelineProto/CombinedAPI.php b/src/danog/MadelineProto/CombinedAPI.php deleted file mode 100644 index ba95959e..00000000 --- a/src/danog/MadelineProto/CombinedAPI.php +++ /dev/null @@ -1,219 +0,0 @@ -. - * - * @author Daniil Gentili - * @copyright 2016-2020 Daniil Gentili - * @license https://opensource.org/licenses/AGPL-3.0 AGPLv3 - * - * @link https://docs.madelineproto.xyz MadelineProto documentation - */ - -namespace danog\MadelineProto; - -use Amp\Loop; -use function Amp\Promise\all; - -class CombinedAPI -{ - use \danog\Serializable; - public $session; - public $instance_paths = []; - public $instances = []; - public $timeout = 5; - public $serialization_interval = 30; - public $serialized = 0; - protected $async; - public function __magic_construct($session, $paths = []) - { - \set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']); - \danog\MadelineProto\Magic::classExists(); - $realpaths = new SessionPaths($session); - $this->session = $realpaths->getSessionPath(); - foreach ($paths as $path => $settings) { - $this->addInstance($path, $settings); - } - if (\file_exists($realpaths->getSessionPath())) { - if (!\file_exists($realpaths->getLockPath())) { - \touch($realpaths->getLockPath()); - \clearstatcache(); - } - $lock = \fopen($realpaths->getLockPath(), 'r'); - \danog\MadelineProto\Logger::log('Waiting for shared lock of serialization lockfile...'); - \flock($lock, LOCK_SH); - \danog\MadelineProto\Logger::log('Shared lock acquired, deserializing...'); - try { - $tounserialize = \file_get_contents($realpaths->getSessionPath()); - } finally { - \flock($lock, LOCK_UN); - \fclose($lock); - } - $deserialized = \unserialize($tounserialize); - /*foreach ($deserialized['instance_paths'] as $path) { - $this->addInstance($path, isset($paths[$path]) ? $paths[$path] : []); - }*/ - $this->event_handler = $deserialized['event_handler']; - $this->event_handler_instance = $deserialized['event_handler_instance']; - if ($this->event_handler !== null) { - $this->setEventHandler($this->event_handler); - } - } - foreach ($paths as $path => $settings) { - $this->addInstance($path, $settings); - } - } - public function addInstance($path, $settings = []) - { - if (isset($this->instances[$path]) && isset($this->instance_paths[$path])) { - if (isset($this->event_handler_instance)) { - $this->event_handler_instance->referenceInstance($path); - } - return; - } - //\danog\MadelineProto\Logger::constructor(3); - \danog\MadelineProto\Logger::log("INSTANTIATING {$path}..."); - $instance = new \danog\MadelineProto\API($path, $settings); - $this->instance_paths[$path] = $path; - $this->instances[$path] = $instance; - if (isset($this->event_handler_instance)) { - $this->event_handler_instance->referenceInstance($path); - } - } - public function removeInstance($path) - { - if (isset($this->instance_paths[$path])) { - unset($this->instance_paths[$path]); - } - if (isset($this->instances[$path])) { - unset($this->instances[$path]); - } - if (isset($this->event_handler_instance)) { - $this->event_handler_instance->removeInstance($path); - } - } - public function __destruct() - { - $this->serialize(); - } - public function serialize($filename = '') - { - /*foreach ($this->instances as $instance) { - $instance->serialize(); - }*/ - if (\is_null($this->session)) { - return; - } - if ($filename === '') { - $filename = $this->session; - } - Logger::log(\danog\MadelineProto\Lang::$current_lang['serializing_madelineproto']); - $realpaths = new SessionPaths($filename); - if (!\file_exists($realpaths->getLockPath())) { - \touch($realpaths->getLockPath()); - \clearstatcache(); - } - $lock = \fopen($realpaths->getLockPath(), 'w'); - \danog\MadelineProto\Logger::log('Waiting for exclusive lock of serialization lockfile...'); - \flock($lock, LOCK_EX); - \danog\MadelineProto\Logger::log('Lock acquired, serializing'); - try { - $wrote = \file_put_contents($realpaths->getTempPath(), \serialize(['event_handler' => $this->event_handler, 'event_handler_instance' => $this->event_handler_instance, 'instance_paths' => $this->instance_paths])); - \rename($realpaths->getTempPath(), $realpaths->getSessionPath()); - } finally { - \flock($lock, LOCK_UN); - \fclose($lock); - } - $this->serialized = \time(); - return $wrote; - } - public $event_handler; - private $event_handler_instance; - private $event_handler_methods = []; - public function getEventHandler() - { - return $this->event_handler_instance; - } - public function setEventHandler($event_handler) - { - if (!\class_exists($event_handler) || !\is_subclass_of($event_handler, '\\danog\\MadelineProto\\CombinedEventHandler')) { - throw new \danog\MadelineProto\Exception('Wrong event handler was defined'); - } - $this->event_handler = $event_handler; - if (!$this->event_handler_instance instanceof $this->event_handler) { - $class_name = $this->event_handler; - $this->event_handler_instance = new $class_name($this); - } else { - $this->event_handler_instance->__construct($this); - } - $this->event_handler_methods = []; - foreach (\get_class_methods($this->event_handler) as $method) { - if ($method === 'onLoop') { - $this->loop_callback = [$this->event_handler_instance, 'onLoop']; - } elseif ($method === 'onAny') { - foreach (\end($this->instances)->API->getTL()->getConstructors()->by_id as $constructor) { - if ($constructor['type'] === 'Update' && !isset($this->event_handler_methods[$constructor['predicate']])) { - $this->event_handler_methods[$constructor['predicate']] = [$this->event_handler_instance, 'onAny']; - } - } - } else { - $method_name = \lcfirst(\substr($method, 2)); - $this->event_handler_methods[$method_name] = [$this->event_handler_instance, $method]; - } - } - } - public function eventUpdateHandler($update, $instance) - { - if (isset($this->event_handler_methods[$update['_']])) { - return $this->event_handler_methods[$update['_']]($update, $instance); - } - } - private $loop_callback; - public function async($async) - { - $this->async = $async; - foreach ($this->instances as $instance) { - $instance->async($async); - } - } - public function setLoopCallback($callback) - { - $this->loop_callback = $callback; - } - public function getUpdates($params = []) - { - } - public function loop($max_forks = 0) - { - if (\is_callable($max_forks)) { - return \danog\MadelineProto\Tools::wait($max_forks()); - } - $loops = []; - foreach ($this->instances as $path => $instance) { - \danog\MadelineProto\Tools::wait($instance->initAsynchronously()); - if ($instance->API->authorized !== MTProto::LOGGED_IN) { - continue; - } - $instance->setCallback(function ($update) use ($path) { - return $this->eventUpdateHandler($update, $path); - }, ['async' => false]); - if ($this->loop_callback !== null) { - $instance->setLoopCallback($this->loop_callback, ['async' => false]); - } - $loops[] = \danog\MadelineProto\Tools::call($instance->loop(0, ['async' => true])); - } - Loop::repeat($this->serialization_interval * 1000, function () { - \danog\MadelineProto\Logger::log('Serializing combined event handler'); - $this->serialize(); - }); - \danog\MadelineProto\Logger::log('Started update loop', \danog\MadelineProto\Logger::NOTICE); - \danog\MadelineProto\Tools::wait(all($loops)); - } -} diff --git a/src/danog/MadelineProto/DataCenter.php b/src/danog/MadelineProto/DataCenter.php index ff1dc58d..736f5bd0 100644 --- a/src/danog/MadelineProto/DataCenter.php +++ b/src/danog/MadelineProto/DataCenter.php @@ -216,7 +216,6 @@ class DataCenter $this->settings = $settings; foreach ($this->sockets as $key => $socket) { if ($socket instanceof DataCenterConnection && !\strpos($key, '_bk')) { - //$this->API->logger->logger(\sprintf(Lang::$current_lang['dc_con_stop'], $key), Logger::VERBOSE); if ($reconnectAll || isset($changed[$id])) { $this->API->logger->logger("Disconnecting all before reconnect!"); $socket->needReconnect(true); diff --git a/src/danog/MadelineProto/InternalDoc.php b/src/danog/MadelineProto/InternalDoc.php index 96592cd0..e6a1e209 100644 --- a/src/danog/MadelineProto/InternalDoc.php +++ b/src/danog/MadelineProto/InternalDoc.php @@ -5133,8 +5133,6 @@ class InternalDoc extends APIFactory } /** * Get logger. - * - * @return Logger */ public function getLogger(): \danog\MadelineProto\Logger { @@ -5209,6 +5207,13 @@ class InternalDoc extends APIFactory { return $this->__call(__FUNCTION__, [$data, $extra]); } + /** + * Get PSR logger. + */ + public function getPsrLogger(): \Psr\Log\LoggerInterface + { + return $this->API->getPsrLogger(); + } /** * Get full info about peer (including full list of channel members), returns a Chat object. * @@ -6123,14 +6128,13 @@ class InternalDoc extends APIFactory /** * Parse, update and store settings. * - * @param Settings|SettingsEmpty $settings Settings - * @param bool $reinit Whether to reinit the instance + * @param SettingsAbstract $settings Settings * * @return \Generator */ - public function updateSettings(\danog\MadelineProto\SettingsAbstract $settings, bool $reinit = true, array $extra = []) + public function updateSettings(\danog\MadelineProto\SettingsAbstract $settings, array $extra = []) { - return $this->__call(__FUNCTION__, [$settings, $reinit, $extra]); + return $this->__call(__FUNCTION__, [$settings, $extra]); } /** * Upload file. diff --git a/src/danog/MadelineProto/Ipc/Runner/entry.php b/src/danog/MadelineProto/Ipc/Runner/entry.php index 92cb8945..d34087d3 100644 --- a/src/danog/MadelineProto/Ipc/Runner/entry.php +++ b/src/danog/MadelineProto/Ipc/Runner/entry.php @@ -22,7 +22,7 @@ use danog\MadelineProto\Ipc\Server; use danog\MadelineProto\Logger; use danog\MadelineProto\Magic; use danog\MadelineProto\SessionPaths; -use danog\MadelineProto\Settings; +use danog\MadelineProto\Settings\Ipc; use danog\MadelineProto\Tools; (static function (): void { @@ -97,7 +97,7 @@ use danog\MadelineProto\Tools; Magic::classExists(); Magic::$script_cwd = $_GET['cwd'] ?? Magic::getcwd(); - $API = new API($ipcPath, (new Settings)->getSerialization()->setForceFull(true)); + $API = new API($ipcPath, (new Ipc)->setForceFull(true)); while (true) { try { diff --git a/src/danog/MadelineProto/Ipc/Server.php b/src/danog/MadelineProto/Ipc/Server.php index 74f93a28..dfeb6c07 100644 --- a/src/danog/MadelineProto/Ipc/Server.php +++ b/src/danog/MadelineProto/Ipc/Server.php @@ -28,6 +28,7 @@ use danog\MadelineProto\Ipc\Runner\WebRunner; use danog\MadelineProto\Logger; use danog\MadelineProto\Loop\InternalLoop; use danog\MadelineProto\SessionPaths; +use danog\MadelineProto\Settings\Ipc; use danog\MadelineProto\Tools; /** @@ -56,6 +57,10 @@ class Server extends SignalLoop * Callback IPC server. */ private ServerCallback $callback; + /** + * IPC settings. + */ + private Ipc $settings; /** * Set IPC path. * @@ -221,4 +226,18 @@ class Server extends SignalLoop { return "IPC server"; } + + /** + * Set IPC settings. + * + * @param Ipc $settings IPC settings + * + * @return self + */ + public function setSettings(Ipc $settings): self + { + $this->settings = $settings; + + return $this; + } } diff --git a/src/danog/MadelineProto/Lang.php b/src/danog/MadelineProto/Lang.php index 048d2da7..b83afe9b 100644 --- a/src/danog/MadelineProto/Lang.php +++ b/src/danog/MadelineProto/Lang.php @@ -12,7 +12,7 @@ * @author Daniil Gentili * @copyright 2016-2020 Daniil Gentili * @license https://opensource.org/licenses/AGPL-3.0 AGPLv3 - * @link https://docs.madelineproto.xyz MadelineProto documentation + * @link https://docs.madelineproto.xyz MadelineProto documentation */ namespace danog\MadelineProto; @@ -23,27 +23,13 @@ class Lang 'it' => [ 'phpseclib_fork' => 'Per favore installa questo fork di phpseclib: https://github.com/danog/phpseclib', - 'inst_dc' => 'Istanziamento dei DataCenter...', - 'load_rsa' => 'Caricamento delle chiavi RSA...', - 'TL_translation' => 'Translazione degli schemi TL...', - 'dh_prime_check_0' => 'Esecuzione dei check dh_prime (0/3)...', 'nearest_dc' => 'Siamo in %s, il DC più vicino è %d.', 'serialization_ofd' => 'La serializzazione non è aggiornata, reistanziamento dell\'oggetto in corso!', 'getupdates_deserialization' => 'Ottenimento aggiornamenti dopo deserializzazione...', - 'shutdown_reader_pool' => 'Chiusura pool di lettura, %d thread rimasti', - 'threading_on' => 'IL THREADING È ABILITATO', - 'socket_reader' => 'Lettore socket su DC %s: ', - 'socket_status_1' => 'CREAZIONE', - 'socket_status_2' => 'INVIO', - 'socket_status_3' => 'ATTESA', - 'socket_status_4' => 'PRONTO', - 'socket_status_5' => 'AVVIATO', 'api_not_set' => 'Devi specificare una chiave ed un ID API, ottienili su https://my.telegram.org', 'session_corrupted' => 'La sessione si è corrotta!', - 'resetSession_seqno' => 'Resettando ID sessione e numero di sequenza sul DC %s...', 'gen_perm_auth_key' => 'Generando chiave di autorizzazione permanente per il DC %s...', 'gen_temp_auth_key' => 'Generando chiave di autorizzazione temporanea per il DC %s...', - 'copy_auth_dcs' => 'Copiando autorizzazione dal DC %s al DC %s...', 'write_client_info' => 'Scrittura info sul client (eseguendo nel contempo il metodo %s)...', 'config_updated' => 'La configurazione è stata aggiornata!', 'length_not_4' => 'La lunghezza non è uguale a 4', @@ -57,29 +43,12 @@ class Lang 'encode_double_error' => 'Non sono riuscito a codificare il numero a virgola mobile fornito', 'file_not_exist' => 'Il file specificato non esiste', 'deserialization_error' => 'C\'è stato un errore durante la deserializzazione', - 'rsa_init' => 'Istanziamento di \\tgseclib\\Crypt\\RSA in corso...', - 'loading_key' => 'Caricamento della chiave in corso...', - 'computing_fingerprint' => 'Calcolo del fingerprint in corso...', - 'rsa_encrypting' => 'Criptando con chiave RSA...', 'rpc_tg_error' => 'Telegram ha ritornato un errore RPC: %s (%s), causato da %s:%s%sTL trace:', 'v_error' => '506572206661766f726520616767696f726e612071756573746120696e7374616c6c617a696f6e65206469204d6164656c696e6550726f746f20636f6e206769742070756c6c206520636f6d706f73657220757064617465', 'v_tgerror' => '506572206661766f726520616767696f726e61207068702d6c69627467766f6970', - 'no_mode_specified' => 'Nessuna modalità di logging è stata specificata!', - 'constructor_function_uncalled' => 'Il metodo costruttore non è stato ancora chiamato! Per favore chiama il metodo costruttore prima di usare questo metodo.', - 'proxy_class_invalid' => 'È stata specificata una classe proxy errata!', - 'socket_con_error' => 'Connessione fallita.', - 'protocol_not_implemented' => 'Questo protocollo non è stato ancora implementato.', 'protocol_invalid' => 'È stato fornito un protocollo non valido', - 'nothing_in_socket' => 'Non c\'è niente nel socket!', - 'wrong_length_read' => 'ATTENZIONE: Non sono stati letti abbastanza byte (dovevo leggere %s, ho letto %s)!', - 'no_data_in_socket' => 'Non ci sono dati nel socket!', - 'dc_con_start' => 'Connessione al DC %s in corso...', - 'dc_con_stop' => 'Disconnessione dal DC %s in corso...', - 'dc_con_test_start' => 'Connessione al DC %s (server %s, %s, %s)...', 'script_not_exist' => 'Lo script fornito non esiste', - 'apifactory_start' => 'Sto avviando la fabbrica di API...', 'madelineproto_ready' => 'MadelineProto è pronto!', - 'logout_error' => 'C\'è stato un errore durante il logout!', 'logout_ok' => 'Il logout è stato eseguito correttamente!', 'already_loggedIn' => 'Questa istanza di MadelineProto è già loggata, prima faccio il logout...', 'login_ok' => 'Il login è stato eseguito correttamente!', @@ -96,10 +65,8 @@ class Lang 'signing_up' => 'Mi sto registrando su telegram come utente normale...', 'signup_ok' => 'Mi sono registrato su Telegram!', '2fa_uncalled' => 'Non sto aspettando la password, chiama prima le funzioni phoneLogin e completePhoneLogin!', - 'getting_dialogs' => 'Sto ottenendo la lista delle chat...', 'libtgvoip_required' => 'È necessario installare l\'estensione php-libtgvoip per accettare e gestire chiamate vocali, vistate https://docs.madelineproto.xyz per più info.', 'peer_not_in_db' => 'Questo utente/gruppo/canale non è presente nel database interno MadelineProto', - 'calling_user' => 'Sto chiamando %s...', 'generating_a' => 'Sto generando a...', 'generating_g_a' => 'Sto generando g_a...', 'call_error_1' => 'Impossibile trovare ed accettare la chiamata %s', @@ -135,62 +102,108 @@ class Lang 'sec_peer_not_in_db' => 'La chat segreta non è presente nel database interno MadelineProto', 'stream_handle_invalid' => 'Il valore fornito non è uno stream', 'length_too_big' => 'Il valore fornito è troppo lungo', - 'deserialize_not_str' => 'Il valore generato non è una stringa', 'type_extract_error_id' => 'Non sono riuscito ad estrarre il tipo %s con ID %s', - 'vector_invalid' => 'ID vettore non valido: ', 'constructor_not_found' => 'Costruttore non trovato per tipo: ', 'rand_bytes_too_small' => 'random_bytes è troppo corto!', 'botapi_conversion_error' => 'NOn sono risucito a convertire %s in un oggetto bot API', 'non_text_conversion' => 'Non posso ancora convertire messaggi media', 'last_byte_invalid' => 'L\'ultimo byte non è valido', 'file_type_invalid' => 'È stato fornito un tipo file errato', - 'recreate_temp_auth_key' => 'Sono stato costretto a rigenerare la chiave di autorizzazione temporanea', - 'resetting_auth_key' => 'ATTENZIONE: Sto resettando la chiave temporanea...', - 'shutting_down_reader_pool' => 'Chisura pool di lettura', - 'shutting_down_handler_pool' => 'Chiusura pool di gestione per DC %s, %d thread rimasti', 'secret_chat_skipping' => 'Non ho la chat segreta %s nel database, ignorando messaggio', 'fingerprint_mismatch' => 'Fingerprint della chiave non valido', 'msg_data_length_too_big' => 'message_data_length è troppo grande', 'length_not_divisible_16' => 'La lunghezza dei dati decifrati non è divisibile per 16', - 'msg_key_mismatch' => 'msg_key non valido', - 'rand_bytes_too_short' => 'random_bytes è troppo corto!', - 'resending_unsupported' => 'IL riinvio di messaggi non è ancora supportato', - 'unrecognized_dec_msg' => 'È stato ricevuto un messaggio decifrato sconosciuto: ', - 'serializing_madelineproto' => 'Sto serializzando MadelineProto...', - 'req_pq' => 'Sto richiedendo pq...', 'done' => 'Fatto!', 'cdn_reupload' => 'Il file non è disponibile sul nostro CDN, richiedo la copia!', 'stored_on_cdn' => 'Il file è scaricabile tramite CDN!', + 'apiAppInstructionsAuto0' => 'Inserisci il nome dell\'app, può essere qualsiasi cosa: ', + 'apiAppInstructionsAuto1' => 'Inserisci il nome ridotto dell\'app, caratteri alfanumerici: ', + 'apiAppInstructionsAuto2' => 'Inserisci il sito internet dell\'app, oppure t.me/username: ', + 'apiAppInstructionsAuto3' => 'Inserisci la piattaforma dell\'app: ', + 'apiAppInstructionsAuto4' => 'Descrivi la tua app: ', + 'apiAppInstructionsManual0' => 'titolo dell\'app, può essere qualsiasi cosa', + 'apiAppInstructionsManual1' => 'il nome ridotto dell\'app, caratteri alfanumerici: ', + 'apiAppInstructionsManual2' => '', + 'apiAppInstructionsManual3' => '', + 'apiAppInstructionsManual4' => '', + 'apiAutoPrompt0' => '', + 'apiAutoPrompt1' => '', + 'apiChooseManualAuto' => '', + 'apiChooseManualAutoTip' => '', + 'apiChoosePrompt' => '', + 'apiError' => '', + 'apiManualInstructions0' => '', + 'apiManualInstructions1' => '', + 'apiManualInstructions2' => '', + 'apiManualPrompt0' => '', + 'apiManualPrompt1' => '', + 'apiParamsError' => '', + 'loginBot' => '', + 'loginChoosePrompt' => '', + 'loginNoCode' => '', + 'loginNoName' => '', + 'loginNoPass' => '', + 'loginUser' => '', + 'loginUser2FA' => '', + 'loginUser2FAHint' => '', + 'loginUser2FAWeb' => '', + 'loginUserCode' => '', + 'signupFirstName' => '', + 'signupFirstNameWeb' => '', + 'signupLastName' => '', + 'signupLastNameWeb' => '', + 'signupWeb' => '', ], 'en' => [ - 'req_pq' => 'Requesting pq...', + 'apiChooseManualAuto' => 'You did not define a valid API ID/API hash. Do you want to define it now manually, or automatically? (m/a)', + 'apiChooseManualAutoTip' => 'Note that you can also provide the API parameters directly in the code using the settings: %s', + 'apiChoosePrompt' => 'Your choice (m/a): ', + 'apiManualInstructions0' => 'Login to my.telegram.org', + 'apiManualInstructions1' => 'Go to API development tools', + 'apiManualInstructions2' => 'Click on create application', + 'apiAppInstructionsManual0' => 'your app\'s name, can be anything', + 'apiAppInstructionsManual1' => 'your app\'s short name, alphanumeric', + 'apiAppInstructionsManual2' => 'your app/website\'s URL, or t.me/yourusername', + 'apiAppInstructionsManual3' => 'anything', + 'apiAppInstructionsManual4' => 'Describe your app here', + 'apiManualPrompt0' => 'Enter your API ID: ', + 'apiManualPrompt1' => 'Enter your API hash: ', + 'apiAutoPrompt0' => 'Enter a phone number that is already registered on Telegram: ', + 'apiAutoPrompt1' => 'Enter the verification code you received in Telegram: ', + 'apiAppInstructionsAuto0' => 'Enter the app\'s name, can be anything: ', + 'apiAppInstructionsAuto1' => 'Enter the app\'s short name, alphanumeric: ', + 'apiAppInstructionsAuto2' => 'Enter the app/website\'s URL, or t.me/yourusername: ', + 'apiAppInstructionsAuto3' => 'Enter the app platform: ', + 'apiAppInstructionsAuto4' => 'Describe your app: ', + 'apiParamsError' => 'You didn\'t provide all of the required parameters!', + 'apiError' => 'ERROR: %s. Try again.', + 'loginChoosePrompt' => 'Do you want to login as user or bot (u/b)? ', + 'loginBot' => 'Enter your bot token: ', + 'loginUser' => 'Enter your phone number: ', + 'loginUserCode' => 'Enter the phone code: ', + 'loginUser2FA' => 'Enter your password (hint %s): ', + 'loginUser2FAWeb' => 'Enter your password: ', + 'loginUser2FAHint' => 'Hint: %s', + 'signupFirstName' => 'Enter your first name: ', + 'signupLastName' => 'Enter your last name (can be empty): ', + 'signupWeb' => 'Sign up please', + 'signupFirstNameWeb' => 'First name', + 'signupLastNameWeb' => 'Last name', + 'loginNoCode' => 'You didn\'t provide a phone code!', + 'loginNoPass' => 'You didn\'t provide the password!', + 'loginNoName' => 'You didn\'t provide the first name!', 'done' => 'Done!', 'cdn_reupload' => 'File is not stored on CDN, requesting reupload!', 'stored_on_cdn' => 'File is stored on CDN!', - 'serializing_madelineproto' => 'Serializing MadelineProto...', 'phpseclib_fork' => 'Please install this fork of phpseclib: https://github.com/danog/phpseclib', - 'inst_dc' => 'Istantiating DataCenter...', - 'load_rsa' => 'Loading RSA keys...', - 'TL_translation' => 'Translating TL schemas...', - 'dh_prime_check_0' => 'Executing dh_prime checks (0/3)...', 'nearest_dc' => 'We\'re in %s, nearest DC is %d.', 'serialization_ofd' => 'Serialization is out of date, reconstructing object!', 'getupdates_deserialization' => 'Getting updates after deserialization...', - 'shutdown_reader_pool' => 'Shutting down reader pool, %d jobs left', - 'threading_on' => 'THREADING IS ENABLED', - 'socket_reader' => 'Socket reader on DC %s: ', - 'socket_status_1' => 'CREATING', - 'socket_status_2' => 'SUBMITTING', - 'socket_status_3' => 'WAITING', - 'socket_status_4' => 'READY', - 'socket_status_5' => 'WORKING', 'api_not_set' => 'You must provide an api key and an api id, get your own @ my.telegram.org', 'session_corrupted' => 'The session is corrupted!', - 'resetSession_seqno' => 'Resetting session id and seq_no in DC %s...', 'gen_perm_auth_key' => 'Generating permanent authorization key for DC %s...', 'gen_temp_auth_key' => 'Generating temporary authorization key for DC %s...', - 'copy_auth_dcs' => 'Copying authorization from DC %s to DC %s...', 'write_client_info' => 'Writing client info (also executing %s)...', 'config_updated' => 'Updated config!', 'length_not_4' => 'Length is not equal to 4', @@ -204,29 +217,12 @@ class Lang 'encode_double_error' => 'Could not properly encode double', 'file_not_exist' => 'File does not exist', 'deserialization_error' => 'An error occurred on deserialization', - 'rsa_init' => 'Istantiating \\tgseclib\\Crypt\\RSA...', - 'loading_key' => 'Loading key...', - 'computing_fingerprint' => 'Computing fingerprint...', - 'rsa_encrypting' => 'Encrypting with rsa key...', 'rpc_tg_error' => 'Telegram returned an RPC error: %s (%s), caused by %s:%s%sTL trace:', 'v_error' => '506c656173652075706461746520746f20746865206c61746573742076657273696f6e206f66204d6164656c696e6550726f746f2e', 'v_tgerror' => '506c6561736520757064617465207068702d6c69627467766f6970', - 'no_mode_specified' => 'No mode was specified!', - 'constructor_function_uncalled' => 'The constructor function wasn\'t called! Please call the constructor function before using this method.', - 'proxy_class_invalid' => 'Invalid proxy class provided!', - 'socket_con_error' => 'Connection: couldn\'t connect to socket.', - 'protocol_not_implemented' => 'Connection: This protocol isn\'t implemented yet.', 'protocol_invalid' => 'Connection: invalid protocol specified.', - 'nothing_in_socket' => 'Nothing in the socket!', - 'wrong_length_read' => 'WARNING: Wrong length was read (should\'ve read %s, read %s)!', - 'no_data_in_socket' => 'No data in the socket!', - 'dc_con_start' => 'Connecting to DC %s...', - 'dc_con_stop' => 'Disconnecting from DC %s...', - 'dc_con_test_start' => 'Connecting to DC %s (%s server, %s, %s)...', 'script_not_exist' => 'Provided script does not exist', - 'apifactory_start' => 'Running APIFactory...', 'madelineproto_ready' => 'MadelineProto is ready!', - 'logout_error' => 'An error occurred while logging out!', 'logout_ok' => 'Logged out successfully!', 'already_loggedIn' => 'This instance of MadelineProto is already logged in. Logging out first...', 'login_ok' => 'Logged in successfully!', @@ -243,10 +239,8 @@ class Lang 'signing_up' => 'Signing up as a normal user...', 'signup_ok' => 'Signed up in successfully!', '2fa_uncalled' => 'I\'m not waiting for the password! Please call the phoneLogin and the completePhoneLogin methods first!', - 'getting_dialogs' => 'Getting dialogs...', 'libtgvoip_required' => 'The php-libtgvoip extension is required to accept and manage calls. See daniil.it/MadelineProto for more info.', 'peer_not_in_db' => 'This peer is not present in the internal peer database', - 'calling_user' => 'Calling %s...', 'generating_a' => 'Generating a...', 'generating_g_a' => 'Generating g_a...', 'call_error_1' => 'Could not find and accept call %s', @@ -276,65 +270,76 @@ class Lang 'long_not_64' => 'Given value is not 64 bytes long', 'array_invalid' => 'You didn\'t provide a valid array', 'predicate_not_set' => 'Predicate (value under _) was not set!', - 'type_extract_error' => 'Could not extract type "%s"', + 'type_extract_error' => 'Could not extract type "%s", you should update MadelineProto!', 'method_not_found' => 'Could not find method: ', 'params_missing' => 'Missing required parameter', 'sec_peer_not_in_db' => 'This secret peer is not present in the internal peer database', 'stream_handle_invalid' => 'An invalid stream handle was provided.', 'length_too_big' => 'Length is too big', - 'deserialize_not_str' => 'Deserialize: Generated value isn\'t a string', - 'type_extract_error_id' => 'Could not extract type: %s with id %s', - 'vector_invalid' => 'Invalid vector constructor: ', + 'type_extract_error_id' => 'Could not extract type: %s with id %s, you should update MadelineProto!', 'constructor_not_found' => 'Constructor not found for type: ', 'rand_bytes_too_small' => 'Random_bytes is too small!', 'botapi_conversion_error' => 'Can\'t convert %s to a bot API object', 'non_text_conversion' => 'Can\'t convert non text messages yet!', 'last_byte_invalid' => 'Invalid last byte', 'file_type_invalid' => 'Invalid file type detected (%s)', - 'recreate_temp_auth_key' => 'I had to recreate the temporary authorization key', - 'resetting_auth_key' => 'WARNING: Resetting auth key...', - 'shutting_down_reader_pool' => 'Shutting down reader pool ', - 'shutting_down_handler_pool' => 'Shutting down handler pool for dc %s, %d jobs left', 'secret_chat_skipping' => 'I do not have the secret chat %s in the database, skipping message...', 'fingerprint_mismatch' => 'Key fingerprint mismatch', 'msg_data_length_too_big' => 'Message_data_length is too big', 'length_not_divisible_16' => 'Length of decrypted data is not divisible by 16', - 'msg_key_mismatch' => 'Msg_key mismatch', - 'rand_bytes_too_short' => 'Random_bytes is too short!', - 'resending_unsupported' => 'Resending of messages is not yet supported', - 'unrecognized_dec_msg' => 'Unrecognized decrypted message received: ', ], ]; // THIS WILL BE OVERWRITTEN BY $lang["en"] public static $current_lang = [ - 'req_pq' => 'Requesting pq...', + 'apiChooseManualAuto' => 'You did not define a valid API ID/API hash. Do you want to define it now manually, or automatically? (m/a)', + 'apiChooseManualAutoTip' => 'Note that you can also provide the API parameters directly in the code using the settings: %s', + 'apiChoosePrompt' => 'Your choice (m/a): ', + 'apiManualInstructions0' => 'Login to my.telegram.org', + 'apiManualInstructions1' => 'Go to API development tools', + 'apiManualInstructions2' => 'Click on create application', + 'apiAppInstructionsManual0' => 'your app\'s name, can be anything', + 'apiAppInstructionsManual1' => 'your app\'s short name, alphanumeric', + 'apiAppInstructionsManual2' => 'your app/website\'s URL, or t.me/yourusername', + 'apiAppInstructionsManual3' => 'anything', + 'apiAppInstructionsManual4' => 'Describe your app here', + 'apiManualPrompt0' => 'Enter your API ID: ', + 'apiManualPrompt1' => 'Enter your API hash: ', + 'apiAutoPrompt0' => 'Enter a phone number that is already registered on Telegram: ', + 'apiAutoPrompt1' => 'Enter the verification code you received in Telegram: ', + 'apiAppInstructionsAuto0' => 'Enter the app\'s name, can be anything: ', + 'apiAppInstructionsAuto1' => 'Enter the app\'s short name, alphanumeric: ', + 'apiAppInstructionsAuto2' => 'Enter the app/website\'s URL, or t.me/yourusername: ', + 'apiAppInstructionsAuto3' => 'Enter the app platform: ', + 'apiAppInstructionsAuto4' => 'Describe your app: ', + 'apiParamsError' => 'You didn\'t provide all of the required parameters!', + 'apiError' => 'ERROR: %s. Try again.', + 'loginChoosePrompt' => 'Do you want to login as user or bot (u/b)? ', + 'loginBot' => 'Enter your bot token: ', + 'loginUser' => 'Enter your phone number: ', + 'loginUserCode' => 'Enter the phone code: ', + 'loginUser2FA' => 'Enter your password (hint %s): ', + 'loginUser2FAWeb' => 'Enter your password: ', + 'loginUser2FAHint' => 'Hint: %s', + 'signupFirstName' => 'Enter your first name: ', + 'signupLastName' => 'Enter your last name (can be empty): ', + 'signupWeb' => 'Sign up please', + 'signupFirstNameWeb' => 'First name', + 'signupLastNameWeb' => 'Last name', + 'loginNoCode' => 'You didn\'t provide a phone code!', + 'loginNoPass' => 'You didn\'t provide the password!', + 'loginNoName' => 'You didn\'t provide the first name!', 'done' => 'Done!', 'cdn_reupload' => 'File is not stored on CDN, requesting reupload!', 'stored_on_cdn' => 'File is stored on CDN!', - 'serializing_madelineproto' => 'Serializing MadelineProto...', 'phpseclib_fork' => 'Please install this fork of phpseclib: https://github.com/danog/phpseclib', - 'inst_dc' => 'Istantiating DataCenter...', - 'load_rsa' => 'Loading RSA keys...', - 'TL_translation' => 'Translating TL schemas...', - 'dh_prime_check_0' => 'Executing dh_prime checks (0/3)...', 'nearest_dc' => 'We\'re in %s, nearest DC is %d.', 'serialization_ofd' => 'Serialization is out of date, reconstructing object!', 'getupdates_deserialization' => 'Getting updates after deserialization...', - 'shutdown_reader_pool' => 'Shutting down reader pool, %d jobs left', - 'threading_on' => 'THREADING IS ENABLED', - 'socket_reader' => 'Socket reader on DC %s: ', - 'socket_status_1' => 'CREATING', - 'socket_status_2' => 'SUBMITTING', - 'socket_status_3' => 'WAITING', - 'socket_status_4' => 'READY', - 'socket_status_5' => 'WORKING', 'api_not_set' => 'You must provide an api key and an api id, get your own @ my.telegram.org', 'session_corrupted' => 'The session is corrupted!', - 'resetSession_seqno' => 'Resetting session id and seq_no in DC %s...', 'gen_perm_auth_key' => 'Generating permanent authorization key for DC %s...', 'gen_temp_auth_key' => 'Generating temporary authorization key for DC %s...', - 'copy_auth_dcs' => 'Copying authorization from DC %s to DC %s...', 'write_client_info' => 'Writing client info (also executing %s)...', 'config_updated' => 'Updated config!', 'length_not_4' => 'Length is not equal to 4', @@ -348,29 +353,12 @@ class Lang 'encode_double_error' => 'Could not properly encode double', 'file_not_exist' => 'File does not exist', 'deserialization_error' => 'An error occurred on deserialization', - 'rsa_init' => 'Istantiating \\tgseclib\\Crypt\\RSA...', - 'loading_key' => 'Loading key...', - 'computing_fingerprint' => 'Computing fingerprint...', - 'rsa_encrypting' => 'Encrypting with rsa key...', 'rpc_tg_error' => 'Telegram returned an RPC error: %s (%s), caused by %s:%s%sTL trace:', 'v_error' => '506c656173652075706461746520746f20746865206c61746573742076657273696f6e206f66204d6164656c696e6550726f746f2e', 'v_tgerror' => '506c6561736520757064617465207068702d6c69627467766f6970', - 'no_mode_specified' => 'No mode was specified!', - 'constructor_function_uncalled' => 'The constructor function wasn\'t called! Please call the constructor function before using this method.', - 'proxy_class_invalid' => 'Invalid proxy class provided!', - 'socket_con_error' => 'Connection: couldn\'t connect to socket.', - 'protocol_not_implemented' => 'Connection: This protocol isn\'t implemented yet.', 'protocol_invalid' => 'Connection: invalid protocol specified.', - 'nothing_in_socket' => 'Nothing in the socket!', - 'wrong_length_read' => 'WARNING: Wrong length was read (should\'ve read %s, read %s)!', - 'no_data_in_socket' => 'No data in the socket!', - 'dc_con_start' => 'Connecting to DC %s...', - 'dc_con_stop' => 'Disconnecting from DC %s...', - 'dc_con_test_start' => 'Connecting to DC %s (%s server, %s, %s)...', 'script_not_exist' => 'Provided script does not exist', - 'apifactory_start' => 'Running APIFactory...', 'madelineproto_ready' => 'MadelineProto is ready!', - 'logout_error' => 'An error occurred while logging out!', 'logout_ok' => 'Logged out successfully!', 'already_loggedIn' => 'This instance of MadelineProto is already logged in. Logging out first...', 'login_ok' => 'Logged in successfully!', @@ -387,10 +375,8 @@ class Lang 'signing_up' => 'Signing up as a normal user...', 'signup_ok' => 'Signed up in successfully!', '2fa_uncalled' => 'I\'m not waiting for the password! Please call the phoneLogin and the completePhoneLogin methods first!', - 'getting_dialogs' => 'Getting dialogs...', 'libtgvoip_required' => 'The php-libtgvoip extension is required to accept and manage calls. See daniil.it/MadelineProto for more info.', 'peer_not_in_db' => 'This peer is not present in the internal peer database', - 'calling_user' => 'Calling %s...', 'generating_a' => 'Generating a...', 'generating_g_a' => 'Generating g_a...', 'call_error_1' => 'Could not find and accept call %s', @@ -420,32 +406,22 @@ class Lang 'long_not_64' => 'Given value is not 64 bytes long', 'array_invalid' => 'You didn\'t provide a valid array', 'predicate_not_set' => 'Predicate (value under _) was not set!', - 'type_extract_error' => 'Could not extract type "%s"', + 'type_extract_error' => 'Could not extract type "%s", you should update MadelineProto!', 'method_not_found' => 'Could not find method: ', 'params_missing' => 'Missing required parameter', 'sec_peer_not_in_db' => 'This secret peer is not present in the internal peer database', 'stream_handle_invalid' => 'An invalid stream handle was provided.', 'length_too_big' => 'Length is too big', - 'deserialize_not_str' => 'Deserialize: Generated value isn\'t a string', - 'type_extract_error_id' => 'Could not extract type: %s with id %s', - 'vector_invalid' => 'Invalid vector constructor: ', + 'type_extract_error_id' => 'Could not extract type: %s with id %s, you should update MadelineProto!', 'constructor_not_found' => 'Constructor not found for type: ', 'rand_bytes_too_small' => 'Random_bytes is too small!', 'botapi_conversion_error' => 'Can\'t convert %s to a bot API object', 'non_text_conversion' => 'Can\'t convert non text messages yet!', 'last_byte_invalid' => 'Invalid last byte', 'file_type_invalid' => 'Invalid file type detected (%s)', - 'recreate_temp_auth_key' => 'I had to recreate the temporary authorization key', - 'resetting_auth_key' => 'WARNING: Resetting auth key...', - 'shutting_down_reader_pool' => 'Shutting down reader pool ', - 'shutting_down_handler_pool' => 'Shutting down handler pool for dc %s, %d jobs left', 'secret_chat_skipping' => 'I do not have the secret chat %s in the database, skipping message...', 'fingerprint_mismatch' => 'Key fingerprint mismatch', 'msg_data_length_too_big' => 'Message_data_length is too big', 'length_not_divisible_16' => 'Length of decrypted data is not divisible by 16', - 'msg_key_mismatch' => 'Msg_key mismatch', - 'rand_bytes_too_short' => 'Random_bytes is too short!', - 'resending_unsupported' => 'Resending of messages is not yet supported', - 'unrecognized_dec_msg' => 'Unrecognized decrypted message received: ', ]; } diff --git a/src/danog/MadelineProto/Logger.php b/src/danog/MadelineProto/Logger.php index 8b563ebf..830e4f6b 100644 --- a/src/danog/MadelineProto/Logger.php +++ b/src/danog/MadelineProto/Logger.php @@ -23,6 +23,7 @@ use Amp\ByteStream\ResourceOutputStream; use Amp\Failure; use Amp\Loop; use danog\MadelineProto\Settings\Logger as SettingsLogger; +use Psr\Log\LoggerInterface; use function Amp\ByteStream\getStderr; use function Amp\ByteStream\getStdout; @@ -94,6 +95,10 @@ class Logger * Log rotation loop ID. */ private string $rotateId = ''; + /** + * PSR logger. + */ + private PsrLogger $psr; /** * Ultra verbose logging. */ @@ -171,6 +176,7 @@ class Logger */ public function __construct(SettingsLogger $settings, string $prefix = '') { + $this->psr = new PsrLogger($this); $this->prefix = $prefix === '' ? '' : ', '.$prefix; $this->mode = $settings->getType(); @@ -237,7 +243,7 @@ class Logger if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') { try { \error_reporting(E_ALL); - \ini_set('log_errors', 1); + \ini_set('log_errors', "1"); \ini_set('error_log', $this->mode === self::FILE_LOGGER ? $this->optional : Magic::$script_cwd.'/MadelineProto.log'); @@ -327,4 +333,14 @@ class Logger } } } + + /** + * Get PSR logger. + * + * @return LoggerInterface + */ + public function getPsrLogger(): LoggerInterface + { + return $this->psr; + } } diff --git a/src/danog/MadelineProto/MTProto.php b/src/danog/MadelineProto/MTProto.php index 43f2d9a6..95996c6e 100644 --- a/src/danog/MadelineProto/MTProto.php +++ b/src/danog/MadelineProto/MTProto.php @@ -45,6 +45,7 @@ use danog\MadelineProto\Settings\Database\Memory; use danog\MadelineProto\Settings\TLSchema; use danog\MadelineProto\TL\TL; use danog\MadelineProto\TL\TLCallback; +use Psr\Log\LoggerInterface; use function Amp\File\exists; use function Amp\File\size; @@ -532,19 +533,16 @@ class MTProto extends AsyncConstruct implements TLCallback // Initialize needed stuffs Magic::classExists(); // Parse and store settings - yield from $this->updateSettings($settings, false); + $this->updateSettingsInternal($settings); // Actually instantiate needed classes like a boss - $this->logger->logger(Lang::$current_lang['inst_dc'], Logger::ULTRA_VERBOSE); yield from $this->cleanupProperties(); // Load rsa keys - $this->logger->logger(Lang::$current_lang['load_rsa'], Logger::ULTRA_VERBOSE); $this->rsa_keys = []; foreach ($this->settings->getAuth()->getRsaKeys() as $key) { $key = (yield from (new RSA())->load($this->TL, $key)); $this->rsa_keys[$key->fp] = $key; } // (re)-initialize TL - $this->logger->logger(Lang::$current_lang['TL_translation'], Logger::ULTRA_VERBOSE); $callbacks = [$this, $this->referenceDatabase]; if (!($this->authorization['user']['bot'] ?? false)) { $callbacks[] = $this->minDatabase; @@ -570,6 +568,7 @@ class MTProto extends AsyncConstruct implements TLCallback $this->startUpdateSystem(true); $this->v = self::V; + $this->settings->applyChanges(); GarbageCollector::start(); } /** @@ -734,13 +733,18 @@ class MTProto extends AsyncConstruct implements TLCallback } /** * Get logger. - * - * @return Logger */ public function getLogger(): Logger { return $this->logger; } + /** + * Get PSR logger. + */ + public function getPsrLogger(): LoggerInterface + { + return $this->logger->getPsrLogger(); + } /** * Get async HTTP client. * @@ -828,6 +832,7 @@ class MTProto extends AsyncConstruct implements TLCallback } if (!$this->ipcServer) { $this->ipcServer = new Server($this); + $this->ipcServer->setSettings($this->settings->getIpc()); $this->ipcServer->setIpcPath($this->wrapper->session); } $this->callCheckerLoop->start(); @@ -933,7 +938,6 @@ class MTProto extends AsyncConstruct implements TLCallback } if (!isset($this->TL)) { $this->TL = new TL($this); - $this->logger->logger(Lang::$current_lang['TL_translation'], Logger::ULTRA_VERBOSE); $callbacks = [$this, $this->referenceDatabase]; if (!($this->authorization['user']['bot'] ?? false)) { $callbacks[] = $this->minDatabase; @@ -1075,7 +1079,7 @@ class MTProto extends AsyncConstruct implements TLCallback // Reset MTProto session (not related to user session) $this->resetMTProtoSession(); // Update settings from constructor - yield from $this->updateSettings($settings, false); + $this->updateSettingsInternal($settings); // Session update process for BC $forceDialogs = false; if (!isset($this->v) @@ -1178,12 +1182,40 @@ class MTProto extends AsyncConstruct implements TLCallback /** * Parse, update and store settings. * - * @param Settings|SettingsEmpty $settings Settings - * @param bool $reinit Whether to reinit the instance + * @param SettingsAbstract $settings Settings * * @return \Generator */ - public function updateSettings(SettingsAbstract $settings, bool $reinit = true): \Generator + public function updateSettings(SettingsAbstract $settings): \Generator + { + $this->updateSettingsInternal($settings); + + if ($this->settings->getDb()->hasChanged()) { + yield from $this->initDb($this); + $this->settings->getDb()->applyChanges(); + } + if ($this->settings->getIpc()->hasChanged()) { + $this->ipcServer->setSettings($this->settings->getIpc()->applyChanges()); + } + if ($this->settings->getSerialization()->hasChanged()) { + $this->serializeLoop->signal(true); + $this->serializeLoop = new PeriodicLoopInternal($this, [$this, 'serialize'], 'serialize', $this->settings->getSerialization()->applyChanges()->getInterval() * 1000); + } + if ($this->settings->getAuth()->hasChanged() + || $this->settings->getConnection()->hasChanged() + || $this->settings->getSchema()->hasChanged() + || $this->settings->getSchema()->needsUpgrade()) { + yield from $this->__construct_async($this->settings); + } + } + /** + * Parse, update and store settings. + * + * @param SettingsAbstract $settings Settings + * + * @return void + */ + private function updateSettingsInternal(SettingsAbstract $settings): void { if ($settings instanceof SettingsEmpty) { if (!isset($this->settings)) { @@ -1208,10 +1240,8 @@ class MTProto extends AsyncConstruct implements TLCallback } // Setup logger - $this->setupLogger(); - - if ($reinit) { - yield from $this->__construct_async($this->settings); + if ($this->settings->getLogger()->hasChanged() || !$this->logger) { + $this->setupLogger(); } } /** diff --git a/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php b/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php index 17ef3038..37a7bca1 100644 --- a/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php @@ -69,7 +69,7 @@ trait AuthKeyHandler $req_pq = $cdn ? 'req_pq' : 'req_pq_multi'; for ($retry_id_total = 1; $retry_id_total <= $this->settings->getAuth()->getMaxAuthTries(); $retry_id_total++) { try { - $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['req_pq'], \danog\MadelineProto\Logger::VERBOSE); + $this->logger->logger("Requesting pq...", \danog\MadelineProto\Logger::VERBOSE); /** * *********************************************************************** * Make pq request, DH exchange initiation. diff --git a/src/danog/MadelineProto/PsrLogger.php b/src/danog/MadelineProto/PsrLogger.php new file mode 100644 index 00000000..5cf6c94a --- /dev/null +++ b/src/danog/MadelineProto/PsrLogger.php @@ -0,0 +1,48 @@ + Logger::LEVEL_FATAL, + LogLevel::ALERT => Logger::LEVEL_FATAL, + LogLevel::CRITICAL => Logger::LEVEL_FATAL, + LogLevel::ERROR => Logger::LEVEL_ERROR, + LogLevel::WARNING => Logger::LEVEL_WARNING, + LogLevel::NOTICE => Logger::LEVEL_NOTICE, + LogLevel::INFO => Logger::LEVEL_VERBOSE, + LogLevel::DEBUG => Logger::LEVEL_ULTRA_VERBOSE + ]; + /** + * Logger. + */ + private Logger $logger; + /** + * Constructor. + * + * @param Logger $logger + */ + public function __construct(Logger $logger) + { + $this->logger = $logger; + } + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param mixed[] $context + * + * @return void + * + * @throws \Psr\Log\InvalidArgumentException + */ + public function log($level, $message, array $context = []) + { + $this->logger->logger($message, self::LEVEL_MAP[$level]); + } +} diff --git a/src/danog/MadelineProto/RSA.php b/src/danog/MadelineProto/RSA.php index c2a25376..4cd7ce42 100644 --- a/src/danog/MadelineProto/RSA.php +++ b/src/danog/MadelineProto/RSA.php @@ -55,12 +55,9 @@ class RSA */ public function load(TL $TL, string $rsa_key): \Generator { - \danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['rsa_init'], Logger::ULTRA_VERBOSE); - \danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['loading_key'], Logger::ULTRA_VERBOSE); $key = \tgseclib\Crypt\RSA::load($rsa_key); $this->n = Tools::getVar($key, 'modulus'); $this->e = Tools::getVar($key, 'exponent'); - \danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['computing_fingerprint'], Logger::ULTRA_VERBOSE); $this->fp = \substr(\sha1((yield from $TL->serializeObject(['type' => 'bytes'], $this->n->toBytes(), 'key')).(yield from $TL->serializeObject(['type' => 'bytes'], $this->e->toBytes(), 'key')), true), -8); return $this; } diff --git a/src/danog/MadelineProto/SecretChats/MessageHandler.php b/src/danog/MadelineProto/SecretChats/MessageHandler.php index e39b1037..d8c7d12b 100644 --- a/src/danog/MadelineProto/SecretChats/MessageHandler.php +++ b/src/danog/MadelineProto/SecretChats/MessageHandler.php @@ -135,7 +135,7 @@ trait MessageHandler throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['msg_data_length_too_big']); } if ($message_key != \substr(\sha1(\substr($decrypted_data, 0, 4 + $message_data_length), true), -16)) { - throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['msg_key_mismatch']); + throw new \danog\MadelineProto\SecurityException('Msg_key mismatch'); } if (\strlen($decrypted_data) - 4 - $message_data_length > 15) { throw new \danog\MadelineProto\SecurityException('difference between message_data_length and the length of the remaining decrypted buffer is too big'); @@ -155,7 +155,7 @@ trait MessageHandler 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)) { - throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['msg_key_mismatch']); + throw new \danog\MadelineProto\SecurityException('Msg_key mismatch'); } if (\strlen($decrypted_data) - 4 - $message_data_length < 12) { throw new \danog\MadelineProto\SecurityException('padding is too small'); diff --git a/src/danog/MadelineProto/SecretChats/ResponseHandler.php b/src/danog/MadelineProto/SecretChats/ResponseHandler.php index cf8d38fd..a844da29 100644 --- a/src/danog/MadelineProto/SecretChats/ResponseHandler.php +++ b/src/danog/MadelineProto/SecretChats/ResponseHandler.php @@ -26,9 +26,6 @@ trait ResponseHandler { private function handleDecryptedUpdate($update): \Generator { - /*if (isset($update['message']['decrypted_message']['random_bytes']) && strlen($update['message']['decrypted_message']['random_bytes']) < 15) { - throw new \danog\MadelineProto\ResponseException(\danog\MadelineProto\Lang::$current_lang['rand_bytes_too_short']); - }*/ // already checked in TL.php switch ($update['message']['decrypted_message']['_']) { case 'decryptedMessageService': @@ -92,7 +89,7 @@ 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('Unrecognized decrypted message received: '.\var_export($update, true)); break; } } diff --git a/src/danog/MadelineProto/Settings.php b/src/danog/MadelineProto/Settings.php index 6be169d9..910b0bd3 100644 --- a/src/danog/MadelineProto/Settings.php +++ b/src/danog/MadelineProto/Settings.php @@ -11,6 +11,7 @@ use danog\MadelineProto\Settings\Database\Postgres; use danog\MadelineProto\Settings\Database\Redis; use danog\MadelineProto\Settings\DatabaseAbstract; use danog\MadelineProto\Settings\Files; +use danog\MadelineProto\Settings\Ipc; use danog\MadelineProto\Settings\Logger; use danog\MadelineProto\Settings\Peer; use danog\MadelineProto\Settings\Pwr; @@ -37,6 +38,10 @@ class Settings extends SettingsAbstract * File management settings. */ protected Files $files; + /** + * IPC server settings. + */ + protected Ipc $ipc; /** * Logger settings. */ @@ -106,6 +111,7 @@ class Settings extends SettingsAbstract $this->serialization = new Serialization; $this->schema = new TLSchema; $this->db = new DatabaseMemory; + $this->ipc = new IPc; } /** * Merge legacy array settings. @@ -177,6 +183,8 @@ class Settings extends SettingsAbstract $this->serialization->merge($settings); } elseif ($settings instanceof TLSchema) { $this->schema->merge($settings); + } elseif ($settings instanceof Ipc) { + $this->ipc->merge($settings); } elseif ($settings instanceof DatabaseAbstract) { if (!$this->db instanceof $settings) { $this->db = $settings; @@ -197,6 +205,7 @@ class Settings extends SettingsAbstract $this->secretChats->merge($settings->secretChats); $this->serialization->merge($settings->serialization); $this->schema->merge($settings->schema); + $this->ipc->merge($settings->ipc); if (!$this->db instanceof $settings->db) { $this->db = $settings->db; @@ -524,4 +533,36 @@ class Settings extends SettingsAbstract return $this; } + + /** + * Get IPC server settings. + * + * @return Ipc + */ + public function getIpc(): Ipc + { + return $this->ipc; + } + + /** + * Set IPC server settings. + * + * @param Ipc $ipc IPC server settings. + * + * @return self + */ + public function setIpc(Ipc $ipc): self + { + $this->ipc = $ipc; + + return $this; + } + + public function applyChanges(): SettingsAbstract + { + foreach (\get_object_vars($this) as $setting) { + $setting->applyChanges(); + } + return $this; + } } diff --git a/src/danog/MadelineProto/Settings/Ipc.php b/src/danog/MadelineProto/Settings/Ipc.php new file mode 100644 index 00000000..19dcafa1 --- /dev/null +++ b/src/danog/MadelineProto/Settings/Ipc.php @@ -0,0 +1,43 @@ +forceFull; + } + + /** + * Set WARNING: this will cause slow startup if enabled. + * + * @param bool $forceFull WARNING: this will cause slow startup if enabled. + * + * @return self + */ + public function setForceFull(bool $forceFull): self + { + $this->forceFull = $forceFull; + + return $this; + } +} diff --git a/src/danog/MadelineProto/Settings/Serialization.php b/src/danog/MadelineProto/Settings/Serialization.php index ae884c17..8da90224 100644 --- a/src/danog/MadelineProto/Settings/Serialization.php +++ b/src/danog/MadelineProto/Settings/Serialization.php @@ -10,12 +10,6 @@ class Serialization extends SettingsAbstract * Serialization interval, in seconds. */ protected int $interval = 30; - /** - * Whether to force full deserialization of instance, without using the IPC server/client. - * - * WARNING: this will cause slow startup if enabled. - */ - protected bool $forceFull = false; public function mergeArray(array $settings): void { @@ -46,28 +40,4 @@ class Serialization extends SettingsAbstract return $this; } - - /** - * Get WARNING: this will cause slow startup if enabled. - * - * @return bool - */ - public function getForceFull(): bool - { - return $this->forceFull; - } - - /** - * Set WARNING: this will cause slow startup if enabled. - * - * @param bool $forceFull WARNING: this will cause slow startup if enabled. - * - * @return self - */ - public function setForceFull(bool $forceFull): self - { - $this->forceFull = $forceFull; - - return $this; - } } diff --git a/src/danog/MadelineProto/Settings/Templates.php b/src/danog/MadelineProto/Settings/Templates.php new file mode 100644 index 00000000..bf656a4b --- /dev/null +++ b/src/danog/MadelineProto/Settings/Templates.php @@ -0,0 +1,61 @@ +MadelineProto

MadelineProto

%s

%s
'; + /** + * Prompt user to choose manual or automatic API ID generation. + */ + protected string $apiChooseManualAuto = 'You did not define a valid API ID/API hash. Do you want to define it now manually, or automatically? (m/a)'; + /** + * Settings tip for API ID generation. + */ + protected string $apiChooseManualAutoTip = 'Note that you can also provide the API parameters directly in the code using the settings: %s'; + /** + * Final prompt to choose mode. + */ + protected string $apiChoosePrompt = 'Your choice (m/a): '; + /** + * Instructions for manual API ID generation. + * + * @var array{0: string, 1: string, 2: array{0: string, 1: string, 2: string, 3: string, 4: string}, 3: string} + */ + protected array $apiManualInstructions = [ + 'Login to my.telegram.org', + 'Go to API development tools', + [ + 'App title: your app\'s name, can be anything', + 'Short name: your app\'s short name, can be anything', + 'URL: your app/website\'s URL, or t.me/yourusername', + 'Platform: anything', + 'Description: Describe your app here', + ], + 'Click on create application' + ]; + /** + * Manual API ID/hash prompts. + */ + protected array $apiManualPrompts = [ + 'Enter your API ID: ', + 'Enter your API hash: ' + ]; + /** + * Auto API ID/hash prompts. + */ + protected array $apiAutoPrompts = [ + 'Enter a phone number that is already registered on Telegram: ', + 'Enter the verification code you received in Telegram: ', + 'Enter the app\'s name, can be anything: ', + 'Enter the app\'s short name, can be anything: ', + 'Enter the app/website\'s URL, or t.me/yourusername: ', + 'Describe your app: ', + ]; +} diff --git a/src/danog/MadelineProto/SettingsAbstract.php b/src/danog/MadelineProto/SettingsAbstract.php index 27e105ce..3061458d 100644 --- a/src/danog/MadelineProto/SettingsAbstract.php +++ b/src/danog/MadelineProto/SettingsAbstract.php @@ -7,6 +7,12 @@ use ReflectionProperty; abstract class SettingsAbstract { + /** + * Whether this setting was changed. + * + * @var boolean + */ + protected $changed = true; /** * Merge legacy settings array. * @@ -29,13 +35,16 @@ abstract class SettingsAbstract $defaults = $class->getDefaultProperties(); foreach ($class->getProperties(ReflectionProperty::IS_PROTECTED|ReflectionProperty::IS_PUBLIC) as $property) { $name = $property->getName(); + $uc = \ucfirst($name); if (isset($other->{$name}) && ( !isset($defaults[$name]) || $other->{$name} !== $defaults[$name] // Isn't equal to the default value ) + && $other->{$name} !== $this->{"get$uc"} ) { - $this->{'set'.\ucfirst($name)}($other->{$name}); + $this->{"set$uc"}($other->{$name}); + $this->changed = true; } } } @@ -54,4 +63,24 @@ abstract class SettingsAbstract } return $result; } + + /** + * Get whether this setting was changed, also applies changes. + * + * @return boolean + */ + public function hasChanged(): bool + { + return $this->changed; + } + /** + * Apply changes. + * + * @return static + */ + public function applyChanges(): self + { + $this->changed = false; + return $this; + } } diff --git a/src/danog/MadelineProto/TL/TL.php b/src/danog/MadelineProto/TL/TL.php index a8ad19f9..75553a5e 100644 --- a/src/danog/MadelineProto/TL/TL.php +++ b/src/danog/MadelineProto/TL/TL.php @@ -878,7 +878,7 @@ class TL } } if (!\is_string($x)) { - throw new Exception(\danog\MadelineProto\Lang::$current_lang['deserialize_not_str']); + throw new Exception("Generated value isn't a string"); } return $type['type'] === 'bytes' ? new Types\Bytes($x) : $x; case 'Vector t': @@ -908,7 +908,7 @@ class TL case 'vector': break; default: - throw new Exception(\danog\MadelineProto\Lang::$current_lang['vector_invalid'].$constructorData['predicate']); + throw new Exception('Invalid vector constructor: '.$constructorData['predicate']); } // no break case 'vector': diff --git a/src/danog/MadelineProto/VoIP/AuthKeyHandler.php b/src/danog/MadelineProto/VoIP/AuthKeyHandler.php index 8cc805cd..2ee00b7c 100644 --- a/src/danog/MadelineProto/VoIP/AuthKeyHandler.php +++ b/src/danog/MadelineProto/VoIP/AuthKeyHandler.php @@ -98,7 +98,7 @@ trait AuthKeyHandler throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['peer_not_in_db']); } $user = $user['InputUser']; - $this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['calling_user'], $user['user_id']), \danog\MadelineProto\Logger::VERBOSE); + $this->logger->logger(\sprintf("Calling %s...", $user['user_id']), \danog\MadelineProto\Logger::VERBOSE); $dh_config = (yield from $this->getDhConfig()); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['generating_a'], \danog\MadelineProto\Logger::VERBOSE); $a = \tgseclib\Math\BigInteger::randomRange(\danog\MadelineProto\Magic::$two, $dh_config['p']->subtract(\danog\MadelineProto\Magic::$two)); diff --git a/src/danog/MadelineProto/Wrappers/DialogHandler.php b/src/danog/MadelineProto/Wrappers/DialogHandler.php index 82c76e5a..b6b1bb5f 100644 --- a/src/danog/MadelineProto/Wrappers/DialogHandler.php +++ b/src/danog/MadelineProto/Wrappers/DialogHandler.php @@ -75,7 +75,7 @@ trait DialogHandler $res = ['dialogs' => [0], 'count' => 1]; $datacenter = $this->datacenter->curdc; $dialogs = []; - $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['getting_dialogs']); + $this->logger->logger("Getting dialogs..."); while ($this->dialog_params['count'] < $res['count']) { $res = yield from $this->methodCallAsyncRead('messages.getDialogs', $this->dialog_params, ['datacenter' => $datacenter, 'FloodWaitLimit' => 100]); $last_peer = 0; diff --git a/tools/translator.php b/tools/translator.php index 98815176..e472abeb 100644 --- a/tools/translator.php +++ b/tools/translator.php @@ -1,5 +1,7 @@ &$currentLang) { + if ($code === 'en') { + continue; + } + $currentLang = \array_intersect_key($currentLang, Lang::$lang['en']); +} + $lang_code = \readline('Enter the language you whish to localize: '); -if (!isset(\danog\MadelineProto\Lang::$lang[$lang_code])) { - \danog\MadelineProto\Lang::$lang[$lang_code] = \danog\MadelineProto\Lang::$current_lang; +if (!isset(Lang::$lang[$lang_code])) { + Lang::$lang[$lang_code] = Lang::$current_lang; echo 'New language detected!'.PHP_EOL.PHP_EOL; } else { echo 'Completing localization of existing language'.PHP_EOL.PHP_EOL; } -$count = \count(\danog\MadelineProto\Lang::$lang[$lang_code]); +$count = \count(Lang::$lang[$lang_code]); $curcount = 0; -\ksort(\danog\MadelineProto\Lang::$current_lang); -foreach (\danog\MadelineProto\Lang::$current_lang as $key => $value) { - if (!isset(\danog\MadelineProto\Lang::$lang[$lang_code][$key])) { - \danog\MadelineProto\Lang::$lang[$lang_code][$key] = $value; - } - \preg_match('/^[^_]+_(.*?)(?:_param_(.*)_type_(.*))?$/', $key, $matches); - $method_name = isset($matches[1]) ? $matches[1] : ''; - $param_name = isset($matches[2]) ? $matches[2] : ''; - $param_type = isset($matches[3]) ? $matches[3] : ''; - if (isset(\danog\MadelineProto\MTProto::DISALLOWED_METHODS[$method_name])) { - \danog\MadelineProto\Lang::$lang[$lang_code][$key] = \danog\MadelineProto\MTProto::DISALLOWED_METHODS[$method_name]; +\ksort(Lang::$current_lang); +foreach (Lang::$current_lang as $key => $value) { + if (!isset(Lang::$lang[$lang_code][$key])) { + Lang::$lang[$lang_code][$key] = $value; } - if (\danog\MadelineProto\Lang::$lang[$lang_code][$key] === $value && ( - $lang_code !== 'en' || $value == '' || - \strpos($value, 'You cannot use this method directly') === 0 || - \strpos($value, 'Update ') === 0 || - \ctype_lower($value[0]) + if (Lang::$lang[$lang_code][$key] === $value && ( + $lang_code !== 'en' || $value == '' )) { - $value = \danog\MadelineProto\Lang::$lang[$lang_code][$key]; + $value = Lang::$lang[$lang_code][$key]; if (\in_array($key, ['v_error', 'v_tgerror'])) { $value = \hex2bin($value); } if ($value == '') { $value = $key; } - \preg_match('/^[^_]+_(.*?)(?:_param_(.*)_type_(.*))?$/', $key, $matches); - $method_name = isset($matches[1]) ? $matches[1] : ''; - $param_name = isset($matches[2]) ? $matches[2] : ''; - $param_type = isset($matches[3]) ? $matches[3] : ''; - if (isset(\danog\MadelineProto\MTProto::DISALLOWED_METHODS[$method_name])) { - \danog\MadelineProto\Lang::$lang[$lang_code][$key] = \danog\MadelineProto\MTProto::DISALLOWED_METHODS[$method_name]; - } + Lang::$lang[$lang_code][$key] = \readline($value.' => '); /* if ($param_name === 'nonce' && $param_type === 'int128') { - \danog\MadelineProto\Lang::$lang[$lang_code][$key] = 'Random number for cryptographic security'; + Lang::$lang[$lang_code][$key] = 'Random number for cryptographic security'; } elseif ($param_name === 'server_nonce' && $param_type === 'int128') { - \danog\MadelineProto\Lang::$lang[$lang_code][$key] = 'Random number for cryptographic security, given by server'; + Lang::$lang[$lang_code][$key] = 'Random number for cryptographic security, given by server'; } elseif ($param_name === 'random_id' && $param_type === 'long') { - \danog\MadelineProto\Lang::$lang[$lang_code][$key] = 'Random number for cryptographic security'; + Lang::$lang[$lang_code][$key] = 'Random number for cryptographic security'; } else elseif (\strpos($value, 'Update ') === 0) { if (!$param_name && \strpos($key, 'object_') === 0) { $value = \str_replace('Update ', '', $value).' update'; } //} elseif (ctype_lower($value[0])) { } else { - \danog\MadelineProto\Lang::$lang[$lang_code][$key] = \readline($value.' => '); - if (\danog\MadelineProto\Lang::$lang[$lang_code][$key] === '') { + Lang::$lang[$lang_code][$key] = \readline($value.' => '); + if (Lang::$lang[$lang_code][$key] === '') { if ($param_name) { $l = \str_replace('_', ' ', $param_name); } else { @@ -115,18 +108,18 @@ foreach (\danog\MadelineProto\Lang::$current_lang as $key => $value) { $l .= '?'; } - \danog\MadelineProto\Lang::$lang[$lang_code][$key] = $l; - echo 'Using default value '.\danog\MadelineProto\Lang::$lang[$lang_code][$key].PHP_EOL; + Lang::$lang[$lang_code][$key] = $l; + echo 'Using default value '.Lang::$lang[$lang_code][$key].PHP_EOL; } }*/ - \danog\MadelineProto\Lang::$lang[$lang_code][$key] = \ucfirst(\danog\MadelineProto\Lang::$lang[$lang_code][$key]); + Lang::$lang[$lang_code][$key] = \ucfirst(Lang::$lang[$lang_code][$key]); if (\in_array($key, ['v_error', 'v_tgerror'])) { - \danog\MadelineProto\Lang::$lang[$lang_code][$key] = \bin2hex(\danog\MadelineProto\Lang::$lang[$lang_code][$key]); + Lang::$lang[$lang_code][$key] = \bin2hex(Lang::$lang[$lang_code][$key]); } - \file_put_contents('src/danog/MadelineProto/Lang.php', \sprintf($template, \var_export(\danog\MadelineProto\Lang::$lang, true), \var_export(\danog\MadelineProto\Lang::$lang['en'], true))); + \file_put_contents('src/danog/MadelineProto/Lang.php', \sprintf($template, \var_export(Lang::$lang, true), \var_export(Lang::$lang['en'], true))); echo 'OK, '.($curcount * 100 / $count).'% done. edit src/danog/MadelineProto/Lang.php to fix mistakes.'.PHP_EOL; } $curcount++; } -\file_put_contents('src/danog/MadelineProto/Lang.php', \sprintf($template, \var_export(\danog\MadelineProto\Lang::$lang, true), \var_export(\danog\MadelineProto\Lang::$lang['en'], true))); +\file_put_contents('src/danog/MadelineProto/Lang.php', \sprintf($template, \var_export(Lang::$lang, true), \var_export(Lang::$lang['en'], true))); echo 'OK. edit src/danog/MadelineProto/Lang.php to fix mistakes.'.PHP_EOL;