Completely refactor settings, remove old (internal and external) APIs, generic code cleanup
This commit is contained in:
parent
26d746d749
commit
0001a45cd2
@ -12,6 +12,7 @@
|
||||
"php": ">=7.4.0",
|
||||
"danog/primemodule": "^1",
|
||||
"erusev/parsedown": "^1.7",
|
||||
"symfony/polyfill-mbstring": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-json": "*",
|
||||
"ext-xml": "*",
|
||||
@ -34,7 +35,6 @@
|
||||
"danog/magicalserializer": "^1.0",
|
||||
"league/uri": "^6",
|
||||
"danog/ipc": "^0.1",
|
||||
"tivie/htaccess-parser": "^0.2.3",
|
||||
"amphp/log": "^1.1",
|
||||
"danog/loop": "^0.1.0",
|
||||
"danog/tgseclib": "^3",
|
||||
|
@ -20,6 +20,7 @@
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
use Amp\Promise;
|
||||
use danog\MadelineProto\Settings\Logger as SettingsLogger;
|
||||
|
||||
/**
|
||||
* Main API wrapper for MadelineProto.
|
||||
@ -94,7 +95,7 @@ class API extends InternalDoc
|
||||
/**
|
||||
* Global session unlock callback.
|
||||
*
|
||||
* @var callback
|
||||
* @var callable
|
||||
*/
|
||||
private $unlock;
|
||||
|
||||
@ -103,12 +104,13 @@ class API extends InternalDoc
|
||||
* Magic constructor function.
|
||||
*
|
||||
* @param string $session Session name
|
||||
* @param array $settings Settings
|
||||
* @param array|Settings $settings Settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __magic_construct(string $session, array $settings = []): void
|
||||
public function __magic_construct(string $session, $settings = []): void
|
||||
{
|
||||
$settings = Settings::parseFromLegacy($settings);
|
||||
$this->wrapper = new APIWrapper($this, $this->exportNamespace());
|
||||
|
||||
Magic::classExists();
|
||||
@ -126,13 +128,15 @@ class API extends InternalDoc
|
||||
* Async constructor function.
|
||||
*
|
||||
* @param string $session Session name
|
||||
* @param array $settings Settings
|
||||
* @param Settings|SettingsEmpty $settings Settings
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function __construct_async(string $session, array $settings = []): \Generator
|
||||
public function __construct_async(string $session, SettingsAbstract $settings): \Generator
|
||||
{
|
||||
Logger::constructorFromSettings($settings);
|
||||
Logger::constructorFromSettings($settings instanceof SettingsEmpty
|
||||
? new SettingsLogger
|
||||
: $settings->getLogger());
|
||||
$this->session = $session = Tools::absolute($session);
|
||||
[$unserialized, $this->unlock] = yield from Serialization::legacyUnserialize($session);
|
||||
if ($unserialized) {
|
||||
@ -146,25 +150,28 @@ class API extends InternalDoc
|
||||
|
||||
unset($unserialized);
|
||||
|
||||
$this->API->wrapper = $this->wrapper;
|
||||
yield from $this->API->initAsynchronously();
|
||||
yield from $this->API->wakeup($settings, $this->wrapper);
|
||||
$this->APIFactory();
|
||||
$this->logger->logger(Lang::$current_lang['madelineproto_ready'], Logger::NOTICE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($settings['app_info']['api_id']) || !$settings['app_info']['api_id']) {
|
||||
$app = (yield from $this->APIStart($settings));
|
||||
if ($settings instanceof SettingsEmpty) {
|
||||
$settings = new Settings;
|
||||
}
|
||||
|
||||
$appInfo = $settings->getAppInfo();
|
||||
if (!$appInfo->hasApiInfo()) {
|
||||
$app = yield from $this->APIStart($settings);
|
||||
if (!$app) {
|
||||
$this->forceInit(true);
|
||||
die();
|
||||
}
|
||||
$settings['app_info']['api_id'] = $app['api_id'];
|
||||
$settings['app_info']['api_hash'] = $app['api_hash'];
|
||||
$appInfo->setApiId($app['api_id']);
|
||||
$appInfo->setApiHash($app['api_hash']);
|
||||
}
|
||||
$this->API = new MTProto($settings);
|
||||
$this->API->wrapper = $this->wrapper;
|
||||
$this->API = new MTProto($settings, $this->wrapper);
|
||||
yield from $this->API->initAsynchronously();
|
||||
$this->APIFactory();
|
||||
$this->logger->logger(Lang::$current_lang['madelineproto_ready'], Logger::NOTICE);
|
||||
@ -218,10 +225,6 @@ class API extends InternalDoc
|
||||
}
|
||||
}
|
||||
$this->methods = self::getInternalMethodList($this->API);
|
||||
$this->API->wrapper = $this->wrapper;
|
||||
if ($this->API->event_handler && \class_exists($this->API->event_handler) && \is_subclass_of($this->API->event_handler, EventHandler::class)) {
|
||||
$this->API->setEventHandler($this->API->event_handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,6 +291,7 @@ class API extends InternalDoc
|
||||
*/
|
||||
public function startAndLoopAsync(string $eventHandler): \Generator
|
||||
{
|
||||
$errors = [];
|
||||
$this->async(true);
|
||||
while (true) {
|
||||
try {
|
||||
@ -295,6 +299,13 @@ class API extends InternalDoc
|
||||
yield $this->setEventHandler($eventHandler);
|
||||
return yield from $this->API->loop();
|
||||
} catch (\Throwable $e) {
|
||||
$errors = [\time() => $errors[\time()] ?? 0];
|
||||
$errors[\time()]++;
|
||||
if ($errors[\time()] > 100 && !$this->inited()) {
|
||||
$this->logger->logger("More than 100 errors in a second and not inited, exiting!", Logger::FATAL_ERROR);
|
||||
return;
|
||||
}
|
||||
echo $e;
|
||||
$this->logger->logger((string) $e, Logger::FATAL_ERROR);
|
||||
$this->report("Surfaced: $e");
|
||||
}
|
||||
|
@ -204,23 +204,9 @@ final class APIWrapper
|
||||
$unlock = yield Tools::flock($realpaths->getLockPath(), LOCK_EX);
|
||||
Logger::log('Lock acquired, serializing');
|
||||
try {
|
||||
if (!$this->gettingApiId) {
|
||||
$update_closure = $this->API->settings['updates']['callback'];
|
||||
if ($this->API->settings['updates']['callback'] instanceof \Closure) {
|
||||
$this->API->settings['updates']['callback'] = [$this->API, 'noop'];
|
||||
}
|
||||
$logger_closure = $this->API->settings['logger']['logger_param'];
|
||||
if ($this->API->settings['logger']['logger_param'] instanceof \Closure) {
|
||||
$this->API->settings['logger']['logger_param'] = [$this->API, 'noop'];
|
||||
}
|
||||
}
|
||||
$wrote = yield put($realpaths->getTempPath(), \serialize($this));
|
||||
yield renameAsync($realpaths->getTempPath(), $realpaths->getSessionPath());
|
||||
} finally {
|
||||
if (!$this->gettingApiId) {
|
||||
$this->API->settings['updates']['callback'] = $update_closure;
|
||||
$this->API->settings['logger']['logger_param'] = $logger_closure;
|
||||
}
|
||||
$unlock();
|
||||
}
|
||||
Logger::log('Done serializing');
|
||||
|
@ -177,7 +177,7 @@ abstract class AbstractAPIFactory extends AsyncConstruct
|
||||
/**
|
||||
* Get fully resolved method list for object, including snake_case and camelCase variants.
|
||||
*
|
||||
* @param API $value Value
|
||||
* @param API|MTProto $value Value
|
||||
* @param string $class Custom class name
|
||||
*
|
||||
* @return array
|
||||
|
@ -20,6 +20,7 @@
|
||||
namespace danog\MadelineProto\ApiWrappers;
|
||||
|
||||
use danog\MadelineProto\MyTelegramOrgWrapper;
|
||||
use danog\MadelineProto\Settings;
|
||||
use danog\MadelineProto\Tools;
|
||||
use function Amp\ByteStream\getStdout;
|
||||
|
||||
@ -31,11 +32,11 @@ trait Start
|
||||
/**
|
||||
* Start API ID generation process.
|
||||
*
|
||||
* @param array $settings Settings
|
||||
* @param Settings $settings Settings
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
private function APIStart(array $settings): \Generator
|
||||
private function APIStart(Settings $settings): \Generator
|
||||
{
|
||||
if (\defined(\MADELINE_WORKER::class)) {
|
||||
throw new \danog\MadelineProto\Exception('Not inited!');
|
||||
@ -111,7 +112,7 @@ Note that you can also provide the API parameters directly in the code using the
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private function webAPIPhoneLogin(array $settings): \Generator
|
||||
private function webAPIPhoneLogin(Settings $settings): \Generator
|
||||
{
|
||||
try {
|
||||
$this->myTelegramOrgWrapper = new MyTelegramOrgWrapper($settings);
|
||||
|
@ -78,7 +78,7 @@ class CombinedAPI
|
||||
}
|
||||
return;
|
||||
}
|
||||
\danog\MadelineProto\Logger::constructor(3);
|
||||
//\danog\MadelineProto\Logger::constructor(3);
|
||||
\danog\MadelineProto\Logger::log("INSTANTIATING {$path}...");
|
||||
$instance = new \danog\MadelineProto\API($path, $settings);
|
||||
$this->instance_paths[$path] = $path;
|
||||
@ -201,10 +201,6 @@ class CombinedAPI
|
||||
if ($instance->API->authorized !== MTProto::LOGGED_IN) {
|
||||
continue;
|
||||
}
|
||||
if (!$instance->API->settings['updates']['handle_updates']) {
|
||||
$instance->API->settings['updates']['handle_updates'] = true;
|
||||
$instance->API->startUpdateSystem();
|
||||
}
|
||||
$instance->setCallback(function ($update) use ($path) {
|
||||
return $this->eventUpdateHandler($update, $path);
|
||||
}, ['async' => false]);
|
||||
|
@ -294,7 +294,7 @@ class Connection extends Session
|
||||
*/
|
||||
public function isHttp(): bool
|
||||
{
|
||||
return \in_array($this->ctx->getStreamName(), [HttpStream::getName(), HttpsStream::getName()]);
|
||||
return \in_array($this->ctx->getStreamName(), [HttpStream::class, HttpsStream::class]);
|
||||
}
|
||||
/**
|
||||
* Check if is a media connection.
|
||||
@ -348,7 +348,7 @@ class Connection extends Session
|
||||
if (!isset($this->waiter)) {
|
||||
$this->waiter = new HttpWaitLoop($this);
|
||||
}
|
||||
if (!isset($this->pinger) && ($this->ctx->hasStreamName(WssStream::getName()) || $this->ctx->hasStreamName(WsStream::getName()))) {
|
||||
if (!isset($this->pinger) && ($this->ctx->hasStreamName(WssStream::class) || $this->ctx->hasStreamName(WsStream::class))) {
|
||||
$this->pinger = new PingLoop($this);
|
||||
}
|
||||
foreach ($this->new_outgoing as $message_id) {
|
||||
|
@ -38,6 +38,7 @@ use Amp\Websocket\Client\Handshake;
|
||||
use Amp\Websocket\Client\Rfc6455Connector;
|
||||
use danog\MadelineProto\MTProto\PermAuthKey;
|
||||
use danog\MadelineProto\MTProto\TempAuthKey;
|
||||
use danog\MadelineProto\Settings\Connection as ConnectionSettings;
|
||||
use danog\MadelineProto\Stream\Common\BufferedRawStream;
|
||||
use danog\MadelineProto\Stream\Common\UdpBufferedStream;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
@ -48,9 +49,6 @@ use danog\MadelineProto\Stream\MTProtoTransport\HttpStream;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\IntermediatePaddedStream;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\IntermediateStream;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\ObfuscatedStream;
|
||||
use danog\MadelineProto\Stream\Proxy\HttpProxy;
|
||||
use danog\MadelineProto\Stream\Proxy\SocksProxy;
|
||||
use danog\MadelineProto\Stream\StreamInterface;
|
||||
use danog\MadelineProto\Stream\Transport\DefaultStream;
|
||||
use danog\MadelineProto\Stream\Transport\WssStream;
|
||||
use danog\MadelineProto\Stream\Transport\WsStream;
|
||||
@ -70,7 +68,7 @@ class DataCenter
|
||||
/**
|
||||
* Current DC ID.
|
||||
*
|
||||
* @var string
|
||||
* @var string|int
|
||||
*/
|
||||
public $curdc = 0;
|
||||
/**
|
||||
@ -88,9 +86,9 @@ class DataCenter
|
||||
/**
|
||||
* Settings.
|
||||
*
|
||||
* @var array
|
||||
* @var ConnectionSettings
|
||||
*/
|
||||
private $settings = [];
|
||||
private $settings;
|
||||
/**
|
||||
* HTTP client.
|
||||
*
|
||||
@ -132,9 +130,14 @@ class DataCenter
|
||||
}
|
||||
public function __wakeup()
|
||||
{
|
||||
if (\is_array($this->settings)) {
|
||||
$settings = new ConnectionSettings;
|
||||
$settings->mergeArray(['connection_settings' => $this->settings]);
|
||||
$this->settings = $settings;
|
||||
}
|
||||
$array = [];
|
||||
foreach ($this->sockets as $id => $socket) {
|
||||
if ($socket instanceof Connection) {
|
||||
if ($socket instanceof \danog\MadelineProto\Connection) {
|
||||
if ($socket->temp_auth_key) {
|
||||
$array[$id]['tempAuthKey'] = $socket->temp_auth_key;
|
||||
}
|
||||
@ -187,17 +190,17 @@ class DataCenter
|
||||
*
|
||||
* @param MTProto $API Main MTProto instance
|
||||
* @param array $dclist DC IP list
|
||||
* @param array $settings Settings
|
||||
* @param ConnectionSettings $settings Settings
|
||||
* @param boolean $reconnectAll Whether to reconnect to all DCs or just to changed ones
|
||||
* @param CookieJar $jar Cookie jar
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __magic_construct($API, array $dclist, array $settings, bool $reconnectAll = true, CookieJar $jar = null)
|
||||
public function __magic_construct($API, array $dclist, ConnectionSettings $settings, bool $reconnectAll = true, CookieJar $jar = null)
|
||||
{
|
||||
$this->API = $API;
|
||||
$changed = [];
|
||||
$changedSettings = $this->settings !== $settings;
|
||||
$changedSettings = $settings->haveChanged();
|
||||
if (!$reconnectAll) {
|
||||
$changed = [];
|
||||
$test = $API->getCachedConfig()['test_mode'] ?? false ? 'test' : 'main';
|
||||
@ -238,6 +241,7 @@ class DataCenter
|
||||
$this->webSocketConnnector = new Rfc6455Connector($this->HTTPClient);
|
||||
}
|
||||
}
|
||||
$this->settings->applyChanges();
|
||||
}
|
||||
/**
|
||||
* Set VoIP endpoints.
|
||||
@ -304,108 +308,73 @@ class DataCenter
|
||||
{
|
||||
$ctxs = [];
|
||||
$combos = [];
|
||||
$dc_config_number = isset($this->settings[$dc_number]) ? $dc_number : 'all';
|
||||
$test = $this->settings[$dc_config_number]['test_mode'] ? 'test' : 'main';
|
||||
$ipv6 = $this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4';
|
||||
switch ($this->settings[$dc_config_number]['protocol']) {
|
||||
case 'abridged':
|
||||
case 'tcp_abridged':
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [AbridgedStream::getName(), []]];
|
||||
$test = $this->settings->getTestMode() ? 'test' : 'main';
|
||||
$ipv6 = $this->settings->getIpv6() ? 'ipv6' : 'ipv4';
|
||||
switch ($this->settings->getProtocol()) {
|
||||
case AbridgedStream::class:
|
||||
$default = [[DefaultStream::class, []], [BufferedRawStream::class, []], [AbridgedStream::class, []]];
|
||||
break;
|
||||
case 'intermediate':
|
||||
case 'tcp_intermediate':
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [IntermediateStream::getName(), []]];
|
||||
case IntermediateStream::class:
|
||||
$default = [[DefaultStream::class, []], [BufferedRawStream::class, []], [IntermediateStream::class, []]];
|
||||
break;
|
||||
case 'obfuscated2':
|
||||
$this->settings[$dc_config_number]['protocol'] = 'tcp_intermediate_padded';
|
||||
$this->settings[$dc_config_number]['obfuscated'] = true;
|
||||
// no break
|
||||
case 'intermediate_padded':
|
||||
case 'tcp_intermediate_padded':
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [IntermediatePaddedStream::getName(), []]];
|
||||
case IntermediatePaddedStream::class:
|
||||
$default = [[DefaultStream::class, []], [BufferedRawStream::class, []], [IntermediatePaddedStream::class, []]];
|
||||
break;
|
||||
case 'full':
|
||||
case 'tcp_full':
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [FullStream::getName(), []]];
|
||||
case FullStream::class:
|
||||
$default = [[DefaultStream::class, []], [BufferedRawStream::class, []], [FullStream::class, []]];
|
||||
break;
|
||||
case 'http':
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [HttpStream::getName(), []]];
|
||||
case HttpStream::class:
|
||||
$default = [[DefaultStream::class, []], [BufferedRawStream::class, []], [HttpStream::class, []]];
|
||||
break;
|
||||
case 'https':
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [HttpsStream::getName(), []]];
|
||||
case HttpsStream::class:
|
||||
$default = [[DefaultStream::class, []], [BufferedRawStream::class, []], [HttpsStream::class, []]];
|
||||
break;
|
||||
case 'udp':
|
||||
$default = [[DefaultStream::getName(), []], [UdpBufferedStream::getName(), []]];
|
||||
case UdpBufferedStream::class:
|
||||
$default = [[DefaultStream::class, []], [UdpBufferedStream::class, []]];
|
||||
break;
|
||||
default:
|
||||
throw new Exception(Lang::$current_lang['protocol_invalid']);
|
||||
}
|
||||
if ($this->settings[$dc_config_number]['obfuscated'] && !\in_array($default[2][0], [HttpsStream::getName(), HttpStream::getName()])) {
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [ObfuscatedStream::getName(), []], \end($default)];
|
||||
if ($this->settings->getObfuscated() && !\in_array($default[2][0], [HttpsStream::class, HttpStream::class])) {
|
||||
$default = [[DefaultStream::class, []], [BufferedRawStream::class, []], [ObfuscatedStream::class, []], \end($default)];
|
||||
}
|
||||
if ($this->settings[$dc_config_number]['transport'] && !\in_array($default[2][0], [HttpsStream::getName(), HttpStream::getName()])) {
|
||||
switch ($this->settings[$dc_config_number]['transport']) {
|
||||
case 'tcp':
|
||||
if ($this->settings[$dc_config_number]['obfuscated']) {
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [ObfuscatedStream::getName(), []], \end($default)];
|
||||
if ($this->settings->getTransport() && !\in_array($default[2][0], [HttpsStream::class, HttpStream::class])) {
|
||||
switch ($this->settings->getTransport()) {
|
||||
case DefaultStream::class:
|
||||
if ($this->settings->getObfuscated()) {
|
||||
$default = [[DefaultStream::class, []], [BufferedRawStream::class, []], [ObfuscatedStream::class, []], \end($default)];
|
||||
}
|
||||
break;
|
||||
case 'wss':
|
||||
$default = [[DefaultStream::getName(), []], [WssStream::getName(), []], [BufferedRawStream::getName(), []], [ObfuscatedStream::getName(), []], \end($default)];
|
||||
case WssStream::class:
|
||||
$default = [[DefaultStream::class, []], [WssStream::class, []], [BufferedRawStream::class, []], [ObfuscatedStream::class, []], \end($default)];
|
||||
break;
|
||||
case 'ws':
|
||||
$default = [[DefaultStream::getName(), []], [WsStream::getName(), []], [BufferedRawStream::getName(), []], [ObfuscatedStream::getName(), []], \end($default)];
|
||||
case WsStream::class:
|
||||
$default = [[DefaultStream::class, []], [WsStream::class, []], [BufferedRawStream::class, []], [ObfuscatedStream::class, []], \end($default)];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$dc_number) {
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []]];
|
||||
$default = [[DefaultStream::class, []], [BufferedRawStream::class, []]];
|
||||
}
|
||||
$combos[] = $default;
|
||||
if (!isset($this->settings[$dc_config_number]['do_not_retry'])) {
|
||||
if (!$this->settings->getRetry()) {
|
||||
if (isset($this->dclist[$test][$ipv6][$dc_number]['tcpo_only']) && $this->dclist[$test][$ipv6][$dc_number]['tcpo_only'] || isset($this->dclist[$test][$ipv6][$dc_number]['secret'])) {
|
||||
$extra = isset($this->dclist[$test][$ipv6][$dc_number]['secret']) ? ['secret' => $this->dclist[$test][$ipv6][$dc_number]['secret']] : [];
|
||||
$combos[] = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [ObfuscatedStream::getName(), $extra], [IntermediatePaddedStream::getName(), []]];
|
||||
$combos[] = [[DefaultStream::class, []], [BufferedRawStream::class, []], [ObfuscatedStream::class, $extra], [IntermediatePaddedStream::class, []]];
|
||||
}
|
||||
if (\is_iterable($this->settings[$dc_config_number]['proxy'])) {
|
||||
$proxies = $this->settings[$dc_config_number]['proxy'];
|
||||
$proxy_extras = $this->settings[$dc_config_number]['proxy_extra'];
|
||||
} else {
|
||||
$proxies = [$this->settings[$dc_config_number]['proxy']];
|
||||
$proxy_extras = [$this->settings[$dc_config_number]['proxy_extra']];
|
||||
}
|
||||
foreach ($proxies as $key => $proxy) {
|
||||
// Convert old settings
|
||||
if ($proxy === '\\Socket') {
|
||||
$proxy = DefaultStream::getName();
|
||||
}
|
||||
if ($proxy === '\\SocksProxy') {
|
||||
$proxy = SocksProxy::getName();
|
||||
}
|
||||
if ($proxy === '\\HttpProxy') {
|
||||
$proxy = HttpProxy::getName();
|
||||
}
|
||||
if ($proxy === '\\MTProxySocket') {
|
||||
$proxy = ObfuscatedStream::getName();
|
||||
}
|
||||
if ($proxy === DefaultStream::getName()) {
|
||||
foreach ($this->settings->getProxies() as $proxy => $extras) {
|
||||
if (!$dc_number && $proxy === ObfuscatedStream::class) {
|
||||
continue;
|
||||
}
|
||||
if (!$dc_number && $proxy === ObfuscatedStream::getName()) {
|
||||
continue;
|
||||
}
|
||||
$extra = $proxy_extras[$key];
|
||||
if (!isset(\class_implements($proxy)[StreamInterface::class])) {
|
||||
throw new Exception(Lang::$current_lang['proxy_class_invalid']);
|
||||
}
|
||||
if ($proxy === ObfuscatedStream::getName() && \in_array(\strlen($extra['secret']), [17, 34])) {
|
||||
$combos[] = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [$proxy, $extra], [IntermediatePaddedStream::getName(), []]];
|
||||
foreach ($extras as $extra) {
|
||||
if ($proxy === ObfuscatedStream::class && \in_array(\strlen($extra['secret']), [17, 34])) {
|
||||
$combos[] = [[DefaultStream::class, []], [BufferedRawStream::class, []], [$proxy, $extra], [IntermediatePaddedStream::class, []]];
|
||||
}
|
||||
foreach ($combos as $k => $orig) {
|
||||
$combo = [];
|
||||
if ($proxy === ObfuscatedStream::getName()) {
|
||||
if ($proxy === ObfuscatedStream::class) {
|
||||
$combo = $orig;
|
||||
if ($combo[\count($combo) - 2][0] === ObfuscatedStream::getName()) {
|
||||
if ($combo[\count($combo) - 2][0] === ObfuscatedStream::class) {
|
||||
$combo[\count($combo) - 2][1] = $extra;
|
||||
} else {
|
||||
$mtproto = \end($combo);
|
||||
@ -413,13 +382,13 @@ class DataCenter
|
||||
$combo[] = $mtproto;
|
||||
}
|
||||
} else {
|
||||
if ($orig[1][0] === BufferedRawStream::getName()) {
|
||||
if ($orig[1][0] === BufferedRawStream::class) {
|
||||
list($first, $second) = [\array_slice($orig, 0, 2), \array_slice($orig, 2)];
|
||||
$first[] = [$proxy, $extra];
|
||||
$combo = \array_merge($first, $second);
|
||||
} elseif (\in_array($orig[1][0], [WsStream::getName(), WssStream::getName()])) {
|
||||
} elseif (\in_array($orig[1][0], [WsStream::class, WssStream::class])) {
|
||||
list($first, $second) = [\array_slice($orig, 0, 1), \array_slice($orig, 1)];
|
||||
$first[] = [BufferedRawStream::getName(), []];
|
||||
$first[] = [BufferedRawStream::class, []];
|
||||
$first[] = [$proxy, $extra];
|
||||
$combo = \array_merge($first, $second);
|
||||
}
|
||||
@ -428,23 +397,27 @@ class DataCenter
|
||||
//unset($combos[$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($dc_number) {
|
||||
$combos[] = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [HttpsStream::getName(), []]];
|
||||
$combos[] = [[DefaultStream::class, []], [BufferedRawStream::class, []], [HttpsStream::class, []]];
|
||||
}
|
||||
$combos = \array_unique($combos, SORT_REGULAR);
|
||||
}
|
||||
/* @var $context \Amp\ConnectContext */
|
||||
$context = $context ?? (new ConnectContext())->withMaxAttempts(1)->withConnectTimeout(1000 * $this->settings[$dc_config_number]['timeout']);
|
||||
$context = $context ?? (new ConnectContext())->withMaxAttempts(1)->withConnectTimeout(1000 * $this->settings->getTimeout());
|
||||
foreach ($combos as $combo) {
|
||||
foreach ([true, false] as $useDoH) {
|
||||
$ipv6Combos = [$this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4', $this->settings[$dc_config_number]['ipv6'] ? 'ipv4' : 'ipv6'];
|
||||
$ipv6Combos = [
|
||||
$this->settings->getIpv6() ? 'ipv6' : 'ipv4',
|
||||
$this->settings->getIpv6() ? 'ipv4' : 'ipv6'
|
||||
];
|
||||
foreach ($ipv6Combos as $ipv6) {
|
||||
// This is only for non-MTProto connections
|
||||
if (!$dc_number) {
|
||||
/* @var $ctx \danog\MadelineProto\Stream\ConnectionContext */
|
||||
$ctx = (new ConnectionContext())->setSocketContext($context)->setUri($uri)->setIpv6($ipv6 === 'ipv6');
|
||||
foreach ($combo as $stream) {
|
||||
if ($stream[0] === DefaultStream::getName() && $stream[1] === []) {
|
||||
if ($stream[0] === DefaultStream::class && $stream[1] === []) {
|
||||
$stream[1] = $useDoH ? new DoHConnector($this, $ctx) : $this->dnsConnector;
|
||||
}
|
||||
$ctx->addStream(...$stream);
|
||||
@ -466,7 +439,7 @@ class DataCenter
|
||||
$port = $this->dclist[$test][$ipv6][$dc_number]['port'];
|
||||
foreach (\array_unique([$port, 443, 80, 88, 5222]) as $port) {
|
||||
$stream = \end($combo)[0];
|
||||
if ($stream === HttpsStream::getName()) {
|
||||
if ($stream === HttpsStream::class) {
|
||||
if (\strpos($dc_number, '_cdn') !== false) {
|
||||
continue;
|
||||
}
|
||||
@ -474,33 +447,33 @@ class DataCenter
|
||||
if (\strpos($dc_number, '_media') !== false) {
|
||||
$subdomain .= '-1';
|
||||
}
|
||||
$path = $this->settings[$dc_config_number]['test_mode'] ? 'apiw_test1' : 'apiw1';
|
||||
$path = $this->settings->getTestMode() ? 'apiw_test1' : 'apiw1';
|
||||
$uri = 'tcp://'.$subdomain.'.web.telegram.org:'.$port.'/'.$path;
|
||||
} elseif ($stream === HttpStream::getName()) {
|
||||
} elseif ($stream === HttpStream::class) {
|
||||
$uri = 'tcp://'.$address.':'.$port.'/api';
|
||||
} else {
|
||||
$uri = 'tcp://'.$address.':'.$port;
|
||||
}
|
||||
if ($combo[1][0] === WssStream::getName()) {
|
||||
if ($combo[1][0] === WssStream::class) {
|
||||
$subdomain = $this->dclist['ssl_subdomains'][\preg_replace('/\\D+/', '', $dc_number)];
|
||||
if (\strpos($dc_number, '_media') !== false) {
|
||||
$subdomain .= '-1';
|
||||
}
|
||||
$path = $this->settings[$dc_config_number]['test_mode'] ? 'apiws_test' : 'apiws';
|
||||
$path = $this->settings->getTestMode() ? 'apiws_test' : 'apiws';
|
||||
$uri = 'tcp://'.$subdomain.'.web.telegram.org:'.$port.'/'.$path;
|
||||
} elseif ($combo[1][0] === WsStream::getName()) {
|
||||
} elseif ($combo[1][0] === WsStream::class) {
|
||||
$subdomain = $this->dclist['ssl_subdomains'][\preg_replace('/\\D+/', '', $dc_number)];
|
||||
if (\strpos($dc_number, '_media') !== false) {
|
||||
$subdomain .= '-1';
|
||||
}
|
||||
$path = $this->settings[$dc_config_number]['test_mode'] ? 'apiws_test' : 'apiws';
|
||||
$path = $this->settings->getTestMode() ? 'apiws_test' : 'apiws';
|
||||
//$uri = 'tcp://' . $subdomain . '.web.telegram.org:' . $port . '/' . $path;
|
||||
$uri = 'tcp://'.$address.':'.$port.'/'.$path;
|
||||
}
|
||||
/* @var $ctx \danog\MadelineProto\Stream\ConnectionContext */
|
||||
$ctx = (new ConnectionContext())->setDc($dc_number)->setTest($this->settings[$dc_config_number]['test_mode'])->setSocketContext($context)->setUri($uri)->setIpv6($ipv6 === 'ipv6');
|
||||
$ctx = (new ConnectionContext())->setDc($dc_number)->setTest($this->settings->getTestMode())->setSocketContext($context)->setUri($uri)->setIpv6($ipv6 === 'ipv6');
|
||||
foreach ($combo as $stream) {
|
||||
if ($stream[0] === DefaultStream::getName() && $stream[1] === []) {
|
||||
if ($stream[0] === DefaultStream::class && $stream[1] === []) {
|
||||
$stream[1] = $useDoH ? new DoHConnector($this, $ctx) : $this->dnsConnector;
|
||||
}
|
||||
if (\in_array($stream[0], [WsStream::class, WssStream::class]) && $stream[1] === []) {
|
||||
@ -675,8 +648,8 @@ class DataCenter
|
||||
*/
|
||||
public function getDcs($all = true): array
|
||||
{
|
||||
$test = $this->settings['all']['test_mode'] ? 'test' : 'main';
|
||||
$ipv6 = $this->settings['all']['ipv6'] ? 'ipv6' : 'ipv4';
|
||||
$test = $this->settings->getTestMode() ? 'test' : 'main';
|
||||
$ipv6 = $this->settings->getIpv6() ? 'ipv6' : 'ipv4';
|
||||
return $all ? \array_keys((array) $this->dclist[$test][$ipv6]) : \array_keys((array) $this->sockets);
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ use danog\MadelineProto\Loop\Generic\PeriodicLoopInternal;
|
||||
use danog\MadelineProto\MTProto\AuthKey;
|
||||
use danog\MadelineProto\MTProto\PermAuthKey;
|
||||
use danog\MadelineProto\MTProto\TempAuthKey;
|
||||
use danog\MadelineProto\Settings\Connection as ConnectionSettings;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\HttpsStream;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\HttpStream;
|
||||
@ -361,10 +362,10 @@ class DataCenterConnection implements JsonSerializable
|
||||
$this->ctx = $ctx->getCtx();
|
||||
$this->datacenter = $ctx->getDc();
|
||||
$media = $ctx->isMedia() || $ctx->isCDN();
|
||||
$count = $media ? $this->API->settings['connection_settings']['media_socket_count']['min'] : 1;
|
||||
$count = $media ? $this->API->getSettings()->getConnection()->getMinMediaSocketCount() : 1;
|
||||
if ($count > 1) {
|
||||
if (!$this->robinLoop) {
|
||||
$this->robinLoop = new PeriodicLoopInternal($this->API, [$this, 'even'], "robin loop DC {$this->datacenter}", $this->API->settings['connection_settings']['robin_period'] * 1000);
|
||||
$this->robinLoop = new PeriodicLoopInternal($this->API, [$this, 'even'], "robin loop DC {$this->datacenter}", $this->API->getSettings()->getConnection()->getRobinPeriod() * 1000);
|
||||
}
|
||||
$this->robinLoop->start();
|
||||
}
|
||||
@ -558,7 +559,7 @@ class DataCenterConnection implements JsonSerializable
|
||||
$count += 50;
|
||||
}
|
||||
} elseif ($min < 100) {
|
||||
$max = $this->isMedia() || $this->isCDN() ? $this->API->settings['connection_settings']['media_socket_count']['max'] : 1;
|
||||
$max = $this->isMedia() || $this->isCDN() ? $this->API->getSettings()->getConnection()->getMaxMediaSocketCount() : 1;
|
||||
if (\count($this->availableConnections) < $max) {
|
||||
$this->connectMore(2);
|
||||
} else {
|
||||
@ -619,7 +620,7 @@ class DataCenterConnection implements JsonSerializable
|
||||
*/
|
||||
public function isHttp(): bool
|
||||
{
|
||||
return \in_array($this->ctx->getStreamName(), [HttpStream::getName(), HttpsStream::getName()]);
|
||||
return \in_array($this->ctx->getStreamName(), [HttpStream::class, HttpsStream::class]);
|
||||
}
|
||||
/**
|
||||
* Check if is connected directly by IP address.
|
||||
@ -628,7 +629,7 @@ class DataCenterConnection implements JsonSerializable
|
||||
*/
|
||||
public function byIPAddress(): bool
|
||||
{
|
||||
return !$this->ctx->hasStreamName(WssStream::getName()) && !$this->ctx->hasStreamName(HttpsStream::getName());
|
||||
return !$this->ctx->hasStreamName(WssStream::class) && !$this->ctx->hasStreamName(HttpsStream::class);
|
||||
}
|
||||
/**
|
||||
* Check if is a media connection.
|
||||
@ -651,12 +652,20 @@ class DataCenterConnection implements JsonSerializable
|
||||
/**
|
||||
* Get DC-specific settings.
|
||||
*
|
||||
* @return array
|
||||
* @return ConnectionSettings
|
||||
*/
|
||||
public function getSettings(): array
|
||||
public function getSettings(): ConnectionSettings
|
||||
{
|
||||
$dc_config_number = isset($this->API->settings['connection_settings'][$this->datacenter]) ? $this->datacenter : 'all';
|
||||
return $this->API->settings['connection_settings'][$dc_config_number];
|
||||
return $this->API->getSettings()->getConnection();
|
||||
}
|
||||
/**
|
||||
* Get global settings.
|
||||
*
|
||||
* @return Settings
|
||||
*/
|
||||
public function getGenericSettings(): Settings
|
||||
{
|
||||
return $this->API->getSettings();
|
||||
}
|
||||
/**
|
||||
* JSON serialize function.
|
||||
|
@ -16,10 +16,18 @@ trait ArrayCacheTrait
|
||||
*/
|
||||
protected array $ttlValues = [];
|
||||
|
||||
/**
|
||||
* TTL interval.
|
||||
*/
|
||||
protected int $ttl = 5 * 60;
|
||||
/**
|
||||
* TTL cleanup interval.
|
||||
*/
|
||||
private int $ttlCheckInterval = 60;
|
||||
|
||||
protected string $ttl = '+5 minutes';
|
||||
private string $ttlCheckInterval = '+1 minute';
|
||||
|
||||
/**
|
||||
* Cache cleanup watcher ID.
|
||||
*/
|
||||
private ?string $cacheCleanupId = null;
|
||||
|
||||
protected function getCache(string $key, $default = null)
|
||||
@ -55,7 +63,7 @@ trait ArrayCacheTrait
|
||||
|
||||
protected function startCacheCleanupLoop(): void
|
||||
{
|
||||
$this->cacheCleanupId = Loop::repeat(\strtotime($this->ttlCheckInterval, 0) * 1000, fn () => $this->cleanupCache());
|
||||
$this->cacheCleanupId = Loop::repeat($this->ttlCheckInterval * 1000, fn () => $this->cleanupCache());
|
||||
}
|
||||
protected function stopCacheCleanupLoop(): void
|
||||
{
|
||||
|
@ -3,11 +3,16 @@
|
||||
namespace danog\MadelineProto\Db;
|
||||
|
||||
use Amp\Promise;
|
||||
use danog\MadelineProto\Settings\Database\Memory;
|
||||
use danog\MadelineProto\Settings\Database\Mysql;
|
||||
use danog\MadelineProto\Settings\Database\Postgres;
|
||||
use danog\MadelineProto\Settings\Database\Redis;
|
||||
use danog\MadelineProto\Settings\DatabaseAbstract;
|
||||
|
||||
class DbPropertiesFactory
|
||||
{
|
||||
/**
|
||||
* @param array $dbSettings
|
||||
* @param DatabaseAbstract $dbSettings
|
||||
* @param string $namePrefix
|
||||
* @param string $propertyType
|
||||
* @param string $name
|
||||
@ -16,29 +21,29 @@ class DbPropertiesFactory
|
||||
* @return Promise<DbType>
|
||||
*
|
||||
* @uses \danog\MadelineProto\Db\MemoryArray
|
||||
* @uses \danog\MadelineProto\Db\SharedMemoryArray
|
||||
* @uses \danog\MadelineProto\Db\MysqlArray
|
||||
* @uses \danog\MadelineProto\Db\PostgresArray
|
||||
* @uses \danog\MadelineProto\Db\RedisArray
|
||||
*/
|
||||
public static function get(array $dbSettings, string $namePrefix, string $propertyType, string $name, $value = null): Promise
|
||||
public static function get(DatabaseAbstract $dbSettings, string $namePrefix, string $propertyType, string $name, $value = null): Promise
|
||||
{
|
||||
$class = __NAMESPACE__;
|
||||
|
||||
switch (\strtolower($dbSettings['type'])) {
|
||||
case 'memory':
|
||||
switch (true) {
|
||||
case $dbSettings instanceof Memory:
|
||||
$class .= '\\Memory';
|
||||
break;
|
||||
case 'mysql':
|
||||
case $dbSettings instanceof Mysql:
|
||||
$class .= '\\Mysql';
|
||||
break;
|
||||
case 'postgres':
|
||||
case $dbSettings instanceof Postgres:
|
||||
$class .= '\\Postgres';
|
||||
break;
|
||||
case 'redis':
|
||||
case $dbSettings instanceof Redis:
|
||||
$class .= '\\Redis';
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException("Unknown dbType: {$dbSettings['type']}");
|
||||
throw new \InvalidArgumentException("Unknown dbType: ".\get_class($dbSettings));
|
||||
|
||||
}
|
||||
|
||||
@ -51,6 +56,6 @@ class DbPropertiesFactory
|
||||
throw new \InvalidArgumentException("Unknown $propertyType: {$propertyType}");
|
||||
}
|
||||
|
||||
return $class::getInstance($name, $value, $namePrefix, $dbSettings[$dbSettings['type']]??[]);
|
||||
return $class::getInstance($name, $value, $namePrefix, $dbSettings);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ trait DbPropertiesTrait
|
||||
if (empty(static::$dbProperties)) {
|
||||
throw new \LogicException(static::class.' must have $dbProperties');
|
||||
}
|
||||
$dbSettings = $MadelineProto->settings['db'];
|
||||
$dbSettings = $MadelineProto->settings->getDb();
|
||||
$prefix = static::getSessionId($MadelineProto);
|
||||
|
||||
foreach (static::$dbProperties as $property => $type) {
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace danog\MadelineProto\Db;
|
||||
|
||||
use Amp\Promise;
|
||||
use danog\MadelineProto\Settings\DatabaseAbstract;
|
||||
|
||||
interface DbType
|
||||
{
|
||||
@ -10,9 +11,9 @@ interface DbType
|
||||
* @param string $name
|
||||
* @param null $value
|
||||
* @param string $tablePrefix
|
||||
* @param array $settings
|
||||
* @param DatabaseAbstract $settings
|
||||
*
|
||||
* @return Promise<self>
|
||||
*/
|
||||
public static function getInstance(string $name, $value = null, string $tablePrefix = '', array $settings = []): Promise;
|
||||
public static function getInstance(string $name, $value = null, string $tablePrefix = '', $settings): Promise;
|
||||
}
|
||||
|
@ -4,8 +4,9 @@ namespace danog\MadelineProto\Db\Driver;
|
||||
|
||||
use Amp\Mysql\ConnectionConfig;
|
||||
use Amp\Mysql\Pool;
|
||||
use Amp\Sql\Common\ConnectionPool;
|
||||
use danog\MadelineProto\Logger;
|
||||
use danog\MadelineProto\Settings\Database\Mysql as DatabaseMysql;
|
||||
|
||||
use function Amp\Mysql\Pool;
|
||||
|
||||
class Mysql
|
||||
@ -29,23 +30,17 @@ class Mysql
|
||||
*
|
||||
* @return \Generator<Pool>
|
||||
*/
|
||||
public static function getConnection(
|
||||
string $host = '127.0.0.1',
|
||||
int $port = 3306,
|
||||
string $user = 'root',
|
||||
string $password = '',
|
||||
string $db = 'MadelineProto',
|
||||
int $maxConnections = ConnectionPool::DEFAULT_MAX_CONNECTIONS,
|
||||
int $idleTimeout = ConnectionPool::DEFAULT_IDLE_TIMEOUT
|
||||
): \Generator {
|
||||
$dbKey = "$host:$port:$db";
|
||||
public static function getConnection(DatabaseMysql $settings): \Generator
|
||||
{
|
||||
$dbKey = $settings->getKey();
|
||||
if (empty(static::$connections[$dbKey])) {
|
||||
$config = ConnectionConfig::fromString(
|
||||
"host={$host} port={$port} user={$user} password={$password} db={$db}"
|
||||
);
|
||||
$config = ConnectionConfig::fromString("host=".\str_replace("tcp://", "", $settings->getUri()))
|
||||
->withUser($settings->getUsername())
|
||||
->withPassword($settings->getPassword())
|
||||
->withDatabase($settings->getDatabase());
|
||||
|
||||
yield from static::createDb($config);
|
||||
static::$connections[$dbKey] = pool($config, $maxConnections, $idleTimeout);
|
||||
static::$connections[$dbKey] = pool($config, $settings->getMaxConnections(), $settings->getIdleTimeout());
|
||||
}
|
||||
|
||||
return static::$connections[$dbKey];
|
||||
|
@ -4,8 +4,9 @@ namespace danog\MadelineProto\Db\Driver;
|
||||
|
||||
use Amp\Postgres\ConnectionConfig;
|
||||
use Amp\Postgres\Pool;
|
||||
use Amp\Sql\Common\ConnectionPool;
|
||||
use danog\MadelineProto\Logger;
|
||||
use danog\MadelineProto\Settings\Database\Postgres as DatabasePostgres;
|
||||
|
||||
use function Amp\Postgres\Pool;
|
||||
|
||||
class Postgres
|
||||
@ -29,23 +30,17 @@ class Postgres
|
||||
*
|
||||
* @return \Generator<Pool>
|
||||
*/
|
||||
public static function getConnection(
|
||||
string $host = '127.0.0.1',
|
||||
int $port = 5432,
|
||||
string $user = 'root',
|
||||
string $password = '',
|
||||
string $db = 'MadelineProto',
|
||||
int $maxConnections = ConnectionPool::DEFAULT_MAX_CONNECTIONS,
|
||||
int $idleTimeout = ConnectionPool::DEFAULT_IDLE_TIMEOUT
|
||||
): \Generator {
|
||||
$dbKey = "$host:$port:$db";
|
||||
public static function getConnection(DatabasePostgres $settings): \Generator
|
||||
{
|
||||
$dbKey = $settings->getKey();
|
||||
if (empty(static::$connections[$dbKey])) {
|
||||
$config = ConnectionConfig::fromString(
|
||||
"host={$host} port={$port} user={$user} password={$password} db={$db}"
|
||||
);
|
||||
$config = ConnectionConfig::fromString("host=".\str_replace("tcp://", "", $settings->getUri()))
|
||||
->withUser($settings->getUsername())
|
||||
->withPassword($settings->getPassword())
|
||||
->withDatabase($settings->getDatabase());
|
||||
|
||||
yield from static::createDb($config);
|
||||
static::$connections[$dbKey] = pool($config, $maxConnections, $idleTimeout);
|
||||
static::$connections[$dbKey] = pool($config, $settings->getMaxConnections(), $settings->getIdleTimeout());
|
||||
}
|
||||
|
||||
return static::$connections[$dbKey];
|
||||
|
@ -5,6 +5,7 @@ namespace danog\MadelineProto\Db\Driver;
|
||||
use Amp\Redis\Config;
|
||||
use Amp\Redis\Redis as RedisRedis;
|
||||
use Amp\Redis\RemoteExecutorFactory;
|
||||
use danog\MadelineProto\Settings\Database\Redis as DatabaseRedis;
|
||||
|
||||
class Redis
|
||||
{
|
||||
@ -27,17 +28,13 @@ class Redis
|
||||
*
|
||||
* @return \Generator<RedisRedis>
|
||||
*/
|
||||
public static function getConnection(
|
||||
string $host = '127.0.0.1',
|
||||
int $port = 6379,
|
||||
string $password = '',
|
||||
int $db = 0
|
||||
): \Generator {
|
||||
$dbKey = "$host:$port:$db";
|
||||
public static function getConnection(DatabaseRedis $settings): \Generator
|
||||
{
|
||||
$dbKey = $settings->getKey();
|
||||
if (empty(static::$connections[$dbKey])) {
|
||||
$config = Config::fromUri(
|
||||
"{$host}:{$port}?password={$password}&db={$db}"
|
||||
);
|
||||
$config = Config::fromUri($settings->getUri())
|
||||
->withPassword($settings->getPassword())
|
||||
->withDatabase($settings->getDatabase());
|
||||
|
||||
static::$connections[$dbKey] = new RedisRedis((new RemoteExecutorFactory($config))->createQueryExecutor());
|
||||
yield static::$connections[$dbKey]->ping();
|
||||
|
@ -3,6 +3,8 @@
|
||||
namespace danog\MadelineProto\Db;
|
||||
|
||||
use danog\MadelineProto\Logger;
|
||||
use danog\MadelineProto\SettingsAbstract;
|
||||
use ReflectionClass;
|
||||
|
||||
abstract class DriverArray implements DbArray
|
||||
{
|
||||
@ -14,12 +16,22 @@ abstract class DriverArray implements DbArray
|
||||
}
|
||||
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
if (isset($this->settings) && \is_array($this->settings)) {
|
||||
$clazz = (new ReflectionClass($this))->getProperty('dbSettings')->getType()->getName();
|
||||
/** @var SettingsAbstract */
|
||||
$this->dbSettings = new $clazz;
|
||||
$this->dbSettings->mergeArray($this->settings);
|
||||
unset($this->settings);
|
||||
}
|
||||
}
|
||||
public function offsetExists($index): bool
|
||||
{
|
||||
throw new \RuntimeException('Native isset not support promises. Use isset method');
|
||||
}
|
||||
|
||||
abstract protected function initConnection(array $settings): \Generator;
|
||||
abstract protected function initConnection($settings): \Generator;
|
||||
|
||||
/**
|
||||
* @param self $new
|
||||
|
@ -6,6 +6,8 @@ use Amp\Producer;
|
||||
use Amp\Promise;
|
||||
use Amp\Success;
|
||||
use danog\MadelineProto\Logger;
|
||||
use danog\MadelineProto\Settings\Database\Memory;
|
||||
|
||||
use function Amp\call;
|
||||
|
||||
class MemoryArray extends \ArrayIterator implements DbArray
|
||||
@ -15,7 +17,16 @@ class MemoryArray extends \ArrayIterator implements DbArray
|
||||
parent::__construct((array) $array, $flags | self::STD_PROP_LIST);
|
||||
}
|
||||
|
||||
public static function getInstance(string $name, $value = null, string $tablePrefix = '', array $settings = []): Promise
|
||||
/**
|
||||
* Get instance.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @param string $tablePrefix
|
||||
* @param Memory $settings
|
||||
* @return Promise
|
||||
*/
|
||||
public static function getInstance(string $name, $value = null, string $tablePrefix = '', $settings): Promise
|
||||
{
|
||||
return call(static function () use ($value) {
|
||||
if ($value instanceof MemoryArray) {
|
||||
@ -23,6 +34,9 @@ class MemoryArray extends \ArrayIterator implements DbArray
|
||||
}
|
||||
if ($value instanceof DbArray) {
|
||||
Logger::log("Loading database to memory. Please wait.", Logger::WARNING);
|
||||
if ($value instanceof DriverArray) {
|
||||
yield from $value->initConnection($value->dbSettings);
|
||||
}
|
||||
$value = yield $value->getArrayCopy();
|
||||
}
|
||||
return new static($value);
|
||||
|
@ -9,17 +9,22 @@ use Amp\Sql\ResultSet;
|
||||
use Amp\Success;
|
||||
use danog\MadelineProto\Db\Driver\Mysql;
|
||||
use danog\MadelineProto\Logger;
|
||||
use danog\MadelineProto\Settings\Database\Mysql as DatabaseMysql;
|
||||
|
||||
use function Amp\call;
|
||||
|
||||
class MysqlArray extends SqlArray
|
||||
{
|
||||
protected string $table;
|
||||
protected array $settings;
|
||||
protected DatabaseMysql $dbSettings;
|
||||
private Pool $db;
|
||||
|
||||
// Legacy
|
||||
protected array $settings;
|
||||
|
||||
public function __sleep(): array
|
||||
{
|
||||
return ['table', 'settings'];
|
||||
return ['table', 'dbSettings'];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -177,18 +182,16 @@ class MysqlArray extends SqlArray
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function initConnection(array $settings): \Generator
|
||||
/**
|
||||
* Initialize connection.
|
||||
*
|
||||
* @param DatabaseMysql $settings
|
||||
* @return \Generator
|
||||
*/
|
||||
protected function initConnection($settings): \Generator
|
||||
{
|
||||
if (!isset($this->db)) {
|
||||
$this->db = yield from Mysql::getConnection(
|
||||
$settings['host'],
|
||||
$settings['port'],
|
||||
$settings['user'],
|
||||
$settings['password'],
|
||||
$settings['database'],
|
||||
$settings['max_connections'],
|
||||
$settings['idle_timeout']
|
||||
);
|
||||
$this->db = yield from Mysql::getConnection($settings);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,26 +9,29 @@ use Amp\Sql\ResultSet;
|
||||
use Amp\Success;
|
||||
use danog\MadelineProto\Db\Driver\Postgres;
|
||||
use danog\MadelineProto\Logger;
|
||||
use danog\MadelineProto\Settings\Database\Postgres as DatabasePostgres;
|
||||
|
||||
use function Amp\call;
|
||||
|
||||
class PostgresArray extends SqlArray
|
||||
{
|
||||
protected string $table;
|
||||
protected array $settings;
|
||||
public DatabasePostgres $dbSettings;
|
||||
private Pool $db;
|
||||
|
||||
protected function initConnection(array $settings): \Generator
|
||||
// Legacy
|
||||
protected array $settings;
|
||||
|
||||
/**
|
||||
* Initialize connection.
|
||||
*
|
||||
* @param DatabasePostgres $settings
|
||||
* @return \Generator
|
||||
*/
|
||||
protected function initConnection($settings): \Generator
|
||||
{
|
||||
if (!isset($this->db)) {
|
||||
$this->db = yield from Postgres::getConnection(
|
||||
$settings['host'],
|
||||
$settings['port'],
|
||||
$settings['user'],
|
||||
$settings['password'],
|
||||
$settings['database'],
|
||||
$settings['max_connections'],
|
||||
$settings['idle_timeout']
|
||||
);
|
||||
$this->db = yield from Postgres::getConnection($settings);
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,7 +110,7 @@ class PostgresArray extends SqlArray
|
||||
|
||||
public function __sleep()
|
||||
{
|
||||
return ['table', 'settings'];
|
||||
return ['table', 'dbSettings'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,6 +8,7 @@ use Amp\Redis\Redis as RedisRedis;
|
||||
use Amp\Success;
|
||||
use danog\MadelineProto\Db\Driver\Redis as Redis;
|
||||
use danog\MadelineProto\Logger;
|
||||
use danog\MadelineProto\Settings\Database\Redis as DatabaseRedis;
|
||||
use Generator;
|
||||
|
||||
use function Amp\call;
|
||||
@ -15,9 +16,12 @@ use function Amp\call;
|
||||
class RedisArray extends SqlArray
|
||||
{
|
||||
protected string $table;
|
||||
protected array $settings;
|
||||
public DatabaseRedis $dbSettings;
|
||||
private RedisRedis $db;
|
||||
|
||||
// Legacy
|
||||
protected array $settings;
|
||||
|
||||
protected function prepareTable(): Generator
|
||||
{
|
||||
yield new Success;
|
||||
@ -39,22 +43,24 @@ class RedisArray extends SqlArray
|
||||
}
|
||||
}
|
||||
|
||||
protected function initConnection(array $settings): \Generator
|
||||
/**
|
||||
* Initialize connection.
|
||||
*
|
||||
* @param DatabaseRedis $settings
|
||||
* @return \Generator
|
||||
*/
|
||||
protected function initConnection($settings): \Generator
|
||||
{
|
||||
if (!isset($this->db)) {
|
||||
$this->db = yield from Redis::getConnection(
|
||||
$settings['host'],
|
||||
$settings['port'],
|
||||
$settings['password'],
|
||||
$settings['database']
|
||||
);
|
||||
$this->db = yield from Redis::getConnection($settings);
|
||||
}
|
||||
}
|
||||
|
||||
public function __sleep()
|
||||
{
|
||||
return ['table', 'settings'];
|
||||
return ['table', 'dbSettings'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get redis key name.
|
||||
*
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace danog\MadelineProto\Db;
|
||||
|
||||
use Amp\Promise;
|
||||
use danog\MadelineProto\Settings\Database\DatabaseAbstract;
|
||||
|
||||
use function Amp\call;
|
||||
|
||||
@ -22,11 +23,11 @@ abstract class SqlArray extends DriverArray
|
||||
* @param string $name
|
||||
* @param DbArray|array|null $value
|
||||
* @param string $tablePrefix
|
||||
* @param array $settings
|
||||
* @param DatabaseAbstract $settings
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public static function getInstance(string $name, $value = null, string $tablePrefix = '', array $settings = []): Promise
|
||||
public static function getInstance(string $name, $value = null, string $tablePrefix = '', $settings): Promise
|
||||
{
|
||||
$tableName = "{$tablePrefix}_{$name}";
|
||||
if ($value instanceof static && $value->table === $tableName) {
|
||||
@ -36,8 +37,8 @@ abstract class SqlArray extends DriverArray
|
||||
$instance->table = $tableName;
|
||||
}
|
||||
|
||||
$instance->settings = $settings;
|
||||
$instance->ttl = $settings['cache_ttl'] ?? $instance->ttl;
|
||||
$instance->dbSettings = $settings;
|
||||
$instance->ttl = $settings->getCacheTtl();
|
||||
|
||||
$instance->startCacheCleanupLoop();
|
||||
|
||||
@ -48,7 +49,7 @@ abstract class SqlArray extends DriverArray
|
||||
// Skip migrations if its same object
|
||||
if ($instance !== $value) {
|
||||
if ($value instanceof DriverArray) {
|
||||
yield from $value->initConnection($value->settings);
|
||||
yield from $value->initConnection($value->dbSettings);
|
||||
}
|
||||
yield from static::renameTmpTable($instance, $value);
|
||||
yield from static::migrateDataToDb($instance, $value);
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
use danog\MadelineProto\Settings\TLSchema;
|
||||
use danog\MadelineProto\TL\TL;
|
||||
|
||||
// This code was written a few years ago: it is garbage, and has to be rewritten
|
||||
@ -48,7 +49,9 @@ class DocsBuilder
|
||||
$this->logger = $logger;
|
||||
}
|
||||
});
|
||||
$this->TL->init($settings['tl_schema']);
|
||||
$new = new TLSchema;
|
||||
$new->mergeArray($settings['tl_schema']);
|
||||
$this->TL->init($new);
|
||||
if (isset($settings['tl_schema']['td']) && !isset($settings['tl_schema']['telegram'])) {
|
||||
$this->td = true;
|
||||
}
|
||||
|
@ -22,18 +22,19 @@ namespace danog\MadelineProto;
|
||||
/**
|
||||
* Event handler.
|
||||
*/
|
||||
class EventHandler extends InternalDoc
|
||||
abstract class EventHandler extends InternalDoc
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
* Internal constructor.
|
||||
*
|
||||
* @param APIWrapper|null $MadelineProto MadelineProto instance
|
||||
* @internal
|
||||
*
|
||||
* @param APIWrapper $MadelineProto MadelineProto instance
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(?APIWrapper $MadelineProto)
|
||||
public function initInternal(APIWrapper $MadelineProto): void
|
||||
{
|
||||
if (!$MadelineProto) {
|
||||
return;
|
||||
}
|
||||
self::link($this, $MadelineProto->getFactory());
|
||||
$this->API =& $MadelineProto->getAPI();
|
||||
foreach ($this->API->getMethodNamespaces() as $namespace) {
|
||||
|
@ -5249,9 +5249,9 @@ class InternalDoc extends APIFactory
|
||||
/**
|
||||
* Return current settings array.
|
||||
*
|
||||
* @return array
|
||||
* @return Settings
|
||||
*/
|
||||
public function getSettings(): array
|
||||
public function getSettings(): Settings
|
||||
{
|
||||
return $this->API->getSettings();
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class Client
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->server = $server;
|
||||
Tools::callFork($this->loop());
|
||||
Tools::callFork($this->loopInternal());
|
||||
}
|
||||
/**
|
||||
* Logger.
|
||||
@ -78,7 +78,7 @@ class Client
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
private function loop(): \Generator
|
||||
private function loopInternal(): \Generator
|
||||
{
|
||||
while ($payload = yield $this->server->receive()) {
|
||||
[$id, $payload] = $payload;
|
||||
@ -96,6 +96,17 @@ class Client
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Run the provided async callable.
|
||||
*
|
||||
* @param callable $callback Async callable to run
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function loop(callable $callback): \Generator
|
||||
{
|
||||
return yield $callback();
|
||||
}
|
||||
/**
|
||||
* Unreference.
|
||||
*
|
||||
|
@ -22,6 +22,7 @@ namespace danog\MadelineProto;
|
||||
use Amp\ByteStream\ResourceOutputStream;
|
||||
use Amp\Failure;
|
||||
use Amp\Loop;
|
||||
use danog\MadelineProto\Settings\Logger as SettingsLogger;
|
||||
|
||||
use function Amp\ByteStream\getStderr;
|
||||
use function Amp\ByteStream\getStdout;
|
||||
@ -44,7 +45,7 @@ class Logger
|
||||
/**
|
||||
* Optional logger parameter.
|
||||
*
|
||||
* @var mixed
|
||||
* @var null|string|callable
|
||||
*/
|
||||
public $optional = null;
|
||||
/**
|
||||
@ -80,7 +81,7 @@ class Logger
|
||||
/**
|
||||
* Default logger instance.
|
||||
*
|
||||
* @var self
|
||||
* @var ?self
|
||||
*/
|
||||
public static $default;
|
||||
/**
|
||||
@ -93,117 +94,105 @@ class Logger
|
||||
* Log rotation loop ID.
|
||||
*/
|
||||
private string $rotateId = '';
|
||||
/**
|
||||
* Ultra verbose logging.
|
||||
*/
|
||||
const ULTRA_VERBOSE = 5;
|
||||
/**
|
||||
* Verbose logging.
|
||||
*/
|
||||
const VERBOSE = 4;
|
||||
/**
|
||||
* Notice logging.
|
||||
*/
|
||||
const NOTICE = 3;
|
||||
/**
|
||||
* Warning logging.
|
||||
*/
|
||||
const WARNING = 2;
|
||||
/**
|
||||
* Error logging.
|
||||
*/
|
||||
const ERROR = 1;
|
||||
/**
|
||||
* Log only fatal errors.
|
||||
*/
|
||||
const FATAL_ERROR = 0;
|
||||
|
||||
/**
|
||||
* Disable logger (DEPRECATED).
|
||||
*/
|
||||
const NO_LOGGER = 0;
|
||||
/**
|
||||
* Default logger (syslog).
|
||||
*/
|
||||
const DEFAULT_LOGGER = 1;
|
||||
/**
|
||||
* File logger.
|
||||
*/
|
||||
const FILE_LOGGER = 2;
|
||||
/**
|
||||
* Echo logger.
|
||||
*/
|
||||
const ECHO_LOGGER = 3;
|
||||
/**
|
||||
* Callable logger.
|
||||
*/
|
||||
const CALLABLE_LOGGER = 4;
|
||||
|
||||
const LEVEL_ULTRA_VERBOSE = self::ULTRA_VERBOSE;
|
||||
const LEVEL_VERBOSE = self::VERBOSE;
|
||||
const LEVEL_NOTICE = self::NOTICE;
|
||||
const LEVEL_WARNING = self::WARNING;
|
||||
const LEVEL_ERROR = self::ERROR;
|
||||
const LEVEL_FATAL = self::FATAL_ERROR;
|
||||
|
||||
const LOGGER_DEFAULT = self::DEFAULT_LOGGER;
|
||||
const LOGGER_ECHO = self::ECHO_LOGGER;
|
||||
const LOGGER_FILE = self::FILE_LOGGER;
|
||||
const LOGGER_CALLABLE = self::CALLABLE_LOGGER;
|
||||
/**
|
||||
* Construct global static logger from MadelineProto settings.
|
||||
*
|
||||
* @param array $settings Settings array
|
||||
* @param SettingsLogger $settings Settings instance
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function constructorFromSettings(array $settings)
|
||||
public static function constructorFromSettings(SettingsLogger $settings): void
|
||||
{
|
||||
if (!self::$default) {
|
||||
// The getLogger function will automatically init the static logger, but we'll do it again anyway
|
||||
self::$default = self::getLoggerFromSettings(MTProto::parseSettings($settings));
|
||||
self::$default = new self($settings);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get logger from MadelineProto settings.
|
||||
*
|
||||
* @param array $settings Settings array
|
||||
* @param string $prefix Optional prefix for log messages
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getLoggerFromSettings(array $settings, string $prefix = ''): self
|
||||
{
|
||||
if (!isset($settings['logger']['logger_param']) && isset($settings['logger']['param'])) {
|
||||
$settings['logger']['logger_param'] = $settings['logger']['param'];
|
||||
}
|
||||
if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' && isset($settings['logger']['logger_param']) && $settings['logger']['logger_param'] === 'MadelineProto.log') {
|
||||
$settings['logger']['logger_param'] = Magic::$script_cwd.'/MadelineProto.log';
|
||||
}
|
||||
$logger = new self($settings['logger']['logger'], $settings['logger']['logger_param'] ?? '', $prefix, $settings['logger']['logger_level'] ?? Logger::VERBOSE, $settings['logger']['max_size'] ?? 100 * 1024 * 1024);
|
||||
if (!self::$default) {
|
||||
self::$default = $logger;
|
||||
}
|
||||
if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
|
||||
try {
|
||||
\error_reporting(E_ALL);
|
||||
\ini_set('log_errors', 1);
|
||||
\ini_set('error_log', $settings['logger']['logger'] === self::FILE_LOGGER ? $settings['logger']['logger_param'] : Magic::$script_cwd.'/MadelineProto.log');
|
||||
\error_log('Enabled PHP logging');
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$logger->logger('Could not enable PHP logging');
|
||||
}
|
||||
}
|
||||
return $logger;
|
||||
}
|
||||
/**
|
||||
* Construct global logger.
|
||||
*
|
||||
* @param int $mode One of the logger constants
|
||||
* @param mixed $optional Optional parameter for logger
|
||||
* @param string $prefix Prefix for log messages
|
||||
* @param int $level Default logging level
|
||||
* @param int $max_size Maximum size for logfile
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function constructor(int $mode, $optional = null, string $prefix = '', int $level = self::NOTICE, int $max_size = 100 * 1024 * 1024)
|
||||
{
|
||||
self::$default = new self($mode, $optional, $prefix, $level, $max_size);
|
||||
}
|
||||
/**
|
||||
* Construct global logger.
|
||||
*
|
||||
* @param int $mode One of the logger constants
|
||||
* @param mixed $optional Optional parameter for logger
|
||||
* @param string $prefix Prefix for log messages
|
||||
* @param int $level Default logging level
|
||||
* @param int $max_size Maximum size for logfile
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(int $mode, $optional = null, string $prefix = '', int $level = self::NOTICE, int $max_size = 10 * 1024 * 1024)
|
||||
{
|
||||
if ($mode === null) {
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['no_mode_specified']);
|
||||
}
|
||||
if ($mode === self::NO_LOGGER) {
|
||||
$mode = (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') ? Logger::ECHO_LOGGER : Logger::FILE_LOGGER;
|
||||
}
|
||||
if (\defined(\MADELINE_WORKER::class)) {
|
||||
$mode = Logger::FILE_LOGGER;
|
||||
}
|
||||
$level = \max($level, self::NOTICE);
|
||||
$max_size = \max($max_size, 100 * 1024);
|
||||
|
||||
$this->mode = $mode;
|
||||
$this->optional = $mode == self::FILE_LOGGER ? Tools::absolute($optional) : $optional;
|
||||
/**
|
||||
* Construct logger.
|
||||
*
|
||||
* @param SettingsLogger $settings
|
||||
* @param string $prefix
|
||||
*/
|
||||
public function __construct(SettingsLogger $settings, string $prefix = '')
|
||||
{
|
||||
$this->prefix = $prefix === '' ? '' : ', '.$prefix;
|
||||
$this->level = $level;
|
||||
if ($this->mode === self::FILE_LOGGER && !\file_exists(\pathinfo($this->optional, PATHINFO_DIRNAME))) {
|
||||
|
||||
$this->mode = $settings->getType();
|
||||
$this->optional = $settings->getExtra();
|
||||
$this->level = $settings->getLevel();
|
||||
|
||||
$maxSize = $settings->getMaxSize();
|
||||
|
||||
if ($this->mode === self::FILE_LOGGER) {
|
||||
if (!\file_exists(\pathinfo($this->optional, PATHINFO_DIRNAME))) {
|
||||
$this->optional = Magic::$script_cwd.'/MadelineProto.log';
|
||||
}
|
||||
if ($this->mode === self::FILE_LOGGER && !\preg_match('/\\.log$/', $this->optional)) {
|
||||
if (!str_ends_with($this->optional, '.log')) {
|
||||
$this->optional .= '.log';
|
||||
}
|
||||
if ($mode === self::FILE_LOGGER && $max_size !== -1 && \file_exists($this->optional) && \filesize($this->optional) > $max_size) {
|
||||
if ($maxSize !== -1 && \file_exists($this->optional) && \filesize($this->optional) > $maxSize) {
|
||||
\unlink($this->optional);
|
||||
}
|
||||
}
|
||||
$this->colors[self::ULTRA_VERBOSE] = \implode(';', [self::FOREGROUND['light_gray'], self::SET['dim']]);
|
||||
$this->colors[self::VERBOSE] = \implode(';', [self::FOREGROUND['green'], self::SET['bold']]);
|
||||
$this->colors[self::NOTICE] = \implode(';', [self::FOREGROUND['yellow'], self::SET['bold']]);
|
||||
@ -219,16 +208,16 @@ class Logger
|
||||
} elseif ($this->mode === self::FILE_LOGGER) {
|
||||
Snitch::logFile($this->optional);
|
||||
$this->stdout = new ResourceOutputStream(\fopen($this->optional, 'a'));
|
||||
if ($max_size !== -1) {
|
||||
if ($maxSize !== -1) {
|
||||
$this->rotateId = Loop::repeat(
|
||||
10*1000,
|
||||
function () use ($max_size) {
|
||||
function () use ($maxSize) {
|
||||
\clearstatcache(true, $this->optional);
|
||||
if (\file_exists($this->optional) && \filesize($this->optional) >= $max_size) {
|
||||
if (\file_exists($this->optional) && \filesize($this->optional) >= $maxSize) {
|
||||
$this->stdout = null;
|
||||
\unlink($this->optional);
|
||||
$this->stdout = new ResourceOutputStream(\fopen($this->optional, 'a'));
|
||||
$this->logger("Automatically truncated logfile to $max_size");
|
||||
$this->logger("Automatically truncated logfile to $maxSize");
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -244,6 +233,22 @@ class Logger
|
||||
$this->stdout = getStderr();
|
||||
}
|
||||
}
|
||||
|
||||
if (!self::$default) {
|
||||
self::$default = $this;
|
||||
}
|
||||
if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
|
||||
try {
|
||||
\error_reporting(E_ALL);
|
||||
\ini_set('log_errors', 1);
|
||||
\ini_set('error_log', $this->mode === self::FILE_LOGGER
|
||||
? $this->optional
|
||||
: Magic::$script_cwd.'/MadelineProto.log');
|
||||
\error_log('Enabled PHP logging');
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->logger('Could not enable PHP logging');
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Destructor function.
|
||||
|
@ -43,7 +43,7 @@ class CheckLoop extends ResumableSignalLoop
|
||||
$datacenter = $this->datacenter;
|
||||
$connection = $this->connection;
|
||||
$shared = $this->datacenterConnection;
|
||||
$timeout = $shared->getSettings()['timeout'];
|
||||
$timeout = $shared->getSettings()->getTimeout();
|
||||
$timeoutMs = $timeout * 1000;
|
||||
$timeoutResend = $timeout * $timeout;
|
||||
// Typically 25 seconds, good enough
|
||||
|
@ -40,7 +40,7 @@ class PingLoop extends ResumableSignalLoop
|
||||
$datacenter = $this->datacenter;
|
||||
$connection = $this->connection;
|
||||
$shared = $this->datacenterConnection;
|
||||
$timeout = $shared->getSettings()['timeout'];
|
||||
$timeout = $shared->getSettings()->getTimeout();
|
||||
$timeoutMs = $timeout * 1000;
|
||||
while (true) {
|
||||
while (!$shared->hasTempAuthKey()) {
|
||||
|
@ -165,7 +165,7 @@ class WriteLoop extends ResumableSignalLoop
|
||||
unset($connection->pending_outgoing[$k]);
|
||||
continue;
|
||||
}
|
||||
if ($shared->getSettings()['pfs'] && !$shared->isBound() && !$connection->isCDN() && !\in_array($message['_'], ['http_wait', 'auth.bindTempAuthKey']) && $message['method']) {
|
||||
if ($shared->getGenericSettings()->getAuth()->getPfs() && !$shared->isBound() && !$connection->isCDN() && !\in_array($message['_'], ['http_wait', 'auth.bindTempAuthKey']) && $message['method']) {
|
||||
$API->logger->logger("Skipping {$message['_']} due to unbound keys in DC {$datacenter}");
|
||||
$skipped = true;
|
||||
continue;
|
||||
@ -210,7 +210,7 @@ class WriteLoop extends ResumableSignalLoop
|
||||
if (!$shared->getTempAuthKey()->isInited() && $message['_'] !== 'auth.bindTempAuthKey' && !$inited) {
|
||||
$inited = true;
|
||||
$API->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['write_client_info'], $message['_']), \danog\MadelineProto\Logger::NOTICE);
|
||||
$MTmessage['body'] = (yield from $API->getTL()->serializeMethod('invokeWithLayer', ['layer' => $API->settings['tl_schema']['layer'], 'query' => yield from $API->getTL()->serializeMethod('initConnection', ['api_id' => $API->settings['app_info']['api_id'], 'api_hash' => $API->settings['app_info']['api_hash'], 'device_model' => !$connection->isCDN() ? $API->settings['app_info']['device_model'] : 'n/a', 'system_version' => !$connection->isCDN() ? $API->settings['app_info']['system_version'] : 'n/a', 'app_version' => $API->settings['app_info']['app_version'], 'system_lang_code' => $API->settings['app_info']['lang_code'], 'lang_code' => $API->settings['app_info']['lang_code'], 'lang_pack' => $API->settings['app_info']['lang_pack'], 'proxy' => $connection->getCtx()->getInputClientProxy(), 'query' => $MTmessage['body']])]));
|
||||
$MTmessage['body'] = (yield from $API->getTL()->serializeMethod('invokeWithLayer', ['layer' => $API->settings->getSchema()->getLayer(), 'query' => yield from $API->getTL()->serializeMethod('initConnection', ['api_id' => $API->settings->getAppInfo()->getApiId(), 'api_hash' => $API->settings->getAppInfo()->getApiHash(), 'device_model' => !$connection->isCDN() ? $API->settings->getAppInfo()->getDeviceModel() : 'n/a', 'system_version' => !$connection->isCDN() ? $API->settings->getAppInfo()->getSystemVersion() : 'n/a', 'app_version' => $API->settings->getAppInfo()->getAppVersion(), 'system_lang_code' => $API->settings->getAppInfo()->getLangCode(), 'lang_code' => $API->settings->getAppInfo()->getLangCode(), 'lang_pack' => $API->settings->getAppInfo()->getLangPack(), 'proxy' => $connection->getCtx()->getInputClientProxy(), 'query' => $MTmessage['body']])]));
|
||||
} else {
|
||||
if (isset($message['queue'])) {
|
||||
if (!isset($connection->call_queue[$message['queue']])) {
|
||||
@ -218,7 +218,7 @@ class WriteLoop extends ResumableSignalLoop
|
||||
}
|
||||
$MTmessage['body'] = (yield from $API->getTL()->serializeMethod('invokeAfterMsgs', ['msg_ids' => $connection->call_queue[$message['queue']], 'query' => $MTmessage['body']]));
|
||||
$connection->call_queue[$message['queue']][$message_id] = $message_id;
|
||||
if (\count($connection->call_queue[$message['queue']]) > $API->settings['msg_array_limit']['call_queue']) {
|
||||
if (\count($connection->call_queue[$message['queue']]) > $API->settings->getRpc()->getLimitCallQueue()) {
|
||||
\reset($connection->call_queue[$message['queue']]);
|
||||
$key = \key($connection->call_queue[$message['queue']]);
|
||||
unset($connection->call_queue[$message['queue']][$key]);
|
||||
|
@ -75,17 +75,14 @@ class FeedLoop extends ResumableSignalLoop
|
||||
{
|
||||
$API = $this->API;
|
||||
$this->updater = $API->updaters[$this->channelId];
|
||||
if (!$this->API->settings['updates']['handle_updates']) {
|
||||
return false;
|
||||
}
|
||||
while (!$this->API->settings['updates']['handle_updates'] || !$API->hasAllAuth()) {
|
||||
while (!$API->hasAllAuth()) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$this->state = $this->channelId === self::GENERIC ? yield from $API->loadUpdateState() : $API->loadChannelState($this->channelId);
|
||||
while (true) {
|
||||
while (!$this->API->settings['updates']['handle_updates'] || !$API->hasAllAuth()) {
|
||||
while (!$API->hasAllAuth()) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
return;
|
||||
}
|
||||
@ -93,9 +90,6 @@ class FeedLoop extends ResumableSignalLoop
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
return;
|
||||
}
|
||||
if (!$this->API->settings['updates']['handle_updates']) {
|
||||
return;
|
||||
}
|
||||
$API->logger->logger("Resumed {$this}");
|
||||
while ($this->incomingUpdates) {
|
||||
$updates = $this->incomingUpdates;
|
||||
|
@ -51,17 +51,14 @@ class SeqLoop extends ResumableSignalLoop
|
||||
{
|
||||
$API = $this->API;
|
||||
$this->feeder = $API->feeders[FeedLoop::GENERIC];
|
||||
if (!$this->API->settings['updates']['handle_updates']) {
|
||||
return false;
|
||||
}
|
||||
while (!$this->API->settings['updates']['handle_updates'] || !$API->hasAllAuth()) {
|
||||
while (!$API->hasAllAuth()) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$this->state = (yield from $API->loadUpdateState());
|
||||
while (true) {
|
||||
while (!$this->API->settings['updates']['handle_updates'] || !$API->hasAllAuth()) {
|
||||
while (!$API->hasAllAuth()) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
return;
|
||||
}
|
||||
@ -69,9 +66,6 @@ class SeqLoop extends ResumableSignalLoop
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
return;
|
||||
}
|
||||
if (!$this->API->settings['updates']['handle_updates']) {
|
||||
return;
|
||||
}
|
||||
while ($this->incomingUpdates) {
|
||||
$updates = $this->incomingUpdates;
|
||||
$this->incomingUpdates = [];
|
||||
|
@ -69,17 +69,17 @@ class UpdateLoop extends ResumableSignalLoop
|
||||
{
|
||||
$API = $this->API;
|
||||
$feeder = $this->feeder = $API->feeders[$this->channelId];
|
||||
while (!$API->settings['updates']['handle_updates'] || !$API->hasAllAuth()) {
|
||||
while (!$API->hasAllAuth()) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
$API->logger->logger("Exiting {$this} due to signal");
|
||||
return;
|
||||
}
|
||||
}
|
||||
$this->state = $state = $this->channelId === self::GENERIC ? yield from $API->loadUpdateState() : $API->loadChannelState($this->channelId);
|
||||
$timeout = $API->settings['updates']['getdifference_interval'] * 1000;
|
||||
$timeout = 30 * 1000;
|
||||
$first = true;
|
||||
while (true) {
|
||||
while (!$API->settings['updates']['handle_updates'] || !$API->hasAllAuth()) {
|
||||
while (!$API->hasAllAuth()) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
$API->logger->logger("Exiting {$this} due to signal");
|
||||
return;
|
||||
@ -160,7 +160,7 @@ class UpdateLoop extends ResumableSignalLoop
|
||||
}
|
||||
} else {
|
||||
$API->logger->logger('Resumed and fetching normal difference...', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$difference = yield from $API->methodCallAsyncRead('updates.getDifference', ['pts' => $state->pts(), 'date' => $state->date(), 'qts' => $state->qts()], ['datacenter' => $API->settings['connection_settings']['default_dc']]);
|
||||
$difference = yield from $API->methodCallAsyncRead('updates.getDifference', ['pts' => $state->pts(), 'date' => $state->date(), 'qts' => $state->qts()], $API->settings->getDefaultDcParams());
|
||||
$API->logger->logger('Got '.$difference['_'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
switch ($difference['_']) {
|
||||
case 'updates.differenceEmpty':
|
||||
|
@ -22,7 +22,7 @@ namespace danog\MadelineProto;
|
||||
class Lua
|
||||
{
|
||||
use \danog\Serializable;
|
||||
public $MadelineProto;
|
||||
public API $MadelineProto;
|
||||
protected $Lua;
|
||||
protected $script;
|
||||
public function __magic_construct($script, $MadelineProto)
|
||||
@ -31,8 +31,7 @@ class Lua
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['script_not_exist']);
|
||||
}
|
||||
$this->MadelineProto = $MadelineProto;
|
||||
$this->MadelineProto->settings['updates']['handle_updates'] = true;
|
||||
$this->MadelineProto->API->datacenter->sockets[$this->MadelineProto->settings['connection_settings']['default_dc']]->startUpdateLoop();
|
||||
$this->MadelineProto->API->datacenter->sockets[$this->MadelineProto->getSettings()->getConnection()->getDefaultDc()]->startUpdateLoop();
|
||||
$this->script = $script;
|
||||
$this->__wakeup();
|
||||
}
|
||||
|
@ -23,12 +23,11 @@ use Amp\Dns\Resolver;
|
||||
use Amp\File\StatCache;
|
||||
use Amp\Http\Client\HttpClient;
|
||||
use Amp\Promise;
|
||||
use Closure;
|
||||
use danog\MadelineProto\Async\AsyncConstruct;
|
||||
use danog\MadelineProto\Db\DbArray;
|
||||
use danog\MadelineProto\Db\DbPropertiesFactory;
|
||||
use danog\MadelineProto\Db\DbPropertiesTrait;
|
||||
use danog\MadelineProto\Db\Driver\Redis;
|
||||
use danog\MadelineProto\Db\Mysql;
|
||||
use danog\MadelineProto\Ipc\Server;
|
||||
use danog\MadelineProto\Loop\Generic\PeriodicLoopInternal;
|
||||
use danog\MadelineProto\Loop\Update\FeedLoop;
|
||||
@ -39,6 +38,8 @@ use danog\MadelineProto\MTProtoTools\GarbageCollector;
|
||||
use danog\MadelineProto\MTProtoTools\MinDatabase;
|
||||
use danog\MadelineProto\MTProtoTools\ReferenceDatabase;
|
||||
use danog\MadelineProto\MTProtoTools\UpdatesState;
|
||||
use danog\MadelineProto\Settings\Database\Memory;
|
||||
use danog\MadelineProto\Settings\TLSchema;
|
||||
use danog\MadelineProto\TL\TL;
|
||||
use danog\MadelineProto\TL\TLCallback;
|
||||
|
||||
@ -160,6 +161,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
* @var int
|
||||
*/
|
||||
const SECRET_READY = 2;
|
||||
const GETUPDATES_HANDLER = 'getUpdates';
|
||||
const TD_PARAMS_CONVERSION = ['updateNewMessage' => ['_' => 'updateNewMessage', 'disable_notification' => ['message', 'silent'], 'message' => ['message']], 'message' => ['_' => 'message', 'id' => ['id'], 'sender_user_id' => ['from_id'], 'chat_id' => ['to_id', 'choose_chat_id_from_botapi'], 'send_state' => ['choose_incoming_or_sent'], 'can_be_edited' => ['choose_can_edit'], 'can_be_deleted' => ['choose_can_delete'], 'is_post' => ['post'], 'date' => ['date'], 'edit_date' => ['edit_date'], 'forward_info' => ['fwd_info', 'choose_forward_info'], 'reply_to_message_id' => ['reply_to_msg_id'], 'ttl' => ['choose_ttl'], 'ttl_expires_in' => ['choose_ttl_expires_in'], 'via_bot_user_id' => ['via_bot_id'], 'views' => ['views'], 'content' => ['choose_message_content'], 'reply_markup' => ['reply_markup']], 'messages.sendMessage' => ['chat_id' => ['peer'], 'reply_to_message_id' => ['reply_to_msg_id'], 'disable_notification' => ['silent'], 'from_background' => ['background'], 'input_message_content' => ['choose_message_content'], 'reply_markup' => ['reply_markup']]];
|
||||
const TD_REVERSE = ['sendMessage' => 'messages.sendMessage'];
|
||||
const TD_IGNORE = ['updateMessageID'];
|
||||
@ -213,9 +215,9 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
/**
|
||||
* Settings array.
|
||||
*
|
||||
* @var array
|
||||
* @var Settings
|
||||
*/
|
||||
public $settings = [];
|
||||
public $settings;
|
||||
/**
|
||||
* Config array.
|
||||
*
|
||||
@ -413,6 +415,58 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
*/
|
||||
private Snitch $snitch;
|
||||
|
||||
/**
|
||||
* DC list.
|
||||
*/
|
||||
protected array $dcList = [
|
||||
'test' => [
|
||||
// Test datacenters
|
||||
'ipv4' => [
|
||||
// ipv4 addresses
|
||||
2 => [
|
||||
// The rest will be fetched using help.getConfig
|
||||
'ip_address' => '149.154.167.40',
|
||||
'port' => 443,
|
||||
'media_only' => false,
|
||||
'tcpo_only' => false,
|
||||
],
|
||||
],
|
||||
'ipv6' => [
|
||||
// ipv6 addresses
|
||||
2 => [
|
||||
// The rest will be fetched using help.getConfig
|
||||
'ip_address' => '2001:067c:04e8:f002:0000:0000:0000:000e',
|
||||
'port' => 443,
|
||||
'media_only' => false,
|
||||
'tcpo_only' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
'main' => [
|
||||
// Main datacenters
|
||||
'ipv4' => [
|
||||
// ipv4 addresses
|
||||
2 => [
|
||||
// The rest will be fetched using help.getConfig
|
||||
'ip_address' => '149.154.167.51',
|
||||
'port' => 443,
|
||||
'media_only' => false,
|
||||
'tcpo_only' => false,
|
||||
],
|
||||
],
|
||||
'ipv6' => [
|
||||
// ipv6 addresses
|
||||
2 => [
|
||||
// The rest will be fetched using help.getConfig
|
||||
'ip_address' => '2001:067c:04e8:f002:0000:0000:0000:000a',
|
||||
'port' => 443,
|
||||
'media_only' => false,
|
||||
'tcpo_only' => false,
|
||||
],
|
||||
],
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* List of properties stored in database (memory or external).
|
||||
* @see DbPropertiesFactory
|
||||
@ -428,32 +482,36 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
/**
|
||||
* Constructor function.
|
||||
*
|
||||
* @param array $settings Settings
|
||||
* @param Settings|SettingsEmpty $settings Settings
|
||||
* @param APIWrapper $wrapper API wrapper
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __magic_construct($settings = [])
|
||||
public function __magic_construct(SettingsAbstract $settings, APIWrapper $wrapper)
|
||||
{
|
||||
$this->wrapper = $wrapper;
|
||||
$this->setInitPromise($this->__construct_async($settings));
|
||||
}
|
||||
/**
|
||||
* Async constructor function.
|
||||
*
|
||||
* @param array $settings Settings
|
||||
* @param Settings|SettingsEmpty $settings Settings
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function __construct_async($settings = []): \Generator
|
||||
public function __construct_async(SettingsAbstract $settings): \Generator
|
||||
{
|
||||
// Initialize needed stuffs
|
||||
Magic::classExists();
|
||||
// Parse and store settings
|
||||
yield from $this->updateSettings($settings, false);
|
||||
// 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['authorization']['rsa_keys'] as $key) {
|
||||
foreach ($this->settings->getAuth()->getRsaKeys() as $key) {
|
||||
$key = (yield from (new RSA())->load($this->TL, $key));
|
||||
$this->rsa_keys[$key->fp] = $key;
|
||||
}
|
||||
@ -463,7 +521,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
if (!($this->authorization['user']['bot'] ?? false)) {
|
||||
$callbacks[] = $this->minDatabase;
|
||||
}
|
||||
$this->TL->init($this->settings['tl_schema']['src'], $callbacks);
|
||||
$this->TL->init($this->settings->getSchema(), $callbacks);
|
||||
yield from $this->connectToAllDcs();
|
||||
$this->startLoops();
|
||||
$this->datacenter->curdc = 2;
|
||||
@ -472,7 +530,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
$nearest_dc = yield from $this->methodCallAsyncRead('help.getNearestDc', [], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->logger->logger(\sprintf(Lang::$current_lang['nearest_dc'], $nearest_dc['country'], $nearest_dc['nearest_dc']), Logger::NOTICE);
|
||||
if ($nearest_dc['nearest_dc'] != $nearest_dc['this_dc']) {
|
||||
$this->settings['connection_settings']['default_dc'] = $this->datacenter->curdc = (int) $nearest_dc['nearest_dc'];
|
||||
$this->settings->setDefaultDc($this->datacenter->curdc = (int) $nearest_dc['nearest_dc']);
|
||||
}
|
||||
} catch (RPCErrorException $e) {
|
||||
if ($e->rpc !== 'BOT_METHOD_INVALID') {
|
||||
@ -486,6 +544,13 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
|
||||
GarbageCollector::start();
|
||||
}
|
||||
/**
|
||||
* Set API wrapper needed for triggering serialization functions.
|
||||
*/
|
||||
public function setWrapper(APIWrapper $wrapper): void
|
||||
{
|
||||
$this->wrapper = $wrapper;
|
||||
}
|
||||
/**
|
||||
* Sleep function.
|
||||
*
|
||||
@ -493,13 +558,11 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
*/
|
||||
public function __sleep(): array
|
||||
{
|
||||
if (
|
||||
$this->settings['serialization']['cleanup_before_serialization']
|
||||
&& $this->settings['db']['type'] === 'memory'
|
||||
) {
|
||||
$db = $this->settings->getDb();
|
||||
if ($db instanceof Memory && $db->getCleanup()) {
|
||||
$this->cleanup();
|
||||
}
|
||||
return [
|
||||
$res = [
|
||||
// Databases
|
||||
'chats',
|
||||
'full_chats',
|
||||
@ -529,6 +592,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
// Settings
|
||||
'settings',
|
||||
'config',
|
||||
'dcList',
|
||||
|
||||
// Authorization keys
|
||||
'datacenter',
|
||||
@ -561,6 +625,10 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
// Report URI
|
||||
'reportDest',
|
||||
];
|
||||
if (!$this->updateHandler instanceof Closure) {
|
||||
$res[] = 'updateHandler';
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -716,7 +784,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
$this->callCheckerLoop = new PeriodicLoopInternal($this, [$this, 'checkCalls'], 'call check', 10 * 1000);
|
||||
}
|
||||
if (!$this->serializeLoop) {
|
||||
$this->serializeLoop = new PeriodicLoopInternal($this, [$this, 'serialize'], 'serialize', $this->settings['serialization']['serialization_interval'] * 1000);
|
||||
$this->serializeLoop = new PeriodicLoopInternal($this, [$this, 'serialize'], 'serialize', $this->settings->getSerialization()->getInterval() * 1000);
|
||||
}
|
||||
if (!$this->phoneConfigLoop) {
|
||||
$this->phoneConfigLoop = new PeriodicLoopInternal($this, [$this, 'getPhoneConfig'], 'phone config', 24 * 3600 * 1000);
|
||||
@ -821,7 +889,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
unset($this->updates_state);
|
||||
}
|
||||
if (!isset($this->datacenter)) {
|
||||
$this->datacenter = new DataCenter($this, $this->settings['connection'], $this->settings['connection_settings']);
|
||||
$this->datacenter ??= new DataCenter($this, $this->dcList, $this->settings->getConnection());
|
||||
}
|
||||
if (!isset($this->referenceDatabase)) {
|
||||
$this->referenceDatabase = new ReferenceDatabase($this);
|
||||
@ -842,7 +910,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
if (!($this->authorization['user']['bot'] ?? false)) {
|
||||
$callbacks[] = $this->minDatabase;
|
||||
}
|
||||
$this->TL->init($this->settings['tl_schema']['src'], $callbacks);
|
||||
$this->TL->init($this->settings->getSchema(), $callbacks);
|
||||
}
|
||||
|
||||
yield from $this->initDb($this);
|
||||
@ -869,17 +937,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
$socket->authorized(true);
|
||||
}
|
||||
}
|
||||
$settings = $this->settings;
|
||||
if (isset($settings['updates']['callback'][0]) && $settings['updates']['callback'][0] === $this) {
|
||||
$settings['updates']['callback'] = 'getUpdatesUpdateHandler';
|
||||
}
|
||||
if (isset($settings['updates']['getdifference_interval']) && $settings['updates']['getdifference_interval'] === -1) {
|
||||
unset($settings['updates']['getdifference_interval']);
|
||||
}
|
||||
unset($settings['tl_schema']);
|
||||
if (isset($settings['authorization']['rsa_key'])) {
|
||||
unset($settings['authorization']['rsa_key']);
|
||||
}
|
||||
$this->settings->setSchema(new TLSchema);
|
||||
|
||||
yield from $this->initDb($this);
|
||||
|
||||
@ -907,35 +965,10 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
}
|
||||
unset($chat);
|
||||
|
||||
foreach ($settings['connection_settings'] as $key => &$connection) {
|
||||
if (\in_array($key, ['default_dc', 'media_socket_count', 'robin_period'])) {
|
||||
continue;
|
||||
}
|
||||
if (!\is_array($connection)) {
|
||||
unset($settings['connection_settings'][$key]);
|
||||
continue;
|
||||
}
|
||||
if (!isset($connection['proxy'])) {
|
||||
$connection['proxy'] = '\\Socket';
|
||||
}
|
||||
if (!isset($connection['proxy_extra'])) {
|
||||
$connection['proxy_extra'] = [];
|
||||
}
|
||||
if (!isset($connection['pfs'])) {
|
||||
$connection['pfs'] = \extension_loaded('gmp');
|
||||
}
|
||||
if ($connection['protocol'] === 'obfuscated2') {
|
||||
$connection['protocol'] = 'tcp_intermediate_padded';
|
||||
$connection['obfuscated'] = true;
|
||||
}
|
||||
}
|
||||
unset($connection);
|
||||
|
||||
$this->resetMTProtoSession(true, true);
|
||||
$this->config = ['expires' => -1];
|
||||
$this->dh_config = ['version' => 0];
|
||||
$this->settings = $settings;
|
||||
yield from $this->__construct_async($settings);
|
||||
yield from $this->__construct_async($this->settings);
|
||||
foreach ($this->secret_chats as $chat => $data) {
|
||||
try {
|
||||
if (isset($this->secret_chats[$chat]) && $this->secret_chats[$chat]['InputEncryptedChat'] !== null) {
|
||||
@ -946,53 +979,82 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Wakeup function.
|
||||
* Post-deserialization initialization function.
|
||||
*
|
||||
* @param Settings|SettingsEmpty $settings New settings
|
||||
* @param APIWrapper $wrapper API wrapper
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function __wakeup()
|
||||
public function wakeup(SettingsAbstract $settings, APIWrapper $wrapper): \Generator
|
||||
{
|
||||
$backtrace = \debug_backtrace(0, 4);
|
||||
$backtrace = \end($backtrace);
|
||||
// Set API wrapper
|
||||
$this->wrapper = $wrapper;
|
||||
// BC stuff
|
||||
if ($this->authorized === true) {
|
||||
$this->authorized = self::LOGGED_IN;
|
||||
}
|
||||
// Convert old array settings to new settings object
|
||||
if (\is_array($this->settings)) {
|
||||
if (($this->settings['updates']['callback'] ?? '') === 'getUpdatesUpdateHandler') {
|
||||
$this->settings['updates']['callback'] = [$this, 'getUpdatesUpdateHandler'];
|
||||
}
|
||||
if (\is_callable($this->settings['updates']['callback'] ?? null)) {
|
||||
$this->updateHandler = $this->settings['updates']['callback'];
|
||||
}
|
||||
|
||||
$this->dcList = $this->settings['connection'] ?? $this->dcList;
|
||||
}
|
||||
$this->settings = Settings::parseFromLegacy($this->settings);
|
||||
// Clean up phone call array
|
||||
foreach ($this->calls as $id => $controller) {
|
||||
if (!\is_object($controller)) {
|
||||
unset($this->calls[$id]);
|
||||
} elseif ($controller->getCallState() === VoIP::CALL_STATE_ENDED) {
|
||||
$controller->setMadeline($this);
|
||||
$controller->discard();
|
||||
} else {
|
||||
$controller->setMadeline($this);
|
||||
}
|
||||
}
|
||||
|
||||
$this->forceInit(false);
|
||||
$this->setInitPromise($this->__wakeup_async($backtrace));
|
||||
$this->setInitPromise($this->wakeupAsync($settings));
|
||||
|
||||
return $this->initAsynchronously();
|
||||
}
|
||||
/**
|
||||
* Async wakeup function.
|
||||
*
|
||||
* @param array $backtrace Stack trace
|
||||
* @param Settings|SettingsEmpty $settings New settings
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function __wakeup_async(array $backtrace): \Generator
|
||||
private function wakeupAsync(SettingsAbstract $settings): \Generator
|
||||
{
|
||||
// Setup one-time stuffs
|
||||
Magic::classExists();
|
||||
$this->settings->getConnection()->init();
|
||||
// Setup logger
|
||||
$this->setupLogger();
|
||||
// Setup language
|
||||
Lang::$current_lang =& Lang::$lang['en'];
|
||||
if (Lang::$lang[$this->settings['app_info']['lang_code'] ?? 'en'] ?? false) {
|
||||
Lang::$current_lang =& Lang::$lang[$this->settings['app_info']['lang_code']];
|
||||
if (Lang::$lang[$this->settings->getAppInfo()->getLangCode()] ?? false) {
|
||||
Lang::$current_lang =& Lang::$lang[$this->settings->getAppInfo()->getLangCode()];
|
||||
}
|
||||
$this->settings['connection_settings']['all']['ipv6'] = Magic::$ipv6;
|
||||
if ($this->authorized === true) {
|
||||
$this->authorized = self::LOGGED_IN;
|
||||
}
|
||||
$force = false;
|
||||
// Reset MTProto session (not related to user session)
|
||||
$this->resetMTProtoSession();
|
||||
if (isset($backtrace['function'], $backtrace['class'], $backtrace['args']) && $backtrace['class'] === 'danog\\MadelineProto\\API' && $backtrace['function'] === '__construct_async') {
|
||||
if (\count($backtrace['args']) >= 2) {
|
||||
yield from $this->updateSettings($backtrace['args'][1], false);
|
||||
}
|
||||
}
|
||||
if (($this->settings['tl_schema']['src']['botAPI'] ?? '') !== __DIR__.'/TL_botAPI.tl') {
|
||||
unset($this->v);
|
||||
}
|
||||
if (!\file_exists($this->settings['tl_schema']['src']['telegram'])) {
|
||||
unset($this->v);
|
||||
}
|
||||
if (!isset($this->v) || $this->v !== self::V) {
|
||||
// Update settings from constructor
|
||||
yield from $this->updateSettings($settings, false);
|
||||
// Session update process for BC
|
||||
$forceDialogs = false;
|
||||
if (!isset($this->v)
|
||||
|| $this->v !== self::V
|
||||
|| $this->settings->getSchema()->needsUpgrade()) {
|
||||
yield from $this->upgradeMadelineProto();
|
||||
$force = true;
|
||||
$forceDialogs = true;
|
||||
}
|
||||
// Cleanup old properties, init new stuffs
|
||||
yield from $this->cleanupProperties();
|
||||
@ -1002,20 +1064,8 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
$callbacks[] = $this->minDatabase;
|
||||
}
|
||||
$this->TL->updateCallbacks($callbacks);
|
||||
if ($this->event_handler && \class_exists($this->event_handler) && \is_subclass_of($this->event_handler, EventHandler::class)) {
|
||||
$this->setEventHandler($this->event_handler);
|
||||
}
|
||||
// Connect to all DCs, start internal loops
|
||||
yield from $this->connectToAllDcs();
|
||||
foreach ($this->calls as $id => $controller) {
|
||||
if (!\is_object($controller)) {
|
||||
unset($this->calls[$id]);
|
||||
} elseif ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) {
|
||||
$controller->setMadeline($this);
|
||||
$controller->discard();
|
||||
} else {
|
||||
$controller->setMadeline($this);
|
||||
}
|
||||
}
|
||||
$this->startLoops();
|
||||
if (yield from $this->fullGetSelf()) {
|
||||
$this->authorized = self::LOGGED_IN;
|
||||
@ -1023,11 +1073,15 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
yield from $this->getCdnConfig($this->datacenter->curdc);
|
||||
yield from $this->initAuthorization();
|
||||
}
|
||||
$this->startUpdateSystem(true);
|
||||
if ($this->authorized === self::LOGGED_IN && !$this->authorization['user']['bot'] && $this->settings['peer']['cache_all_peers_on_startup']) {
|
||||
yield from $this->getDialogs($force);
|
||||
// onStart event handler
|
||||
if ($this->event_handler && \class_exists($this->event_handler) && \is_subclass_of($this->event_handler, EventHandler::class)) {
|
||||
yield from $this->setEventHandler($this->event_handler);
|
||||
}
|
||||
if ($this->authorized === self::LOGGED_IN && $this->settings['updates']['handle_updates']) {
|
||||
$this->startUpdateSystem(true);
|
||||
if ($this->authorized === self::LOGGED_IN && !$this->authorization['user']['bot'] && $this->settings->getPeer()->getCacheAllPeersOnStartup()) {
|
||||
yield from $this->getDialogs($forceDialogs);
|
||||
}
|
||||
if ($this->authorized === self::LOGGED_IN) {
|
||||
$this->logger->logger(Lang::$current_lang['getupdates_deserialization'], Logger::NOTICE);
|
||||
yield $this->updaters[UpdateLoop::GENERIC]->resume();
|
||||
}
|
||||
@ -1075,379 +1129,33 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
$this->unreference();
|
||||
$this->logger("Successfully destroyed MadelineProto");
|
||||
}
|
||||
/**
|
||||
* Get correct settings array for the latest version.
|
||||
*
|
||||
* @param array $settings Current settings array
|
||||
* @param array $previousSettings Previous settings array
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function parseSettings(array $settings, array $previousSettings = []): array
|
||||
{
|
||||
//Magic::classExists();
|
||||
$settings = \array_replace_recursive($previousSettings, $settings);
|
||||
if (isset($previousSettings['connection_settings']['default_dc'])) {
|
||||
$settings['connection_settings']['default_dc'] = $previousSettings['connection_settings']['default_dc'];
|
||||
}
|
||||
if (!isset($settings['app_info']['api_id']) || !$settings['app_info']['api_id']) {
|
||||
if (isset($previousSettings['app_info']['api_id']) && $previousSettings['app_info']['api_id']) {
|
||||
$settings['app_info']['api_id'] = $previousSettings['app_info']['api_id'];
|
||||
$settings['app_info']['api_hash'] = $previousSettings['app_info']['api_hash'];
|
||||
} else {
|
||||
$settings['app_info'] = [];
|
||||
}
|
||||
}
|
||||
// Detect device model
|
||||
try {
|
||||
$device_model = \php_uname('s');
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$device_model = 'Web server';
|
||||
}
|
||||
if (($settings['app_info']['api_id'] ?? 0) === 6) {
|
||||
// TG DEV NOTICE: these app info spoofing measures were implemented for NON-MALICIOUS purposes.
|
||||
// All accounts registered with a custom API ID require manual verification through recover@telegram.org, to avoid instant permabans.
|
||||
// This makes usage of all MTProto libraries very difficult, at least for new users.
|
||||
// To help a bit, when the android API ID is used, the android app infos are spoofed too.
|
||||
// THE ANDROID API HASH IS NOT PRESENT IN THIS REPOSITORY, AND WILL NOT BE GIVEN TO EVERYONE.
|
||||
// This measure was NOT created with the intent to aid spammers, flooders, and other scum.
|
||||
//
|
||||
// I understand that automated account registration through headless libraries may indicate the creation of a botnet,
|
||||
// ...and I understand why these automatic bans were implemented in the first place.
|
||||
// Manual requests to activate numbers through recover@telegram.org will still be required for the majority of users of this library,
|
||||
// ...those that choose to user their own API ID for their application.
|
||||
//
|
||||
// To be honest, I wrote this feature just for me, since I honestly don't want to
|
||||
// ...go through the hassle of registering => recovering => logging in to every account I use for my services (mainly webradios and test userbots)
|
||||
$device_model = 'LGENexus 5';
|
||||
}
|
||||
// Detect system version
|
||||
try {
|
||||
$system_version = \php_uname('r');
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$system_version = PHP_VERSION;
|
||||
}
|
||||
if (($settings['app_info']['api_id'] ?? 0) === 6) {
|
||||
// TG DEV NOTICE: these app info spoofing measures were implemented for NON-MALICIOUS purposes.
|
||||
// All accounts registered with a custom API ID require manual verification through recover@telegram.org, to avoid instant permabans.
|
||||
// This makes usage of all MTProto libraries very difficult, at least for new users.
|
||||
// To help a bit, when the android API ID is used, the android app infos are spoofed too.
|
||||
// THE ANDROID API HASH IS NOT PRESENT IN THIS REPOSITORY, AND WILL NOT BE GIVEN TO EVERYONE.
|
||||
// This measure was NOT created with the intent to aid spammers, flooders, and other scum.
|
||||
//
|
||||
// I understand that automated account registration through headless libraries may indicate the creation of a botnet,
|
||||
// ...and I understand why these automatic bans were implemented in the first place.
|
||||
// Manual requests to activate numbers through recover@telegram.org will still be required for the majority of users of this library,
|
||||
// ...and in particular those that choose to user their own API ID for their application.
|
||||
//
|
||||
// To be honest, I wrote this feature just for me, since I honestly don't want to
|
||||
// ...go through the hassle of registering => recovering => logging in to every account I use for my services (mainly webradios and test userbots)
|
||||
$system_version = 'SDK 28';
|
||||
}
|
||||
// Detect language
|
||||
$lang_code = 'en';
|
||||
Lang::$current_lang =& Lang::$lang[$lang_code];
|
||||
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
||||
$lang_code = \substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
|
||||
} elseif (isset($_SERVER['LANG'])) {
|
||||
$lang_code = \explode('_', $_SERVER['LANG'])[0];
|
||||
}
|
||||
if (isset(Lang::$lang[$lang_code])) {
|
||||
Lang::$current_lang =& Lang::$lang[$lang_code];
|
||||
}
|
||||
// Detect language pack
|
||||
$lang_pack = '';
|
||||
if (($settings['app_info']['api_id'] ?? 0) === 6) {
|
||||
// TG DEV NOTICE: these app info spoofing measures were implemented for NON-MALICIOUS purposes.
|
||||
// All accounts registered with a custom API ID require manual verification through recover@telegram.org, to avoid instant permabans.
|
||||
// This makes usage of all MTProto libraries very difficult, at least for new users.
|
||||
// To help a bit, when the android API ID is used, the android app infos are spoofed too.
|
||||
// THE ANDROID API HASH IS NOT PRESENT IN THIS REPOSITORY, AND WILL NOT BE GIVEN TO EVERYONE.
|
||||
// This measure was NOT created with the intent to aid spammers, flooders, and other scum.
|
||||
//
|
||||
// I understand that automated account registration through headless libraries may indicate the creation of a botnet,
|
||||
// ...and I understand why these automatic bans were implemented in the first place.
|
||||
// Manual requests to activate numbers through recover@telegram.org will still be required for the majority of users of this library,
|
||||
// ...and in particular those that choose to user their own API ID for their application.
|
||||
//
|
||||
// To be honest, I wrote this feature just for me, since I honestly don't want to
|
||||
// ...go through the hassle of registering => recovering => logging in to every account I use for my services (mainly webradios and test userbots)
|
||||
$lang_pack = 'android';
|
||||
}
|
||||
// Detect app version
|
||||
$app_version = self::RELEASE.' ('.self::V.', '.\str_replace(' (AN UPDATE IS REQUIRED)', '', Magic::$revision).')';
|
||||
if (($settings['app_info']['api_id'] ?? 0) === 6) {
|
||||
// TG DEV NOTICE: these app info spoofing measures were implemented for NON-MALICIOUS purposes.
|
||||
// All accounts registered with a custom API ID require manual verification through recover@telegram.org, to avoid instant permabans.
|
||||
// This makes usage of all MTProto libraries very difficult, at least for new users.
|
||||
// To help a bit, when the android API ID is used, the android app infos are spoofed too.
|
||||
// THE ANDROID API HASH IS NOT PRESENT IN THIS REPOSITORY, AND WILL NOT BE GIVEN TO EVERYONE.
|
||||
// This measure was NOT created with the intent to aid spammers, flooders, and other scum.
|
||||
//
|
||||
// I understand that automated account registration through headless libraries may indicate the creation of a botnet,
|
||||
// ...and I understand why these automatic bans were implemented in the first place.
|
||||
// Manual requests to activate numbers through recover@telegram.org will still be required for the majority of users of this library,
|
||||
// ...and in particular those that choose to user their own API ID for their application.
|
||||
//
|
||||
// To be honest, I wrote this feature just for me, since I honestly don't want to
|
||||
// ...go through the hassle of registering => recovering => logging in to every account I use for my services (mainly webradios and test userbots)
|
||||
$app_version = '4.9.1 (13613)';
|
||||
}
|
||||
// Set default settings
|
||||
$default_settings = ['authorization' => [
|
||||
// Authorization settings
|
||||
'default_temp_auth_key_expires_in' => 1 * 24 * 60 * 60,
|
||||
// validity of temporary keys and the binding of the temporary and permanent keys
|
||||
'rsa_keys' => ["-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6\nlyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS\nan9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw\nEfzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+\n8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n\nSlv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB\n-----END RSA PUBLIC KEY-----", "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAxq7aeLAqJR20tkQQMfRn+ocfrtMlJsQ2Uksfs7Xcoo77jAid0bRt\nksiVmT2HEIJUlRxfABoPBV8wY9zRTUMaMA654pUX41mhyVN+XoerGxFvrs9dF1Ru\nvCHbI02dM2ppPvyytvvMoefRoL5BTcpAihFgm5xCaakgsJ/tH5oVl74CdhQw8J5L\nxI/K++KJBUyZ26Uba1632cOiq05JBUW0Z2vWIOk4BLysk7+U9z+SxynKiZR3/xdi\nXvFKk01R3BHV+GUKM2RYazpS/P8v7eyKhAbKxOdRcFpHLlVwfjyM1VlDQrEZxsMp\nNTLYXb6Sce1Uov0YtNx5wEowlREH1WOTlwIDAQAB\n-----END RSA PUBLIC KEY-----", "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAsQZnSWVZNfClk29RcDTJQ76n8zZaiTGuUsi8sUhW8AS4PSbPKDm+\nDyJgdHDWdIF3HBzl7DHeFrILuqTs0vfS7Pa2NW8nUBwiaYQmPtwEa4n7bTmBVGsB\n1700/tz8wQWOLUlL2nMv+BPlDhxq4kmJCyJfgrIrHlX8sGPcPA4Y6Rwo0MSqYn3s\ng1Pu5gOKlaT9HKmE6wn5Sut6IiBjWozrRQ6n5h2RXNtO7O2qCDqjgB2vBxhV7B+z\nhRbLbCmW0tYMDsvPpX5M8fsO05svN+lKtCAuz1leFns8piZpptpSCFn7bWxiA9/f\nx5x17D7pfah3Sy2pA+NDXyzSlGcKdaUmwQIDAQAB\n-----END RSA PUBLIC KEY-----", "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAwqjFW0pi4reKGbkc9pK83Eunwj/k0G8ZTioMMPbZmW99GivMibwa\nxDM9RDWabEMyUtGoQC2ZcDeLWRK3W8jMP6dnEKAlvLkDLfC4fXYHzFO5KHEqF06i\nqAqBdmI1iBGdQv/OQCBcbXIWCGDY2AsiqLhlGQfPOI7/vvKc188rTriocgUtoTUc\n/n/sIUzkgwTqRyvWYynWARWzQg0I9olLBBC2q5RQJJlnYXZwyTL3y9tdb7zOHkks\nWV9IMQmZmyZh/N7sMbGWQpt4NMchGpPGeJ2e5gHBjDnlIf2p1yZOYeUYrdbwcS0t\nUiggS4UeE8TzIuXFQxw7fzEIlmhIaq3FnwIDAQAB\n-----END RSA PUBLIC KEY-----"],
|
||||
], 'connection' => [
|
||||
// List of datacenters/subdomains where to connect
|
||||
'ssl_subdomains' => [
|
||||
// Subdomains of web.telegram.org for https protocol
|
||||
1 => 'pluto',
|
||||
2 => 'venus',
|
||||
3 => 'aurora',
|
||||
4 => 'vesta',
|
||||
5 => 'flora',
|
||||
],
|
||||
'test' => [
|
||||
// Test datacenters
|
||||
'ipv4' => [
|
||||
// ipv4 addresses
|
||||
2 => [
|
||||
// The rest will be fetched using help.getConfig
|
||||
'ip_address' => '149.154.167.40',
|
||||
'port' => 443,
|
||||
'media_only' => false,
|
||||
'tcpo_only' => false,
|
||||
],
|
||||
],
|
||||
'ipv6' => [
|
||||
// ipv6 addresses
|
||||
2 => [
|
||||
// The rest will be fetched using help.getConfig
|
||||
'ip_address' => '2001:067c:04e8:f002:0000:0000:0000:000e',
|
||||
'port' => 443,
|
||||
'media_only' => false,
|
||||
'tcpo_only' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
'main' => [
|
||||
// Main datacenters
|
||||
'ipv4' => [
|
||||
// ipv4 addresses
|
||||
2 => [
|
||||
// The rest will be fetched using help.getConfig
|
||||
'ip_address' => '149.154.167.51',
|
||||
'port' => 443,
|
||||
'media_only' => false,
|
||||
'tcpo_only' => false,
|
||||
],
|
||||
],
|
||||
'ipv6' => [
|
||||
// ipv6 addresses
|
||||
2 => [
|
||||
// The rest will be fetched using help.getConfig
|
||||
'ip_address' => '2001:067c:04e8:f002:0000:0000:0000:000a',
|
||||
'port' => 443,
|
||||
'media_only' => false,
|
||||
'tcpo_only' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
], 'connection_settings' => [
|
||||
// connection settings
|
||||
'all' => [
|
||||
// These settings will be applied on every datacenter that hasn't a custom settings subarray...
|
||||
'protocol' => 'tcp_abridged',
|
||||
// can be tcp_full, tcp_abridged, tcp_intermediate, http, https, obfuscated2, udp (unsupported)
|
||||
'test_mode' => false,
|
||||
// decides whether to connect to the main telegram servers or to the testing servers (deep telegram)
|
||||
'ipv6' => Magic::$ipv6,
|
||||
// decides whether to use ipv6, ipv6 attribute of API attribute of API class contains autodetected boolean
|
||||
'timeout' => 2,
|
||||
// RPC timeout
|
||||
'drop_timeout' => 5*60,
|
||||
// timeout for sockets
|
||||
'proxy' => Magic::$altervista ? '\\HttpProxy' : '\\Socket',
|
||||
// The proxy class to use
|
||||
'proxy_extra' => Magic::$altervista ? ['address' => 'localhost', 'port' => 80] : [],
|
||||
// Extra parameters to pass to the proxy class using setExtra
|
||||
'obfuscated' => false,
|
||||
'transport' => 'tcp',
|
||||
'pfs' => false,
|
||||
],
|
||||
'media_socket_count' => ['min' => 5, 'max' => 10],
|
||||
'robin_period' => 10,
|
||||
'default_dc' => 2,
|
||||
], 'app_info' => [
|
||||
// obtained in https://my.telegram.org
|
||||
//'api_id' => you should put an API id in the settings array you provide
|
||||
//'api_hash' => you should put an API hash in the settings array you provide
|
||||
'device_model' => $device_model,
|
||||
'system_version' => $system_version,
|
||||
'app_version' => $app_version,
|
||||
// 🌚
|
||||
// 'app_version' => self::V,
|
||||
'lang_code' => $lang_code,
|
||||
'lang_pack' => $lang_pack,
|
||||
], 'tl_schema' => [
|
||||
// TL scheme files
|
||||
'layer' => 117,
|
||||
// layer version
|
||||
'src' => [
|
||||
// mtproto TL scheme
|
||||
'mtproto' => __DIR__.'/TL_mtproto_v1.tl',
|
||||
// telegram TL scheme
|
||||
'telegram' => __DIR__.'/TL_telegram_v117.tl',
|
||||
// secret chats TL scheme
|
||||
'secret' => __DIR__.'/TL_secret.tl',
|
||||
// bot API TL scheme
|
||||
'botAPI' => __DIR__.'/TL_botAPI.tl',
|
||||
],
|
||||
], 'logger' => [
|
||||
// Logger settings
|
||||
/*
|
||||
* logger modes:
|
||||
* 0 - No logger
|
||||
* 1 - Log to the default logger destination
|
||||
* 2 - Log to file defined in second parameter
|
||||
* 3 - Echo logs
|
||||
* 4 - Call callable provided in logger_param. logger_param must accept two parameters: array $message, int $level
|
||||
* $message is an array containing the messages the log, $level, is the logging level
|
||||
*/
|
||||
// write to
|
||||
'logger_param' => Magic::$script_cwd.'/MadelineProto.log',
|
||||
'logger' => (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') ? Logger::ECHO_LOGGER : Logger::FILE_LOGGER,
|
||||
// overwrite previous setting and echo logs
|
||||
'logger_level' => Logger::VERBOSE,
|
||||
'max_size' => 1 * 1024 * 1024,
|
||||
// Logging level, available logging levels are: ULTRA_VERBOSE, VERBOSE, NOTICE, WARNING, ERROR, FATAL_ERROR. Can be provided as last parameter to the logging function.
|
||||
], 'max_tries' => [
|
||||
'query' => 5,
|
||||
// How many times should I try to call a method or send an object before throwing an exception
|
||||
'authorization' => 5,
|
||||
// How many times should I try to generate an authorization key before throwing an exception
|
||||
'response' => 5,
|
||||
], 'flood_timeout' => ['wait_if_lt' => 10 * 60], 'msg_array_limit' => [
|
||||
// How big should be the arrays containing the incoming and outgoing messages?
|
||||
'incoming' => 100,
|
||||
'outgoing' => 100,
|
||||
'call_queue' => 200,
|
||||
], 'peer' => [
|
||||
'full_info_cache_time' => 3600,
|
||||
// Full peer info cache validity
|
||||
'full_fetch' => false,
|
||||
// Should madeline fetch the full member list of every group it meets?
|
||||
'cache_all_peers_on_startup' => false,
|
||||
], 'requests' => ['gzip_encode_if_gt' => 1024 * 1024], 'updates' => [
|
||||
'handle_updates' => true,
|
||||
// Should I handle updates?
|
||||
'handle_old_updates' => true,
|
||||
// Should I handle old updates on startup?
|
||||
'getdifference_interval' => 10,
|
||||
// Getdifference manual polling interval
|
||||
'callback' => 'getUpdatesUpdateHandler',
|
||||
// Update callback
|
||||
'run_callback' => false,
|
||||
], 'secret_chats' => ['accept_chats' => true],
|
||||
'serialization' => ['serialization_interval' => 30, 'cleanup_before_serialization' => false],
|
||||
/**
|
||||
* Where internal database will be stored?
|
||||
* memory - session file
|
||||
* mysql - mysql database.
|
||||
*/
|
||||
'db' => [
|
||||
'type' => 'memory',
|
||||
/** @see Mysql */
|
||||
'mysql' => [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 3306,
|
||||
'user' => 'root',
|
||||
'password' => '',
|
||||
'database' => 'MadelineProto', //will be created automatically
|
||||
'max_connections' => 10,
|
||||
'idle_timeout' => 60,
|
||||
'cache_ttl' => '+5 minutes', //keep records in memory after last read
|
||||
],
|
||||
/** @see Postgres */
|
||||
'postgres' => [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 5432,
|
||||
'user' => 'root',
|
||||
'password' => '',
|
||||
'database' => 'MadelineProto', //will be created automatically
|
||||
'max_connections' => 10,
|
||||
'idle_timeout' => 60,
|
||||
'cache_ttl' => '+5 minutes', //keep records in memory after last read
|
||||
],
|
||||
/** @see Redis */
|
||||
'redis' => [
|
||||
'host' => 'redis://127.0.0.1',
|
||||
'port' => 6379,
|
||||
'password' => '',
|
||||
'database' => 0, //will be created automatically
|
||||
'cache_ttl' => '+5 minutes', //keep records in memory after last read
|
||||
],
|
||||
],
|
||||
'upload' => ['allow_automatic_upload' => true, 'part_size' => 512 * 1024, 'parallel_chunks' => 20], 'download' => ['report_broken_media' => true, 'part_size' => 1024 * 1024, 'parallel_chunks' => 20], 'pwr' => [
|
||||
'pwr' => false,
|
||||
// Need info ?
|
||||
'db_token' => false,
|
||||
// Need info ?
|
||||
'strict' => false,
|
||||
// Need info ?
|
||||
'requests' => true,
|
||||
]];
|
||||
$settings = \array_replace_recursive($default_settings, $settings);
|
||||
if (isset(Lang::$lang[$settings['app_info']['lang_code']])) {
|
||||
Lang::$current_lang =& Lang::$lang[$settings['app_info']['lang_code']];
|
||||
}
|
||||
/*if ($settings['app_info']['api_id'] < 20) {
|
||||
$settings['connection_settings']['all']['protocol'] = 'obfuscated2';
|
||||
}*/
|
||||
switch ($settings['logger']['logger_level']) {
|
||||
case 'ULTRA_VERBOSE':
|
||||
$settings['logger']['logger_level'] = 5;
|
||||
break;
|
||||
case 'VERBOSE':
|
||||
$settings['logger']['logger_level'] = 4;
|
||||
break;
|
||||
case 'NOTICE':
|
||||
$settings['logger']['logger_level'] = 3;
|
||||
break;
|
||||
case 'WARNING':
|
||||
$settings['logger']['logger_level'] = 2;
|
||||
break;
|
||||
case 'ERROR':
|
||||
$settings['logger']['logger_level'] = 1;
|
||||
break;
|
||||
case 'FATAL ERROR':
|
||||
$settings['logger']['logger_level'] = 0;
|
||||
break;
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
/**
|
||||
* Parse, update and store settings.
|
||||
*
|
||||
* @param array $settings Settings
|
||||
* @param Settings|SettingsEmpty $settings Settings
|
||||
* @param bool $reinit Whether to reinit the instance
|
||||
*
|
||||
* @return void
|
||||
* @return \Generator
|
||||
*/
|
||||
public function updateSettings(array $settings, bool $reinit = true): \Generator
|
||||
public function updateSettings(SettingsAbstract $settings, bool $reinit = true): \Generator
|
||||
{
|
||||
$settings = self::parseSettings($settings, $this->settings);
|
||||
if ($settings['app_info'] === null) {
|
||||
if ($settings instanceof SettingsEmpty) {
|
||||
if (!isset($this->settings)) {
|
||||
$this->settings = new Settings;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!isset($this->settings)) {
|
||||
$this->settings = $settings;
|
||||
} else {
|
||||
$this->settings->merge($settings);
|
||||
}
|
||||
}
|
||||
if (!$this->settings->getAppInfo()->hasApiInfo()) {
|
||||
throw new \danog\MadelineProto\Exception(Lang::$current_lang['api_not_set'], 0, null, 'MadelineProto', 1);
|
||||
}
|
||||
$this->settings = $settings;
|
||||
if (!$this->settings['updates']['handle_updates']) {
|
||||
$this->updates = [];
|
||||
}
|
||||
|
||||
// Setup logger
|
||||
$this->setupLogger();
|
||||
|
||||
@ -1457,11 +1165,11 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Return current settings array.
|
||||
* Return current settings.
|
||||
*
|
||||
* @return array
|
||||
* @return Settings
|
||||
*/
|
||||
public function getSettings(): array
|
||||
public function getSettings(): Settings
|
||||
{
|
||||
return $this->settings;
|
||||
}
|
||||
@ -1472,7 +1180,10 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
*/
|
||||
public function setupLogger(): void
|
||||
{
|
||||
$this->logger = Logger::getLoggerFromSettings($this->settings, isset($this->authorization['user']) ? (isset($this->authorization['user']['username']) ? $this->authorization['user']['username'] : $this->authorization['user']['id']) : '');
|
||||
$this->logger = new Logger(
|
||||
$this->settings->getLogger(),
|
||||
$this->authorization['user']['username'] ?? $this->authorization['user']['id'] ?? ''
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Reset all MTProto sessions.
|
||||
@ -1561,7 +1272,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
if (!isset($this->seqUpdater)) {
|
||||
$this->seqUpdater = new SeqLoop($this);
|
||||
}
|
||||
$this->datacenter->__construct($this, $this->settings['connection'], $this->settings['connection_settings'], $reconnectAll);
|
||||
$this->datacenter->__construct($this, $this->dcList, $this->settings->getConnection(), $reconnectAll);
|
||||
$dcs = [];
|
||||
foreach ($this->datacenter->getDcs() as $new_dc) {
|
||||
$dcs[] = $this->datacenter->dcConnect($new_dc);
|
||||
@ -1717,9 +1428,12 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
*/
|
||||
public function getPhoneConfig($watcherId = null): \Generator
|
||||
{
|
||||
if ($this->authorized === self::LOGGED_IN && \class_exists(VoIPServerConfigInternal::class) && !$this->authorization['user']['bot'] && $this->datacenter->getDataCenterConnection($this->settings['connection_settings']['default_dc'])->hasTempAuthKey()) {
|
||||
if ($this->authorized === self::LOGGED_IN
|
||||
&& \class_exists(VoIPServerConfigInternal::class)
|
||||
&& !$this->authorization['user']['bot']
|
||||
&& $this->datacenter->getDataCenterConnection($this->settings->getDefaultDc())->hasTempAuthKey()) {
|
||||
$this->logger->logger('Fetching phone config...');
|
||||
VoIPServerConfig::updateDefault(yield from $this->methodCallAsyncRead('phone.getCallConfig', [], ['datacenter' => $this->settings['connection_settings']['default_dc']]));
|
||||
VoIPServerConfig::updateDefault(yield from $this->methodCallAsyncRead('phone.getCallConfig', [], $this->settings->getDefaultDcParams()));
|
||||
} else {
|
||||
$this->logger->logger('Not fetching phone config');
|
||||
}
|
||||
@ -1764,7 +1478,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
if ($this->config['expires'] > \time()) {
|
||||
return $this->config;
|
||||
}
|
||||
$this->config = empty($config) ? yield from $this->methodCallAsyncRead('help.getConfig', $config, $options ?: ['datacenter' => $this->settings['connection_settings']['default_dc']]) : $config;
|
||||
$this->config = empty($config) ? yield from $this->methodCallAsyncRead('help.getConfig', $config, $options ?: $this->settings->getDefaultDcParams()) : $config;
|
||||
yield from $this->parseConfig();
|
||||
$this->logger->logger(Lang::$current_lang['config_updated'], Logger::NOTICE);
|
||||
$this->logger->logger($this->config, Logger::NOTICE);
|
||||
@ -1792,7 +1506,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
*/
|
||||
private function parseDcOptions(array $dc_options): \Generator
|
||||
{
|
||||
$previous = $this->settings;
|
||||
$previous = $this->dcList;
|
||||
foreach ($dc_options as $dc) {
|
||||
$test = $this->config['test_mode'] ? 'test' : 'main';
|
||||
$id = $dc['id'];
|
||||
@ -1808,10 +1522,10 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
$id = (int) $id;
|
||||
}
|
||||
unset($dc['cdn'], $dc['media_only'], $dc['id'], $dc['ipv6']);
|
||||
$this->settings['connection'][$test][$ipv6][$id] = $dc;
|
||||
$this->dcList[$test][$ipv6][$id] = $dc;
|
||||
}
|
||||
$curdc = $this->datacenter->curdc;
|
||||
if ($previous !== $this->settings && (!$this->datacenter->has($curdc) || $this->datacenter->getDataCenterConnection($curdc)->byIPAddress())) {
|
||||
if ($previous !== $this->dcList && (!$this->datacenter->has($curdc) || $this->datacenter->getDataCenterConnection($curdc)->byIPAddress())) {
|
||||
$this->logger->logger('Got new DC options, reconnecting');
|
||||
yield from $this->connectToAllDcs(false);
|
||||
}
|
||||
@ -1877,8 +1591,13 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
if (!(\is_array($userOrId) && !isset($userOrId['_']) && !isset($userOrId['id']))) {
|
||||
$userOrId = [$userOrId];
|
||||
}
|
||||
foreach ($userOrId as &$peer) {
|
||||
foreach ($userOrId as $k => &$peer) {
|
||||
try {
|
||||
$peer = (yield from $this->getInfo($peer))['bot_api_id'];
|
||||
} catch (\Throwable $e) {
|
||||
unset($userOrId[$k]);
|
||||
$this->logger("Could not obtain info about report peer $peer: $e", Logger::FATAL_ERROR);
|
||||
}
|
||||
}
|
||||
$this->reportDest = $userOrId;
|
||||
}
|
||||
@ -1895,7 +1614,8 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
return;
|
||||
}
|
||||
$file = null;
|
||||
if ($this->settings['logger']['logger'] === Logger::FILE_LOGGER && $path = $this->settings['logger']['logger_param']) {
|
||||
if ($this->settings->getLogger()->getType() === Logger::FILE_LOGGER
|
||||
&& $path = $this->settings->getLogger()->getExtra()) {
|
||||
StatCache::clear($path);
|
||||
if (!yield exists($path)) {
|
||||
$message = "!!! WARNING !!!\nThe logfile does not exist, please DO NOT delete the logfile to avoid errors in MadelineProto!\n\n$message";
|
||||
|
@ -19,8 +19,12 @@
|
||||
|
||||
namespace danog\MadelineProto\MTProtoSession;
|
||||
|
||||
use danog\MadelineProto\DataCenterConnection;
|
||||
|
||||
/**
|
||||
* Manages acknowledgement of messages.
|
||||
*
|
||||
* @property DataCenterConnection $shared
|
||||
*/
|
||||
trait AckHandler
|
||||
{
|
||||
@ -89,9 +93,8 @@ trait AckHandler
|
||||
*/
|
||||
public function hasPendingCalls(): bool
|
||||
{
|
||||
$settings = $this->shared->getSettings();
|
||||
$timeout = $settings['timeout'];
|
||||
$pfs = $settings['pfs'];
|
||||
$timeout = $this->shared->getSettings()->getTimeout();
|
||||
$pfs = $this->shared->getGenericSettings()->getAuth()->getPfs();
|
||||
$unencrypted = !$this->shared->hasTempAuthKey();
|
||||
$notBound = !$this->shared->isBound();
|
||||
$pfsNotBound = $pfs && $notBound;
|
||||
@ -113,9 +116,10 @@ trait AckHandler
|
||||
public function getPendingCalls(): array
|
||||
{
|
||||
$settings = $this->shared->getSettings();
|
||||
$dropTimeout = $settings['drop_timeout'];
|
||||
$timeout = $settings['timeout'];
|
||||
$pfs = $settings['pfs'];
|
||||
$global = $this->shared->getGenericSettings();
|
||||
$dropTimeout = $global->getRpc()->getRpcTimeout();
|
||||
$timeout = $settings->getTimeout();
|
||||
$pfs = $global->getAuth()->getPfs();
|
||||
$unencrypted = !$this->shared->hasTempAuthKey();
|
||||
$notBound = !$this->shared->isBound();
|
||||
$pfsNotBound = $pfs && $notBound;
|
||||
|
@ -20,10 +20,13 @@
|
||||
namespace danog\MadelineProto\MTProtoSession\MsgIdHandler;
|
||||
|
||||
use danog\MadelineProto\MTProtoSession\MsgIdHandler;
|
||||
use danog\MadelineProto\MTProtoSession\Session;
|
||||
use tgseclib\Math\BigInteger;
|
||||
|
||||
/**
|
||||
* Manages message ids.
|
||||
*
|
||||
* @property Session $session
|
||||
*/
|
||||
class MsgIdHandler32 extends MsgIdHandler
|
||||
{
|
||||
@ -66,7 +69,7 @@ class MsgIdHandler32 extends MsgIdHandler
|
||||
if ($newMessageId->compare($key = $this->getMaxId($incoming = false)) <= 0) {
|
||||
throw new \danog\MadelineProto\Exception('Given message id ('.$newMessageId.') is lower than or equal to the current limit ('.$key.'). Consider syncing your date.', 1);
|
||||
}
|
||||
if (\count($this->session->outgoing_messages) > $this->session->API->settings['msg_array_limit']['outgoing']) {
|
||||
if (\count($this->session->outgoing_messages) > $this->session->API->settings->getRpc()->getLimitOutgoing()) {
|
||||
\reset($this->session->outgoing_messages);
|
||||
$key = \key($this->session->outgoing_messages);
|
||||
if (!isset($this->session->outgoing_messages[$key]['promise'])) {
|
||||
@ -89,7 +92,7 @@ class MsgIdHandler32 extends MsgIdHandler
|
||||
$this->session->API->logger->logger('WARNING: Given message id ('.$newMessageId.') is lower than or equal to the current limit ('.$key.'). Consider syncing your date.', \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
}
|
||||
if (\count($this->session->incoming_messages) > $this->session->API->settings['msg_array_limit']['incoming']) {
|
||||
if (\count($this->session->incoming_messages) > $this->session->API->settings->getRpc()->getLimitIncoming()) {
|
||||
\reset($this->session->incoming_messages);
|
||||
$key = \key($this->session->incoming_messages);
|
||||
if (!isset($this->session->incoming_messages[$key]['promise'])) {
|
||||
|
@ -65,7 +65,7 @@ class MsgIdHandler64 extends MsgIdHandler
|
||||
if ($newMessageId <= $this->maxOutgoingId) {
|
||||
throw new \danog\MadelineProto\Exception('Given message id ('.$newMessageId.') is lower than or equal to the current limit ('.$this->maxOutgoingId.'). Consider syncing your date.');
|
||||
}
|
||||
if (\count($this->session->outgoing_messages) > $this->session->API->settings['msg_array_limit']['outgoing']) {
|
||||
if (\count($this->session->outgoing_messages) > $this->session->API->settings->getRpc()->getLimitOutgoing()) {
|
||||
\reset($this->session->outgoing_messages);
|
||||
$key = \key($this->session->outgoing_messages);
|
||||
if (!isset($this->session->outgoing_messages[$key]['promise'])) {
|
||||
@ -88,7 +88,7 @@ class MsgIdHandler64 extends MsgIdHandler
|
||||
$this->session->API->logger->logger('WARNING: Given message id ('.$newMessageId.') is lower than or equal to the current limit ('.$key.'). Consider syncing your date.', \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
}
|
||||
if (\count($this->session->incoming_messages) > $this->session->API->settings['msg_array_limit']['incoming']) {
|
||||
if (\count($this->session->incoming_messages) > $this->session->API->settings->getRpc()->getLimitIncoming()) {
|
||||
\reset($this->session->incoming_messages);
|
||||
$key = \key($this->session->incoming_messages);
|
||||
if (!isset($this->session->incoming_messages[$key]['promise'])) {
|
||||
|
@ -336,7 +336,7 @@ trait ResponseHandler
|
||||
$datacenter .= '_media';
|
||||
}
|
||||
if (isset($request['user_related']) && $request['user_related']) {
|
||||
$this->API->settings['connection_settings']['default_dc'] = $this->API->authorized_dc = $this->API->datacenter->curdc;
|
||||
$this->API->settings->setDefaultDc($this->API->authorized_dc = $this->API->datacenter->curdc);
|
||||
}
|
||||
Loop::defer([$this, 'methodRecall'], ['message_id' => $request_id, 'datacenter' => $datacenter]);
|
||||
//$this->API->methodRecall('', ['message_id' => $request_id, 'datacenter' => $datacenter, 'postpone' => true]);
|
||||
@ -422,7 +422,7 @@ trait ResponseHandler
|
||||
return;
|
||||
case 420:
|
||||
$seconds = \preg_replace('/[^0-9]+/', '', $response['error_message']);
|
||||
$limit = $request['FloodWaitLimit'] ?? $this->API->settings['flood_timeout']['wait_if_lt'];
|
||||
$limit = $request['FloodWaitLimit'] ?? $this->API->settings->getRPC()->getFloodTimeout();
|
||||
if (\is_numeric($seconds) && $seconds < $limit) {
|
||||
//$this->gotResponseForOutgoingMessageId($request_id);
|
||||
$this->logger->logger('Flood, waiting '.$seconds.' seconds before repeating async call of '.($request['_'] ?? '').'...', \danog\MadelineProto\Logger::NOTICE);
|
||||
@ -449,6 +449,9 @@ trait ResponseHandler
|
||||
$this->shared->getTempAuthKey()->setServerSalt($response['new_server_salt']);
|
||||
$this->methodRecall('', ['message_id' => $request_id, 'postpone' => true]);
|
||||
return;
|
||||
case 20:
|
||||
$this->methodRecall('', ['message_id' => $request_id, 'postpone' => true]);
|
||||
return;
|
||||
case 16:
|
||||
case 17:
|
||||
$this->time_delta = (int) (new \tgseclib\Math\BigInteger(\strrev($response_id), 256))->bitwise_rightShift(32)->subtract(new \tgseclib\Math\BigInteger(\time()))->toString();
|
||||
|
@ -21,6 +21,8 @@ namespace danog\MadelineProto\MTProtoSession;
|
||||
|
||||
/**
|
||||
* Manages MTProto session-specific data.
|
||||
*
|
||||
* @property MTProto $API
|
||||
*/
|
||||
abstract class Session
|
||||
{
|
||||
|
@ -25,7 +25,9 @@ use danog\MadelineProto\MTProto;
|
||||
use danog\MadelineProto\MTProto\AuthKey;
|
||||
use danog\MadelineProto\MTProto\PermAuthKey;
|
||||
use danog\MadelineProto\MTProto\TempAuthKey;
|
||||
use danog\MadelineProto\Settings;
|
||||
use danog\PrimeModule;
|
||||
|
||||
use tgseclib\Math\BigInteger;
|
||||
|
||||
/**
|
||||
@ -33,6 +35,8 @@ use tgseclib\Math\BigInteger;
|
||||
*
|
||||
* https://core.telegram.org/mtproto/auth_key
|
||||
* https://core.telegram.org/mtproto/samples-auth_key
|
||||
*
|
||||
* @property Settings $settings Settings
|
||||
*/
|
||||
trait AuthKeyHandler
|
||||
{
|
||||
@ -63,7 +67,7 @@ trait AuthKeyHandler
|
||||
$connection = $this->datacenter->getAuthConnection($datacenter);
|
||||
$cdn = $connection->isCDN();
|
||||
$req_pq = $cdn ? 'req_pq' : 'req_pq_multi';
|
||||
for ($retry_id_total = 1; $retry_id_total <= $this->settings['max_tries']['authorization']; $retry_id_total++) {
|
||||
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);
|
||||
/**
|
||||
@ -264,7 +268,7 @@ trait AuthKeyHandler
|
||||
$this->logger->logger(\sprintf('Server-client time delta = %.1f s', $connection->time_delta), \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->checkPG($dh_prime, $g);
|
||||
$this->checkG($g_a, $dh_prime);
|
||||
for ($retry_id = 0; $retry_id <= $this->settings['max_tries']['authorization']; $retry_id++) {
|
||||
for ($retry_id = 0; $retry_id <= $this->settings->getAuth()->getMaxAuthTries(); $retry_id++) {
|
||||
$this->logger->logger('Generating b...', \danog\MadelineProto\Logger::VERBOSE);
|
||||
$b = new BigInteger(\danog\MadelineProto\Tools::random(256), 256);
|
||||
$this->logger->logger('Generating g_b...', \danog\MadelineProto\Logger::VERBOSE);
|
||||
@ -510,7 +514,7 @@ trait AuthKeyHandler
|
||||
{
|
||||
$datacenterConnection = $this->datacenter->getDataCenterConnection($datacenter);
|
||||
$connection = $datacenterConnection->getAuthConnection();
|
||||
for ($retry_id_total = 1; $retry_id_total <= $this->settings['max_tries']['authorization']; $retry_id_total++) {
|
||||
for ($retry_id_total = 1; $retry_id_total <= $this->settings->getAuth()->getMaxAuthTries(); $retry_id_total++) {
|
||||
try {
|
||||
$this->logger->logger('Binding authorization keys...', \danog\MadelineProto\Logger::VERBOSE);
|
||||
$nonce = \danog\MadelineProto\Tools::random(8);
|
||||
@ -669,19 +673,19 @@ trait AuthKeyHandler
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ($this->datacenter->getDataCenterConnection($id)->getSettings()['pfs']) {
|
||||
if ($this->getSettings()->getAuth()->getPfs()) {
|
||||
if (!$cdn) {
|
||||
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['gen_temp_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
//$authorized = $socket->authorized;
|
||||
//$socket->authorized = false;
|
||||
$socket->setTempAuthKey(null);
|
||||
$socket->setTempAuthKey(yield from $this->createAuthKey($this->settings['authorization']['default_temp_auth_key_expires_in'], $id));
|
||||
yield from $this->bindTempAuthKey($this->settings['authorization']['default_temp_auth_key_expires_in'], $id);
|
||||
$socket->setTempAuthKey(yield from $this->createAuthKey($this->settings->getAuth()->getDefaultTempAuthKeyExpiresIn(), $id));
|
||||
yield from $this->bindTempAuthKey($this->settings->getAuth()->getDefaultTempAuthKeyExpiresIn(), $id);
|
||||
$this->config = yield from $connection->methodCallAsyncRead('help.getConfig', []);
|
||||
yield from $this->syncAuthorization($id);
|
||||
} elseif (!$socket->hasTempAuthKey()) {
|
||||
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['gen_temp_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
$socket->setTempAuthKey(yield from $this->createAuthKey($this->settings['authorization']['default_temp_auth_key_expires_in'], $id));
|
||||
$socket->setTempAuthKey(yield from $this->createAuthKey($this->settings->getAuth()->getDefaultTempAuthKeyExpiresIn(), $id));
|
||||
}
|
||||
} else {
|
||||
if (!$cdn) {
|
||||
@ -690,7 +694,7 @@ trait AuthKeyHandler
|
||||
yield from $this->syncAuthorization($id);
|
||||
} elseif (!$socket->hasTempAuthKey()) {
|
||||
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['gen_temp_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
$socket->setTempAuthKey(yield from $this->createAuthKey($this->settings['authorization']['default_temp_auth_key_expires_in'], $id));
|
||||
$socket->setTempAuthKey(yield from $this->createAuthKey($this->settings->getAuth()->getDefaultTempAuthKeyExpiresIn(), $id));
|
||||
}
|
||||
}
|
||||
} elseif (!$cdn) {
|
||||
|
@ -19,8 +19,12 @@
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
use danog\MadelineProto\Settings;
|
||||
|
||||
/**
|
||||
* Manages method and object calls.
|
||||
*
|
||||
* @property Settings $settings Settings
|
||||
*/
|
||||
trait CallHandler
|
||||
{
|
||||
|
@ -38,22 +38,26 @@ use Amp\Promise;
|
||||
use Amp\Success;
|
||||
use danog\MadelineProto\Exception;
|
||||
use danog\MadelineProto\FileCallbackInterface;
|
||||
use danog\MadelineProto\Settings;
|
||||
use danog\MadelineProto\Stream\Common\BufferedRawStream;
|
||||
use danog\MadelineProto\Stream\Common\SimpleBufferedRawStream;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use danog\MadelineProto\Stream\Transport\PremadeStream;
|
||||
use danog\MadelineProto\Tools;
|
||||
|
||||
use tgseclib\Crypt\AES;
|
||||
|
||||
use const danog\Decoder\TYPES;
|
||||
|
||||
use function Amp\File\exists;
|
||||
use function Amp\File\open;
|
||||
use function Amp\File\stat as statAsync;
|
||||
|
||||
use function Amp\Promise\all;
|
||||
|
||||
/**
|
||||
* Manages upload and download of files.
|
||||
*
|
||||
* @property Settings $settings Settings
|
||||
*/
|
||||
trait Files
|
||||
{
|
||||
@ -83,7 +87,7 @@ trait Files
|
||||
if (\is_resource($file) || (\is_object($file) && $file instanceof InputStream)) {
|
||||
return yield from $this->uploadFromStream($file, 0, '', $fileName, $cb, $encrypted);
|
||||
}
|
||||
if (!$this->settings['upload']['allow_automatic_upload']) {
|
||||
if (!$this->settings->getFiles()->getAllowAutomaticUpload()) {
|
||||
return yield from $this->uploadFromUrl($file, 0, $fileName, $cb, $encrypted);
|
||||
}
|
||||
$file = Tools::absolute($file);
|
||||
@ -194,7 +198,7 @@ trait Files
|
||||
};
|
||||
} else {
|
||||
if (!$stream instanceof BufferedRawStream) {
|
||||
$ctx = (new ConnectionContext())->addStream(PremadeStream::getName(), $stream)->addStream(SimpleBufferedRawStream::getName());
|
||||
$ctx = (new ConnectionContext())->addStream(PremadeStream::class, $stream)->addStream(SimpleBufferedRawStream::class);
|
||||
$stream = (yield from $ctx->getStream());
|
||||
$created = true;
|
||||
}
|
||||
@ -263,12 +267,12 @@ trait Files
|
||||
$this->logger->logger('Upload status: '.$percent.'%', \danog\MadelineProto\Logger::NOTICE);
|
||||
};
|
||||
}
|
||||
$datacenter = $this->settings['connection_settings']['default_dc'];
|
||||
$datacenter = $this->settings->getDefaultDc();
|
||||
if ($this->datacenter->has($datacenter.'_media')) {
|
||||
$datacenter .= '_media';
|
||||
}
|
||||
$part_size = $this->settings['upload']['part_size'];
|
||||
$parallel_chunks = $this->settings['upload']['parallel_chunks'] ?? 4000;
|
||||
$part_size = 512 * 1024;
|
||||
$parallel_chunks = $this->settings->getFiles()->getUploadParallelChunks();
|
||||
$part_total_num = (int) \ceil($size / $part_size);
|
||||
$part_num = 0;
|
||||
$method = $size > 10 * 1024 * 1024 ? 'upload.saveBigFilePart' : 'upload.saveFilePart';
|
||||
@ -395,7 +399,7 @@ trait Files
|
||||
}
|
||||
$size = $media['size'];
|
||||
$mime = $media['mime'];
|
||||
$chunk_size = $this->settings['upload']['part_size'];
|
||||
$chunk_size = 512 * 1024;
|
||||
$bridge = new class($size, $chunk_size, $cb) {
|
||||
/**
|
||||
* Read promises.
|
||||
@ -1134,9 +1138,9 @@ trait Files
|
||||
if ($end === -1 && isset($messageMedia['size'])) {
|
||||
$end = $messageMedia['size'];
|
||||
}
|
||||
$part_size = $part_size ?? $this->settings['download']['part_size'];
|
||||
$parallel_chunks = $this->settings['download']['parallel_chunks'] ?? 4000;
|
||||
$datacenter = $messageMedia['InputFileLocation']['dc_id'] ?? $this->settings['connection_settings']['default_dc'];
|
||||
$part_size = $part_size ?? 1024 * 1024;
|
||||
$parallel_chunks = $this->settings->getFiles()->getDownloadParallelChunks();
|
||||
$datacenter = $messageMedia['InputFileLocation']['dc_id'] ?? $this->settings->getDefaultDc();
|
||||
if ($this->datacenter->has($datacenter.'_media')) {
|
||||
$datacenter .= '_media';
|
||||
}
|
||||
|
@ -23,11 +23,14 @@ use Amp\Http\Client\Request;
|
||||
use danog\Decoder\FileId;
|
||||
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceDialogPhoto;
|
||||
use danog\MadelineProto\Db\DbArray;
|
||||
use danog\MadelineProto\Settings;
|
||||
|
||||
use const danog\Decoder\PROFILE_PHOTO;
|
||||
|
||||
/**
|
||||
* Manages peers.
|
||||
*
|
||||
* @property Settings $settings Settings
|
||||
*/
|
||||
trait PeerHandler
|
||||
{
|
||||
@ -157,7 +160,7 @@ trait PeerHandler
|
||||
if (!$existingChat || $existingChat !== $chat) {
|
||||
$this->logger->logger("Updated chat -{$chat['id']}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$this->chats[-$chat['id']] = $chat;
|
||||
$this->cachePwrChat(-$chat['id'], $this->settings['peer']['full_fetch'], true);
|
||||
$this->cachePwrChat(-$chat['id'], $this->getSettings()->getPeer()->getFullFetch(), true);
|
||||
}
|
||||
$this->cacheChatUsername(-$chat['id'], $chat);
|
||||
break;
|
||||
@ -196,8 +199,8 @@ trait PeerHandler
|
||||
}
|
||||
$this->chats[$bot_api_id] = $chat;
|
||||
$fullChat = yield $this->full_chats[$bot_api_id];
|
||||
if ($this->settings['peer']['full_fetch'] && (!$fullChat || $fullChat['full']['participants_count'] !== (yield from $this->getFullInfo($bot_api_id))['full']['participants_count'])) {
|
||||
$this->cachePwrChat($bot_api_id, $this->settings['peer']['full_fetch'], true);
|
||||
if ($this->getSettings()->getPeer()->getFullFetch() && (!$fullChat || $fullChat['full']['participants_count'] !== (yield from $this->getFullInfo($bot_api_id))['full']['participants_count'])) {
|
||||
$this->cachePwrChat($bot_api_id, $this->getSettings()->getPeer()->getFullFetch(), true);
|
||||
}
|
||||
}
|
||||
$this->cacheChatUsername($bot_api_id, $chat);
|
||||
@ -545,7 +548,7 @@ trait PeerHandler
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isset($this->settings['pwr']['requests']) || $this->settings['pwr']['requests'] === true && $recursive) {
|
||||
if ($this->settings->getPwr()->getRequests() && $recursive) {
|
||||
$dbres = [];
|
||||
try {
|
||||
$dbres = \json_decode(yield from $this->datacenter->fileGetContents('https://id.pwrtelegram.xyz/db/getusername?id='.$id), true);
|
||||
@ -582,7 +585,7 @@ trait PeerHandler
|
||||
}
|
||||
if ($id === 'support') {
|
||||
if (!$this->supportUser) {
|
||||
yield from $this->methodCallAsyncRead('help.getSupport', [], ['datacenter' => $this->settings['connection_settings']['default_dc']]);
|
||||
yield from $this->methodCallAsyncRead('help.getSupport', [], $this->settings->getDefaultDcParams());
|
||||
}
|
||||
return yield from $this->getInfo($this->supportUser);
|
||||
}
|
||||
@ -704,7 +707,7 @@ trait PeerHandler
|
||||
public function getFullInfo($id): \Generator
|
||||
{
|
||||
$partial = (yield from $this->getInfo($id));
|
||||
if (\time() - (yield from $this->fullChatLastUpdated($partial['bot_api_id'])) < (isset($this->settings['peer']['full_info_cache_time']) ? $this->settings['peer']['full_info_cache_time'] : 0)) {
|
||||
if (\time() - (yield from $this->fullChatLastUpdated($partial['bot_api_id'])) < $this->getSettings()->getPeer()->getFullInfoCacheTime()) {
|
||||
return \array_merge($partial, yield $this->full_chats[$partial['bot_api_id']]);
|
||||
}
|
||||
switch ($partial['type']) {
|
||||
@ -1008,8 +1011,7 @@ trait PeerHandler
|
||||
}
|
||||
private function storeDb($res, $force = false): \Generator
|
||||
{
|
||||
$settings = isset($this->settings['connection_settings'][$this->datacenter->curdc]) ? $this->settings['connection_settings'][$this->datacenter->curdc] : $this->settings['connection_settings']['all'];
|
||||
if (!isset($this->settings['pwr']) || $this->settings['pwr']['pwr'] === false || $settings['test_mode']) {
|
||||
if (!$this->settings->getPwr()->getDbToken() || $this->settings->getConnection()->getTestMode()) {
|
||||
return;
|
||||
}
|
||||
if (!empty($res)) {
|
||||
@ -1030,7 +1032,7 @@ trait PeerHandler
|
||||
//$path = '/tmp/ids'.hash('sha256', $payload);
|
||||
//file_put_contents($path, $payload);
|
||||
$id = isset($this->authorization['user']['username']) ? $this->authorization['user']['username'] : $this->authorization['user']['id'];
|
||||
$request = new Request('https://id.pwrtelegram.xyz/db'.$this->settings['pwr']['db_token'].'/addnewmadeline?d=pls&from='.$id, 'POST');
|
||||
$request = new Request('https://id.pwrtelegram.xyz/db'.$this->settings->getPwr()->getDbToken().'/addnewmadeline?d=pls&from='.$id, 'POST');
|
||||
$request->setHeader('content-type', 'application/json');
|
||||
$request->setBody($payload);
|
||||
$result = yield (yield $this->datacenter->getHTTPClient()->request($request))->getBody()->buffer();
|
||||
|
@ -422,10 +422,10 @@ class ReferenceDatabase implements TLCallback
|
||||
$origin['peer'] = $this->API->getId($origin['peer']);
|
||||
}
|
||||
if ($origin['peer'] < 0) {
|
||||
yield from $this->API->methodCallAsyncRead('channels.getMessages', ['channel' => $origin['peer'], 'id' => [$origin['msg_id']]], ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
yield from $this->API->methodCallAsyncRead('channels.getMessages', ['channel' => $origin['peer'], 'id' => [$origin['msg_id']]], $this->API->getSettings()->getDefaultDcParams());
|
||||
break;
|
||||
}
|
||||
yield from $this->API->methodCallAsyncRead('messages.getMessages', ['id' => [$origin['msg_id']]], ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
yield from $this->API->methodCallAsyncRead('messages.getMessages', ['id' => [$origin['msg_id']]], $this->API->getSettings()->getDefaultDcParams());
|
||||
break;
|
||||
// Peer + photo ID
|
||||
case self::PEER_PHOTO_ORIGIN:
|
||||
@ -438,25 +438,25 @@ class ReferenceDatabase implements TLCallback
|
||||
break;
|
||||
// Peer (default photo ID)
|
||||
case self::USER_PHOTO_ORIGIN:
|
||||
yield from $this->API->methodCallAsyncRead('photos.getUserPhotos', $origin, ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
yield from $this->API->methodCallAsyncRead('photos.getUserPhotos', $origin, $this->API->getSettings()->getDefaultDcParams());
|
||||
break;
|
||||
case self::SAVED_GIFS_ORIGIN:
|
||||
yield from $this->API->methodCallAsyncRead('messages.getSavedGifs', $origin, ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
yield from $this->API->methodCallAsyncRead('messages.getSavedGifs', $origin, $this->API->getSettings()->getDefaultDcParams());
|
||||
break;
|
||||
case self::STICKER_SET_ID_ORIGIN:
|
||||
yield from $this->API->methodCallAsyncRead('messages.getStickerSet', $origin, ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
yield from $this->API->methodCallAsyncRead('messages.getStickerSet', $origin, $this->API->getSettings()->getDefaultDcParams());
|
||||
break;
|
||||
case self::STICKER_SET_RECENT_ORIGIN:
|
||||
yield from $this->API->methodCallAsyncRead('messages.getRecentStickers', $origin, ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
yield from $this->API->methodCallAsyncRead('messages.getRecentStickers', $origin, $this->API->getSettings()->getDefaultDcParams());
|
||||
break;
|
||||
case self::STICKER_SET_FAVED_ORIGIN:
|
||||
yield from $this->API->methodCallAsyncRead('messages.getFavedStickers', $origin, ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
yield from $this->API->methodCallAsyncRead('messages.getFavedStickers', $origin, $this->API->getSettings()->getDefaultDcParams());
|
||||
break;
|
||||
case self::STICKER_SET_EMOTICON_ORIGIN:
|
||||
yield from $this->API->methodCallAsyncRead('messages.getStickers', $origin, ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
yield from $this->API->methodCallAsyncRead('messages.getStickers', $origin, $this->API->getSettings()->getDefaultDcParams());
|
||||
break;
|
||||
case self::WALLPAPER_ORIGIN:
|
||||
yield from $this->API->methodCallAsyncRead('account.getWallPapers', $origin, ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
yield from $this->API->methodCallAsyncRead('account.getWallPapers', $origin, $this->API->getSettings()->getDefaultDcParams());
|
||||
break;
|
||||
default:
|
||||
throw new \danog\MadelineProto\Exception("Unknown origin type {$originType}");
|
||||
|
@ -20,59 +20,33 @@
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
use Amp\Deferred;
|
||||
use Amp\Http\Client\Request;
|
||||
use Amp\Loop;
|
||||
use danog\MadelineProto\Logger;
|
||||
use danog\MadelineProto\Loop\Update\FeedLoop;
|
||||
use danog\MadelineProto\Loop\Update\UpdateLoop;
|
||||
use danog\MadelineProto\MTProto;
|
||||
use danog\MadelineProto\RPCErrorException;
|
||||
|
||||
use danog\MadelineProto\Settings;
|
||||
|
||||
/**
|
||||
* Manages updates.
|
||||
*
|
||||
* @property Settings $settings Settings
|
||||
*/
|
||||
trait UpdateHandler
|
||||
{
|
||||
/**
|
||||
* Update handler callback.
|
||||
*
|
||||
* @var ?callable
|
||||
*/
|
||||
private $updateHandler;
|
||||
private $got_state = false;
|
||||
private $channels_state;
|
||||
public $updates = [];
|
||||
public $updates_key = 0;
|
||||
/**
|
||||
* PWR update handler.
|
||||
*
|
||||
* @param array $update Update
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function pwrUpdateHandler($update)
|
||||
{
|
||||
if (isset($this->settings['pwr']['updateHandler'])) {
|
||||
if (\is_array($this->settings['pwr']['updateHandler']) && $this->settings['pwr']['updateHandler'][0] === false) {
|
||||
$this->settings['pwr']['updateHandler'] = $this->settings['pwr']['updateHandler'][1];
|
||||
}
|
||||
if (\is_string($this->settings['pwr']['updateHandler'])) {
|
||||
return $this->{$this->settings['pwr']['updateHandler']}($update);
|
||||
}
|
||||
\in_array($this->settings['pwr']['updateHandler'], [['danog\\MadelineProto\\API', 'getUpdatesUpdateHandler'], 'getUpdatesUpdateHandler']) ? $this->getUpdatesUpdateHandler($update) : $this->settings['pwr']['updateHandler']($update);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Getupdates update handler.
|
||||
*
|
||||
* @param array $update Update
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getUpdatesUpdateHandler(array $update): void
|
||||
{
|
||||
if (!$this->settings['updates']['handle_updates']) {
|
||||
return;
|
||||
}
|
||||
$this->updates[$this->updates_key++] = $update;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get updates.
|
||||
*
|
||||
@ -84,23 +58,17 @@ trait UpdateHandler
|
||||
*/
|
||||
public function getUpdates($params = []): \Generator
|
||||
{
|
||||
if (!$this->settings['updates']['handle_updates']) {
|
||||
$this->settings['updates']['handle_updates'] = true;
|
||||
$this->startUpdateSystem();
|
||||
}
|
||||
if (!$this->settings['updates']['run_callback']) {
|
||||
$this->settings['updates']['run_callback'] = true;
|
||||
}
|
||||
$params = \array_merge(self::DEFAULT_GETUPDATES_PARAMS, $params);
|
||||
$this->updateHandler = MTProto::GETUPDATES_HANDLER;
|
||||
$params = \array_merge(MTProto::DEFAULT_GETUPDATES_PARAMS, $params);
|
||||
if (empty($this->updates)) {
|
||||
$this->update_deferred = new Deferred();
|
||||
if (!$params['timeout']) {
|
||||
$params['timeout'] = 0.001;
|
||||
}
|
||||
yield $this->waitUpdate();
|
||||
yield from $this->waitUpdate();
|
||||
}
|
||||
if (empty($this->updates)) {
|
||||
return [];
|
||||
return $this->updates;
|
||||
}
|
||||
if ($params['offset'] < 0) {
|
||||
$params['offset'] = \array_reverse(\array_keys((array) $this->updates))[\abs($params['offset']) - 1];
|
||||
@ -229,8 +197,8 @@ trait UpdateHandler
|
||||
*/
|
||||
public function getUpdatesState(): \Generator
|
||||
{
|
||||
$data = yield from $this->methodCallAsyncRead('updates.getState', [], ['datacenter' => $this->settings['connection_settings']['default_dc']]);
|
||||
yield from $this->getCdnConfig($this->settings['connection_settings']['default_dc']);
|
||||
$data = yield from $this->methodCallAsyncRead('updates.getState', [], $this->settings->getDefaultDcParams());
|
||||
yield from $this->getCdnConfig($this->settings->getDefaultDc());
|
||||
return $data;
|
||||
}
|
||||
/**
|
||||
@ -245,9 +213,6 @@ trait UpdateHandler
|
||||
*/
|
||||
public function handleUpdates($updates, $actual_updates = null): \Generator
|
||||
{
|
||||
if (!$this->settings['updates']['handle_updates']) {
|
||||
return;
|
||||
}
|
||||
if ($actual_updates) {
|
||||
$updates = $actual_updates;
|
||||
}
|
||||
@ -395,7 +360,7 @@ trait UpdateHandler
|
||||
return false;
|
||||
}
|
||||
$this->logger->logger('Applying qts: '.$update['qts'].' over current qts '.$cur_state->qts().', chat id: '.$update['message']['chat_id'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
yield from $this->methodCallAsyncRead('messages.receivedQueue', ['max_qts' => $cur_state->qts($update['qts'])], ['datacenter' => $this->settings['connection_settings']['default_dc']]);
|
||||
yield from $this->methodCallAsyncRead('messages.receivedQueue', ['max_qts' => $cur_state->qts($update['qts'])], $this->settings->getDefaultDcParams());
|
||||
}
|
||||
yield from $this->handleEncryptedUpdate($update);
|
||||
return;
|
||||
@ -408,7 +373,7 @@ trait UpdateHandler
|
||||
if ($update['_'] === 'updateEncryption') {
|
||||
switch ($update['chat']['_']) {
|
||||
case 'encryptedChatRequested':
|
||||
if ($this->settings['secret_chats']['accept_chats'] === false || \is_array($this->settings['secret_chats']['accept_chats']) && !\in_array($update['chat']['admin_id'], $this->settings['secret_chats']['accept_chats'])) {
|
||||
if (!$this->settings->getSecretChats()->canAccept($update['chat']['admin_id'])) {
|
||||
return;
|
||||
}
|
||||
$this->logger->logger('Accepting secret chat '.$update['chat']['id'], \danog\MadelineProto\Logger::NOTICE);
|
||||
@ -439,7 +404,7 @@ trait UpdateHandler
|
||||
}
|
||||
//if ($update['_'] === 'updateServiceNotification' && strpos($update['type'], 'AUTH_KEY_DROP_') === 0) {
|
||||
//}
|
||||
if (!$this->settings['updates']['handle_updates']) {
|
||||
if (!$this->updateHandler) {
|
||||
return;
|
||||
}
|
||||
if (isset($update['message']['_']) && $update['message']['_'] === 'messageEmpty') {
|
||||
@ -448,42 +413,7 @@ trait UpdateHandler
|
||||
if (isset($update['message']['from_id']) && $update['message']['from_id'] === $this->authorization['user']['id']) {
|
||||
$update['message']['out'] = true;
|
||||
}
|
||||
//$this->logger->logger('Saving an update of type '.$update['_'].'...', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
if (isset($this->settings['pwr']['strict']) && $this->settings['pwr']['strict'] && isset($this->settings['pwr']['updateHandler'])) {
|
||||
$this->pwrUpdateHandler($update);
|
||||
} elseif ($this->settings['updates']['run_callback']) {
|
||||
$this->getUpdatesUpdateHandler($update);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Send update to webhook.
|
||||
*
|
||||
* @param array $update Update
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function pwrWebhook(array $update): void
|
||||
{
|
||||
$payload = \json_encode($update);
|
||||
//$this->logger->logger($update, $payload, json_last_error());
|
||||
if ($payload === '') {
|
||||
$this->logger->logger('EMPTY UPDATE');
|
||||
return;
|
||||
}
|
||||
\danog\MadelineProto\Tools::callFork((function () use ($payload): \Generator {
|
||||
$request = new Request($this->hook_url, 'POST');
|
||||
$request->setHeader('content-type', 'application/json');
|
||||
$request->setBody($payload);
|
||||
$result = yield (yield $this->datacenter->getHTTPClient()->request($request))->getBody()->buffer();
|
||||
$this->logger->logger('Result of webhook query is '.$result, \danog\MadelineProto\Logger::NOTICE);
|
||||
$result = \json_decode($result, true);
|
||||
if (\is_array($result) && isset($result['method']) && $result['method'] != '' && \is_string($result['method'])) {
|
||||
try {
|
||||
$this->logger->logger('Reverse webhook command returned', yield from $this->methodCallAsyncRead($result['method'], $result, ['datacenter' => $this->datacenter->curdc]));
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->logger("Reverse webhook command returned: {$e}");
|
||||
}
|
||||
}
|
||||
})());
|
||||
// First save to array, then once the feed loop signals resumal of loop, resume and handle
|
||||
$this->updates[$this->updates_key++] = $update;
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ use Amp\Loop;
|
||||
use Amp\Loop\Driver;
|
||||
use ReflectionClass;
|
||||
use function Amp\ByteStream\getStdin;
|
||||
use function Amp\Log\hasColorSupport;
|
||||
use function Amp\Promise\wait;
|
||||
|
||||
class Magic
|
||||
@ -247,7 +248,7 @@ class Magic
|
||||
}
|
||||
// Check if we're in a console, for colorful log output
|
||||
try {
|
||||
self::$isatty = \defined(\STDOUT::class) && \function_exists('posix_isatty') && \posix_isatty(\STDOUT);
|
||||
self::$isatty = \defined(\STDOUT::class) && hasColorSupport();
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
}
|
||||
// Important, obtain root relative to caller script
|
||||
|
@ -48,7 +48,7 @@ class MyTelegramOrgWrapper
|
||||
/**
|
||||
* Settings.
|
||||
*/
|
||||
private array $settings = [];
|
||||
private Settings $settings;
|
||||
/**
|
||||
* Async setting.
|
||||
*/
|
||||
@ -79,11 +79,11 @@ class MyTelegramOrgWrapper
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $settings
|
||||
* @param array|Settings $settings
|
||||
*/
|
||||
public function __construct(array $settings = [])
|
||||
public function __construct($settings)
|
||||
{
|
||||
$this->settings = MTProto::parseSettings($settings, $this->settings);
|
||||
$this->settings = Settings::parseFromLegacy($settings);
|
||||
$this->__wakeup();
|
||||
}
|
||||
/**
|
||||
@ -93,23 +93,24 @@ class MyTelegramOrgWrapper
|
||||
*/
|
||||
public function __wakeup(): void
|
||||
{
|
||||
if ($this->settings === null) {
|
||||
$this->settings = [];
|
||||
if (!$this->settings) {
|
||||
$this->settings = new Settings;
|
||||
} elseif (\is_array($this->settings)) {
|
||||
$this->settings = Settings::parseFromLegacy($this->settings);
|
||||
}
|
||||
if (!$this->jar || !$this->jar instanceof InMemoryCookieJar) {
|
||||
$this->jar = new InMemoryCookieJar();
|
||||
}
|
||||
$this->settings = MTProto::parseSettings($this->settings);
|
||||
$this->datacenter = new DataCenter(new class($this->settings) {
|
||||
public function __construct($settings)
|
||||
$this->datacenter = new DataCenter(new class(new Logger($this->settings->getLogger())) {
|
||||
public function __construct(Logger $logger)
|
||||
{
|
||||
$this->logger = Logger::getLoggerFromSettings($settings);
|
||||
$this->logger = $logger;
|
||||
}
|
||||
public function getLogger()
|
||||
{
|
||||
return $this->logger;
|
||||
}
|
||||
}, [], $this->settings['connection_settings'], true, $this->jar);
|
||||
}, [], $this->settings->getConnection(), true, $this->jar);
|
||||
}
|
||||
/**
|
||||
* Login.
|
||||
|
498
src/danog/MadelineProto/Settings.php
Normal file
498
src/danog/MadelineProto/Settings.php
Normal file
@ -0,0 +1,498 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
use danog\MadelineProto\Settings\AppInfo;
|
||||
use danog\MadelineProto\Settings\Auth;
|
||||
use danog\MadelineProto\Settings\Connection;
|
||||
use danog\MadelineProto\Settings\Database\Memory as DatabaseMemory;
|
||||
use danog\MadelineProto\Settings\Database\Mysql;
|
||||
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\Logger;
|
||||
use danog\MadelineProto\Settings\Peer;
|
||||
use danog\MadelineProto\Settings\Pwr;
|
||||
use danog\MadelineProto\Settings\RPC;
|
||||
use danog\MadelineProto\Settings\SecretChats;
|
||||
use danog\MadelineProto\Settings\Serialization;
|
||||
use danog\MadelineProto\Settings\TLSchema;
|
||||
|
||||
class Settings extends SettingsAbstract
|
||||
{
|
||||
/**
|
||||
* App information.
|
||||
*/
|
||||
protected AppInfo $appInfo;
|
||||
/**
|
||||
* Cryptography settings.
|
||||
*/
|
||||
protected Auth $auth;
|
||||
/**
|
||||
* Connection settings.
|
||||
*/
|
||||
protected Connection $connection;
|
||||
/**
|
||||
* File management settings.
|
||||
*/
|
||||
protected Files $files;
|
||||
/**
|
||||
* Logger settings.
|
||||
*/
|
||||
protected Logger $logger;
|
||||
/**
|
||||
* Peer database settings.
|
||||
*/
|
||||
protected Peer $peer;
|
||||
/**
|
||||
* PWRTelegram settings.
|
||||
*/
|
||||
protected Pwr $pwr;
|
||||
/**
|
||||
* RPC settings.
|
||||
*/
|
||||
protected RPC $rpc;
|
||||
/**
|
||||
* Secret chat settings.
|
||||
*/
|
||||
protected SecretChats $secretChats;
|
||||
/**
|
||||
* Serialization settings.
|
||||
*/
|
||||
protected Serialization $serialization;
|
||||
/**
|
||||
* TL schema settings.
|
||||
*/
|
||||
protected TLSchema $schema;
|
||||
/**
|
||||
* DatabaseAbstract settings.
|
||||
*/
|
||||
protected DatabaseAbstract $db;
|
||||
|
||||
/**
|
||||
* Create settings object from possibly legacy settings array.
|
||||
*
|
||||
* @param SettingsAbstract|array $settings Settings
|
||||
*
|
||||
* @return SettingsAbstract
|
||||
*/
|
||||
public static function parseFromLegacy($settings): SettingsAbstract
|
||||
{
|
||||
if (\is_array($settings)) {
|
||||
if (empty($settings)) {
|
||||
return new SettingsEmpty;
|
||||
}
|
||||
$settingsNew = new Settings;
|
||||
$settingsNew->mergeArray($settings);
|
||||
return $settingsNew;
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->appInfo = new AppInfo;
|
||||
$this->auth = new Auth;
|
||||
$this->connection = new Connection;
|
||||
$this->files = new Files;
|
||||
$this->logger = new Logger;
|
||||
$this->peer = new Peer;
|
||||
$this->pwr = new Pwr;
|
||||
$this->rpc = new RPC;
|
||||
$this->secretChats = new SecretChats;
|
||||
$this->serialization = new Serialization;
|
||||
$this->schema = new TLSchema;
|
||||
$this->db = new DatabaseMemory;
|
||||
}
|
||||
/**
|
||||
* Merge legacy array settings.
|
||||
*
|
||||
* @param array $settings Settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
$this->appInfo->mergeArray($settings);
|
||||
$this->auth->mergeArray($settings);
|
||||
$this->connection->mergeArray($settings);
|
||||
$this->files->mergeArray($settings);
|
||||
$this->logger->mergeArray($settings);
|
||||
$this->peer->mergeArray($settings);
|
||||
$this->pwr->mergeArray($settings);
|
||||
$this->rpc->mergeArray($settings);
|
||||
$this->secretChats->mergeArray($settings);
|
||||
$this->serialization->mergeArray($settings);
|
||||
$this->schema->mergeArray($settings);
|
||||
|
||||
switch ($settings['db']['type'] ?? 'memory') {
|
||||
case 'memory':
|
||||
$this->db = new DatabaseMemory;
|
||||
break;
|
||||
case 'mysql':
|
||||
$this->db = new Mysql;
|
||||
break;
|
||||
case 'postgres':
|
||||
$this->db = new Postgres;
|
||||
break;
|
||||
case 'redis':
|
||||
$this->db = new Redis;
|
||||
break;
|
||||
}
|
||||
$this->db->mergeArray($settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge another instance of settings.
|
||||
*
|
||||
* @param SettingsAbstract $settings Settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function merge(SettingsAbstract $settings): void
|
||||
{
|
||||
if (!$settings instanceof self) {
|
||||
return;
|
||||
}
|
||||
$this->appInfo->merge($settings->appInfo);
|
||||
$this->auth->merge($settings->auth);
|
||||
$this->connection->merge($settings->connection);
|
||||
$this->files->merge($settings->files);
|
||||
$this->logger->merge($settings->logger);
|
||||
$this->peer->merge($settings->peer);
|
||||
$this->pwr->merge($settings->pwr);
|
||||
$this->rpc->merge($settings->rpc);
|
||||
$this->secretChats->merge($settings->secretChats);
|
||||
$this->serialization->merge($settings->serialization);
|
||||
$this->schema->merge($settings->schema);
|
||||
|
||||
if (!$this->db instanceof $settings->db) {
|
||||
$this->db = $settings->db;
|
||||
} else {
|
||||
$this->db->merge($settings->db);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default DC ID.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getDefaultDc(): int
|
||||
{
|
||||
return $this->connection->getDefaultDc();
|
||||
}
|
||||
/**
|
||||
* Get default DC params.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDefaultDcParams(): array
|
||||
{
|
||||
return $this->connection->getDefaultDcParams();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default DC ID.
|
||||
*
|
||||
* @param int $dc DC ID
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setDefaultDc(int $dc): self
|
||||
{
|
||||
$this->connection->setDefaultDc($dc);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get app information.
|
||||
*
|
||||
* @return AppInfo
|
||||
*/
|
||||
public function getAppInfo(): AppInfo
|
||||
{
|
||||
return $this->appInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set app information.
|
||||
*
|
||||
* @param AppInfo $appInfo App information.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setAppInfo(AppInfo $appInfo): self
|
||||
{
|
||||
$this->appInfo = $appInfo;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cryptography settings.
|
||||
*
|
||||
* @return Auth
|
||||
*/
|
||||
public function getAuth(): Auth
|
||||
{
|
||||
return $this->auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cryptography settings.
|
||||
*
|
||||
* @param Auth $auth Cryptography settings.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setAuth(Auth $auth): self
|
||||
{
|
||||
$this->auth = $auth;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get connection settings.
|
||||
*
|
||||
* @return Connection
|
||||
*/
|
||||
public function getConnection(): Connection
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set connection settings.
|
||||
*
|
||||
* @param Connection $connection Connection settings.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setConnection(Connection $connection): self
|
||||
{
|
||||
$this->connection = $connection;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file management settings.
|
||||
*
|
||||
* @return Files
|
||||
*/
|
||||
public function getFiles(): Files
|
||||
{
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set file management settings.
|
||||
*
|
||||
* @param Files $files File management settings.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setFiles(Files $files): self
|
||||
{
|
||||
$this->files = $files;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get logger settings.
|
||||
*
|
||||
* @return Logger
|
||||
*/
|
||||
public function getLogger(): Logger
|
||||
{
|
||||
return $this->logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set logger settings.
|
||||
*
|
||||
* @param Logger $logger Logger settings.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setLogger(Logger $logger): self
|
||||
{
|
||||
$this->logger = $logger;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get peer database settings.
|
||||
*
|
||||
* @return Peer
|
||||
*/
|
||||
public function getPeer(): Peer
|
||||
{
|
||||
return $this->peer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set peer database settings.
|
||||
*
|
||||
* @param Peer $peer Peer database settings.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setPeer(Peer $peer): self
|
||||
{
|
||||
$this->peer = $peer;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get PWRTelegram settings.
|
||||
*
|
||||
* @return Pwr
|
||||
*/
|
||||
public function getPwr(): Pwr
|
||||
{
|
||||
return $this->pwr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set PWRTelegram settings.
|
||||
*
|
||||
* @param Pwr $pwr PWRTelegram settings.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setPwr(Pwr $pwr): self
|
||||
{
|
||||
$this->pwr = $pwr;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get RPC settings.
|
||||
*
|
||||
* @return RPC
|
||||
*/
|
||||
public function getRpc(): RPC
|
||||
{
|
||||
return $this->rpc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set RPC settings.
|
||||
*
|
||||
* @param RPC $rpc RPC settings.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setRpc(RPC $rpc): self
|
||||
{
|
||||
$this->rpc = $rpc;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get secret chat settings.
|
||||
*
|
||||
* @return SecretChats
|
||||
*/
|
||||
public function getSecretChats(): SecretChats
|
||||
{
|
||||
return $this->secretChats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set secret chat settings.
|
||||
*
|
||||
* @param SecretChats $secretChats Secret chat settings.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setSecretChats(SecretChats $secretChats): self
|
||||
{
|
||||
$this->secretChats = $secretChats;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get serialization settings.
|
||||
*
|
||||
* @return Serialization
|
||||
*/
|
||||
public function getSerialization(): Serialization
|
||||
{
|
||||
return $this->serialization;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set serialization settings.
|
||||
*
|
||||
* @param Serialization $serialization Serialization settings.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setSerialization(Serialization $serialization): self
|
||||
{
|
||||
$this->serialization = $serialization;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get TL schema settings.
|
||||
*
|
||||
* @return TLSchema
|
||||
*/
|
||||
public function getSchema(): TLSchema
|
||||
{
|
||||
return $this->schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set TL schema settings.
|
||||
*
|
||||
* @param TLSchema $schema TL schema settings.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setSchema(TLSchema $schema): self
|
||||
{
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database settings.
|
||||
*
|
||||
* @return DatabaseAbstract
|
||||
*/
|
||||
public function getDb(): DatabaseAbstract
|
||||
{
|
||||
return $this->db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set database settings.
|
||||
*
|
||||
* @param DatabaseAbstract $db DatabaseAbstract settings.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setDb(DatabaseAbstract $db): self
|
||||
{
|
||||
$this->db = $db;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
298
src/danog/MadelineProto/Settings/AppInfo.php
Normal file
298
src/danog/MadelineProto/Settings/AppInfo.php
Normal file
@ -0,0 +1,298 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings;
|
||||
|
||||
use danog\MadelineProto\Exception;
|
||||
use danog\MadelineProto\Lang;
|
||||
use danog\MadelineProto\Magic;
|
||||
use danog\MadelineProto\MTProto;
|
||||
use danog\MadelineProto\SettingsAbstract;
|
||||
|
||||
class AppInfo extends SettingsAbstract
|
||||
{
|
||||
/**
|
||||
* API ID.
|
||||
*/
|
||||
protected int $apiId;
|
||||
/**
|
||||
* API hash.
|
||||
*/
|
||||
protected string $apiHash;
|
||||
/**
|
||||
* Device model.
|
||||
*/
|
||||
protected string $deviceModel;
|
||||
/**
|
||||
* System version.
|
||||
*/
|
||||
protected string $systemVersion;
|
||||
/**
|
||||
* App version.
|
||||
*/
|
||||
protected string $appVersion;
|
||||
/**
|
||||
* Language code.
|
||||
*/
|
||||
protected string $langCode = 'en';
|
||||
/**
|
||||
* Language pack.
|
||||
*/
|
||||
protected string $langPack = '';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// Detect device model
|
||||
try {
|
||||
$this->deviceModel = \php_uname('s');
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->deviceModel = 'Web server';
|
||||
}
|
||||
// Detect system version
|
||||
try {
|
||||
$this->systemVersion = \php_uname('r');
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->systemVersion = PHP_VERSION;
|
||||
}
|
||||
// Detect language
|
||||
Lang::$current_lang =& Lang::$lang[$this->langCode];
|
||||
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
||||
$this->setLangCode(\substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2));
|
||||
} elseif (isset($_SERVER['LANG'])) {
|
||||
$this->setLangCode(\explode('_', $_SERVER['LANG'])[0]);
|
||||
}
|
||||
$this->init();
|
||||
}
|
||||
public function __wakeup()
|
||||
{
|
||||
$this->init();
|
||||
}
|
||||
public function init(): void
|
||||
{
|
||||
Magic::classExists(true);
|
||||
// Detect language pack
|
||||
$this->appVersion = MTProto::RELEASE.' ('.MTProto::V.', '.\str_replace(' (AN UPDATE IS REQUIRED)', '', Magic::$revision).')';
|
||||
}
|
||||
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
foreach (self::toCamel([
|
||||
'api_id',
|
||||
'api_hash',
|
||||
'device_model',
|
||||
'system_version',
|
||||
'app_version',
|
||||
'lang_code',
|
||||
'lang_pack'
|
||||
]) as $object => $array) {
|
||||
if (isset($settings['app_info'][$array])) {
|
||||
$this->{$object}($settings['app_info'][$array]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the settings have API ID/hash information.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasApiInfo(): bool
|
||||
{
|
||||
return isset($this->apiHash, $this->apiId) && $this->apiId;
|
||||
}
|
||||
/**
|
||||
* Get API ID.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getApiId(): int
|
||||
{
|
||||
if (!isset($this->apiId)) {
|
||||
throw new Exception(Lang::$current_lang['api_not_set']);
|
||||
}
|
||||
return $this->apiId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set API ID.
|
||||
*
|
||||
* @param int $apiId API ID.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setApiId(int $apiId): self
|
||||
{
|
||||
$this->apiId = $apiId;
|
||||
if ($apiId === 6) {
|
||||
// TG DEV NOTICE: these app info spoofing measures were implemented for NON-MALICIOUS purposes.
|
||||
// All accounts registered with a custom API ID require manual verification through recover@telegram.org, to avoid instant permabans.
|
||||
// This makes usage of all MTProto libraries very difficult, at least for new users.
|
||||
// To help a bit, when the android API ID is used, the android app infos are spoofed too.
|
||||
// THE ANDROID API HASH IS NOT PRESENT IN THIS REPOSITORY, AND WILL NOT BE GIVEN TO EVERYONE.
|
||||
// This measure was NOT created with the intent to aid spammers, flooders, and other scum.
|
||||
//
|
||||
// I understand that automated account registration through headless libraries may indicate the creation of a botnet,
|
||||
// ...and I understand why these automatic bans were implemented in the first place.
|
||||
// Manual requests to activate numbers through recover@telegram.org will still be required for the majority of users of this library,
|
||||
// ...those that choose to user their own API ID for their application.
|
||||
//
|
||||
// To be honest, I wrote this feature just for me, since I honestly don't want to
|
||||
// ...go through the hassle of registering => recovering => logging in to every account I use for my services (mainly webradios and test userbots)
|
||||
$this->deviceModel = 'LGENexus 5';
|
||||
$this->systemVersion = 'SDK 28';
|
||||
$this->appVersion = '4.9.1 (13613)';
|
||||
$this->langPack = 'android';
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get API hash.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getApiHash(): string
|
||||
{
|
||||
if (!isset($this->apiHash)) {
|
||||
throw new Exception(Lang::$current_lang['api_not_set']);
|
||||
}
|
||||
return $this->apiHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set API hash.
|
||||
*
|
||||
* @param string $apiHash API hash.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setApiHash(string $apiHash): self
|
||||
{
|
||||
$this->apiHash = $apiHash;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get device model.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDeviceModel(): string
|
||||
{
|
||||
return $this->deviceModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set device model.
|
||||
*
|
||||
* @param string $deviceModel Device model.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setDeviceModel(string $deviceModel): self
|
||||
{
|
||||
$this->deviceModel = $deviceModel;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get system version.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSystemVersion(): string
|
||||
{
|
||||
return $this->systemVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set system version.
|
||||
*
|
||||
* @param string $systemVersion System version.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setSystemVersion(string $systemVersion): self
|
||||
{
|
||||
$this->systemVersion = $systemVersion;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get app version.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAppVersion(): string
|
||||
{
|
||||
return $this->appVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set app version.
|
||||
*
|
||||
* @param string $appVersion App version.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setAppVersion(string $appVersion): self
|
||||
{
|
||||
$this->appVersion = $appVersion;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get language code.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLangCode(): string
|
||||
{
|
||||
return $this->langCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set language code.
|
||||
*
|
||||
* @param string $langCode Language code.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setLangCode(string $langCode): self
|
||||
{
|
||||
$this->langCode = $langCode;
|
||||
if (isset(Lang::$lang[$this->langCode])) {
|
||||
Lang::$current_lang =& Lang::$lang[$this->langCode];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get language pack.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLangPack(): string
|
||||
{
|
||||
return $this->langPack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set language pack.
|
||||
*
|
||||
* @param string $langPack Language pack.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setLangPack(string $langPack): self
|
||||
{
|
||||
$this->langPack = $langPack;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
148
src/danog/MadelineProto/Settings/Auth.php
Normal file
148
src/danog/MadelineProto/Settings/Auth.php
Normal file
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings;
|
||||
|
||||
use danog\MadelineProto\SettingsAbstract;
|
||||
|
||||
class Auth extends SettingsAbstract
|
||||
{
|
||||
/**
|
||||
* Validity period of temporary keys.
|
||||
* Validity period of the binding of temporary and permanent keys.
|
||||
*/
|
||||
protected int $defaultTempAuthKeyExpiresIn = 1 * 24 * 60 * 60;
|
||||
/**
|
||||
* MTProto public keys array.
|
||||
*/
|
||||
protected array $rsaKeys = [
|
||||
"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6\nlyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS\nan9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw\nEfzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+\n8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n\nSlv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB\n-----END RSA PUBLIC KEY-----",
|
||||
"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAxq7aeLAqJR20tkQQMfRn+ocfrtMlJsQ2Uksfs7Xcoo77jAid0bRt\nksiVmT2HEIJUlRxfABoPBV8wY9zRTUMaMA654pUX41mhyVN+XoerGxFvrs9dF1Ru\nvCHbI02dM2ppPvyytvvMoefRoL5BTcpAihFgm5xCaakgsJ/tH5oVl74CdhQw8J5L\nxI/K++KJBUyZ26Uba1632cOiq05JBUW0Z2vWIOk4BLysk7+U9z+SxynKiZR3/xdi\nXvFKk01R3BHV+GUKM2RYazpS/P8v7eyKhAbKxOdRcFpHLlVwfjyM1VlDQrEZxsMp\nNTLYXb6Sce1Uov0YtNx5wEowlREH1WOTlwIDAQAB\n-----END RSA PUBLIC KEY-----",
|
||||
"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAsQZnSWVZNfClk29RcDTJQ76n8zZaiTGuUsi8sUhW8AS4PSbPKDm+\nDyJgdHDWdIF3HBzl7DHeFrILuqTs0vfS7Pa2NW8nUBwiaYQmPtwEa4n7bTmBVGsB\n1700/tz8wQWOLUlL2nMv+BPlDhxq4kmJCyJfgrIrHlX8sGPcPA4Y6Rwo0MSqYn3s\ng1Pu5gOKlaT9HKmE6wn5Sut6IiBjWozrRQ6n5h2RXNtO7O2qCDqjgB2vBxhV7B+z\nhRbLbCmW0tYMDsvPpX5M8fsO05svN+lKtCAuz1leFns8piZpptpSCFn7bWxiA9/f\nx5x17D7pfah3Sy2pA+NDXyzSlGcKdaUmwQIDAQAB\n-----END RSA PUBLIC KEY-----",
|
||||
"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAwqjFW0pi4reKGbkc9pK83Eunwj/k0G8ZTioMMPbZmW99GivMibwa\nxDM9RDWabEMyUtGoQC2ZcDeLWRK3W8jMP6dnEKAlvLkDLfC4fXYHzFO5KHEqF06i\nqAqBdmI1iBGdQv/OQCBcbXIWCGDY2AsiqLhlGQfPOI7/vvKc188rTriocgUtoTUc\n/n/sIUzkgwTqRyvWYynWARWzQg0I9olLBBC2q5RQJJlnYXZwyTL3y9tdb7zOHkks\nWV9IMQmZmyZh/N7sMbGWQpt4NMchGpPGeJ2e5gHBjDnlIf2p1yZOYeUYrdbwcS0t\nUiggS4UeE8TzIuXFQxw7fzEIlmhIaq3FnwIDAQAB\n-----END RSA PUBLIC KEY-----"
|
||||
];
|
||||
|
||||
/**
|
||||
* Whether to use PFS.
|
||||
*/
|
||||
protected bool $pfs;
|
||||
|
||||
/**
|
||||
* Max tries for generating auth key.
|
||||
*/
|
||||
protected int $maxAuthTries = 5;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->pfs = \extension_loaded('gmp');
|
||||
}
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
foreach (self::toCamel([
|
||||
'default_temp_auth_key_expires_in',
|
||||
'rsa_keys',
|
||||
]) as $object => $array) {
|
||||
if (isset($settings['authorization'][$array])) {
|
||||
$this->{$object}($settings['authorization'][$array]);
|
||||
}
|
||||
}
|
||||
if (isset($settings['connection_settings']['all']['pfs'])) {
|
||||
$this->setPfs($settings['connection_settings']['all']['pfs']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get MTProto public keys array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRsaKeys(): array
|
||||
{
|
||||
return $this->rsaKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set MTProto public keys array.
|
||||
*
|
||||
* @param array $rsaKeys MTProto public keys array.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setRsaKeys(array $rsaKeys): self
|
||||
{
|
||||
$this->rsaKeys = $rsaKeys;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get validity period of the binding of temporary and permanent keys.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDefaultTempAuthKeyExpiresIn(): int
|
||||
{
|
||||
return $this->defaultTempAuthKeyExpiresIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set validity period of the binding of temporary and permanent keys.
|
||||
*
|
||||
* @param int $defaultTempAuthKeyExpiresIn Validity period of the binding of temporary and permanent keys.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setDefaultTempAuthKeyExpiresIn(int $defaultTempAuthKeyExpiresIn): self
|
||||
{
|
||||
$this->defaultTempAuthKeyExpiresIn = $defaultTempAuthKeyExpiresIn;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether to use PFS.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getPfs(): bool
|
||||
{
|
||||
return $this->pfs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to use PFS.
|
||||
*
|
||||
* @param bool $pfs Whether to use PFS
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setPfs(bool $pfs): self
|
||||
{
|
||||
$this->pfs = $pfs;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max tries for generating auth key.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxAuthTries(): int
|
||||
{
|
||||
return $this->maxAuthTries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set max tries for generating auth key.
|
||||
*
|
||||
* @param int $maxAuthTries Max tries for generating auth key
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setMaxAuthTries(int $maxAuthTries): self
|
||||
{
|
||||
$this->maxAuthTries = $maxAuthTries;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
620
src/danog/MadelineProto/Settings/Connection.php
Normal file
620
src/danog/MadelineProto/Settings/Connection.php
Normal file
@ -0,0 +1,620 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings;
|
||||
|
||||
use danog\MadelineProto\Exception;
|
||||
use danog\MadelineProto\Magic;
|
||||
use danog\MadelineProto\SettingsAbstract;
|
||||
use danog\MadelineProto\Stream\Common\UdpBufferedStream;
|
||||
use danog\MadelineProto\Stream\MTProtoBufferInterface;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\AbridgedStream;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\FullStream;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\HttpsStream;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\HttpStream;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\IntermediatePaddedStream;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\ObfuscatedStream;
|
||||
use danog\MadelineProto\Stream\Proxy\HttpProxy;
|
||||
use danog\MadelineProto\Stream\Proxy\SocksProxy;
|
||||
use danog\MadelineProto\Stream\RawStreamInterface;
|
||||
use danog\MadelineProto\Stream\StreamInterface;
|
||||
use danog\MadelineProto\Stream\Transport\DefaultStream;
|
||||
use danog\MadelineProto\Stream\Transport\WssStream;
|
||||
use danog\MadelineProto\Stream\Transport\WsStream;
|
||||
|
||||
class Connection extends SettingsAbstract
|
||||
{
|
||||
/**
|
||||
* Minimum media socket count.
|
||||
*/
|
||||
protected int $minMediaSocketCount = 5;
|
||||
/**
|
||||
* Maximum media socket count.
|
||||
*/
|
||||
protected int $maxMediaSocketCount = 10;
|
||||
/**
|
||||
* Robin period (seconds).
|
||||
*/
|
||||
protected int $robinPeriod = 10;
|
||||
/**
|
||||
* Default DC ID.
|
||||
*/
|
||||
protected int $defaultDc = 2;
|
||||
/**
|
||||
* Default DC params.
|
||||
*/
|
||||
private array $defaultDcParams = ['datacenter' => 2];
|
||||
/**
|
||||
* Protocol identifier.
|
||||
*
|
||||
* @var class-string<MTProtoBufferInterface>
|
||||
*/
|
||||
protected string $protocol = AbridgedStream::class;
|
||||
/**
|
||||
* Transport identifier.
|
||||
*
|
||||
* @var class-string<RawStreamInterface>
|
||||
*/
|
||||
protected string $transport = DefaultStream::class;
|
||||
/**
|
||||
* Proxy identifiers.
|
||||
*
|
||||
* @var array<class-string<StreamInterface>, array>
|
||||
*/
|
||||
protected array $proxy = [];
|
||||
/**
|
||||
* Whether to use the obfuscated protocol.
|
||||
*/
|
||||
protected bool $obfuscated = false;
|
||||
|
||||
/**
|
||||
* Whether we're in test mode.
|
||||
*/
|
||||
protected bool $testMode = false;
|
||||
|
||||
/**
|
||||
* Whether to use ipv6.
|
||||
*/
|
||||
protected bool $ipv6;
|
||||
|
||||
/**
|
||||
* Connection timeout.
|
||||
*/
|
||||
protected int $timeout = 2;
|
||||
|
||||
/**
|
||||
* Whether to retry connection.
|
||||
*/
|
||||
protected bool $retry = true;
|
||||
|
||||
/**
|
||||
* Whether the connection settings changed.
|
||||
*/
|
||||
private bool $changed = true;
|
||||
/**
|
||||
* Subdomains of web.telegram.org for https protocol.
|
||||
*/
|
||||
protected array $sslSubdomains = [
|
||||
1 => 'pluto',
|
||||
2 => 'venus',
|
||||
3 => 'aurora',
|
||||
4 => 'vesta',
|
||||
5 => 'flora',
|
||||
];
|
||||
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
if (isset($settings['connection']['ssl_subdomains'])) {
|
||||
$this->setSslSubdomains($settings['connection']['ssl_subdomains']);
|
||||
}
|
||||
$settings = $settings['connection_settings'] ?? [];
|
||||
if (isset($settings['media_socket_count']['min'])) {
|
||||
$this->setMinMediaSocketCount($settings['media_socket_count']['min']);
|
||||
}
|
||||
if (isset($settings['media_socket_count']['max'])) {
|
||||
$this->setMaxMediaSocketCount($settings['media_socket_count']['max']);
|
||||
}
|
||||
foreach (self::toCamel([
|
||||
'robin_period',
|
||||
'default_dc',
|
||||
'pfs'
|
||||
]) as $object => $array) {
|
||||
if (isset($settings[$array])) {
|
||||
$this->{$object}($settings[$array]);
|
||||
}
|
||||
}
|
||||
|
||||
$settings = $settings['all'] ?? [];
|
||||
foreach (self::toCamel([
|
||||
'test_mode',
|
||||
'ipv6',
|
||||
'timeout',
|
||||
'obfuscated',
|
||||
]) as $object => $array) {
|
||||
if (isset($settings[$array])) {
|
||||
$this->{$object}($settings[$array]);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($settings['do_not_retry'])) {
|
||||
$this->setRetry(false);
|
||||
}
|
||||
if (isset($settings['proxy'])) {
|
||||
foreach (\is_iterable($settings['proxy']) ? $settings['proxy'] : [$settings['proxy']] as $key => $proxy) {
|
||||
if ($proxy === '\\Socket') {
|
||||
$proxy = DefaultStream::class;
|
||||
} elseif ($proxy === '\\SocksProxy') {
|
||||
$proxy = SocksProxy::class;
|
||||
} elseif ($proxy === '\\HttpProxy') {
|
||||
$proxy = HttpProxy::class;
|
||||
} elseif ($proxy === '\\MTProxySocket') {
|
||||
$proxy = ObfuscatedStream::class;
|
||||
}
|
||||
if ($proxy !== DefaultStream::class) {
|
||||
$this->addProxy($proxy, $settings['proxy_extra'][$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($settings['transport'])) {
|
||||
$transport = $settings['transport'];
|
||||
if ($transport === 'tcp') {
|
||||
$transport = DefaultStream::class;
|
||||
} elseif ($transport === 'ws') {
|
||||
$transport = WsStream::class;
|
||||
} elseif ($transport === 'wss') {
|
||||
$transport = WssStream::class;
|
||||
}
|
||||
$this->setTransport($transport);
|
||||
}
|
||||
if (isset($settings['protocol'])) {
|
||||
$protocol = $settings['protocol'];
|
||||
switch ($protocol) {
|
||||
case 'abridged':
|
||||
case 'tcp_abridged':
|
||||
$protocol = AbridgedStream::class;
|
||||
break;
|
||||
case 'intermediate':
|
||||
case 'tcp_intermediate':
|
||||
$protocol = AbridgedStream::class;
|
||||
break;
|
||||
case 'obfuscated2':
|
||||
$this->setObfuscated(true);
|
||||
// no break
|
||||
case 'intermediate_padded':
|
||||
case 'tcp_intermediate_padded':
|
||||
$protocol = IntermediatePaddedStream::class;
|
||||
break;
|
||||
case 'full':
|
||||
case 'tcp_full':
|
||||
$protocol = FullStream::class;
|
||||
break;
|
||||
case 'http':
|
||||
$protocol = HttpStream::class;
|
||||
break;
|
||||
case 'https':
|
||||
$protocol = HttpsStream::class;
|
||||
break;
|
||||
case 'udp':
|
||||
$protocol = UdpBufferedStream::class;
|
||||
break;
|
||||
}
|
||||
$this->setProtocol($protocol);
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->init();
|
||||
}
|
||||
public function __wakeup()
|
||||
{
|
||||
$this->init();
|
||||
}
|
||||
public function init(): void
|
||||
{
|
||||
Magic::classExists(true);
|
||||
|
||||
if (Magic::$altervista) {
|
||||
$this->addProxy(HttpProxy::class, ['address' => 'localhost', 'port' => 80]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Whether the settings have changed.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function haveChanged(): bool
|
||||
{
|
||||
return $this->changed;
|
||||
}
|
||||
/**
|
||||
* Signal that changes have been applied.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function applyChanges(): void
|
||||
{
|
||||
$this->changed = false;
|
||||
}
|
||||
/**
|
||||
* Get protocol identifier.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getProtocol(): string
|
||||
{
|
||||
return $this->protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set protocol identifier.
|
||||
*
|
||||
* @param class-string<MTProtoBufferInterface> $protocol Protocol identifier
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setProtocol(string $protocol): self
|
||||
{
|
||||
if (!isset(\class_implements($protocol)[MTProtoBufferInterface::class])) {
|
||||
throw new Exception("An invalid protocol was specified!");
|
||||
}
|
||||
$this->changed = true;
|
||||
$this->protocol = $protocol;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether to use ipv6.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getIpv6(): bool
|
||||
{
|
||||
return $this->ipv6 ?? Magic::$ipv6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to use ipv6.
|
||||
*
|
||||
* @param bool $ipv6 Whether to use ipv6
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setIpv6(bool $ipv6): self
|
||||
{
|
||||
$this->changed = true;
|
||||
$this->ipv6 = $ipv6;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subdomains of web.telegram.org for https protocol.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSslSubdomains(): array
|
||||
{
|
||||
return $this->sslSubdomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set subdomains of web.telegram.org for https protocol.
|
||||
*
|
||||
* @param array $sslSubdomains Subdomains of web.telegram.org for https protocol.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setSslSubdomains(array $sslSubdomains): self
|
||||
{
|
||||
$this->changed = true;
|
||||
$this->sslSubdomains = $sslSubdomains;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get minimum media socket count.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMinMediaSocketCount(): int
|
||||
{
|
||||
return $this->minMediaSocketCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set minimum media socket count.
|
||||
*
|
||||
* @param int $minMediaSocketCount Minimum media socket count.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setMinMediaSocketCount(int $minMediaSocketCount): self
|
||||
{
|
||||
$this->changed = true;
|
||||
$this->minMediaSocketCount = $minMediaSocketCount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get maximum media socket count.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxMediaSocketCount(): int
|
||||
{
|
||||
return $this->maxMediaSocketCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set maximum media socket count.
|
||||
*
|
||||
* @param int $maxMediaSocketCount Maximum media socket count.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setMaxMediaSocketCount(int $maxMediaSocketCount): self
|
||||
{
|
||||
$this->changed = true;
|
||||
$this->maxMediaSocketCount = $maxMediaSocketCount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get robin period (seconds).
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getRobinPeriod(): int
|
||||
{
|
||||
return $this->robinPeriod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set robin period (seconds).
|
||||
*
|
||||
* @param int $robinPeriod Robin period (seconds).
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setRobinPeriod(int $robinPeriod): self
|
||||
{
|
||||
$this->changed = true;
|
||||
$this->robinPeriod = $robinPeriod;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default DC ID.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDefaultDc(): int
|
||||
{
|
||||
return $this->defaultDc;
|
||||
}
|
||||
/**
|
||||
* Get default DC params.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDefaultDcParams(): array
|
||||
{
|
||||
return $this->defaultDcParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default DC ID.
|
||||
*
|
||||
* @param int $defaultDc Default DC ID.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setDefaultDc(int $defaultDc): self
|
||||
{
|
||||
$this->changed = true;
|
||||
$this->defaultDc = $defaultDc;
|
||||
$this->defaultDcParams = ['datacenter' => $defaultDc];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get proxy identifiers.
|
||||
*
|
||||
* @return array<class-string<StreamInterface>, array>
|
||||
*/
|
||||
public function getProxies(): array
|
||||
{
|
||||
return $this->proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add proxy identifier to list.
|
||||
*
|
||||
* @param class-string<StreamInterface> $proxy Proxy identifier
|
||||
* @param array $extra Extra
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addProxy(string $proxy, array $extra = []): self
|
||||
{
|
||||
if (!isset(\class_implements($proxy)[StreamInterface::class])) {
|
||||
throw new Exception("An invalid proxy class was specified!");
|
||||
}
|
||||
if (!isset($this->proxy[$proxy])) {
|
||||
$this->proxy[$proxy] = [];
|
||||
}
|
||||
$this->changed = true;
|
||||
$this->proxy[$proxy][] = $extra;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear proxies.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function clearProxies(): self
|
||||
{
|
||||
$this->proxy = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove specific proxy pair.
|
||||
*
|
||||
* @param string $proxy
|
||||
* @param array $extra
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function removeProxy(string $proxy, array $extra): self
|
||||
{
|
||||
if (!isset($this->proxy[$proxy])) {
|
||||
return $this;
|
||||
}
|
||||
if (false === $index = \array_search($extra, $this->proxy[$proxy])) {
|
||||
return $this;
|
||||
}
|
||||
$this->changed = true;
|
||||
unset($this->proxy[$proxy][$index]);
|
||||
if (empty($this->proxy[$proxy])) {
|
||||
unset($this->proxy[$proxy]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Get whether to use the obfuscated protocol.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getObfuscated(): bool
|
||||
{
|
||||
return $this->obfuscated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to use the obfuscated protocol.
|
||||
*
|
||||
* @param bool $obfuscated Whether to use the obfuscated protocol.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setObfuscated(bool $obfuscated): self
|
||||
{
|
||||
$this->changed = true;
|
||||
$this->obfuscated = $obfuscated;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether we're in test mode.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getTestMode(): bool
|
||||
{
|
||||
return $this->testMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether we're in test mode.
|
||||
*
|
||||
* @param bool $testMode Whether we're in test mode.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setTestMode(bool $testMode): self
|
||||
{
|
||||
$this->changed = true;
|
||||
$this->testMode = $testMode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transport identifier.
|
||||
*
|
||||
* @return class-string<RawStreamInterface>
|
||||
*/
|
||||
public function getTransport(): string
|
||||
{
|
||||
return $this->transport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set transport identifier.
|
||||
*
|
||||
* @param class-string<RawStreamInterface> $transport Transport identifier.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setTransport(string $transport): self
|
||||
{
|
||||
if (!isset(\class_implements($transport)[RawStreamInterface::class])) {
|
||||
throw new Exception("An invalid transport was specified!");
|
||||
}
|
||||
$this->changed = true;
|
||||
$this->transport = $transport;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether to retry connection.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getRetry(): bool
|
||||
{
|
||||
return $this->retry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to retry connection.
|
||||
*
|
||||
* @param bool $retry Whether to retry connection.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setRetry(bool $retry): self
|
||||
{
|
||||
$this->changed = true;
|
||||
$this->retry = $retry;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get connection timeout.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTimeout(): int
|
||||
{
|
||||
return $this->timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set connection timeout.
|
||||
*
|
||||
* @param int $timeout Connection timeout.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setTimeout(int $timeout): self
|
||||
{
|
||||
$this->changed = true;
|
||||
$this->timeout = $timeout;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
125
src/danog/MadelineProto/Settings/Database/DatabaseAbstract.php
Normal file
125
src/danog/MadelineProto/Settings/Database/DatabaseAbstract.php
Normal file
@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings\Database;
|
||||
|
||||
use danog\MadelineProto\Settings\DatabaseAbstract as SettingsDatabaseAbstract;
|
||||
|
||||
/**
|
||||
* Base class for database backends.
|
||||
*/
|
||||
abstract class DatabaseAbstract extends SettingsDatabaseAbstract
|
||||
{
|
||||
/**
|
||||
* For how long to keep records in memory after last read, for cached backends.
|
||||
*/
|
||||
protected int $cacheTtl = 5 * 60;
|
||||
/**
|
||||
* Database password.
|
||||
*/
|
||||
protected string $password = '';
|
||||
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
foreach (self::toCamel([
|
||||
'database',
|
||||
'password',
|
||||
'cache_ttl'
|
||||
]) as $object => $array) {
|
||||
if (isset($settings[$array])) {
|
||||
if ($array === 'cache_ttl' && \is_string($settings[$array])) {
|
||||
$settings[$array] = \strtotime($settings[$array]);
|
||||
}
|
||||
$this->{$object}($settings[$array]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DB key.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getKey(): string
|
||||
{
|
||||
$uri = \parse_url($this->getUri());
|
||||
$host = $uri['host'] ?? '';
|
||||
$port = $uri['port'] ?? '';
|
||||
return "$host:$port:".$this->getDatabase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get for how long to keep records in memory after last read, for cached backends.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCacheTtl(): int
|
||||
{
|
||||
return $this->cacheTtl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set for how long to keep records in memory after last read, for cached backends.
|
||||
*
|
||||
* @param int $cacheTtl For how long to keep records in memory after last read, for cached backends.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setCacheTtl(int $cacheTtl): self
|
||||
{
|
||||
$this->cacheTtl = $cacheTtl;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get password.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPassword(): string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set password.
|
||||
*
|
||||
* @param string $password Password.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setPassword(string $password): self
|
||||
{
|
||||
$this->password = $password;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database name/ID.
|
||||
*
|
||||
* @return string|int
|
||||
*/
|
||||
abstract public function getDatabase();
|
||||
/**
|
||||
* Get database URI.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getUri(): string;
|
||||
|
||||
/**
|
||||
* Set database name/ID.
|
||||
*
|
||||
* @param int|string $database
|
||||
* @return self
|
||||
*/
|
||||
abstract public function setDatabase($database): self;
|
||||
/**
|
||||
* Set database URI.
|
||||
*
|
||||
* @param string $uri
|
||||
* @return self
|
||||
*/
|
||||
abstract public function setUri(string $uri): self;
|
||||
}
|
43
src/danog/MadelineProto/Settings/Database/Memory.php
Normal file
43
src/danog/MadelineProto/Settings/Database/Memory.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings\Database;
|
||||
|
||||
use danog\MadelineProto\Settings\DatabaseAbstract;
|
||||
|
||||
class Memory extends DatabaseAbstract
|
||||
{
|
||||
/**
|
||||
* Whether to cleanup the memory before serializing.
|
||||
*/
|
||||
protected bool $cleanup = false;
|
||||
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
if (isset($settings['serialization']['cleanup_before_serialization'])) {
|
||||
$this->setCleanup($settings['serialization']['cleanup_before_serialization']);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get whether to cleanup the memory before serializing.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getCleanup(): bool
|
||||
{
|
||||
return $this->cleanup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to cleanup the memory before serializing.
|
||||
*
|
||||
* @param bool $cleanup Whether to cleanup the memory before serializing.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setCleanup(bool $cleanup): self
|
||||
{
|
||||
$this->cleanup = $cleanup;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
15
src/danog/MadelineProto/Settings/Database/Mysql.php
Normal file
15
src/danog/MadelineProto/Settings/Database/Mysql.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings\Database;
|
||||
|
||||
class Mysql extends SqlAbstract
|
||||
{
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
$settings = $settings['db']['mysql'] ?? [];
|
||||
if (isset($settings['host'])) {
|
||||
$this->setUri("tcp://".($settings['host']).(isset($settings['port']) ? ':'.($settings['port']) : ''));
|
||||
}
|
||||
parent::mergeArray($settings);
|
||||
}
|
||||
}
|
15
src/danog/MadelineProto/Settings/Database/Postgres.php
Normal file
15
src/danog/MadelineProto/Settings/Database/Postgres.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings\Database;
|
||||
|
||||
class Postgres extends SqlAbstract
|
||||
{
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
$settings = $settings['db']['postgres'] ?? [];
|
||||
if (isset($settings['host'])) {
|
||||
$this->setUri("tcp://".($settings['host']).(isset($settings['port']) ? ':'.($settings['port']) : ''));
|
||||
}
|
||||
parent::mergeArray($settings);
|
||||
}
|
||||
}
|
72
src/danog/MadelineProto/Settings/Database/Redis.php
Normal file
72
src/danog/MadelineProto/Settings/Database/Redis.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings\Database;
|
||||
|
||||
class Redis extends DatabaseAbstract
|
||||
{
|
||||
/**
|
||||
* Database number.
|
||||
*/
|
||||
protected int $database = 0;
|
||||
/**
|
||||
* Database URI.
|
||||
*/
|
||||
protected string $uri = 'redis://127.0.0.1';
|
||||
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
$settings = $settings['db']['redis'] ?? [];
|
||||
if (isset($settings['host'])) {
|
||||
$this->setUri($settings['host'].(isset($settings['port']) ? ':'.($settings['port']) : ''));
|
||||
}
|
||||
parent::mergeArray($settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database number.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDatabase(): int
|
||||
{
|
||||
return $this->database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set database number.
|
||||
*
|
||||
* @param int $database Database number.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setDatabase($database): self
|
||||
{
|
||||
$this->database = $database;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database URI.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUri(): string
|
||||
{
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set database URI.
|
||||
*
|
||||
* @param string $uri Database URI.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setUri(string $uri): self
|
||||
{
|
||||
$this->uri = $uri;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
167
src/danog/MadelineProto/Settings/Database/SqlAbstract.php
Normal file
167
src/danog/MadelineProto/Settings/Database/SqlAbstract.php
Normal file
@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings\Database;
|
||||
|
||||
use danog\MadelineProto\Settings\Database;
|
||||
|
||||
abstract class SqlAbstract extends DatabaseAbstract
|
||||
{
|
||||
/**
|
||||
* Database name.
|
||||
*/
|
||||
protected string $database = 'MadelineProto';
|
||||
/**
|
||||
* Username.
|
||||
*/
|
||||
protected string $username = 'root';
|
||||
|
||||
/**
|
||||
* Maximum connection limit.
|
||||
*/
|
||||
protected int $maxConnections = 10;
|
||||
|
||||
/**
|
||||
* Idle timeout.
|
||||
*/
|
||||
protected int $idleTimeout = 60;
|
||||
|
||||
/**
|
||||
* Database URI.
|
||||
*/
|
||||
protected string $uri = 'tcp://127.0.0.1';
|
||||
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
foreach (self::toCamel([
|
||||
'max_connections',
|
||||
'idle_timeout',
|
||||
]) as $object => $array) {
|
||||
if (isset($settings[$array])) {
|
||||
$this->{$object}($settings[$array]);
|
||||
}
|
||||
}
|
||||
if (isset($settings['user'])) {
|
||||
$this->setUsername($settings['user']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get maximum connection limit.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxConnections(): int
|
||||
{
|
||||
return $this->maxConnections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set maximum connection limit.
|
||||
*
|
||||
* @param int $maxConnections Maximum connection limit.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setMaxConnections(int $maxConnections): self
|
||||
{
|
||||
$this->maxConnections = $maxConnections;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get idle timeout.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getIdleTimeout(): int
|
||||
{
|
||||
return $this->idleTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set idle timeout.
|
||||
*
|
||||
* @param int $idleTimeout Idle timeout.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setIdleTimeout(int $idleTimeout): self
|
||||
{
|
||||
$this->idleTimeout = $idleTimeout;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDatabase(): string
|
||||
{
|
||||
return $this->database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set database name.
|
||||
*
|
||||
* @param string $database Database name.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setDatabase($database): self
|
||||
{
|
||||
$this->database = $database;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get username.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername(): string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set username.
|
||||
*
|
||||
* @param string $username Username.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setUsername(string $username): self
|
||||
{
|
||||
$this->username = $username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database URI.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUri(): string
|
||||
{
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set database URI.
|
||||
*
|
||||
* @param string $uri Database URI.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setUri(string $uri): self
|
||||
{
|
||||
$this->uri = $uri;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
12
src/danog/MadelineProto/Settings/DatabaseAbstract.php
Normal file
12
src/danog/MadelineProto/Settings/DatabaseAbstract.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings;
|
||||
|
||||
use danog\MadelineProto\SettingsAbstract;
|
||||
|
||||
/**
|
||||
* Base class for storage backends.
|
||||
*/
|
||||
abstract class DatabaseAbstract extends SettingsAbstract
|
||||
{
|
||||
}
|
138
src/danog/MadelineProto/Settings/Files.php
Normal file
138
src/danog/MadelineProto/Settings/Files.php
Normal file
@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings;
|
||||
|
||||
use danog\MadelineProto\SettingsAbstract;
|
||||
|
||||
class Files extends SettingsAbstract
|
||||
{
|
||||
/**
|
||||
* Allow automatic upload of files from file paths present in constructors?
|
||||
*/
|
||||
protected bool $allowAutomaticUpload = true;
|
||||
/**
|
||||
* Upload parallel chunk count.
|
||||
*/
|
||||
protected int $uploadParallelChunks = 20;
|
||||
/**
|
||||
* Download parallel chunk count.
|
||||
*/
|
||||
protected int $downloadParallelChunks = 20;
|
||||
|
||||
/**
|
||||
* Whether to report undownloadable media to TSF.
|
||||
*/
|
||||
protected bool $reportBrokenMedia = true;
|
||||
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
if (isset($settings['upload']['allow_automatic_upload'])) {
|
||||
$this->setAllowAutomaticUpload($settings['upload']['allow_automatic_upload']);
|
||||
}
|
||||
if (isset($settings['download']['report_broken_media'])) {
|
||||
$this->setReportBrokenMedia($settings['download']['report_broken_media']);
|
||||
}
|
||||
if (isset($settings['upload']['parallel_chunks'])) {
|
||||
$this->setUploadParallelChunks($settings['upload']['parallel_chunks']);
|
||||
}
|
||||
if (isset($settings['download']['parallel_chunks'])) {
|
||||
$this->setDownloadParallelChunks($settings['download']['parallel_chunks']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get allow automatic upload of files from file paths present in constructors?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getAllowAutomaticUpload(): bool
|
||||
{
|
||||
return $this->allowAutomaticUpload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set allow automatic upload of files from file paths present in constructors?
|
||||
*
|
||||
* @param bool $allowAutomaticUpload Allow automatic upload of files from file paths present in constructors?
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setAllowAutomaticUpload(bool $allowAutomaticUpload): self
|
||||
{
|
||||
$this->allowAutomaticUpload = $allowAutomaticUpload;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get upload parallel chunk count.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getUploadParallelChunks(): int
|
||||
{
|
||||
return $this->uploadParallelChunks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set upload parallel chunk count.
|
||||
*
|
||||
* @param int $uploadParallelChunks Upload parallel chunk count
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setUploadParallelChunks(int $uploadParallelChunks): self
|
||||
{
|
||||
$this->uploadParallelChunks = $uploadParallelChunks;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get download parallel chunk count.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDownloadParallelChunks(): int
|
||||
{
|
||||
return $this->downloadParallelChunks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set download parallel chunk count.
|
||||
*
|
||||
* @param int $downloadParallelChunks Download parallel chunk count
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setDownloadParallelChunks(int $downloadParallelChunks): self
|
||||
{
|
||||
$this->downloadParallelChunks = $downloadParallelChunks;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether to report undownloadable media to TSF.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getReportBrokenMedia(): bool
|
||||
{
|
||||
return $this->reportBrokenMedia;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to report undownloadable media to TSF.
|
||||
*
|
||||
* @param bool $reportBrokenMedia Whether to report undownloadable media to TSF
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setReportBrokenMedia(bool $reportBrokenMedia): self
|
||||
{
|
||||
$this->reportBrokenMedia = $reportBrokenMedia;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
217
src/danog/MadelineProto/Settings/Logger.php
Normal file
217
src/danog/MadelineProto/Settings/Logger.php
Normal file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings;
|
||||
|
||||
use danog\MadelineProto\Logger as MadelineProtoLogger;
|
||||
use danog\MadelineProto\Magic;
|
||||
use danog\MadelineProto\SettingsAbstract;
|
||||
use danog\MadelineProto\Tools;
|
||||
|
||||
class Logger extends SettingsAbstract
|
||||
{
|
||||
/**
|
||||
* Logger type.
|
||||
*
|
||||
* @var MadelineProtoLogger::LOGGER_* $type Logger type.
|
||||
*/
|
||||
protected int $type;
|
||||
|
||||
/**
|
||||
* Extra parameter for logger.
|
||||
*
|
||||
* @var null|callable|string
|
||||
*/
|
||||
protected $extra;
|
||||
|
||||
/**
|
||||
* Logging level.
|
||||
*
|
||||
* @var MadelineProtoLogger::LEVEL_*
|
||||
*/
|
||||
protected int $level = MadelineProtoLogger::LEVEL_VERBOSE;
|
||||
|
||||
/**
|
||||
* Maximum filesize for logger, in case of file logging.
|
||||
*/
|
||||
protected int $maxSize = 1 * 1024 * 1024;
|
||||
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
if (!isset($settings['logger']['logger_param']) && isset($settings['logger']['param'])) {
|
||||
$settings['logger']['logger_param'] = $settings['logger']['param'];
|
||||
}
|
||||
if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' && isset($settings['logger']['logger_param']) && $settings['logger']['logger_param'] === 'MadelineProto.log') {
|
||||
$settings['logger']['logger_param'] = Magic::$script_cwd.'/MadelineProto.log';
|
||||
}
|
||||
switch ($settings['logger']['logger_level'] ?? null) {
|
||||
case 'ULTRA_VERBOSE':
|
||||
$settings['logger']['logger_level'] = 5;
|
||||
break;
|
||||
case 'VERBOSE':
|
||||
$settings['logger']['logger_level'] = 4;
|
||||
break;
|
||||
case 'NOTICE':
|
||||
$settings['logger']['logger_level'] = 3;
|
||||
break;
|
||||
case 'WARNING':
|
||||
$settings['logger']['logger_level'] = 2;
|
||||
break;
|
||||
case 'ERROR':
|
||||
$settings['logger']['logger_level'] = 1;
|
||||
break;
|
||||
case 'FATAL ERROR':
|
||||
$settings['logger']['logger_level'] = 0;
|
||||
break;
|
||||
}
|
||||
if (isset($settings['logger']['logger'])) {
|
||||
$this->setType($settings['logger']['logger']);
|
||||
}
|
||||
if (isset($settings['logger']['logger_param'])) {
|
||||
$this->setExtra($settings['logger']['logger_param']);
|
||||
}
|
||||
if (isset($settings['logger']['logger_level'])) {
|
||||
$this->setLevel($settings['logger']['logger_level']);
|
||||
}
|
||||
if (isset($settings['logger']['max_size'])) {
|
||||
$this->setMaxSize($settings['logger']['max_size'] ?? 1 * 1024 * 1024);
|
||||
}
|
||||
|
||||
$this->init();
|
||||
}
|
||||
public function __construct()
|
||||
{
|
||||
$this->type = (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg')
|
||||
? MadelineProtoLogger::ECHO_LOGGER
|
||||
: MadelineProtoLogger::FILE_LOGGER;
|
||||
$this->extra = Magic::$script_cwd.'/MadelineProto.log';
|
||||
}
|
||||
|
||||
public function __sleep()
|
||||
{
|
||||
return $this->extra instanceof \Closure
|
||||
? ['type', 'extra', 'level', 'maxSize']
|
||||
: ['type', 'level', 'maxSize'];
|
||||
}
|
||||
/**
|
||||
* Wakeup function.
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
$this->type = (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg')
|
||||
? MadelineProtoLogger::ECHO_LOGGER
|
||||
: MadelineProtoLogger::FILE_LOGGER;
|
||||
$this->init();
|
||||
}
|
||||
/**
|
||||
* Initialize global logging.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function init()
|
||||
{
|
||||
Magic::classExists(false);
|
||||
MadelineProtoLogger::constructorFromSettings($this);
|
||||
}
|
||||
/**
|
||||
* Get $type Logger type.
|
||||
*
|
||||
* @return MadelineProtoLogger::LOGGER_*
|
||||
*/
|
||||
public function getType(): int
|
||||
{
|
||||
return \defined(\MADELINE_WORKER::class) ? MadelineProtoLogger::FILE_LOGGER : $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set $type Logger type.
|
||||
*
|
||||
* @param MadelineProtoLogger::LOGGER_* $type $type Logger type.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setType(int $type): self
|
||||
{
|
||||
if ($type === MadelineProtoLogger::NO_LOGGER) {
|
||||
$type = (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg')
|
||||
? MadelineProtoLogger::ECHO_LOGGER
|
||||
: MadelineProtoLogger::FILE_LOGGER;
|
||||
}
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extra parameter for logger.
|
||||
*
|
||||
* @return null|callable|string
|
||||
*/
|
||||
public function getExtra()
|
||||
{
|
||||
return $this->type === MadelineProtoLogger::FILE_LOGGER
|
||||
? Tools::absolute($this->extra)
|
||||
: $this->extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set extra parameter for logger.
|
||||
*
|
||||
* @param null|callable|string $extra Extra parameter for logger.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setExtra($extra): self
|
||||
{
|
||||
$this->extra = $extra;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get logging level.
|
||||
*
|
||||
* @return MadelineProtoLogger::LEVEL_*
|
||||
*/
|
||||
public function getLevel(): int
|
||||
{
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set logging level.
|
||||
*
|
||||
* @param MadelineProtoLogger::LEVEL_* $level Logging level.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setLevel(int $level): self
|
||||
{
|
||||
$this->level = \max($level, MadelineProtoLogger::NOTICE);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get maximum filesize for logger, in case of file logging.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxSize(): int
|
||||
{
|
||||
return $this->maxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set maximum filesize for logger, in case of file logging.
|
||||
*
|
||||
* @param int $maxSize Maximum filesize for logger, in case of file logging.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setMaxSize(int $maxSize): self
|
||||
{
|
||||
$this->maxSize = \max($maxSize, 100 * 1024);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
105
src/danog/MadelineProto/Settings/Peer.php
Normal file
105
src/danog/MadelineProto/Settings/Peer.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings;
|
||||
|
||||
use danog\MadelineProto\SettingsAbstract;
|
||||
|
||||
class Peer extends SettingsAbstract
|
||||
{
|
||||
/**
|
||||
* Cache time for full peer information (seconds).
|
||||
*/
|
||||
protected int $fullInfoCacheTime = 60*60;
|
||||
/**
|
||||
* Should madeline fetch the full member list of every group it meets?
|
||||
*/
|
||||
protected bool $fullFetch = false;
|
||||
/**
|
||||
* Whether to cache all peers on startup for userbots.
|
||||
*/
|
||||
protected bool $cacheAllPeersOnStartup = false;
|
||||
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
foreach (self::toCamel([
|
||||
'full_info_cache_time',
|
||||
'full_fetch',
|
||||
'cache_all_peers_on_startup'
|
||||
]) as $object => $array) {
|
||||
if (isset($settings['peer'][$array])) {
|
||||
$this->{$object}($settings['peer'][$array]);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get cache time for full peer information (seconds).
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getFullInfoCacheTime(): int
|
||||
{
|
||||
return $this->fullInfoCacheTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cache time for full peer information (seconds).
|
||||
*
|
||||
* @param int $fullInfoCacheTime Cache time for full peer information (seconds).
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setFullInfoCacheTime(int $fullInfoCacheTime): self
|
||||
{
|
||||
$this->fullInfoCacheTime = $fullInfoCacheTime;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get should madeline fetch the full member list of every group it meets?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getFullFetch(): bool
|
||||
{
|
||||
return $this->fullFetch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set should madeline fetch the full member list of every group it meets?
|
||||
*
|
||||
* @param bool $fullFetch Should madeline fetch the full member list of every group it meets?
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setFullFetch(bool $fullFetch): self
|
||||
{
|
||||
$this->fullFetch = $fullFetch;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether to cache all peers on startup for userbots.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getCacheAllPeersOnStartup(): bool
|
||||
{
|
||||
return $this->cacheAllPeersOnStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to cache all peers on startup for userbots.
|
||||
*
|
||||
* @param bool $cacheAllPeersOnStartup Whether to cache all peers on startup for userbots.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setCacheAllPeersOnStartup(bool $cacheAllPeersOnStartup): self
|
||||
{
|
||||
$this->cacheAllPeersOnStartup = $cacheAllPeersOnStartup;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
71
src/danog/MadelineProto/Settings/Pwr.php
Normal file
71
src/danog/MadelineProto/Settings/Pwr.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings;
|
||||
|
||||
use danog\MadelineProto\SettingsAbstract;
|
||||
|
||||
class Pwr extends SettingsAbstract
|
||||
{
|
||||
/**
|
||||
* Whether to try resolving usernames using PWRTelegram DB.
|
||||
*/
|
||||
protected bool $requests = true;
|
||||
/**
|
||||
* DB token.
|
||||
*/
|
||||
protected string $dbToken = '';
|
||||
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
$this->requests = $settings['pwr']['requests'] ?? true;
|
||||
$this->dbToken = $settings['pwr']['db_token'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether to try resolving usernames using PWRTelegram DB.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getRequests(): bool
|
||||
{
|
||||
return $this->requests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to try resolving usernames using PWRTelegram DB.
|
||||
*
|
||||
* @param bool $requests Whether to try resolving usernames using PWRTelegram DB.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setRequests(bool $requests): self
|
||||
{
|
||||
$this->requests = $requests;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DB token.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDbToken(): string
|
||||
{
|
||||
return $this->dbToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set DB token.
|
||||
*
|
||||
* @param string $dbToken DB token.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setDbToken(string $dbToken): self
|
||||
{
|
||||
$this->dbToken = $dbToken;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
202
src/danog/MadelineProto/Settings/RPC.php
Normal file
202
src/danog/MadelineProto/Settings/RPC.php
Normal file
@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings;
|
||||
|
||||
use danog\MadelineProto\SettingsAbstract;
|
||||
|
||||
class RPC extends SettingsAbstract
|
||||
{
|
||||
/**
|
||||
* RPC timeout.
|
||||
*/
|
||||
protected int $rpcTimeout = 5*60;
|
||||
|
||||
/**
|
||||
* Flood timeout: if FLOOD_WAIT_ time is bigger than this, throw exception instead of waiting asynchronously.
|
||||
*/
|
||||
protected int $floodTimeout = 10*60;
|
||||
|
||||
/**
|
||||
* Maximum number of messages to be stored in the incoming queue.
|
||||
*/
|
||||
protected int $limitIncoming = 100;
|
||||
/**
|
||||
* Maximum number of messages to be stored in the outgoing queue.
|
||||
*/
|
||||
protected int $limitOutgoing = 100;
|
||||
/**
|
||||
* Maximum number of message IDs to consider when using call queues.
|
||||
*/
|
||||
protected int $limitCallQueue = 100;
|
||||
|
||||
/**
|
||||
* Encode payload with GZIP if bigger than.
|
||||
*/
|
||||
protected int $gzipEncodeIfGt = 1024 * 1024;
|
||||
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
if (isset($settings['connection_settings']['all']['drop_timeout'])) {
|
||||
$this->setRpcTimeout($settings['connection_settings']['all']['drop_timeout']);
|
||||
}
|
||||
if (isset($settings['flood_timeout']['wait_if_lt'])) {
|
||||
$this->setFloodTimeout($settings['flood_timeout']['wait_if_lt']);
|
||||
}
|
||||
if (isset($settings['msg_array_limit']['incoming'])) {
|
||||
$this->setLimitIncoming($settings['msg_array_limit']['incoming']);
|
||||
}
|
||||
if (isset($settings['msg_array_limit']['outgoing'])) {
|
||||
$this->setLimitOutgoing($settings['msg_array_limit']['outgoing']);
|
||||
}
|
||||
if (isset($settings['msg_array_limit']['call_queue'])) {
|
||||
$this->setLimitCallQueue($settings['msg_array_limit']['call_queue']);
|
||||
}
|
||||
if (isset($settings['requests']['gzip_encode_if_gt'])) {
|
||||
$this->setLimitCallQueue($settings['requests']['gzip_encode_if_gt']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get RPC timeout.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getRpcTimeout(): int
|
||||
{
|
||||
return $this->rpcTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set RPC timeout.
|
||||
*
|
||||
* @param int $rpcTimeout RPC timeout.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setRpcTimeout(int $rpcTimeout): self
|
||||
{
|
||||
$this->rpcTimeout = $rpcTimeout;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get flood timeout: if FLOOD_WAIT_ time is bigger than this, throw exception instead of waiting asynchronously.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getFloodTimeout(): int
|
||||
{
|
||||
return $this->floodTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set flood timeout: if FLOOD_WAIT_ time is bigger than this, throw exception instead of waiting asynchronously.
|
||||
*
|
||||
* @param int $floodTimeout Flood timeout: if FLOOD_WAIT_ time is bigger than this, throw exception instead of waiting asynchronously
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setFloodTimeout(int $floodTimeout): self
|
||||
{
|
||||
$this->floodTimeout = $floodTimeout;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get maximum number of messages to be stored in the incoming queue.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLimitIncoming(): int
|
||||
{
|
||||
return $this->limitIncoming;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set maximum number of messages to be stored in the incoming queue.
|
||||
*
|
||||
* @param int $limitIncoming Maximum number of messages to be stored in the incoming queue
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setLimitIncoming(int $limitIncoming): self
|
||||
{
|
||||
$this->limitIncoming = $limitIncoming;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get maximum number of messages to be stored in the outgoing queue.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLimitOutgoing(): int
|
||||
{
|
||||
return $this->limitOutgoing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set maximum number of messages to be stored in the outgoing queue.
|
||||
*
|
||||
* @param int $limitOutgoing Maximum number of messages to be stored in the outgoing queue
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setLimitOutgoing(int $limitOutgoing): self
|
||||
{
|
||||
$this->limitOutgoing = $limitOutgoing;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get maximum number of messages to consider when using call queues.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLimitCallQueue(): int
|
||||
{
|
||||
return $this->limitCallQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set maximum number of messages to consider when using call queues.
|
||||
*
|
||||
* @param int $limitCallQueue Maximum number of messages to consider when using call queues
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setLimitCallQueue(int $limitCallQueue): self
|
||||
{
|
||||
$this->limitCallQueue = $limitCallQueue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get encode payload with GZIP if bigger than.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getGzipEncodeIfGt(): int
|
||||
{
|
||||
return $this->gzipEncodeIfGt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set encode payload with GZIP if bigger than.
|
||||
*
|
||||
* @param int $gzipEncodeIfGt Encode payload with GZIP if bigger than
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setGzipEncodeIfGt(int $gzipEncodeIfGt): self
|
||||
{
|
||||
$this->gzipEncodeIfGt = $gzipEncodeIfGt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
66
src/danog/MadelineProto/Settings/SecretChats.php
Normal file
66
src/danog/MadelineProto/Settings/SecretChats.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings;
|
||||
|
||||
use danog\MadelineProto\SettingsAbstract;
|
||||
|
||||
class SecretChats extends SettingsAbstract
|
||||
{
|
||||
/**
|
||||
* What secret chats to accept.
|
||||
*
|
||||
* Boolean or array of IDs
|
||||
*
|
||||
* @var bool|array<int>
|
||||
*/
|
||||
protected $accept = true;
|
||||
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
if (isset($settings['secret_chats']['accept_chats'])) {
|
||||
$this->setAccept($settings['secret_chats']['accept_chats']);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get boolean or array of IDs.
|
||||
*
|
||||
* @return bool|array<int>
|
||||
*/
|
||||
public function getAccept()
|
||||
{
|
||||
return $this->accept;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set boolean or array of IDs.
|
||||
*
|
||||
* @param bool|array<int> $accept Boolean or array of IDs
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setAccept($accept): self
|
||||
{
|
||||
$this->accept = $accept;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we accept this chat.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @param integer $id
|
||||
* @return boolean
|
||||
*/
|
||||
public function canAccept(int $id): bool
|
||||
{
|
||||
if ($this->accept) {
|
||||
return false;
|
||||
}
|
||||
if ($this->accept === true) {
|
||||
return true;
|
||||
}
|
||||
return \in_array($id, $this->accept);
|
||||
}
|
||||
}
|
43
src/danog/MadelineProto/Settings/Serialization.php
Normal file
43
src/danog/MadelineProto/Settings/Serialization.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings;
|
||||
|
||||
use danog\MadelineProto\SettingsAbstract;
|
||||
|
||||
class Serialization extends SettingsAbstract
|
||||
{
|
||||
/**
|
||||
* Serialization interval, in seconds.
|
||||
*/
|
||||
protected int $interval = 30;
|
||||
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
if (isset($settings['serialization']['serialization_interval'])) {
|
||||
$this->setInterval($settings['serialization']['serialization_interval']);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get serialization interval, in seconds.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getInterval(): int
|
||||
{
|
||||
return $this->interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set serialization interval, in seconds.
|
||||
*
|
||||
* @param int $interval Serialization interval, in seconds.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setInterval(int $interval): self
|
||||
{
|
||||
$this->interval = $interval;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
207
src/danog/MadelineProto/Settings/TLSchema.php
Normal file
207
src/danog/MadelineProto/Settings/TLSchema.php
Normal file
@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto\Settings;
|
||||
|
||||
use danog\MadelineProto\SettingsAbstract;
|
||||
|
||||
class TLSchema extends SettingsAbstract
|
||||
{
|
||||
/**
|
||||
* TL layer version.
|
||||
*/
|
||||
protected int $layer = 117;
|
||||
/**
|
||||
* MTProto schema path.
|
||||
*/
|
||||
protected string $MTProtoSchema = __DIR__.'/../TL_mtproto_v1.tl';
|
||||
/**
|
||||
* API schema path.
|
||||
*/
|
||||
protected string $APISchema = __DIR__.'/../TL_telegram_v117.tl';
|
||||
/**
|
||||
* Secret schema path.
|
||||
*/
|
||||
protected string $secretSchema = __DIR__.'/../TL_secret.tl';
|
||||
/**
|
||||
* @internal Other schemas
|
||||
*/
|
||||
protected array $other = [];
|
||||
/**
|
||||
* Whether the scheme was upgraded.
|
||||
*/
|
||||
private bool $wasUpgraded = true;
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
$settings = $settings['tl_schema'] ?? [];
|
||||
if (isset($settings['layer'])) {
|
||||
$this->setLayer($settings['layer']);
|
||||
}
|
||||
if (isset($settings['src'])) {
|
||||
$src = $settings['src'];
|
||||
if (isset($src['mtproto'])) {
|
||||
$this->setMTProtoSchema($src['mtproto']);
|
||||
}
|
||||
if (isset($src['telegram'])) {
|
||||
$this->setAPISchema($src['telegram']);
|
||||
}
|
||||
if (isset($src['secret'])) {
|
||||
$this->setSecretSchema($src['secret']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrade scheme autonomously.
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
if (!\file_exists($this->APISchema) // Scheme was upgraded
|
||||
&& $this->APISchema !== __DIR__.'/../TL_mtproto_v117.tl' // Session path has changed
|
||||
) {
|
||||
$new = new self;
|
||||
$this->setAPISchema($new->getAPISchema());
|
||||
$this->setMTProtoSchema($new->getMTProtoSchema());
|
||||
$this->setSecretSchema($new->getSecretSchema());
|
||||
$this->setLayer($this->getLayer());
|
||||
$this->wasUpgraded = true;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns whether the TL parser should re-parse the TL schemes.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function needsUpgrade(): bool
|
||||
{
|
||||
return $this->wasUpgraded;
|
||||
}
|
||||
/**
|
||||
* Signal that scheme was re-parsed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function upgrade(): void
|
||||
{
|
||||
$this->wasUpgraded = false;
|
||||
}
|
||||
/**
|
||||
* Get TL layer version.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLayer(): int
|
||||
{
|
||||
return $this->layer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set TL layer version.
|
||||
*
|
||||
* @param int $layer TL layer version.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setLayer(int $layer): self
|
||||
{
|
||||
$this->layer = $layer;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get MTProto schema path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMTProtoSchema(): string
|
||||
{
|
||||
return $this->MTProtoSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set MTProto schema path.
|
||||
*
|
||||
* @param string $MTProtoSchema MTProto schema path.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setMTProtoSchema(string $MTProtoSchema): self
|
||||
{
|
||||
$this->MTProtoSchema = $MTProtoSchema;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get API schema path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAPISchema(): string
|
||||
{
|
||||
return $this->APISchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set API schema path.
|
||||
*
|
||||
* @param string $APISchema API schema path.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setAPISchema(string $APISchema): self
|
||||
{
|
||||
$this->APISchema = $APISchema;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get secret schema path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSecretSchema(): string
|
||||
{
|
||||
return $this->secretSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set secret schema path.
|
||||
*
|
||||
* @param string $secretSchema Secret schema path.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setSecretSchema(string $secretSchema): self
|
||||
{
|
||||
$this->secretSchema = $secretSchema;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of other.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getOther(): array
|
||||
{
|
||||
return $this->other;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of other.
|
||||
*
|
||||
* @param array $other
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setOther(array $other): self
|
||||
{
|
||||
$this->other = $other;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
57
src/danog/MadelineProto/SettingsAbstract.php
Normal file
57
src/danog/MadelineProto/SettingsAbstract.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
use ReflectionClass;
|
||||
use ReflectionProperty;
|
||||
|
||||
abstract class SettingsAbstract
|
||||
{
|
||||
/**
|
||||
* Merge legacy settings array.
|
||||
*
|
||||
* @param array $settings Settings array
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function mergeArray(array $settings): void;
|
||||
|
||||
/**
|
||||
* Merge with other settings instance.
|
||||
*
|
||||
* @param self $other
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function merge(self $other): void
|
||||
{
|
||||
$class = new ReflectionClass($other);
|
||||
$defaults = $class->getDefaultProperties();
|
||||
foreach ($class->getProperties(ReflectionProperty::IS_PROTECTED|ReflectionProperty::IS_PUBLIC) as $property) {
|
||||
$name = $property->getName();
|
||||
if (isset($other->{$name})
|
||||
&& (
|
||||
!isset($defaults[$name])
|
||||
|| $other->{$name} !== $defaults[$name] // Isn't equal to the default value
|
||||
)
|
||||
) {
|
||||
$this->{'set'.\ucfirst($name)}($other->{$name});
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Convert array of legacy array property names to new camel case names.
|
||||
*
|
||||
* @param array $properties Properties
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function toCamel(array $properties): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($properties as $prop) {
|
||||
$result['set'.\ucfirst(Tools::toCamelCase($prop))] = $prop;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
10
src/danog/MadelineProto/SettingsEmpty.php
Normal file
10
src/danog/MadelineProto/SettingsEmpty.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
class SettingsEmpty extends SettingsAbstract
|
||||
{
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
}
|
||||
}
|
@ -111,6 +111,7 @@ class Snitch
|
||||
Shutdown::removeCallback('restarter');
|
||||
$message = "Please do not remove madeline.phar, madeline.php and MadelineProto.log, or else MadelineProto will crash. If you have any problem with MadelineProto, report it to https://github.com/danog/MadelineProto or https://t.me/pwrtelegramgroup";
|
||||
Logger::log($message, Logger::FATAL_ERROR);
|
||||
\file_put_contents(Magic::$cwd.'/DO_NOT_REMOVE_MADELINEPROTO_LOG_SESSION', $message);
|
||||
die("$message\n");
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ use danog\MadelineProto\Exception;
|
||||
use danog\MadelineProto\Stream\Async\BufferedStream;
|
||||
use danog\MadelineProto\Stream\BufferedStreamInterface;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use danog\MadelineProto\Stream\MTProtoBufferInterface;
|
||||
use danog\MadelineProto\Stream\RawStreamInterface;
|
||||
use danog\MadelineProto\Stream\ReadBufferInterface;
|
||||
use danog\MadelineProto\Stream\Transport\DefaultStream;
|
||||
@ -38,7 +39,7 @@ use danog\MadelineProto\Stream\WriteBufferInterface;
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
class UdpBufferedStream extends DefaultStream implements BufferedStreamInterface
|
||||
class UdpBufferedStream extends DefaultStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
{
|
||||
use BufferedStream;
|
||||
private RawStreamInterface $stream;
|
||||
|
@ -432,7 +432,7 @@ class ConnectionContext
|
||||
{
|
||||
foreach ($this->nextStreams as $couple) {
|
||||
list($streamName, $extra) = $couple;
|
||||
if ($streamName === ObfuscatedStream::getName() && isset($extra['address'])) {
|
||||
if ($streamName === ObfuscatedStream::class && isset($extra['address'])) {
|
||||
$extra['_'] = 'inputClientProxy';
|
||||
return $extra;
|
||||
}
|
||||
@ -461,7 +461,7 @@ class ConnectionContext
|
||||
$string .= ' => ';
|
||||
}
|
||||
$string .= \preg_replace('/.*\\\\/', '', $stream[0]);
|
||||
if ($stream[1] && $stream[0] !== DefaultStream::getName()) {
|
||||
if ($stream[1] && $stream[0] !== DefaultStream::class) {
|
||||
$string .= ' ('.\json_encode($stream[1]).')';
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ namespace danog\MadelineProto\TL;
|
||||
|
||||
use Amp\Promise;
|
||||
use danog\MadelineProto\MTProto;
|
||||
use danog\MadelineProto\Settings\TLSchema;
|
||||
use danog\MadelineProto\Tools;
|
||||
|
||||
/**
|
||||
@ -128,12 +129,12 @@ class TL
|
||||
/**
|
||||
* Initialize TL parser.
|
||||
*
|
||||
* @param array $files Scheme files
|
||||
* @param TLSchema $files Scheme files
|
||||
* @param TLCallback[] $objects TL Callback objects
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init(array $files, array $objects = [])
|
||||
public function init(TLSchema $files, array $objects = [])
|
||||
{
|
||||
$this->API->logger->logger(\danog\MadelineProto\Lang::$current_lang['TL_loading'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->updateCallbacks($objects);
|
||||
@ -142,7 +143,12 @@ class TL
|
||||
$this->tdConstructors = new TLConstructors();
|
||||
$this->tdMethods = new TLMethods();
|
||||
$this->tdDescriptions = ['types' => [], 'constructors' => [], 'methods' => []];
|
||||
foreach ($files as $scheme_type => $file) {
|
||||
foreach ([
|
||||
'api' => $files->getAPISchema(),
|
||||
'mtproto' => $files->getMTProtoSchema(),
|
||||
'secret' => $files->getSecretSchema(),
|
||||
...$files->getOther()
|
||||
] as $scheme_type => $file) {
|
||||
$this->API->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['file_parsing'], \basename($file)), \danog\MadelineProto\Logger::VERBOSE);
|
||||
$filec = \file_get_contents(Tools::absolute($file));
|
||||
$TL_dict = \json_decode($filec, true);
|
||||
@ -156,7 +162,7 @@ class TL
|
||||
$class = null;
|
||||
$dparams = [];
|
||||
$lineBuf = '';
|
||||
foreach ($tl_file as $line_number => $line) {
|
||||
foreach ($tl_file as $line) {
|
||||
$line = \rtrim($line);
|
||||
if (\preg_match('|^//@|', $line)) {
|
||||
$list = \explode(' @', \str_replace('//', ' ', $line));
|
||||
@ -232,7 +238,7 @@ class TL
|
||||
$id = \hash('crc32b', $clean);
|
||||
if (\preg_match('/^[^\\s]+#([a-f0-9]*)/i', $line, $matches)) {
|
||||
$nid = \str_pad($matches[1], 8, '0', \STR_PAD_LEFT);
|
||||
if ($id !== $nid && $scheme_type !== 'botAPI') {
|
||||
if ($id !== $nid) {
|
||||
$this->API->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['crc32_mismatch'], $id, $nid, $line), \danog\MadelineProto\Logger::ERROR);
|
||||
}
|
||||
$id = $nid;
|
||||
@ -293,7 +299,7 @@ class TL
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($files['td']) && isset($files['telegram'])) {
|
||||
if (isset($files->getOther()['td'])) {
|
||||
foreach ($this->tdConstructors->by_id as $id => $data) {
|
||||
$name = $data['predicate'];
|
||||
if ($this->constructors->findById($id) === false) {
|
||||
@ -318,6 +324,7 @@ class TL
|
||||
}
|
||||
}
|
||||
}
|
||||
$files->upgrade();
|
||||
}
|
||||
/**
|
||||
* Get TL namespaces.
|
||||
@ -606,7 +613,7 @@ class TL
|
||||
}
|
||||
} elseif ($method === 'messages.sendEncryptedFile') {
|
||||
if (isset($arguments['file'])) {
|
||||
if ((!\is_array($arguments['file']) || !(isset($arguments['file']['_']) && $this->constructors->findByPredicate($arguments['file']['_']) === 'InputEncryptedFile')) && $this->API->settings['upload']['allow_automatic_upload']) {
|
||||
if ((!\is_array($arguments['file']) || !(isset($arguments['file']['_']) && $this->constructors->findByPredicate($arguments['file']['_']) === 'InputEncryptedFile')) && $this->API->getSettings()->getFiles()->getAllowAutomaticUpload()) {
|
||||
$arguments['file'] = (yield from $this->API->uploadEncrypted($arguments['file']));
|
||||
}
|
||||
if (isset($arguments['file']['key'])) {
|
||||
|
@ -124,7 +124,7 @@ class ADNLConnection
|
||||
$payload .= $encryptedRandom;
|
||||
$ip = \long2ip(\unpack('V', Tools::packSignedInt($endpoint['ip']))[1]);
|
||||
$port = $endpoint['port'];
|
||||
$ctx = (new ConnectionContext())->setSocketContext(new ConnectContext())->setUri("tcp://{$ip}:{$port}")->addStream(DefaultStream::getName())->addStream(BufferedRawStream::getName())->addStream(CtrStream::getName(), $obf)->addStream(HashedBufferedStream::getName(), 'sha256')->addStream(ADNLStream::getName());
|
||||
$ctx = (new ConnectionContext())->setSocketContext(new ConnectContext())->setUri("tcp://{$ip}:{$port}")->addStream(DefaultStream::class)->addStream(BufferedRawStream::class)->addStream(CtrStream::class, $obf)->addStream(HashedBufferedStream::class, 'sha256')->addStream(ADNLStream::class);
|
||||
$this->stream = (yield from $ctx->getStream($payload));
|
||||
Tools::callFork((function (): \Generator {
|
||||
//yield Tools::sleep(1);
|
||||
|
@ -307,11 +307,7 @@ trait AuthKeyHandler
|
||||
yield from $this->methodCallAsyncRead('phone.saveCallDebug', ['peer' => $call, 'debug' => $this->calls[$call['id']]->getDebugLog()], ['datacenter' => $this->datacenter->curdc]);
|
||||
}
|
||||
$update = ['_' => 'updatePhoneCall', 'phone_call' => $this->calls[$call['id']]];
|
||||
if (isset($this->settings['pwr']['strict']) && $this->settings['pwr']['strict']) {
|
||||
$this->pwrUpdateHandler($update);
|
||||
} else {
|
||||
\in_array($this->settings['updates']['callback'], [['danog\\MadelineProto\\API', 'getUpdatesUpdateHandler'], 'getUpdatesUpdateHandler']) ? $this->getUpdatesUpdateHandler($update) : $this->settings['updates']['callback']($update);
|
||||
}
|
||||
$this->updates[$this->updates_key++] = $update;
|
||||
unset($this->calls[$call['id']]);
|
||||
}
|
||||
/**
|
||||
|
@ -33,9 +33,7 @@ trait Callback
|
||||
*/
|
||||
public function setCallback($callback): void
|
||||
{
|
||||
$this->settings['updates']['callback'] = $callback;
|
||||
$this->settings['updates']['run_callback'] = true;
|
||||
$this->settings['updates']['handle_updates'] = true;
|
||||
$this->updateHandler = $callback;
|
||||
$this->startUpdateSystem();
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,13 @@
|
||||
|
||||
namespace danog\MadelineProto\Wrappers;
|
||||
|
||||
use danog\MadelineProto\Settings;
|
||||
|
||||
/**
|
||||
* Dialog handler.
|
||||
*
|
||||
* @property Settings $settings Settings
|
||||
*/
|
||||
trait DialogHandler
|
||||
{
|
||||
/**
|
||||
|
@ -20,17 +20,21 @@
|
||||
namespace danog\MadelineProto\Wrappers;
|
||||
|
||||
use danog\MadelineProto\EventHandler;
|
||||
use danog\MadelineProto\Settings;
|
||||
|
||||
use danog\MadelineProto\Tools;
|
||||
|
||||
/**
|
||||
* Event handler.
|
||||
*
|
||||
* @property Settings $settings Settings
|
||||
*/
|
||||
trait Events
|
||||
{
|
||||
/**
|
||||
* Event handler class name.
|
||||
*
|
||||
* @var string
|
||||
* @var class-string<EventHandler>
|
||||
*/
|
||||
public $event_handler;
|
||||
/**
|
||||
@ -46,30 +50,53 @@ trait Events
|
||||
*/
|
||||
private $eventHandlerMethods = [];
|
||||
/**
|
||||
* Set event handler.
|
||||
* Initialize existing event handler.
|
||||
*
|
||||
* @param string|EventHandler $event_handler Event handler
|
||||
* @internal
|
||||
*
|
||||
* @return \Generator
|
||||
* @return void
|
||||
*/
|
||||
public function setEventHandler($event_handler): \Generator
|
||||
public function initExistingEventHandler(): void
|
||||
{
|
||||
if (!\class_exists($event_handler) || !\is_subclass_of($event_handler, '\\danog\\MadelineProto\\EventHandler')) {
|
||||
throw new \danog\MadelineProto\Exception('Wrong event handler was defined');
|
||||
if ($this->event_handler && \class_exists($this->event_handler) && \is_subclass_of($this->API->event_handler, EventHandler::class)) {
|
||||
$this->initEventHandler($this->API->event_handler);
|
||||
}
|
||||
$this->event_handler = $event_handler;
|
||||
}
|
||||
/**
|
||||
* Initialize event handler.
|
||||
*
|
||||
* @param class-string<EventHandler> $eventHandler
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function initEventHandler(string $eventHandler): void
|
||||
{
|
||||
$this->event_handler = $eventHandler;
|
||||
if (!$this->event_handler_instance instanceof $this->event_handler) {
|
||||
$class_name = $this->event_handler;
|
||||
$this->event_handler_instance = new $class_name($this->wrapper);
|
||||
} elseif ($this->wrapper) {
|
||||
$this->event_handler_instance->__construct($this->wrapper);
|
||||
}
|
||||
$this->event_handler_instance->initInternal($this->wrapper);
|
||||
}
|
||||
/**
|
||||
* Set event handler.
|
||||
*
|
||||
* @param class-string<EventHandler> $eventHandler Event handler
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function setEventHandler(string $eventHandler): \Generator
|
||||
{
|
||||
if (!\is_subclass_of($eventHandler, EventHandler::class)) {
|
||||
throw new \danog\MadelineProto\Exception('Wrong event handler was defined');
|
||||
}
|
||||
$this->initEventHandler($eventHandler);
|
||||
$this->eventHandlerMethods = [];
|
||||
foreach (\get_class_methods($this->event_handler) as $method) {
|
||||
if ($method === 'onLoop') {
|
||||
$this->loop_callback = [$this->event_handler_instance, 'onLoop'];
|
||||
} elseif ($method === 'onAny') {
|
||||
foreach ($this->getTL()->getConstructors()->by_id as $id => $constructor) {
|
||||
foreach ($this->getTL()->getConstructors()->by_id as $constructor) {
|
||||
if ($constructor['type'] === 'Update' && !isset($this->eventHandlerMethods[$constructor['predicate']])) {
|
||||
$this->eventHandlerMethods[$constructor['predicate']] = [$this->event_handler_instance, 'onAny'];
|
||||
}
|
||||
@ -82,12 +109,10 @@ trait Events
|
||||
}
|
||||
}
|
||||
yield from $this->setReportPeers($this->event_handler_instance->getReportPeers());
|
||||
$this->settings['updates']['callback'] = [$this, 'eventUpdateHandler'];
|
||||
$this->settings['updates']['handle_updates'] = true;
|
||||
$this->settings['updates']['run_callback'] = true;
|
||||
if (\method_exists($this->event_handler_instance, 'onStart')) {
|
||||
Tools::callFork($this->event_handler_instance->onStart());
|
||||
}
|
||||
$this->updateHandler = [$this, 'eventUpdateHandler'];
|
||||
if ($this->inited()) {
|
||||
$this->startUpdateSystem();
|
||||
}
|
||||
@ -105,9 +130,6 @@ trait Events
|
||||
$this->event_handler_instance = null;
|
||||
$this->eventHandlerMethods = [];
|
||||
$this->setNoop();
|
||||
if ($disableUpdateHandling) {
|
||||
$this->settings['updates']['handle_updates'] = false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get event handler.
|
||||
|
@ -19,11 +19,16 @@
|
||||
|
||||
namespace danog\MadelineProto\Wrappers;
|
||||
|
||||
use danog\MadelineProto\MTProto;
|
||||
use danog\MadelineProto\MTProto\PermAuthKey;
|
||||
use danog\MadelineProto\MTProtoTools\PasswordCalculator;
|
||||
|
||||
use danog\MadelineProto\Settings;
|
||||
|
||||
/**
|
||||
* Manages logging in and out.
|
||||
*
|
||||
* @property Settings $settings Settings
|
||||
*/
|
||||
trait Login
|
||||
{
|
||||
@ -34,7 +39,7 @@ trait Login
|
||||
*/
|
||||
public function logout(): \Generator
|
||||
{
|
||||
yield from $this->methodCallAsyncRead('auth.logOut', [], ['datacenter' => $this->datacenter->curdc]);
|
||||
yield from $this->methodCallAsyncRead('auth.logOut', []);
|
||||
yield from $this->resetSession();
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['logout_ok'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->startUpdateSystem();
|
||||
@ -49,16 +54,21 @@ trait Login
|
||||
*/
|
||||
public function botLogin(string $token): \Generator
|
||||
{
|
||||
if ($this->authorized === self::LOGGED_IN) {
|
||||
if ($this->authorized === MTProto::LOGGED_IN) {
|
||||
return;
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['already_loggedIn'], \danog\MadelineProto\Logger::NOTICE);
|
||||
yield from $this->logout();
|
||||
}
|
||||
$callbacks = [$this, $this->referenceDatabase];
|
||||
$this->TL->updateCallbacks($callbacks);
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_bot'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->authorization = yield from $this->methodCallAsyncRead('auth.importBotAuthorization', ['bot_auth_token' => $token, 'api_id' => $this->settings['app_info']['api_id'], 'api_hash' => $this->settings['app_info']['api_hash']], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->authorized = self::LOGGED_IN;
|
||||
$this->authorization = yield from $this->methodCallAsyncRead(
|
||||
'auth.importBotAuthorization',
|
||||
[
|
||||
'bot_auth_token' => $token,
|
||||
'api_id' => $this->settings->getAppInfo()->getApiId(),
|
||||
'api_hash' => $this->settings->getAppInfo()->getApiHash(),
|
||||
]
|
||||
);
|
||||
$this->authorized = MTProto::LOGGED_IN;
|
||||
$this->authorized_dc = $this->datacenter->curdc;
|
||||
$this->datacenter->getDataCenterConnection($this->datacenter->curdc)->authorized(true);
|
||||
$this->updates = [];
|
||||
@ -78,16 +88,26 @@ trait Login
|
||||
*/
|
||||
public function phoneLogin($number, $sms_type = 5): \Generator
|
||||
{
|
||||
if ($this->authorized === self::LOGGED_IN) {
|
||||
if ($this->authorized === MTProto::LOGGED_IN) {
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['already_loggedIn'], \danog\MadelineProto\Logger::NOTICE);
|
||||
yield from $this->logout();
|
||||
}
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_code_sending'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->authorization = yield from $this->methodCallAsyncRead('auth.sendCode', ['settings' => ['_' => 'codeSettings'], 'phone_number' => $number, 'sms_type' => $sms_type, 'api_id' => $this->settings['app_info']['api_id'], 'api_hash' => $this->settings['app_info']['api_hash'], 'lang_code' => $this->settings['app_info']['lang_code']], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->authorization = yield from $this->methodCallAsyncRead(
|
||||
'auth.sendCode',
|
||||
[
|
||||
'settings' => ['_' => 'codeSettings'],
|
||||
'phone_number' => $number,
|
||||
'sms_type' => $sms_type,
|
||||
'api_id' => $this->settings->getAppInfo()->getApiId(),
|
||||
'api_hash' => $this->settings->getAppInfo()->getApiHash(),
|
||||
'lang_code' => $this->settings->getAppInfo()->getLangCode()
|
||||
]
|
||||
);
|
||||
$this->authorized_dc = $this->datacenter->curdc;
|
||||
$this->authorization['phone_number'] = $number;
|
||||
//$this->authorization['_'] .= 'MP';
|
||||
$this->authorized = self::WAITING_CODE;
|
||||
$this->authorized = MTProto::WAITING_CODE;
|
||||
$this->updates = [];
|
||||
$this->updates_key = 0;
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_code_sent'], \danog\MadelineProto\Logger::NOTICE);
|
||||
@ -102,26 +122,26 @@ trait Login
|
||||
*/
|
||||
public function completePhoneLogin($code): \Generator
|
||||
{
|
||||
if ($this->authorized !== self::WAITING_CODE) {
|
||||
if ($this->authorized !== MTProto::WAITING_CODE) {
|
||||
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['login_code_uncalled']);
|
||||
}
|
||||
$this->authorized = self::NOT_LOGGED_IN;
|
||||
$this->authorized = MTProto::NOT_LOGGED_IN;
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_user'], \danog\MadelineProto\Logger::NOTICE);
|
||||
try {
|
||||
$authorization = yield from $this->methodCallAsyncRead('auth.signIn', ['phone_number' => $this->authorization['phone_number'], 'phone_code_hash' => $this->authorization['phone_code_hash'], 'phone_code' => (string) $code], ['datacenter' => $this->datacenter->curdc]);
|
||||
$authorization = yield from $this->methodCallAsyncRead('auth.signIn', ['phone_number' => $this->authorization['phone_number'], 'phone_code_hash' => $this->authorization['phone_code_hash'], 'phone_code' => (string) $code]);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
if ($e->rpc === 'SESSION_PASSWORD_NEEDED') {
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_2fa_enabled'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->authorization = yield from $this->methodCallAsyncRead('account.getPassword', [], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->authorization = yield from $this->methodCallAsyncRead('account.getPassword', []);
|
||||
if (!isset($this->authorization['hint'])) {
|
||||
$this->authorization['hint'] = '';
|
||||
}
|
||||
$this->authorized = self::WAITING_PASSWORD;
|
||||
$this->authorized = MTProto::WAITING_PASSWORD;
|
||||
return $this->authorization;
|
||||
}
|
||||
if ($e->rpc === 'PHONE_NUMBER_UNOCCUPIED') {
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_need_signup'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->authorized = self::WAITING_SIGNUP;
|
||||
$this->authorized = MTProto::WAITING_SIGNUP;
|
||||
$this->authorization['phone_code'] = $code;
|
||||
return ['_' => 'account.needSignup'];
|
||||
}
|
||||
@ -129,12 +149,12 @@ trait Login
|
||||
}
|
||||
if ($authorization['_'] === 'auth.authorizationSignUpRequired') {
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_need_signup'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->authorized = self::WAITING_SIGNUP;
|
||||
$this->authorized = MTProto::WAITING_SIGNUP;
|
||||
$this->authorization['phone_code'] = $code;
|
||||
$authorization['_'] = 'account.needSignup';
|
||||
return $authorization;
|
||||
}
|
||||
$this->authorized = self::LOGGED_IN;
|
||||
$this->authorized = MTProto::LOGGED_IN;
|
||||
$this->authorization = $authorization;
|
||||
$this->datacenter->getDataCenterConnection($this->datacenter->curdc)->authorized(true);
|
||||
yield from $this->initAuthorization();
|
||||
@ -152,7 +172,7 @@ trait Login
|
||||
*/
|
||||
public function importAuthorization($authorization): \Generator
|
||||
{
|
||||
if ($this->authorized === self::LOGGED_IN) {
|
||||
if ($this->authorized === MTProto::LOGGED_IN) {
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['already_loggedIn'], \danog\MadelineProto\Logger::NOTICE);
|
||||
yield from $this->logout();
|
||||
}
|
||||
@ -167,7 +187,7 @@ trait Login
|
||||
$dataCenterConnection->resetSession();
|
||||
$dataCenterConnection->setPermAuthKey($auth_key);
|
||||
$dataCenterConnection->authorized(true);
|
||||
$this->authorized = self::LOGGED_IN;
|
||||
$this->authorized = MTProto::LOGGED_IN;
|
||||
yield from $this->initAuthorization();
|
||||
yield from $this->getPhoneConfig();
|
||||
$res = (yield from $this->fullGetSelf());
|
||||
@ -186,7 +206,7 @@ trait Login
|
||||
*/
|
||||
public function exportAuthorization(): \Generator
|
||||
{
|
||||
if ($this->authorized !== self::LOGGED_IN) {
|
||||
if ($this->authorized !== MTProto::LOGGED_IN) {
|
||||
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['not_loggedIn']);
|
||||
}
|
||||
yield from $this->fullGetSelf();
|
||||
@ -203,13 +223,13 @@ trait Login
|
||||
*/
|
||||
public function completeSignup(string $first_name, string $last_name = ''): \Generator
|
||||
{
|
||||
if ($this->authorized !== self::WAITING_SIGNUP) {
|
||||
if ($this->authorized !== MTProto::WAITING_SIGNUP) {
|
||||
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['signup_uncalled']);
|
||||
}
|
||||
$this->authorized = self::NOT_LOGGED_IN;
|
||||
$this->authorized = MTProto::NOT_LOGGED_IN;
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['signing_up'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->authorization = yield from $this->methodCallAsyncRead('auth.signUp', ['phone_number' => $this->authorization['phone_number'], 'phone_code_hash' => $this->authorization['phone_code_hash'], 'phone_code' => $this->authorization['phone_code'], 'first_name' => $first_name, 'last_name' => $last_name], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->authorized = self::LOGGED_IN;
|
||||
$this->authorization = yield from $this->methodCallAsyncRead('auth.signUp', ['phone_number' => $this->authorization['phone_number'], 'phone_code_hash' => $this->authorization['phone_code_hash'], 'phone_code' => $this->authorization['phone_code'], 'first_name' => $first_name, 'last_name' => $last_name]);
|
||||
$this->authorized = MTProto::LOGGED_IN;
|
||||
$this->datacenter->getDataCenterConnection($this->datacenter->curdc)->authorized(true);
|
||||
yield from $this->initAuthorization();
|
||||
yield from $this->getPhoneConfig();
|
||||
@ -226,15 +246,15 @@ trait Login
|
||||
*/
|
||||
public function complete2faLogin(string $password): \Generator
|
||||
{
|
||||
if ($this->authorized !== self::WAITING_PASSWORD) {
|
||||
if ($this->authorized !== MTProto::WAITING_PASSWORD) {
|
||||
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['2fa_uncalled']);
|
||||
}
|
||||
$this->authorized = self::NOT_LOGGED_IN;
|
||||
$this->authorized = MTProto::NOT_LOGGED_IN;
|
||||
$hasher = new PasswordCalculator($this->logger);
|
||||
$hasher->addInfo($this->authorization);
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_user'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->authorization = yield from $this->methodCallAsyncRead('auth.checkPassword', ['password' => $hasher->getCheckPassword($password)], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->authorized = self::LOGGED_IN;
|
||||
$this->authorization = yield from $this->methodCallAsyncRead('auth.checkPassword', ['password' => $hasher->getCheckPassword($password)]);
|
||||
$this->authorized = MTProto::LOGGED_IN;
|
||||
$this->datacenter->getDataCenterConnection($this->datacenter->curdc)->authorized(true);
|
||||
yield from $this->initAuthorization();
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_ok'], \danog\MadelineProto\Logger::NOTICE);
|
||||
@ -254,7 +274,7 @@ trait Login
|
||||
public function update2fa(array $params): \Generator
|
||||
{
|
||||
$hasher = new PasswordCalculator($this->logger);
|
||||
$hasher->addInfo(yield from $this->methodCallAsyncRead('account.getPassword', [], ['datacenter' => $this->datacenter->curdc]));
|
||||
return yield from $this->methodCallAsyncRead('account.updatePasswordSettings', $hasher->getPassword($params), ['datacenter' => $this->datacenter->curdc]);
|
||||
$hasher->addInfo(yield from $this->methodCallAsyncRead('account.getPassword', []));
|
||||
return yield from $this->methodCallAsyncRead('account.updatePasswordSettings', $hasher->getPassword($params));
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,15 @@
|
||||
namespace danog\MadelineProto\Wrappers;
|
||||
|
||||
use Amp\Promise;
|
||||
use danog\MadelineProto\Settings;
|
||||
use danog\MadelineProto\Shutdown;
|
||||
|
||||
use danog\MadelineProto\Tools;
|
||||
|
||||
/**
|
||||
* Manages logging in and out.
|
||||
*
|
||||
* @property Settings $settings Settings
|
||||
*/
|
||||
trait Loop
|
||||
{
|
||||
@ -87,7 +91,7 @@ trait Loop
|
||||
/**
|
||||
* Start MadelineProto's update handling loop, or run the provided async callable.
|
||||
*
|
||||
* @param callable $callback Async callable to run
|
||||
* @param callable|null $callback Async callable to run
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@ -105,7 +109,7 @@ trait Loop
|
||||
$this->logger->logger('Not authorized, not starting event loop', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
return false;
|
||||
}
|
||||
if (\in_array($this->settings['updates']['callback'], [['danog\\MadelineProto\\API', 'getUpdatesUpdateHandler'], 'getUpdatesUpdateHandler'])) {
|
||||
if ($this->updateHandler === self::GETUPDATES_HANDLER) {
|
||||
$this->logger->logger('Getupdates event handler is enabled, exiting from loop', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
return false;
|
||||
}
|
||||
@ -113,21 +117,19 @@ trait Loop
|
||||
if (!\is_callable($this->loop_callback)) {
|
||||
$this->loop_callback = null;
|
||||
}
|
||||
if (!$this->settings['updates']['handle_updates']) {
|
||||
$this->settings['updates']['handle_updates'] = true;
|
||||
}
|
||||
if (!$this->settings['updates']['run_callback']) {
|
||||
$this->settings['updates']['run_callback'] = true;
|
||||
}
|
||||
$this->initSelfRestart();
|
||||
$this->startUpdateSystem();
|
||||
$this->logger->logger('Started update loop', \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->stopLoop = false;
|
||||
do {
|
||||
if (!$this->updateHandler) {
|
||||
yield from $this->waitUpdate();
|
||||
continue;
|
||||
}
|
||||
$updates = $this->updates;
|
||||
$this->updates = [];
|
||||
foreach ($updates as $update) {
|
||||
$r = $this->settings['updates']['callback']($update);
|
||||
$r = ($this->updateHandler)($update);
|
||||
if (\is_object($r)) {
|
||||
\danog\MadelineProto\Tools::callFork($r);
|
||||
}
|
||||
|
@ -28,9 +28,7 @@ trait Noop
|
||||
*/
|
||||
public function setNoop(): void
|
||||
{
|
||||
$this->settings['updates']['callback'] = [$this, 'noop'];
|
||||
$this->settings['updates']['run_callback'] = false;
|
||||
$this->settings['updates']['handle_updates'] = true;
|
||||
$this->updateHandler = null;
|
||||
$this->updates = [];
|
||||
$this->startUpdateSystem();
|
||||
}
|
||||
|
@ -20,10 +20,14 @@
|
||||
namespace danog\MadelineProto\Wrappers;
|
||||
|
||||
use danog\MadelineProto\MTProto;
|
||||
use danog\MadelineProto\Settings;
|
||||
|
||||
use danog\MadelineProto\Tools;
|
||||
|
||||
/**
|
||||
* Manages simple logging in and out.
|
||||
*
|
||||
* @property Settings $settings Settings
|
||||
*/
|
||||
trait Start
|
||||
{
|
||||
|
@ -19,8 +19,13 @@
|
||||
|
||||
namespace danog\MadelineProto\Wrappers;
|
||||
|
||||
use Amp\Http\Client\Request;
|
||||
use danog\MadelineProto\Settings;
|
||||
|
||||
/**
|
||||
* Manages logging in and out.
|
||||
*
|
||||
* @property Settings $settings
|
||||
*/
|
||||
trait Webhook
|
||||
{
|
||||
@ -36,9 +41,38 @@ trait Webhook
|
||||
{
|
||||
$this->pem_path = $pem_path;
|
||||
$this->hook_url = $hook_url;
|
||||
$this->settings['updates']['callback'] = [$this, 'pwrWebhook'];
|
||||
$this->settings['updates']['run_callback'] = true;
|
||||
$this->settings['updates']['handle_updates'] = true;
|
||||
$this->updateHandler = [$this, 'pwrWebhook'];
|
||||
$this->startUpdateSystem();
|
||||
}
|
||||
/**
|
||||
* Send update to webhook.
|
||||
*
|
||||
* @param array $update Update
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function pwrWebhook(array $update): void
|
||||
{
|
||||
$payload = \json_encode($update);
|
||||
if ($payload === '') {
|
||||
$this->logger->logger($update, $payload, \json_last_error_msg());
|
||||
$this->logger->logger('EMPTY UPDATE');
|
||||
return;
|
||||
}
|
||||
\danog\MadelineProto\Tools::callFork((function () use ($payload): \Generator {
|
||||
$request = new Request($this->hook_url, 'POST');
|
||||
$request->setHeader('content-type', 'application/json');
|
||||
$request->setBody($payload);
|
||||
$result = yield (yield $this->datacenter->getHTTPClient()->request($request))->getBody()->buffer();
|
||||
$this->logger->logger('Result of webhook query is '.$result, \danog\MadelineProto\Logger::NOTICE);
|
||||
$result = \json_decode($result, true);
|
||||
if (\is_array($result) && isset($result['method']) && $result['method'] != '' && \is_string($result['method'])) {
|
||||
try {
|
||||
$this->logger->logger('Reverse webhook command returned', yield from $this->methodCallAsyncRead($result['method'], $result, ['datacenter' => $this->datacenter->curdc]));
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->logger("Reverse webhook command returned: {$e}");
|
||||
}
|
||||
}
|
||||
})());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user