Add PSR logger, remove CombinedAPI

This commit is contained in:
Daniil Gentili 2020-09-26 17:11:41 +02:00
parent b0bafaf431
commit c9ebdd0fa8
22 changed files with 471 additions and 467 deletions

View File

@ -1,219 +0,0 @@
<?php
/**
* CombinedAPI module.
*
* This file is part of MadelineProto.
* MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
* You should have received a copy of the GNU General Public License along with MadelineProto.
* If not, see <http://www.gnu.org/licenses/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
* @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));
}
}

View File

@ -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);

View File

@ -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.

View File

@ -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 {

View File

@ -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;
}
}

View File

@ -12,7 +12,7 @@
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
* @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: ',
];
}

View File

@ -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;
}
}

View File

@ -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();
}
}
/**

View File

@ -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.

View File

@ -0,0 +1,48 @@
<?php
namespace danog\MadelineProto;
use Psr\Log\AbstractLogger;
use Psr\Log\LogLevel;
class PsrLogger extends AbstractLogger
{
private const LEVEL_MAP = [
LogLevel::EMERGENCY => 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]);
}
}

View File

@ -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;
}

View File

@ -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');

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace danog\MadelineProto\Settings;
use danog\MadelineProto\SettingsAbstract;
class Ipc extends SettingsAbstract
{
/**
* 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
{
}
/**
* 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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace danog\MadelineProto\Settings;
/**
* Web and CLI template settings for login.
*/
class Templates
{
/**
* Web template used for querying app information.
*/
protected string $apiHtmlTemplate = '<!DOCTYPE html><html><head><title>MadelineProto</title></head><body><h1>MadelineProto</h1><p>%s</p><form method="POST">%s<button type="submit"/>Go</button></form></body></html>';
/**
* 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: ',
];
}

View File

@ -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;
}
}

View File

@ -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':

View File

@ -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));

View File

@ -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;

View File

@ -1,5 +1,7 @@
<?php
use danog\MadelineProto\Lang;
require 'vendor/autoload.php';
$template = '<?php
@ -39,64 +41,55 @@ function fromCamelCase($input)
return \implode(' ', $ret);
}
foreach (Lang::$lang as $code => &$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;