Clickable buttons, thread safety (#119)

* I'll just leave this here

* Threading fixes

* Apply fixes from StyleCI

* Composer fixes

* Lots of threading fixes, included all RSA keys

* Apply fixes from StyleCI

* Updated phpseclib

* fixes

* Apply fixes from StyleCI

* final fixes

* git add -A

* Apply fixes from StyleCI

* bugfix

* Fixes

* Apply fixes from StyleCI

* Small fixes

* Final fixes

* Speed improvements

* speed fixes

* Apply fixes from StyleCI

* This is faster than sanic

* Apply fixes from StyleCI

* Final speed fixes

* Apply fixes from StyleCI

* Less logging

* Speed+++

* Apply fixes from StyleCI

* More fixes

* Bug74586Exception

* Apply fixes from StyleCI

* Fixes

* Lemme fix that dumb-ass bug that broke everything

* Apply fixes from StyleCI

* Updated rollbar token

* Fixes for other tcp_* protocols

* Apply fixes from StyleCI

* No need for phpstruct anymore

* Add a conflict (OH NOES pony warfare)

* Less logs, beginning of clickable buttons

* Apply fixes from StyleCI

* Bugfixes, fixed clickable buttons

* Apply fixes from StyleCI

* Better errors

* Apply fixes from StyleCI

* You can now click text buttons

* Apply fixes from StyleCI
This commit is contained in:
Daniil Gentili 2017-05-16 15:12:04 +02:00 committed by GitHub
parent f6208e76cb
commit 915a0cd180
66 changed files with 1101 additions and 818 deletions

1
.gitignore vendored
View File

@ -94,3 +94,4 @@ fuzzer.php
tests/500mb
*.save
*.save.1
*.save.*

44
a.php
View File

@ -1,12 +1,38 @@
<?php
$payload = base64_decode('NAAAAAAAAAAAAAAAAAAAAAAAAAC06N5YFAAAAHiXRmAwx9DuB28gQszy/RHjhN2RdLkYdQ==');
$socket = fsockopen('tcp://149.154.167.91:443'); // DC 4
$socket = fsockopen('tcp://149.154.167.50:443'); // DC 2
stream_set_timeout($socket, 5);
echo 'Wrote '.fwrite($socket, $payload).' bytes'.PHP_EOL;
if (strlen(fread($socket, 100))) {
echo 'Read 100 bytes from socket'.PHP_EOL;
} else {
echo 'No data could be read from socket'.PHP_EOL;
$service_port = getservbyname('www', 'tcp');
$address = gethostbyname('www.google.com');
var_dump(unpack('q', pack('l', 200).chr(0).chr(0).chr(0).chr(0)));
class a extends Volatile
{
public $a = [];
public function run()
{
$this->a[1] = new b();
$this->a[1]->a['a'] = [];
var_dump($this);
}
}
class b extends \Volatile
{
public $a = [];
}
class main extends Threaded
{
public function __construct()
{
$this->a = new a();
var_dump($this->a);
$this->a->run();
// One of the OH NOES (b) is printed here
}
public function run()
{
// $this->a;
}
}
$a = new main();
$pool = new Pool(1);
//$pool->submit($a); // One of the OH NOES (a) is printed here

4
ab.php Normal file
View File

@ -0,0 +1,4 @@
<?php
$packet = file_get_contents('t');
var_dump(strrev(hash('crc32b', substr($packet, 0, -4), true)) !== substr($packet, -4));

View File

@ -5,12 +5,14 @@
"license": "AGPLV3",
"homepage": "https://daniil.it/MadelineProto",
"keywords": ["telegram", "mtproto", "protocol", "bytes", "messenger", "client", "PHP", "video", "stickers", "audio", "files", "GB"],
"conflict": {
"krakjoe/pthreads-polyfill": "*"
},
"require": {
"php": ">=5.6.0",
"danog/phpstruct": "dev-fast",
"danog/primemodule": "dev-master",
"phpseclib/phpseclib": "dev-master#12dc23d9",
"danog/magicalserializer": "dev-master",
"phpseclib/phpseclib": "dev-master#200c2a9",
"vlucas/phpdotenv": "^2.4",
"erusev/parsedown": "^1.6",
"rollbar/rollbar": "dev-master",
@ -29,6 +31,10 @@
"autoload": {
"psr-0": {
"danog\\MadelineProto\\": "src/"
}
},
"files": [
"src/Socket.php",
"src/Volatile.php"
]
}
}

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md)

View File

@ -10,8 +10,6 @@ You should have received a copy of the GNU General Public License along with Mad
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto;
if (!extension_loaded('pthreads')) {
class Socket
{

136
src/Volatile.php Normal file
View File

@ -0,0 +1,136 @@
<?php
if (!extension_loaded('pthreads')) {
class Volatile implements ArrayAccess, Countable, IteratorAggregate
{
const NOTHING = (0);
const STARTED = (1 << 0);
const RUNNING = (1 << 1);
const JOINED = (1 << 2);
const ERROR = (1 << 3);
public function offsetSet($offset, $value)
{
$this->__set($offset, $value);
}
public function offsetGet($offset)
{
return $this->{$offset};
}
public function offsetUnset($offset)
{
$this->__unset($offset);
}
public function offsetExists($offset)
{
return isset($this->{$offset});
}
public function count()
{
return count(get_object_vars($this));
}
public function getIterator()
{
return new ArrayIterator(get_object_vars($this));
}
public function __set($offset, $value)
{
if ($offset === null) {
$offset = count(get_object_vars($this));
}
if (!$this instanceof self) {
if (isset($this->{$offset}) &&
$this->{$offset} instanceof Threaded) {
throw new \RuntimeException();
}
}
return $this->{$offset} = $value;
}
public function __unset($offset)
{
if (!$this instanceof self) {
if (isset($this->{$offset}) && $this->{$offset} instanceof Threaded) {
throw new \RuntimeException();
}
}
unset($this->{$offset});
}
public function wait($timeout = 0)
{
return true;
}
public function notify()
{
return true;
}
public function synchronized(Closure $closure, ...$args)
{
return $closure(...$args);
}
public function isRunning()
{
return $this->state & THREAD::RUNNING;
}
public function isTerminated()
{
return $this->state & THREAD::ERROR;
}
public static function extend($class)
{
return true;
}
public function addRef()
{
}
public function delRef()
{
}
public function getRefCount()
{
}
public function lock()
{
return true;
}
public function unlock()
{
return true;
}
public function isWaiting()
{
return false;
}
public function run()
{
}
public function isGarbage()
{
return true;
}
protected $state;
}
}

View File

@ -16,11 +16,12 @@ class API extends APIFactory
{
use \danog\MadelineProto\Wrappers\Login;
use \danog\MadelineProto\Wrappers\SettingsManager;
use \danog\Serializable;
public $API;
public $namespace = '';
public function __construct($params = [])
public function ___construct($params = [])
{
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
$this->API = new MTProto($params);
@ -46,6 +47,9 @@ class API extends APIFactory
public function __destruct()
{
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
return;
}
restore_error_handler();
}

View File

@ -12,92 +12,9 @@ If not, see <http://www.gnu.org/licenses/>.
namespace danog\MadelineProto;
class APIFactory
class APIFactory extends \Volatile
{
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var phone
*/
public $phone;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var payments
*/
public $payments;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var bots
*/
public $bots;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var help
*/
public $help;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var upload
*/
public $upload;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var photos
*/
public $photos;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var updates
*/
public $updates;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var messages
*/
public $messages;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var contacts
*/
public $contacts;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var users
*/
public $users;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var channels
*/
public $channels;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var account
*/
public $account;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var auth
*/
public $auth;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var contest
*/
public $contest;
use Tools;
public $namespace;
public $API;
@ -112,6 +29,6 @@ class APIFactory
{
$this->API->get_config([], ['datacenter' => $this->API->datacenter->curdc]);
return method_exists($this->API, $this->namespace.$name) ? $this->API->{$this->namespace.$name}(...$arguments) : $this->API->method_call($this->namespace.$name, (isset($arguments[0]) && is_array($arguments[0])) ? $arguments[0] : [], (isset($arguments[1]) && is_array($arguments[1])) ? $arguments[1] : ['datacenter' => $this->API->datacenter->curdc]);
return method_exists($this->API, $this->namespace.$name) ? $this->API->{$this->namespace.$name}(...$arguments) : $this->API->method_call($this->namespace.$name, (isset($arguments[0]) && $this->is_array($arguments[0])) ? $arguments[0] : [], (isset($arguments[1]) && $this->is_array($arguments[1])) ? $arguments[1] : ['datacenter' => $this->API->datacenter->curdc]);
}
}

View File

@ -17,6 +17,7 @@ use phpDocumentor\Reflection\DocBlockFactory;
class AnnotationsBuilder
{
use \danog\MadelineProto\TL\TL;
use Tools;
public function __construct($settings)
{

View File

@ -12,6 +12,6 @@ If not, see <http://www.gnu.org/licenses/>.
namespace danog\MadelineProto;
class RSASerializable extends VolatileSerializer
class Bug74586Exception extends \Exception
{
}

View File

@ -0,0 +1,48 @@
<?php
/*
Copyright 2016-2017 Daniil Gentili
(https://daniil.it)
This file is part of MadelineProto.
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto;
class Button extends \Volatile implements \JsonSerializable
{
use \danog\Serializable;
private $info = [];
public function __construct($API, $message, $button)
{
foreach ($button as $key => $value) {
$this->{$key} = $value;
}
$this->info['peer'] = $message['to_id'];
$this->info['id'] = $message['id'];
$this->info['API'] = $API;
}
public function click($donotwait = false)
{
switch ($this->_) {
default: return false;
case 'keyboardButtonUrl': return $this->url;
case 'keyboardButton': return $this->info['API']->method_call('messages.sendMessage', ['peer' => $this->info['peer'], 'message' => $this->text, 'reply_to_msg_id' => $this->info['id']], ['datacenter' => $this->info['API']->datacenter->curdc]);
case 'keyboardButtonCallback': return $this->info['API']->method_call('messages.getBotCallbackAnswer', ['peer' => $this->info['peer'], 'msg_id' => $this->info['id'], 'data' => $this->data], ['noResponse' => $donotwait, 'datacenter' => $this->info['API']->datacenter->curdc]);
case 'keyboardButtonGame': return $this->info['API']->method_call('messages.getBotCallbackAnswer', ['peer' => $this->info['peer'], 'msg_id' => $this->info['id'], 'game' => true], ['noResponse' => $donotwait, 'datacenter' => $this->info['API']->datacenter->curdc]);
}
}
public function jsonSerialize()
{
$res = get_object_vars($this);
unset($res['info']);
return $res;
}
}

View File

@ -15,8 +15,9 @@ namespace danog\MadelineProto;
/**
* Manages connection to telegram servers.
*/
class Connection
class Connection extends \Volatile
{
use \danog\Serializable;
use \danog\MadelineProto\Tools;
public $sock = null;
@ -24,21 +25,22 @@ class Connection
public $ip = null;
public $port = null;
public $timeout = null;
// public $parsed = [];
public $time_delta = 0;
public $temp_auth_key;
public $auth_key;
public $session_id;
public $session_out_seq_no = 0;
public $session_in_seq_no = 0;
public $ipv6 = false;
public $incoming_messages = [];
public $outgoing_messages = [];
public $new_incoming = [];
public $new_outgoing = [];
public function __construct($ip, $port, $protocol, $timeout)
public function ___construct($ip, $port, $protocol, $timeout, $ipv6)
{
// Can use:
/*
- tcp_full
@ -50,42 +52,63 @@ class Connection
*/
$this->protocol = $protocol;
$this->timeout = $timeout;
$this->ipv6 = $ipv6;
$this->ip = $ip;
$this->port = $port;
switch ($this->protocol) {
case 'tcp_abridged':
$this->sock = fsockopen('tcp://'.$ip.':'.$port);
stream_set_timeout($this->sock, $timeout);
if (!(get_resource_type($this->sock) === 'file' || get_resource_type($this->sock) === 'stream')) {
$this->sock = new \Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
if (!\danog\MadelineProto\Logger::$has_thread) {
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
}
$this->sock->setBlocking(true);
if (!$this->sock->connect($ip, $port)) {
throw new Exception("Connection: couldn't connect to socket.");
}
$this->write(chr(239));
break;
case 'tcp_intermediate':
$this->sock = fsockopen('tcp://'.$ip.':'.$port);
stream_set_timeout($this->sock, $timeout);
if (!(get_resource_type($this->sock) === 'file' || get_resource_type($this->sock) === 'stream')) {
$this->sock = new \Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
if (!$this->sock->connect($ip, $port)) {
throw new Exception("Connection: couldn't connect to socket.");
}
if (!\danog\MadelineProto\Logger::$has_thread) {
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
}
$this->sock->setBlocking(true);
$this->write(str_repeat(chr(238), 4));
break;
case 'tcp_full':
$this->sock = fsockopen('tcp://'.$ip.':'.$port);
stream_set_timeout($this->sock, $timeout);
if (!(get_resource_type($this->sock) === 'file' || get_resource_type($this->sock) === 'stream')) {
$this->sock = new \Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
if (!$this->sock->connect($ip, $port)) {
throw new Exception("Connection: couldn't connect to socket.");
}
if (!\danog\MadelineProto\Logger::$has_thread) {
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
}
$this->sock->setBlocking(true);
$this->out_seq_no = -1;
$this->in_seq_no = -1;
break;
case 'http':
case 'https':
$this->parsed = parse_url($ip);
$this->sock = fsockopen(($this->protocol === 'https' ? 'tls' : 'tcp').'://'.$this->parsed['host'].':'.$port);
stream_set_timeout($this->sock, $timeout);
if (!(get_resource_type($this->sock) === 'file' || get_resource_type($this->sock) === 'stream')) {
$this->sock = new \Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname($this->protocol === 'https' ? 'tls' : 'tcp'));
if (!$this->sock->connect($this->parsed['host'], $port)) {
throw new Exception("Connection: couldn't connect to socket.");
}
if (!\danog\MadelineProto\Logger::$has_thread) {
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
}
$this->sock->setBlocking(true);
break;
case 'udp':
throw new Exception("Connection: This protocol isn't implemented yet.");
@ -97,6 +120,9 @@ class Connection
public function __destruct()
{
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
return;
}
switch ($this->protocol) {
case 'tcp_abridged':
case 'tcp_intermediate':
@ -104,7 +130,7 @@ class Connection
case 'http':
case 'https':
try {
fclose($this->sock);
unset($this->sock);
} catch (\danog\MadelineProto\Exception $e) {
}
break;
@ -119,12 +145,35 @@ class Connection
public function close_and_reopen()
{
$this->__destruct();
$this->__construct($this->ip, $this->port, $this->protocol, $this->timeout);
$this->__construct($this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6);
}
public function __sleep()
{
$t = get_object_vars($this);
if (isset($t['sock'])) {
unset($t['sock']);
}
$keys = array_keys((array) $t);
if (count($keys) !== count(array_unique($keys))) {
throw new Bug74586Exception();
}
return $keys;
}
public function __wakeup()
{
$this->__construct($this->ip, $this->port, $this->protocol, $this->timeout);
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
return;
}
$keys = array_keys((array) get_object_vars($this));
if (count($keys) !== count(array_unique($keys))) {
throw new Bug74586Exception();
}
//$this->__construct($this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6);
}
public function write($what, $length = null)
@ -138,11 +187,10 @@ class Connection
case 'tcp_full':
case 'http':
case 'https':
if (!(get_resource_type($this->sock) === 'file' || get_resource_type($this->sock) === 'stream')) {
throw new Exception("Connection: couldn't connect to socket.");
}
if (($wrote = fwrite($this->sock, $what)) !== strlen($what)) {
throw new \danog\MadelineProto\Exception("WARNING: Wrong length was written (should've written ".strlen($what).', wrote '.$wrote.')!');
$wrote = 0;
$len = strlen($what);
if (($wrote += $this->sock->write($what)) !== $len) {
while (($wrote += $this->sock->write(substr($what, $wrote))) !== $len);
}
return $wrote;
@ -164,15 +212,16 @@ class Connection
case 'tcp_full':
case 'http':
case 'https':
if (!(get_resource_type($this->sock) === 'file' || get_resource_type($this->sock) === 'stream')) {
throw new Exception("Connection: couldn't connect to socket.");
$packet = '';
while (strlen($packet) < $length) {
$packet .= $this->sock->read($length - strlen($packet));
if ($packet === false || strlen($packet) === 0) {
throw new \danog\MadelineProto\NothingInTheSocketException('Nothing in the socket!');
}
}
$packet = stream_get_contents($this->sock, $length);
if ($packet === false || strlen($packet) === 0) {
throw new \danog\MadelineProto\NothingInTheSocketException('Nothing in the socket!');
}
if (strlen($packet) != $length) {
throw new \danog\MadelineProto\Exception("WARNING: Wrong length was read (should've read ".($length).', read '.strlen($packet).')!');
if (strlen($packet) !== $length) {
$this->close_and_reopen();
throw new Exception("WARNING: Wrong length was read (should've read ".($length).', read '.strlen($packet).')!');
}
return $packet;
@ -189,13 +238,14 @@ class Connection
switch ($this->protocol) {
case 'tcp_full':
$packet_length_data = $this->read(4);
$packet_length = \danog\PHP\Struct::unpack('<I', $packet_length_data)[0];
$packet_length = unpack('V', $packet_length_data)[1];
$packet = $this->read($packet_length - 4);
if (strrev(hash('crc32b', $packet_length_data.substr($packet, 0, -4), true)) !== substr($packet, -4)) {
throw new Exception('CRC32 was not correct!');
}
$this->in_seq_no++;
$in_seq_no = \danog\PHP\Struct::unpack('<I', substr($packet, 0, 4))[0];
$in_seq_no = unpack('V', substr($packet, 0, 4))[1];
if ($in_seq_no != $this->in_seq_no) {
throw new Exception('Incoming seq_no mismatch');
}
@ -203,7 +253,7 @@ class Connection
return substr($packet, 4, $packet_length - 12);
case 'tcp_intermediate':
$packet_length_data = $this->read(4);
$packet_length = \danog\PHP\Struct::unpack('<I', $packet_length_data)[0];
$packet_length = unpack('V', $packet_length_data)[1];
return $this->read($packet_length);
case 'tcp_abridged':
@ -213,7 +263,7 @@ class Connection
$packet_length <<= 2;
} else {
$packet_length_data = $this->read(3);
$packet_length = \danog\PHP\Struct::unpack('<I', $packet_length_data.pack('x'))[0] << 2;
$packet_length = unpack('V', $packet_length_data."\0")[1] << 2;
}
return $this->read($packet_length);
@ -234,7 +284,7 @@ class Connection
throw new Exception('No data in the socket!');
}
if (preg_match('|^Content-Length: |i', $current_header)) {
$length = preg_replace('|Content-Length: |i', '', $current_header);
$length = (int) preg_replace('|Content-Length: |i', '', $current_header);
}
if (preg_match('|^Connection: close|i', $current_header)) {
$close = true;
@ -260,19 +310,19 @@ class Connection
switch ($this->protocol) {
case 'tcp_full':
$this->out_seq_no++;
$step1 = \danog\PHP\Struct::pack('<II', (strlen($message) + 12), $this->out_seq_no).$message;
$step1 = pack('VV', (strlen($message) + 12), $this->out_seq_no).$message;
$step2 = $step1.strrev(hash('crc32b', $step1, true));
$this->write($step2);
break;
case 'tcp_intermediate':
$this->write(\danog\PHP\Struct::pack('<I', strlen($message)).$message);
$this->write(pack('V', strlen($message)).$message);
break;
case 'tcp_abridged':
$len = strlen($message) / 4;
if ($len < 127) {
$step1 = chr($len).$message;
} else {
$step1 = chr(127).substr(\danog\PHP\Struct::pack('<I', $len), 0, 3).$message;
$step1 = chr(127).substr(pack('V', $len), 0, 3).$message;
}
$this->write($step1);
break;

View File

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

View File

@ -15,9 +15,10 @@ namespace danog\MadelineProto;
/**
* Manages datacenters.
*/
class DataCenter
class DataCenter extends \Volatile
{
use \danog\MadelineProto\Tools;
use \danog\Serializable;
public $sockets = [];
public $curdc = 0;
@ -29,9 +30,8 @@ class DataCenter
return ['sockets', 'curdc', 'dclist', 'settings'];
}
public function __construct($dclist, $settings)
public function ___construct($dclist, $settings)
{
//if ($this->unserialized($dclist)) return true;
$this->dclist = $dclist;
$this->settings = $settings;
foreach ($this->sockets as $socket) {
@ -63,7 +63,7 @@ class DataCenter
$address = $this->settings[$dc_config_number]['ipv6'] ? '['.$address.']' : $address;
$port = $this->dclist[$test][$ipv6][$dc_number]['port'];
if ($this->settings[$dc_config_number]['protocol'] === 'https') {
$subdomain = $this->dclist['ssl_subdomains'][$dc_config_number];
$subdomain = $this->dclist['ssl_subdomains'][$dc_number];
$path = $this->settings[$dc_config_number]['test_mode'] ? 'apiw_test1' : 'apiw1';
$address = $this->settings[$dc_config_number]['protocol'].'://'.$subdomain.'.web.telegram.org/'.$path;
}
@ -74,17 +74,17 @@ class DataCenter
}
\danog\MadelineProto\Logger::log(['Connecting to DC '.$dc_number.' ('.$test.' server, '.$ipv6.', '.$this->settings[$dc_config_number]['protocol'].')...'], \danog\MadelineProto\Logger::VERBOSE);
$this->sockets[$dc_number] = new Connection($address, $port, $this->settings[$dc_config_number]['protocol'], $this->settings[$dc_config_number]['timeout']);
$this->sockets[$dc_number] = new Connection($address, $port, $this->settings[$dc_config_number]['protocol'], $this->settings[$dc_config_number]['timeout'], $this->settings[$dc_config_number]['ipv6']);
return true;
}
public function get_dcs()
public function get_dcs($all = true)
{
$test = $this->settings['all']['test_mode'] ? 'test' : 'main';
$ipv6 = $this->settings['all']['ipv6'] ? 'ipv6' : 'ipv4';
return array_keys($this->dclist[$test][$ipv6]);
return $all ? array_keys((array) $this->dclist[$test][$ipv6]) : array_keys((array) $this->sockets);
}
/*

View File

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

View File

@ -15,6 +15,7 @@ namespace danog\MadelineProto;
class DocsBuilder
{
use \danog\MadelineProto\TL\TL;
use Tools;
public function __construct($settings)
{
@ -37,7 +38,7 @@ class DocsBuilder
public function mk_docs()
{
$types = [];
// $any = '*';
$any = '*';
\danog\MadelineProto\Logger::log(['Generating documentation index...'], \danog\MadelineProto\Logger::NOTICE);
file_put_contents($this->index, '---
@ -306,7 +307,7 @@ description: List of methods
'.implode('', $methods));
foreach (glob('constructors/*') as $unlink) {
foreach (glob('constructors/'.$any) as $unlink) {
unlink($unlink);
}
@ -588,6 +589,29 @@ $'.$type.' = -147286699; // Numeric chat id returned by request_secret_chat, can
```
';
}
if (in_array($type, ['KeyboardButton'])) {
$header .= 'Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $'.$type.'->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
';
}
$constructors = '### Possible values (constructors):

View File

@ -29,7 +29,7 @@ class Exception extends \Exception
if (in_array($message, ['Re-executing query...', 'I had to recreate the temporary authorization key', 'This peer is not present in the internal peer database', "Couldn't get response", 'Chat forbidden'])) {
return;
}
if (strpos($message, 'fwrite') !== false || strpos($message, 'Received request to switch to DC ') !== false || strpos($message, 'Re-executing query...') !== false || strpos($message, "Couldn't find peer by provided") !== false) {
if (strpos($message, 'socket_write') !== false || strpos($message, 'socket_read') !== false || strpos($message, 'Received request to switch to DC ') !== false || strpos($message, 'Re-executing query...') !== false || strpos($message, "Couldn't find peer by provided") !== false || strpos($message, 'id.pwrtelegram.xyz') !== false) {
return;
}
\Rollbar\Rollbar::log(\Rollbar\Payload\Level::error(), $this, debug_backtrace(0));

View File

@ -17,11 +17,16 @@ namespace danog\MadelineProto;
class Logger
{
public static $storage = [];
public static $mode = null;
public static $optional = null;
public static $constructed = false;
public static $prefix = '';
public static $level = 3;
public static $has_thread = false;
public static $BIG_ENDIAN = false;
public static $bigint = true;
const ULTRA_VERBOSE = 5;
const VERBOSE = 4;
const NOTICE = 3;
@ -29,6 +34,13 @@ class Logger
const ERROR = 1;
const FATAL_ERROR = 0;
public static function class_exists()
{
self::$has_thread = class_exists('\Thread') && method_exists('\Thread', 'getCurrentThread');
self::$BIG_ENDIAN = (pack('L', 1) === pack('N', 1));
self::$bigint = PHP_INT_SIZE < 8;
}
/*
* Constructor function
* Accepts various logger modes:
@ -47,6 +59,7 @@ class Logger
self::$constructed = true;
self::$prefix = $prefix === '' ? '' : ', '.$prefix;
self::$level = $level;
self::class_exists();
}
public static function log($params, $level = self::NOTICE)
@ -58,12 +71,12 @@ class Logger
return false;
}
$prefix = self::$prefix;
if (class_exists('\Thread') && method_exists('\Thread', 'getCurrentThread') && is_object(\Thread::getCurrentThread())) {
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
$prefix .= ' (t)';
}
foreach (is_array($params) ? $params : [$params] as $param) {
if (!is_string($param)) {
$param = var_export($param, true);
$param = json_encode($param, JSON_PRETTY_PRINT);
}
$param = str_pad(basename(debug_backtrace()[0]['file'], '.php').$prefix.': ', 16 + strlen($prefix))."\t".$param;
switch (self::$mode) {

View File

@ -14,11 +14,12 @@ namespace danog\MadelineProto;
class Lua
{
use \danog\Serializable;
public $MadelineProto;
private $Lua;
private $script;
protected $Lua;
protected $script;
public function __construct($script, $MadelineProto)
public function ___construct($script, $MadelineProto)
{
if (!file_exists($script)) {
throw new Exception('Provided script does not exist');

View File

@ -15,8 +15,9 @@ namespace danog\MadelineProto;
/**
* Manages all of the mtproto stuff.
*/
class MTProto
class MTProto extends \Volatile
{
use \danog\Serializable;
use \danog\MadelineProto\MTProtoTools\AckHandler;
use \danog\MadelineProto\MTProtoTools\AuthKeyHandler;
use \danog\MadelineProto\MTProtoTools\CallHandler;
@ -51,11 +52,12 @@ class MTProto
public $bigint = false;
public $run_workers = false;
public $threads = false;
public $rsa_keys = [];
public function __construct($settings = [])
public function ___construct($settings = [])
{
//if ($this->unserialized($settings)) return true;
$this->bigint = PHP_INT_SIZE < 8;
\danog\MadelineProto\Logger::class_exists();
// Parse settings
$this->parse_settings($settings);
@ -68,8 +70,10 @@ class MTProto
}
// Load rsa key
\danog\MadelineProto\Logger::log(['Loading RSA key...'], Logger::ULTRA_VERBOSE);
$key = new RSA($this->settings['authorization']['rsa_key']);
$this->rsa_keys[$key->fp] = $key;
foreach ($this->settings['authorization']['rsa_keys'] as $key) {
$key = new RSA($key);
$this->rsa_keys[$key->fp] = $key;
}
// Istantiate TL class
\danog\MadelineProto\Logger::log(['Translating tl schemas...'], Logger::ULTRA_VERBOSE);
@ -89,8 +93,6 @@ class MTProto
$this->twoe2047 = new \phpseclib\Math\BigInteger('16158503035655503650357438344334975980222051334857742016065172713762327569433945446598600705761456731844358980460949009747059779575245460547544076193224141560315438683650498045875098875194826053398028819192033784138396109321309878080919047169238085235290822926018152521443787945770532904303776199561965192760957166694834171210342487393282284747428088017663161029038902829665513096354230157075129296432088558362971801859230928678799175576150822952201848806616643615613562842355410104862578550863465661734839271290328348967522998634176499319107762583194718667771801067716614802322659239302476074096777926805529798115328');
$this->twoe2048 = new \phpseclib\Math\BigInteger('32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521914333389668342420684974786564569494856176035326322058077805659331026192708460314150258592864177116725943603718461857357598351152301645904403697613233287231227125684710820209725157101726931323469678542580656697935045997268352998638215525166389437335543602135433229604645318478604952148193555853611059596230656');
$this->setup_threads();
$this->connect_to_all_dcs();
$this->datacenter->curdc = 2;
@ -112,23 +114,30 @@ class MTProto
public function __sleep()
{
$t = get_object_vars($this);
if (isset($t['reader_pool'])) {
unset($t['reader_pool']);
}
if (isset($t['readers'])) {
unset($t['readers']);
}
return array_keys($t);
$keys = array_keys((array) $t);
if (count($keys) !== count(array_unique($keys))) {
throw new Bug74586Exception();
}
return $keys;
}
public function __wakeup()
{
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
$this->setup_logger();
if (class_exists('\Thread') && method_exists('\Thread', 'getCurrentThread') && is_object(\Thread::getCurrentThread())) {
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
return;
}
$keys = array_keys((array) get_object_vars($this));
if (count($keys) !== count(array_unique($keys))) {
throw new Bug74586Exception();
}
/*
if (method_exists($this->datacenter, 'wakeup')) $this->datacenter = $this->datacenter->wakeup();
foreach ($this->rsa_keys as $key => $elem) {
@ -139,7 +148,6 @@ class MTProto
}
*/
$this->getting_state = false;
$this->bigint = PHP_INT_SIZE < 8;
$this->reset_session();
if (!isset($this->v) || $this->v !== $this->getV()) {
\danog\MadelineProto\Logger::log(['Serialization is out of date, reconstructing object!'], Logger::WARNING);
@ -148,6 +156,9 @@ class MTProto
$settings['updates']['callback'] = 'get_updates_update_handler';
}
unset($settings['tl_schema']);
if (isset($settings['authorization']['rsa_key'])) {
unset($settings['authorization']['rsa_key']);
}
$this->reset_session(true, true);
$this->__construct($settings);
}
@ -161,12 +172,15 @@ class MTProto
public function __destruct()
{
if (isset($this->reader_pool)) {
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
return;
}
if (isset(Logger::$storage[spl_object_hash($this)])) {
$this->run_workers = false;
while ($number = $this->reader_pool->collect()) {
while ($number = Logger::$storage[spl_object_hash($this)]->collect()) {
\danog\MadelineProto\Logger::log(['Shutting down reader pool, '.$number.' jobs left'], \danog\MadelineProto\Logger::NOTICE);
}
$this->reader_pool->shutdown();
Logger::$storage[spl_object_hash($this)]->shutdown();
}
}
@ -180,28 +194,33 @@ class MTProto
public function start_threads()
{
if ($this->threads) {
$dcs = $this->datacenter->get_dcs();
if (!isset($this->reader_pool)) {
$this->reader_pool = new \Pool(count($dcs));
if ($this->threads && !is_object(\Thread::getCurrentThread())) {
$dcs = $this->datacenter->get_dcs(false);
if (!isset(Logger::$storage[spl_object_hash($this)])) {
Logger::$storage[spl_object_hash($this)] = new \Pool(count($dcs));
}
if (!isset($this->readers)) {
$this->readers = [];
}
foreach ($dcs as $dc) {
if (!isset($this->readers[$dc])) {
Logger::log(['Socket reader on DC '.$dc.': CREATING'], Logger::WARNING);
$this->readers[$dc] = new \danog\MadelineProto\Threads\SocketReader($this, $dc);
}
if (!$this->readers[$dc]->isRunning()) {
Logger::log(['Socket reader on DC '.$dc.': SUBMITTING'], Logger::WARNING);
$this->readers[$dc]->garbage = false;
$this->reader_pool->submit($this->readers[$dc]);
Logger::log(['Socket reader on DC '.$dc.': RESTARTED'], Logger::WARNING);
Logger::$storage[spl_object_hash($this)]->submit($this->readers[$dc]);
Logger::log(['Socket reader on DC '.$dc.': WAITING'], Logger::WARNING);
while (!$this->readers[$dc]->ready);
Logger::log(['Socket reader on DC '.$dc.': READY'], Logger::WARNING);
} else {
Logger::log(['Socket reader on DC '.$dc.': WORKING'], Logger::NOTICE);
Logger::log(['Socket reader on DC '.$dc.': WORKING'], Logger::ULTRA_VERBOSE);
}
}
}
return true;
}
public function parse_settings($settings)
@ -232,14 +251,12 @@ class MTProto
$default_settings = [
'authorization' => [ // Authorization settings
'default_temp_auth_key_expires_in' => 31557600, // validity of temporary keys and the binding of the temporary and permanent keys
'rsa_key' => '-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6
lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS
an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw
Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+
8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n
Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
-----END RSA PUBLIC KEY-----', // RSA public key
'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-----",
], // RSA public keys
],
'connection' => [ // List of datacenters/subdomains where to connect
'ssl_subdomains' => [ // Subdomains of web.telegram.org for https protocol
@ -310,7 +327,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
'telegram' => __DIR__.'/TL_telegram_v66.tl', // telegram TL scheme
'secret' => __DIR__.'/TL_secret.tl', // secret chats TL scheme
'calls' => __DIR__.'/TL_calls.tl', // calls TL scheme
'td' => __DIR__.'/TL_td.tl', // telegram-cli TL scheme
//'td' => __DIR__.'/TL_td.tl', // telegram-cli TL scheme
'botAPI' => __DIR__.'/TL_botAPI.tl', // bot API TL scheme for file ids
],
],
@ -326,6 +343,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
'logger_param' => '/tmp/MadelineProto.log',
'logger' => 3, // overwrite previous setting and echo logs
'logger_level' => Logger::VERBOSE, // Logging level, available logging levels are: ULTRA_VERBOSE, VERBOSE, NOTICE, WARNING, ERROR, FATAL_ERROR. Can be provided as last parameter to the logging function.
'rollbar_token' => 'f9fff6689aea4905b58eec73f66c791d',
//'rollbar_token' => 'f9fff6689aea4905b58eec73f66c791d' // You can provide a token for the rollbar log management system
],
'max_tries' => [
@ -384,7 +402,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
public function setup_logger()
{
\Rollbar\Rollbar::init(['environment' => 'production', 'root' => __DIR__, 'access_token' => (isset($this->settings['logger']['rollbar_token']) && !in_array($this->settings['logger']['rollbar_token'], ['f9fff6689aea4905b58eec73f66c791d'])) ? $this->settings['logger']['rollbar_token'] : '300afd7ccef346ea84d0c185ae831718'], false, false);
\Rollbar\Rollbar::init(['environment' => 'production', 'root' => __DIR__, 'access_token' => (isset($this->settings['logger']['rollbar_token']) && !in_array($this->settings['logger']['rollbar_token'], ['f9fff6689aea4905b58eec73f66c791d', '300afd7ccef346ea84d0c185ae831718'])) ? $this->settings['logger']['rollbar_token'] : '11a8c2fe4c474328b40a28193f8d63f5'], false, false);
\danog\MadelineProto\Logger::constructor($this->settings['logger']['logger'], $this->settings['logger']['logger_param'], isset($this->authorization['user']) ? (isset($this->authorization['user']['username']) ? $this->authorization['user']['username'] : $this->authorization['user']['id']) : '', isset($this->settings['logger']['logger_level']) ? $this->settings['logger']['logger_level'] : Logger::VERBOSE);
}
@ -416,13 +434,14 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
$this->datacenter->dc_connect($new_dc);
}
}
$this->setup_threads();
$this->init_authorization();
if ($old !== $this->datacenter->get_dcs()) {
$this->connect_to_all_dcs();
}
}
private $initing_authorization = false;
protected $initing_authorization = false;
// Creates authorization keys
public function init_authorization()
@ -466,7 +485,8 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
if ($int_dc != $new_dc) {
continue;
}
if (preg_match('|media|', $new_dc)) {
\danog\MadelineProto\Logger::log([$int_dc, $new_dc]);
if (preg_match('|_|', $new_dc)) {
continue;
}
\danog\MadelineProto\Logger::log(['Copying authorization from dc '.$authorized_dc.' to dc '.$new_dc.'...'], Logger::VERBOSE);
@ -538,7 +558,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
public function getV()
{
return 24;
return 29;
}
public function get_self()

View File

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

View File

@ -37,7 +37,7 @@ trait AckHandler
\danog\MadelineProto\Logger::log(["WARNING: Couldn't find message id ".$message_id.' in the array of incomgoing messages. Maybe try to increase its size?'], \danog\MadelineProto\Logger::WARNING);
//throw new \danog\MadelineProto\Exception("Couldn't find message id ".$message_id.' in the array of incoming message ids. Maybe try to increase its size?');
}
if ($this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === null || $this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === str_repeat(chr(0), 8) || (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack']) && $this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack'])) {
if ($this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === null || $this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === "\0\0\0\0\0\0\0\0" || (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack']) && $this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack'])) {
return;
}

View File

@ -79,7 +79,7 @@ trait AuthKeyHandler
*/
foreach ($this->rsa_keys as $fp => $curkey) {
if (in_array($fp, $ResPQ['server_public_key_fingerprints'])) {
if ($this->in_array($fp, $ResPQ['server_public_key_fingerprints'])) {
$key = $curkey;
}
}
@ -546,7 +546,7 @@ trait AuthKeyHandler
$message_id = $this->generate_message_id($datacenter);
$seq_no = 0;
$encrypted_data = $this->random(16).$message_id.\danog\PHP\Struct::pack('<II', $seq_no, strlen($message_data)).$message_data;
$encrypted_data = $this->random(16).$message_id.pack('VV', $seq_no, strlen($message_data)).$message_data;
$message_key = substr(sha1($encrypted_data, true), -16);
$padding = $this->random($this->posmod(-strlen($encrypted_data), 16));
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->sockets[$datacenter]->auth_key['auth_key']);

View File

@ -34,7 +34,7 @@ trait CallHandler
}
$args = $this->botAPI_to_MTProto($args);
if (isset($args['ping_id']) && is_int($args['ping_id'])) {
$args['ping_id'] = \danog\PHP\Struct::pack('<q', $args['ping_id']);
$args['ping_id'] = $this->pack_signed_long($args['ping_id']);
}
if (isset($args['chat_id']) && !isset($args['peer']) && $method !== 'messages.discardEncryption' && (is_object($args['chat_id']) || $args['chat_id'] < 0)) {
$res = $this->get_info($args['chat_id']);
@ -54,12 +54,12 @@ trait CallHandler
try {
\danog\MadelineProto\Logger::log(['Calling method (try number '.$count.' for '.$method.')...'], \danog\MadelineProto\Logger::VERBOSE);
$int_message_id = $this->send_message($serialized, $content_related, $aargs);
$message_id = $this->send_message($serialized, $content_related, $aargs);
if ($method === 'http_wait' || (isset($aargs['noResponse']) && $aargs['noResponse'])) {
return true;
}
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['content'] = ['method' => $method, 'args' => $args];
$this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$int_message_id] = ['msg_id' => $int_message_id, 'method' => $method, 'type' => $type];
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['content'] = ['method' => $method, 'args' => $args];
$this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$message_id] = ['msg_id' => $message_id, 'method' => $method, 'type' => $type];
$res_count = 0;
$server_answer = null;
$update_count = 0;
@ -68,8 +68,8 @@ trait CallHandler
try {
\danog\MadelineProto\Logger::log(['Getting response (try number '.$res_count.' for '.$method.')...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
//sleep(2);
//$this->start_threads();
if (!isset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['response']) || !isset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['response']]['content'])) { // Checks if I have received the response to the called method, if not continue looping
$this->start_threads();
if (!isset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']) || !isset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']]['content'])) { // Checks if I have received the response to the called method, if not continue looping
if ($only_updates) {
if ($update_count > 50) {
$update_count = 0;
@ -79,13 +79,18 @@ trait CallHandler
}
}
} else {
$server_answer = $this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['response']]['content'];
$this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['response']]['content'] = [];
//var_dump(base64_encode($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']), $this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']]);
$server_answer = (array) $this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']]['content'];
$this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']]['content'] = [];
break;
}
if (!$this->threads && !$this->run_workers) {
$this->recv_message($aargs['datacenter']); // This method receives data from the socket, and parses stuff
$only_updates = $this->handle_messages($aargs['datacenter']); // This method receives data from the socket, and parses stuff
} else {
$res_count--;
//var_dump($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages);
sleep(1);
}
} catch (\danog\MadelineProto\Exception $e) {
if ($e->getMessage() === 'I had to recreate the temporary authorization key') {
@ -122,7 +127,7 @@ trait CallHandler
case 16:
case 17:
\danog\MadelineProto\Logger::log(['Received bad_msg_notification: '.$this->bad_msg_error_codes[$server_answer['error_code']]], \danog\MadelineProto\Logger::WARNING);
$this->datacenter->sockets[$aargs['datacenter']]->timedelta = (int) ((new \phpseclib\Math\BigInteger(strrev($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['response']), 256))->bitwise_rightShift(32)->subtract(new \phpseclib\Math\BigInteger(time()))->toString());
$this->datacenter->sockets[$aargs['datacenter']]->timedelta = (int) ((new \phpseclib\Math\BigInteger(strrev($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']), 256))->bitwise_rightShift(32)->subtract(new \phpseclib\Math\BigInteger(time()))->toString());
\danog\MadelineProto\Logger::log(['Set time delta to '.$this->datacenter->sockets[$aargs['datacenter']]->timedelta], \danog\MadelineProto\Logger::WARNING);
$this->reset_session();
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
@ -142,7 +147,7 @@ trait CallHandler
} catch (\danog\MadelineProto\Exception $e) {
$last_error = $e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine();
\danog\MadelineProto\Logger::log(['An error occurred while calling method '.$method.': '.$last_error.'. Recreating connection and retrying to call method...'], \danog\MadelineProto\Logger::WARNING);
if (in_array($this->datacenter->sockets[$aargs['datacenter']]->protocol, ['http', 'https']) && $method !== 'http_wait') {
if ($this->in_array($this->datacenter->sockets[$aargs['datacenter']]->protocol, ['http', 'https']) && $method !== 'http_wait') {
//$this->method_call('http_wait', ['max_wait' => $this->datacenter->sockets[$aargs['datacenter']]->timeout, 'wait_after' => 0, 'max_delay' => 0], ['datacenter' => $aargs['datacenter']]);
} else {
$this->datacenter->sockets[$aargs['datacenter']]->close_and_reopen();
@ -150,14 +155,14 @@ trait CallHandler
//sleep(1); // To avoid flooding
continue;
} finally {
if (isset($aargs['heavy']) && $aargs['heavy'] && isset($int_message_id)) {
//$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['args'] = [];
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id] = [];
unset($this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$int_message_id]);
if (isset($aargs['heavy']) && $aargs['heavy'] && isset($message_id)) {
//$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['args'] = [];
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id] = [];
unset($this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$message_id]);
}
if (isset($int_message_id) && $method === 'req_pq') {
unset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]);
unset($this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$int_message_id]);
if (isset($message_id) && $method === 'req_pq') {
unset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]);
unset($this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$message_id]);
}
if ($canunset) {
$this->getting_state = false;
@ -166,9 +171,9 @@ trait CallHandler
}
if ($server_answer === null) {
if ($last_recv === $this->last_recv && $this->datacenter->sockets[$args['datacenter']]->temp_auth_key !== null) {
if ($last_recv === $this->last_recv && $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key !== null) {
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
$this->datacenter->sockets[$args['datacenter']]->temp_auth_key = null;
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
$this->init_authorization();
return $this->method_call($method, $args, $aargs);
@ -176,7 +181,7 @@ trait CallHandler
throw new \danog\MadelineProto\Exception('An error occurred while calling method '.$method.' ('.$last_error.').');
}
\danog\MadelineProto\Logger::log(['Got response for method '.$method.' @ try '.$count.' (response try '.$res_count.')'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id] = [];
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id] = [];
if (isset($message_chunks) && count($message_chunks)) {
$server_answer = [$server_answer];
foreach ($message_chunks as $message) {
@ -187,7 +192,9 @@ trait CallHandler
return $server_answer;
}
if ($method === 'req_pq') {
throw new \danog\MadelineProto\RPCErrorException('RPC_CALL_FAIL');
}
throw new \danog\MadelineProto\Exception('An error occurred while calling method '.$method.' ('.$last_error.').');
}
@ -202,9 +209,9 @@ trait CallHandler
for ($count = 1; $count <= $this->settings['max_tries']['query']; $count++) {
try {
\danog\MadelineProto\Logger::log([$object === 'msgs_ack' ? 'ack '.$args['msg_ids'][0] : 'Sending object (try number '.$count.' for '.$object.')...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
$int_message_id = $this->send_message($this->serialize_object(['type' => $object], $args), $this->content_related($object), $aargs);
$message_id = $this->send_message($this->serialize_object(['type' => $object], $args), $this->content_related($object), $aargs);
if ($object !== 'msgs_ack') {
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['content'] = ['method' => $object, 'args' => $args];
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['content'] = ['method' => $object, 'args' => $args];
}
} catch (Exception $e) {
\danog\MadelineProto\Logger::log(['An error occurred while calling object '.$object.': '.$e->getMessage().' in '.$e->getFile().':'.$e->getLine().'. Recreating connection and retrying to call object...'], \danog\MadelineProto\Logger::WARNING);
@ -212,7 +219,7 @@ trait CallHandler
continue;
}
return $int_message_id;
return $message_id;
}
throw new \danog\MadelineProto\Exception('An error occurred while sending object '.$object.'.');
}

View File

@ -39,7 +39,7 @@ trait Crypt
public function ctr_encrypt($message, $key, $iv, $length)
{
$cipher = new \phpseclib\Crypt\AES(\phpseclib\Crypt\AES::MODE_CTR);
$iv .= \danog\PHP\Struct::pack('<i', $length);
$iv .= $this->pack_signed_int($length);
$cipher->setKey($key);
$cipher->setIV($iv);

View File

@ -27,7 +27,7 @@ trait Files
}
$datacenter = is_null($datacenter) ? $this->datacenter->curdc : $datacenter;
$file_size = filesize($file);
if ($file_size > 1500 * 1024 * 1024) {
if ($file_size > 1610612736) {
throw new \danog\MadelineProto\Exception('Given file is too big!');
}
if ($cb === null) {
@ -47,7 +47,7 @@ trait Files
$key = $this->random(32);
$iv = $this->random(32);
$digest = hash('md5', $key.$iv, true);
$fingerprint = \danog\PHP\Struct::unpack('<i', substr($digest, 0, 4) ^ substr($digest, 4, 4))[0];
$fingerprint = $this->unpack_signed_int(substr($digest, 0, 4) ^ substr($digest, 4, 4));
$ige = new \phpseclib\Crypt\AES(\phpseclib\Crypt\AES::MODE_IGE);
$ige->setIV($iv);
$ige->setKey($key);
@ -61,7 +61,7 @@ trait Files
$bytes = $ige->encrypt(str_pad($bytes, $part_size, chr(0)));
}
hash_update($ctx, $bytes);
if (!$this->method_call($method, ['file_id' => $file_id, 'file_part' => $part_num++, 'file_total_parts' => $part_total_num, 'bytes' => $bytes], ['heavy' => true, 'datacenter' => $datacenter])) {
if (!$this->method_call($method, ['file_id' => $file_id, 'file_part' => $part_num++, 'file_total_parts' => $part_total_num, 'bytes' => $bytes], ['heavy' => true, 'datacenter' => &$datacenter])) {
throw new \danog\MadelineProto\Exception('An error occurred while uploading file part '.$part_num);
}
$cb(ftell($f) * 100 / $file_size);
@ -254,7 +254,7 @@ trait Files
$datacenter = isset($message_media['InputFileLocation']['dc_id']) ? $message_media['InputFileLocation']['dc_id'] : $this->datacenter->curdc;
if (isset($message_media['key'])) {
$digest = hash('md5', $message_media['key'].$message_media['iv'], true);
$fingerprint = \danog\PHP\Struct::unpack('<i', substr($digest, 0, 4) ^ substr($digest, 4, 4))[0];
$fingerprint = $this->unpack_signed_int(substr($digest, 0, 4) ^ substr($digest, 4, 4));
if ($fingerprint !== $message_media['key_fingerprint']) {
throw new \danog\MadelineProto\Exception('Fingerprint mismatch!');
}
@ -270,7 +270,7 @@ trait Files
$offset -= $start_at;
}
try {
$res = $cdn ? $this->method_call('upload.getCdnFile', ['file_token' => $message_media['file_token'], 'offset' => $offset, 'limit' => $part_size], ['heavy' => true, 'datacenter' => $datacenter]) : $this->method_call('upload.getFile', ['location' => $message_media['InputFileLocation'], 'offset' => $offset, 'limit' => $part_size], ['heavy' => true, 'datacenter' => $datacenter]);
$res = $cdn ? $this->method_call('upload.getCdnFile', ['file_token' => $message_media['file_token'], 'offset' => $offset, 'limit' => $part_size], ['heavy' => true, 'datacenter' => $datacenter]) : $this->method_call('upload.getFile', ['location' => $message_media['InputFileLocation'], 'offset' => $offset, 'limit' => $part_size], ['heavy' => true, 'datacenter' => &$datacenter]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->rpc === 'OFFSET_INVALID') {
\Rollbar\Rollbar::log(\Rollbar\Payload\Level::error(), $e->rpc, ['info' => $message_media, 'offset' => $offset]);

View File

@ -21,7 +21,7 @@ trait MessageHandler
* Forming the message frame and sending message to server
* :param message: byte string to send.
*/
private $last_recv = 0;
protected $last_recv = 0;
public function send_message($message_data, $content_related, $aargs = [])
{
@ -33,22 +33,23 @@ trait MessageHandler
if (!is_string($message_id)) {
throw new \danog\MadelineProto\Exception("Specified message id isn't a string");
}
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages['a'.$message_id] = [];
if ($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['auth_key'] === null || $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['server_salt'] === null) {
$message = str_repeat(chr(0), 8).$message_id.\danog\PHP\Struct::pack('<I', strlen($message_data)).$message_data;
$message = "\0\0\0\0\0\0\0\0".$message_id.$this->pack_unsigned_int(strlen($message_data)).$message_data;
} else {
$seq_no = $this->generate_out_seq_no($aargs['datacenter'], $content_related);
$data2enc = $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['server_salt'].$this->datacenter->sockets[$aargs['datacenter']]->session_id.$message_id.\danog\PHP\Struct::pack('<II', $seq_no, strlen($message_data)).$message_data;
$data2enc = $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['server_salt'].$this->datacenter->sockets[$aargs['datacenter']]->session_id.$message_id.pack('VV', $seq_no, strlen($message_data)).$message_data;
$padding = $this->random($this->posmod(-strlen($data2enc), 16));
$message_key = substr(sha1($data2enc, true), -16);
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['auth_key']);
$message = $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['id'].$message_key.$this->ige_encrypt($data2enc.$padding, $aes_key, $aes_iv);
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['seq_no'] = $seq_no;
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages['a'.$message_id]['seq_no'] = $seq_no;
}
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response'] = -1;
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages['a'.$message_id]['response'] = -1;
$this->datacenter->sockets[$aargs['datacenter']]->send_message($message);
return $message_id;
return 'a'.$message_id;
}
/**
@ -58,7 +59,7 @@ trait MessageHandler
{
$payload = $this->datacenter->sockets[$datacenter]->read_message();
if (strlen($payload) === 4) {
$error = \danog\PHP\Struct::unpack('<i', $payload)[0];
$error = $this->unpack_signed_int($payload);
if ($error === -404) {
if ($this->datacenter->sockets[$datacenter]->temp_auth_key !== null) {
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
@ -70,11 +71,12 @@ trait MessageHandler
throw new \danog\MadelineProto\RPCErrorException($error, $error);
}
$auth_key_id = substr($payload, 0, 8);
if ($auth_key_id === str_repeat(chr(0), 8)) {
$message_id = substr($payload, 8, 8);
if ($auth_key_id === "\0\0\0\0\0\0\0\0") {
$message_id = 'a'.substr($payload, 8, 8);
$this->check_message_id($message_id, ['outgoing' => false, 'datacenter' => $datacenter, 'container' => false]);
$message_length = \danog\PHP\Struct::unpack('<I', substr($payload, 16, 4))[0];
$message_length = unpack('V', substr($payload, 16, 4))[1];
$message_data = substr($payload, 20, $message_length);
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id] = [];
} elseif ($auth_key_id === $this->datacenter->sockets[$datacenter]->temp_auth_key['id']) {
$message_key = substr($payload, 8, 16);
$encrypted_data = substr($payload, 24);
@ -91,13 +93,13 @@ trait MessageHandler
throw new \danog\MadelineProto\Exception('Session id mismatch.');
}
$message_id = substr($decrypted_data, 16, 8);
$message_id = 'a'.substr($decrypted_data, 16, 8);
$this->check_message_id($message_id, ['outgoing' => false, 'datacenter' => $datacenter, 'container' => false]);
$seq_no = \danog\PHP\Struct::unpack('<I', substr($decrypted_data, 24, 4))[0];
$seq_no = unpack('V', substr($decrypted_data, 24, 4))[1];
// Dunno how to handle any incorrect sequence numbers
$message_data_length = \danog\PHP\Struct::unpack('<I', substr($decrypted_data, 28, 4))[0];
$message_data_length = unpack('V', substr($decrypted_data, 28, 4))[1];
if ($message_data_length > strlen($decrypted_data)) {
throw new \danog\MadelineProto\SecurityException('message_data_length is too big');
@ -119,7 +121,7 @@ trait MessageHandler
if ($message_key != substr(sha1(substr($decrypted_data, 0, 32 + $message_data_length), true), -16)) {
throw new \danog\MadelineProto\SecurityException('msg_key mismatch');
}
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['seq_no'] = $seq_no;
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id] = ['seq_no' => $seq_no];
} else {
throw new \danog\MadelineProto\SecurityException('Got unknown auth_key id');
}

View File

@ -20,7 +20,7 @@ trait MsgIdHandler
public function check_message_id($new_message_id, $aargs)
{
if (!is_object($new_message_id)) {
$new_message_id = new \phpseclib\Math\BigInteger(strrev($new_message_id), 256);
$new_message_id = new \phpseclib\Math\BigInteger(strrev(substr($new_message_id, 1)), 256);
}
$min_message_id = (new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$aargs['datacenter']]->time_delta - 300))->bitwise_leftShift(32);
if ($min_message_id->compare($new_message_id) > 0) {
@ -34,7 +34,6 @@ trait MsgIdHandler
if (!$new_message_id->divide($this->four)[1]->equals($this->zero)) {
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is not divisible by 4.');
}
$keys = array_keys($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages);
$key = $this->get_max_id($aargs['datacenter'], false);
if ($new_message_id->compare($key) <= 0) {
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is lower than or equal than the current limit ('.$key.').', 1);
@ -43,7 +42,7 @@ trait MsgIdHandler
reset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages);
unset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[key($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages)]);
}
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[strrev($new_message_id->toBytes())] = [];
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages['a'.strrev($new_message_id->toBytes())] = [];
} else {
if (!$new_message_id->divide($this->four)[1]->equals($this->one) && !$new_message_id->divide($this->four)[1]->equals($this->three)) {
throw new \danog\MadelineProto\Exception('message id mod 4 != 1 or 3');
@ -63,7 +62,7 @@ trait MsgIdHandler
reset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages);
unset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[key($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages)]);
}
$this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[strrev($new_message_id->toBytes())] = [];
$this->datacenter->sockets[$aargs['datacenter']]->incoming_messages['a'.strrev($new_message_id->toBytes())] = [];
}
}
@ -81,12 +80,12 @@ trait MsgIdHandler
public function get_max_id($datacenter, $incoming)
{
$keys = array_keys($this->datacenter->sockets[$datacenter]->{$incoming ? 'incoming_messages' : 'outgoing_messages'});
$keys = array_keys((array) $this->datacenter->sockets[$datacenter]->{$incoming ? 'incoming_messages' : 'outgoing_messages'});
if (empty($keys)) {
return $this->zero;
}
array_walk($keys, function (&$value, $key) {
$value = is_int($value) ? new \phpseclib\Math\BigInteger($value) : new \phpseclib\Math\BigInteger(strrev($value), 256);
$value = new \phpseclib\Math\BigInteger(strrev(substr($value, 1)), 256);
});
return \phpseclib\Math\BigInteger::max(...$keys);

View File

@ -200,7 +200,7 @@ trait PeerHandler
if (is_numeric($id)) {
if (is_string($id)) {
$id = $this->bigint ? ((float) $id) : ((int) $id);
$id = \danog\MadelineProto\Logger::$bigint ? ((float) $id) : ((int) $id);
}
if (isset($this->chats[$id])) {
return $this->gen_all($this->chats[$id]);
@ -212,15 +212,15 @@ trait PeerHandler
}
}
if (!isset($this->settings['pwr']['requests']) || $this->settings['pwr']['requests'] === true) {
$dbres = json_decode(file_get_contents('https://id.pwrtelegram.xyz/db/getusername?id='.$id, false, stream_context_create(['http'=> [
$dbres = json_decode(@file_get_contents('https://id.pwrtelegram.xyz/db/getusername?id='.$id, false, stream_context_create(['http'=> [
'timeout' => 2,
],
])), true);
if ($dbres['ok']) {
if (isset($dbres['ok']) && $dbres['ok']) {
return $this->get_info('@'.$dbres['result']);
}
}
throw new \danog\MadelineProto\Exception("Couldn't find peer by provided chat id ".$id);
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
}
$id = str_replace('@', '', $id);
foreach ($this->chats as $chat) {
@ -233,7 +233,7 @@ trait PeerHandler
return $this->get_info($id, false);
}
throw new \danog\MadelineProto\Exception("Couldn't find peer by provided username ".$id);
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
}
public function gen_all($constructor)

View File

@ -17,8 +17,8 @@ namespace danog\MadelineProto\MTProtoTools;
*/
trait ResponseHandler
{
private $pending_updates = [];
private $bad_msg_error_codes = [
protected $pending_updates = [];
protected $bad_msg_error_codes = [
16 => 'msg_id too low (most likely, client time is wrong; it would be worthwhile to synchronize it using msg_id notifications and re-send the original message with the โ€œcorrectโ€ msg_id or wrap it in a container with a new msg_id if the original message had waited too long on the client to be transmitted)',
17 => 'msg_id too high (similar to the previous case, the client time has to be synchronized, and the message re-sent with the correct msg_id)',
18 => 'incorrect two lower order msg_id bits (the server expects client message msg_id to be divisible by 4)',
@ -31,7 +31,7 @@ trait ResponseHandler
48 => 'incorrect server salt (in this case, the bad_server_salt response is received with the correct salt, and the message is to be re-sent with it)',
64 => 'invalid container.',
];
private $msgs_info_flags = [
protected $msgs_info_flags = [
1 => 'nothing is known about the message (msg_id too low, the other party may have forgotten it)',
2 => 'message not received (msg_id falls within the range of stored identifiers; however, the other party has certainly not received a message like that)',
3 => 'message not received (msg_id too high; however, the other party has certainly not received it yet)',
@ -48,8 +48,8 @@ trait ResponseHandler
$info = '';
foreach ($msg_ids as $msg_id) {
$cur_info = 0;
if (!in_array($msg_id, $this->datacenter->sockets[$datacenter]->incoming_messages)) {
$msg_id = new \phpseclib\Math\BigInteger(strrev($msg_id), 256);
if (!isset($this->datacenter->sockets[$datacenter]->incoming_messages[$msg_id])) {
$msg_id = new \phpseclib\Math\BigInteger(strrev(substr($msg_id, 1)), 256);
if ((new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$datacenter]->time_delta + 30))->bitwise_leftShift(32)->compare($msg_id) < 0) {
$cur_info |= 3;
} elseif ((new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$datacenter]->time_delta - 300))->bitwise_leftShift(32)->compare($msg_id) > 0) {
@ -68,13 +68,31 @@ trait ResponseHandler
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->object_call('msgs_state_info', ['req_msg_id' => $req_msg_id, 'info' => $info], ['datacenter' => $datacenter])]['response'] = $req_msg_id;
}
public $stop = false;
public function handle_messages($datacenter)
{
if ($this->stop) {
return;
}
$only_updates = true;
foreach ($this->datacenter->sockets[$datacenter]->new_incoming as $current_msg_id) {
$unset = false;
\danog\MadelineProto\Logger::log(['Received '.$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].'.'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Received '.$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].'.'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
if (!$this->synchronized(function ($zis, $datacenter, $current_msg_id) {
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['handling'])) {
return false;
}
$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['handling'] = true;
return true;
}, $this, $datacenter, $current_msg_id) || $this->stop) {
\danog\MadelineProto\Logger::log([base64_encode($current_msg_id).$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].' is already being handled'], \danog\MadelineProto\Logger::VERBOSE);
continue;
}
\danog\MadelineProto\Logger::log(['Handling '.base64_encode($current_msg_id).$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].'.'], \danog\MadelineProto\Logger::VERBOSE);
}
switch ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_']) {
case 'msgs_ack':
$this->check_in_seq_no($datacenter, $current_msg_id);
@ -88,10 +106,17 @@ trait ResponseHandler
case 'rpc_result':
$this->ack_incoming_message_id($current_msg_id, $datacenter); // Acknowledge that I received the server's response
$this->ack_outgoing_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id'], $datacenter); // Acknowledge that the server received my request
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]['response'] = $current_msg_id;
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]);
$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'] = $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result'];
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]['response'] = $current_msg_id;
//var_dump($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]);
$content = (array) $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result'];
$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'] = $content;
///var_dump($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]);
///var_dump($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]);
///var_dump($this->datacenter->sockets[$datacenter]->incoming_messages[$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]['response']]['content']);
///$this->stop = true;
//var_dump(base64_encode($current_msg_id), $this->datacenter->sockets[$datacenter]->incoming_messages);
$this->check_in_seq_no($datacenter, $current_msg_id);
$only_updates = false;
break;
@ -200,13 +225,13 @@ trait ResponseHandler
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $key => $msg_id) {
$msg_id = new \phpseclib\Math\BigInteger(strrev($msg_id), 256);
$msg_id = new \phpseclib\Math\BigInteger(strrev(substr($msg_id, 1)), 256);
$status = 'Status for message id '.$msg_id.': ';
if (($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['info'][$key] & 4) === 1) {
if (($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['info'][$key] & 4) !== 0) {
$this->ack_outgoing_message_id($msg_id, $datacenter);
}
foreach ($this->msgs_info_flags as $flag => $description) {
if (($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['info'][$key] & $flag) === 1) {
if (($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['info'][$key] & $flag) !== 0) {
$status .= $description;
}
}
@ -319,41 +344,43 @@ trait ResponseHandler
return $only_updates;
}
public function handle_messages_threaded()
{
}
public function handle_rpc_error($server_answer, &$datacenter)
{
switch ($server_answer['error_code']) {
case 303:
$this->datacenter->curdc = $datacenter = (int) preg_replace('/[^0-9]+/', '', $server_answer['error_message']);
throw new \danog\MadelineProto\Exception('Received request to switch to DC '.$this->datacenter->curdc);
case 401:
switch ($server_answer['error_message']) {
case 'USER_DEACTIVATED':
case 'SESSION_REVOKED':
case 'SESSION_EXPIRED':
$this->datacenter->sockets[$datacenter]->temp_auth_key = null;
$this->datacenter->sockets[$datacenter]->auth_key = null;
$this->authorized = false;
$this->authorization = null;
$this->init_authorization(); // idk
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
case 'AUTH_KEY_UNREGISTERED':
case 'AUTH_KEY_INVALID':
$this->datacenter->sockets[$datacenter]->temp_auth_key = null;
$this->init_authorization(); // idk
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
}
case 420:
$seconds = preg_replace('/[^0-9]+/', '', $server_answer['error_message']);
if (is_numeric($seconds) && isset($this->settings['flood_timeout']['wait_if_lt']) && $seconds < $this->settings['flood_timeout']['wait_if_lt']) {
\danog\MadelineProto\Logger::log(['Flood, waiting '.$seconds.' seconds...'], \danog\MadelineProto\Logger::NOTICE);
sleep($seconds);
throw new \danog\MadelineProto\Exception('Re-executing query...');
}
default:
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
break;
}
case 303:
$this->datacenter->curdc = $datacenter = (int) preg_replace('/[^0-9]+/', '', $server_answer['error_message']);
throw new \danog\MadelineProto\Exception('Received request to switch to DC '.$this->datacenter->curdc);
case 401:
switch ($server_answer['error_message']) {
case 'USER_DEACTIVATED':
case 'SESSION_REVOKED':
case 'SESSION_EXPIRED':
$this->datacenter->sockets[$datacenter]->temp_auth_key = null;
$this->datacenter->sockets[$datacenter]->auth_key = null;
$this->authorized = false;
$this->authorization = null;
$this->init_authorization(); // idk
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
case 'AUTH_KEY_UNREGISTERED':
case 'AUTH_KEY_INVALID':
$this->datacenter->sockets[$datacenter]->temp_auth_key = null;
$this->init_authorization(); // idk
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
}
case 420:
$seconds = preg_replace('/[^0-9]+/', '', $server_answer['error_message']);
if (is_numeric($seconds) && isset($this->settings['flood_timeout']['wait_if_lt']) && $seconds < $this->settings['flood_timeout']['wait_if_lt']) {
\danog\MadelineProto\Logger::log(['Flood, waiting '.$seconds.' seconds...'], \danog\MadelineProto\Logger::NOTICE);
sleep($seconds);
throw new \danog\MadelineProto\Exception('Re-executing query...');
}
default:
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
}
}
public function handle_pending_updates()

View File

@ -44,7 +44,7 @@ trait SeqNoHandler
public function content_related($method)
{
return isset($method['_']) ? !in_array(
return isset($method['_']) ? !$this->in_array(
$method['_'],
[
'rpc_result',

View File

@ -22,9 +22,9 @@ trait UpdateHandler
public $channels_state = [];
public $updates = [];
public $updates_key = 0;
private $getting_state = false;
protected $getting_state = false;
public $full_chats;
private $msg_ids = [];
protected $msg_ids = [];
public function pwr_update_handler($update)
{
@ -80,7 +80,7 @@ trait UpdateHandler
return [];
}
if ($params['offset'] < 0) {
$params['offset'] = array_reverse(array_keys($this->updates))[abs($params['offset']) - 1];
$params['offset'] = array_reverse(array_keys((array) $this->updates))[abs($params['offset']) - 1];
}
$updates = [];
ksort($this->updates);
@ -158,7 +158,6 @@ trait UpdateHandler
}
throw $e;
}
\danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::VERBOSE);
$this->get_channel_state($channel)['sync_loading'] = false;
switch ($difference['_']) {
case 'updates.channelDifferenceEmpty':
@ -175,6 +174,7 @@ trait UpdateHandler
}
break;
case 'updates.channelDifferenceTooLong':
\danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::VERBOSE);
$this->set_channel_state($channel, $difference);
$this->handle_update_messages($difference['messages'], $channel);
unset($difference);
@ -239,7 +239,7 @@ trait UpdateHandler
$this->get_update_state()['pending_seq_updates'] = [];
}
$difference = $this->method_call('updates.getDifference', ['pts' => $this->get_update_state()['pts'], 'date' => $this->get_update_state()['date'], 'qts' => $this->get_update_state()['qts']], ['datacenter' => $this->datacenter->curdc]);
\danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
$this->get_update_state()['sync_loading'] = false;
switch ($difference['_']) {
@ -312,7 +312,7 @@ trait UpdateHandler
} else {
$cur_state = &$this->get_channel_state($channel_id, (isset($update['pts']) ? $update['pts'] : 0) - (isset($update['pts_count']) ? $update['pts_count'] : 0));
}
if ($cur_state['sync_loading'] && in_array($update['_'], ['updateNewMessage', 'updateEditMessage', 'updateNewChannelMessage', 'updateEditChannelMessage'])) {
if ($cur_state['sync_loading'] && $this->in_array($update['_'], ['updateNewMessage', 'updateEditMessage', 'updateNewChannelMessage', 'updateEditChannelMessage'])) {
\danog\MadelineProto\Logger::log(['Sync loading, not handling update'], \danog\MadelineProto\Logger::NOTICE);
return false;
@ -561,7 +561,7 @@ trait UpdateHandler
if ($update['_'] === 'updateEncryption') {
switch ($update['chat']['_']) {
case 'encryptedChatRequested':
if ($this->settings['secret_chats']['accept_chats'] === false || ($this->is_array($this->settings['secret_chats']['accept_chats']) && !in_array($update['chat']['admin_id'], $this->settings['secret_chats']['accept_chats']))) {
if ($this->settings['secret_chats']['accept_chats'] === false || ($this->is_array($this->settings['secret_chats']['accept_chats']) && !$this->in_array($update['chat']['admin_id'], $this->settings['secret_chats']['accept_chats']))) {
return;
}
\danog\MadelineProto\Logger::log(['Accepting secret chat '.$update['chat']['id']], \danog\MadelineProto\Logger::NOTICE);

View File

@ -1,305 +0,0 @@
<?php
/*
Copyright 2016-2017 Daniil Gentili
(https://daniil.it)
This file is part of MadelineProto.
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
The PWRTelegram API is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto;
/**
* Manages connection to telegram servers.
*/
class NewConnection extends SerializableVolatile
{
use \danog\MadelineProto\Tools;
public $sock = null;
public $protocol = null;
public $ip = null;
public $port = null;
public $timeout = null;
public $time_delta = 0;
public $temp_auth_key;
public $auth_key;
public $session_id;
public $session_out_seq_no = 0;
public $session_in_seq_no = 0;
public $ipv6 = false;
public $incoming_messages = [];
public $outgoing_messages = [];
public $new_incoming = [];
public $new_outgoing = [];
public function __construct($ip, $port = null, $protocol = null, $timeout = null, $ipv6 = null)
{
if ($this->unserialized($ip)) {
return true;
}
// Can use:
/*
- tcp_full
- tcp_abridged
- tcp_intermediate
- http
- https
- udp
*/
$this->protocol = $protocol;
$this->timeout = $timeout;
$this->ipv6 = $ipv6;
$this->ip = $ip;
$this->port = $port;
$this->sock = new \Volatile();
switch ($this->protocol) {
case 'tcp_abridged':
$this->sock['pony'] = new \Volatile();
$this->sock['pony']['socket'] = new Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
$this->sock['pony']->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock['pony']->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
if (!$this->sock['pony']->connect($ip, $port)) {
throw new Exception("Connection: couldn't connect to socket.");
}
$this->write(chr(239));
break;
case 'tcp_intermediate':
$this->sock['pony'] = new Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
$this->sock['pony']->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock['pony']->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
if (!$this->sock['pony']->connect($ip, $port)) {
throw new Exception("Connection: couldn't connect to socket.");
}
$this->write(str_repeat(chr(238), 4));
break;
case 'tcp_full':
$this->sock['pony'] = new Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
//$this->sock["pony"]->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
//$this->sock["pony"]->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
if (!$this->sock['pony']->connect($ip, $port)) {
throw new Exception("Connection: couldn't connect to socket.");
}
$this->out_seq_no = -1;
$this->in_seq_no = -1;
break;
case 'http':
case 'https':
$this->parsed = parse_url($ip);
$this->sock['pony'] = new Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname($this->protocol === 'https' ? 'tls' : 'tcp'));
$this->sock['pony']->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock['pony']->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
if (!$this->sock['pony']->connect($this->parsed['host'], $port)) {
throw new Exception("Connection: couldn't connect to socket.");
}
break;
case 'udp':
throw new Exception("Connection: This protocol isn't implemented yet.");
default:
throw new Exception('Connection: invalid protocol specified.');
break;
}
}
public function __destruct()
{
switch ($this->protocol) {
case 'tcp_abridged':
case 'tcp_intermediate':
case 'tcp_full':
case 'http':
case 'https':
try {
unset($this->sock['pony']);
} catch (\danog\MadelineProto\Exception $e) {
}
break;
case 'udp':
throw new Exception("Connection: This protocol wasn't implemented yet.");
default:
throw new Exception('Connection: invalid protocol specified.');
break;
}
}
public function close_and_reopen()
{
$this->__destruct();
$this->__construct($this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6);
}
public function __sleep()
{
$t = get_object_vars($this);
if (isset($t['sock'])) {
unset($t['sock']);
}
return array_keys($t);
}
public function __wakeup()
{
$this->__construct($this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6);
}
public function write($what, $length = null)
{
if ($length !== null) {
$what = substr($what, 0, $length);
}
switch ($this->protocol) {
case 'tcp_abridged':
case 'tcp_intermediate':
case 'tcp_full':
case 'http':
case 'https':
if (($wrote = $this->sock['pony']->write($what)) !== strlen($what)) {
throw new \danog\MadelineProto\Exception("WARNING: Wrong length was written (should've written ".strlen($what).', wrote '.$wrote.')!');
}
return $wrote;
break;
case 'udp':
throw new Exception("Connection: This protocol wasn't implemented yet.");
break;
default:
throw new Exception('Connection: invalid protocol specified.');
break;
}
}
public function read($length)
{
switch ($this->protocol) {
case 'tcp_abridged':
case 'tcp_intermediate':
case 'tcp_full':
case 'http':
case 'https':
$packet = $this->sock['pony']->read($length);
if ($packet === false || strlen($packet) === 0) {
throw new \danog\MadelineProto\NothingInTheSocketException('Nothing in the socket!');
}
if (strlen($packet) != $length) {
throw new \danog\MadelineProto\Exception("WARNING: Wrong length was read (should've read ".($length).', read '.strlen($packet).')!');
}
return $packet;
case 'udp':
throw new Exception("Connection: This protocol wasn't implemented yet.");
default:
throw new Exception('Connection: invalid protocol specified.');
break;
}
}
public function read_message()
{
switch ($this->protocol) {
case 'tcp_full':
$packet_length_data = $this->read(4);
$packet_length = \danog\PHP\Struct::unpack('<I', $packet_length_data)[0];
$packet = $this->read($packet_length - 4);
if (strrev(hash('crc32b', $packet_length_data.substr($packet, 0, -4), true)) !== substr($packet, -4)) {
throw new Exception('CRC32 was not correct!');
}
$this->in_seq_no++;
$in_seq_no = \danog\PHP\Struct::unpack('<I', substr($packet, 0, 4))[0];
if ($in_seq_no != $this->in_seq_no) {
throw new Exception('Incoming seq_no mismatch');
}
return substr($packet, 4, $packet_length - 12);
case 'tcp_intermediate':
$packet_length_data = $this->read(4);
$packet_length = \danog\PHP\Struct::unpack('<I', $packet_length_data)[0];
return $this->read($packet_length);
case 'tcp_abridged':
$packet_length_data = $this->read(1);
$packet_length = ord($packet_length_data);
if ($packet_length < 127) {
$packet_length <<= 2;
} else {
$packet_length_data = $this->read(3);
$packet_length = \danog\PHP\Struct::unpack('<I', $packet_length_data.pack('x'))[0] << 2;
}
return $this->read($packet_length);
case 'http':
case 'https':
$headers = [];
$close = false;
while (true) {
$current_header = '';
while (($curchar = $this->read(1)) !== "\n") {
$current_header .= $curchar;
}
$current_header = rtrim($current_header);
if ($current_header === '') {
break;
}
if ($current_header === false) {
throw new Exception('No data in the socket!');
}
if (preg_match('|^Content-Length: |i', $current_header)) {
$length = preg_replace('|Content-Length: |i', '', $current_header);
}
if (preg_match('|^Connection: close|i', $current_header)) {
$close = true;
}
$headers[] = $current_header;
}
$read = $this->read($length);
if ($headers[0] !== 'HTTP/1.1 200 OK') {
throw new Exception($headers[0]);
}
if ($close) {
$this->close_and_reopen();
}
return $read;
case 'udp':
throw new Exception("Connection: This protocol wasn't implemented yet.");
}
}
public function send_message($message)
{
switch ($this->protocol) {
case 'tcp_full':
$this->out_seq_no++;
$step1 = \danog\PHP\Struct::pack('<II', (strlen($message) + 12), $this->out_seq_no).$message;
$step2 = $step1.strrev(hash('crc32b', $step1, true));
$this->write($step2);
break;
case 'tcp_intermediate':
$this->write(\danog\PHP\Struct::pack('<I', strlen($message)).$message);
break;
case 'tcp_abridged':
$len = strlen($message) / 4;
if ($len < 127) {
$step1 = chr($len).$message;
} else {
$step1 = chr(127).substr(\danog\PHP\Struct::pack('<I', $len), 0, 3).$message;
}
$this->write($step1);
break;
case 'http':
case 'https':
$this->write('POST '.$this->parsed['path']." HTTP/1.1\r\nHost: ".$this->parsed['host']."\r\nContent-Type: application/x-www-form-urlencoded\r\nConnection: keep-alive\r\nContent-Length: ".strlen($message)."\r\n\r\n".$message);
break;
case 'udp':
throw new Exception("Connection: This protocol wasn't implemented yet.");
default:
break;
}
}
}

View File

@ -12,16 +12,17 @@ If not, see <http://www.gnu.org/licenses/>.
namespace danog\MadelineProto;
class RSA
class RSA extends \Volatile
{
use \danog\MadelineProto\TL\TL;
use \danog\MadelineProto\Tools;
use \danog\Serializable;
public $e;
public $n;
public $fp;
public function __construct($rsa_key)
public function ___construct($rsa_key)
{
//if ($this->unserialized($rsa_key)) return true;
\danog\MadelineProto\Logger::log(['Istantiating \phpseclib\Crypt\RSA...'], Logger::ULTRA_VERBOSE);

View File

@ -19,8 +19,8 @@ namespace danog\MadelineProto\SecretChats;
*/
trait AuthKeyHandler
{
private $temp_requested_secret_chats = [];
private $secret_chats = [];
protected $temp_requested_secret_chats = [];
protected $secret_chats = [];
public function accept_secret_chat($params)
{
@ -95,7 +95,7 @@ trait AuthKeyHandler
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNotifyLayer', 'layer' => $this->encrypted_layer]]], ['datacenter' => $this->datacenter->curdc]);
}
private $temp_rekeyed_secret_chats = [];
protected $temp_rekeyed_secret_chats = [];
public function rekey($chat)
{

View File

@ -34,7 +34,7 @@ trait MessageHandler
}
$this->secret_chats[$chat_id]['outgoing'][$this->secret_chats[$chat_id]['out_seq_no']] = $message;
$message = $this->serialize_object(['type' => $this->secret_chats[$chat_id]['layer'] === 8 ? 'DecryptedMessage' : 'DecryptedMessageLayer'], $message, $this->secret_chats[$chat_id]['layer']);
$message = \danog\PHP\Struct::pack('<I', strlen($message)).$message;
$message = $this->pack_unsigned_int(strlen($message)).$message;
$message_key = substr(sha1($message, true), -16);
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->secret_chats[$chat_id]['key']['auth_key'], 'to server');
@ -70,7 +70,7 @@ trait MessageHandler
$encrypted_data = substr($message['message']['bytes'], 24);
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->secret_chats[$message['message']['chat_id']][$old ? 'old_key' : 'key']['auth_key'], 'to server');
$decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
$message_data_length = \danog\PHP\Struct::unpack('<I', substr($decrypted_data, 0, 4))[0];
$message_data_length = unpack('V', substr($decrypted_data, 0, 4))[1];
if ($message_data_length > strlen($decrypted_data)) {
throw new \danog\MadelineProto\SecurityException('message_data_length is too big');
}

View File

@ -1,32 +0,0 @@
<?php
/*
Copyright 2016-2017 Daniil Gentili
(https://daniil.it)
This file is part of MadelineProto.
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto;
abstract class SerializableVolatile
{
public function unserialized($data)
{
if (!isset($data['_']) || $data['_'] !== 'pony') {
return false;
}
unset($data['_']);
foreach ($data as $key => $data) {
$this->{$key} = $data;
}
if (method_exists($this, '__wakeup')) {
$this->__wakeup();
}
return true;
}
}

View File

@ -31,7 +31,7 @@ class Serialization
if ($instance->API->should_serialize || !(file_exists($filename) && !empty(file_get_contents($filename))) || $force) {
$instance->API->should_serialize = false;
return file_put_contents($filename, serialize($instance), LOCK_EX);
return file_put_contents($filename, \danog\MadelineProto\Logger::$has_thread ? \danog\Serialization::serialize($instance) : serialize($instance), LOCK_EX);
}
return false;
@ -54,16 +54,16 @@ class Serialization
$file = fopen($filename, 'r+');
flock($file, LOCK_SH);
$unserialized = stream_get_contents($file);
/*
foreach (['MTProto', 'DataCenter', 'Connection', 'RSA'] as $class) {
$oclass = "danog\\MadelineProto\\".$class;
$nclass = $oclass.'Serializable';
$unserialized = str_replace('O:'.strlen($oclass).':"'.$oclass.'":', 'O:'.strlen($nclass).':"'.$nclass.'":', $unserialized);
}
*/
flock($file, LOCK_UN);
fclose($file);
$unserialized = unserialize($unserialized);
foreach (['RSA', 'TL\TLMethod', 'TL\TLConstructor', 'MTProto', 'API', 'DataCenter', 'Connection'] as $class) {
class_exists('\danog\MadelineProto\\'.$class);
}
try {
$unserialized = \danog\MadelineProto\Logger::$has_thread ? \danog\Serialization::unserialize($unserialized) : unserialize($unserialized);
} catch (Bug74586Exception $e) {
$unserialized = \danog\Serialization::unserialize($unserialized);
}
} else {
throw new Exception('File does not exist');
}

View File

@ -407,7 +407,7 @@ trait Extension
public function get_extension_from_mime($mime)
{
foreach ($this->all_mimes as $key => $value) {
if (array_search($mime, $value) !== false) {
if (array_search($mime, (array) $value) !== false) {
return '.'.$key;
}
}

View File

@ -14,7 +14,7 @@ namespace danog\MadelineProto\TL\Conversion;
trait TD
{
private $td_params_conversion = [
protected $td_params_conversion = [
'updateNewMessage' => [
'_' => 'updateNewMessage',
'disable_notification' => ['message', 'silent'],
@ -51,10 +51,10 @@ trait TD
],
];
private $reverse = [
protected $reverse = [
'sendMessage'=> 'messages.sendMessage',
];
private $ignore = ['updateMessageID'];
protected $ignore = ['updateMessageID'];
public function tdcli_to_td(&$params, $key = null)
{
@ -126,7 +126,7 @@ trait TD
return $params;
}
$newparams = ['_' => $params['_']];
if (in_array($params['_'], $this->ignore)) {
if ($this->in_array($params['_'], $this->ignore)) {
return $params;
}
foreach ($this->td_params_conversion[$params['_']] as $td => $mtproto) {

View File

@ -132,7 +132,7 @@ trait TL
$dparams = [];
}
$TL_dict[$type][$key][$type === 'constructors' ? 'predicate' : 'method'] = $name;
$TL_dict[$type][$key]['id'] = \danog\PHP\Struct::unpack('<i', pack('V', hexdec($id)))[0];
$TL_dict[$type][$key]['id'] = strrev(hex2bin($id));
$TL_dict[$type][$key]['params'] = [];
$TL_dict[$type][$key]['type'] = preg_replace(['/.+\s/', '/;/'], '', $line);
if ($layer !== null) {
@ -150,6 +150,13 @@ trait TL
}
$key++;
}
} else {
foreach ($TL_dict['constructors'] as $key => $value) {
$TL_dict['constructors'][$key]['id'] = $this->pack_signed_int($TL_dict['constructors'][$key]['id']);
}
foreach ($TL_dict['methods'] as $key => $value) {
$TL_dict['methods'][$key]['id'] = $this->pack_signed_int($TL_dict['methods'][$key]['id']);
}
}
if (empty($TL_dict) || empty($TL_dict['constructors']) || !isset($TL_dict['methods'])) {
throw new Exception('Invalid source file was provided: '.$file);
@ -210,12 +217,11 @@ trait TL
public function serialize_bool($bool)
{
return \danog\PHP\Struct::pack('<i', $this->constructors->find_by_predicate('bool'.($bool ? 'True' : 'False'))['id']);
return $this->constructors->find_by_predicate($bool ? 'boolTrue' : 'boolFalse')['id'];
}
public function deserialize_bool($data)
public function deserialize_bool($id)
{
$id = \danog\PHP\Struct::unpack('<i', $data)[0];
$tl_elem = $this->constructors->find_by_id($id);
if ($tl_elem === false) {
throw new Exception('Could not extract boolean');
@ -232,13 +238,13 @@ trait TL
throw new Exception('given value ('.$object.") isn't numeric");
}
return \danog\PHP\Struct::pack('<i', $object);
return $this->pack_signed_int($object);
case '#':
if (!is_numeric($object)) {
throw new Exception('given value ('.$object.") isn't numeric");
}
return pack('V', $object);
return $this->pack_unsigned_int($object);
case 'long':
if (is_object($object)) {
return str_pad(strrev($object->toBytes()), 8, chr(0));
@ -247,11 +253,14 @@ trait TL
if (is_string($object) && strlen($object) === 8) {
return $object;
}
if (is_string($object) && strlen($object) === 9 && $object[0] === 'a') {
return substr($object, 1);
}
if (!is_numeric($object)) {
throw new Exception('given value ('.$object.") isn't numeric");
}
return \danog\PHP\Struct::pack('<q', $object);
return $this->pack_signed_long($object);
case 'int128':
if (strlen($object) !== 16) {
throw new Exception('Given value is not 16 bytes long');
@ -271,7 +280,7 @@ trait TL
return (string) $object;
case 'double':
return \danog\PHP\Struct::pack('<d', $object);
return $this->pack_double($object);
case 'string':
$object = pack('C*', ...unpack('C*', $object));
case 'bytes':
@ -283,7 +292,7 @@ trait TL
$concat .= pack('@'.$this->posmod((-$l - 1), 4));
} else {
$concat .= chr(254);
$concat .= substr(\danog\PHP\Struct::pack('<i', $l), 0, 3);
$concat .= substr($this->pack_signed_int($l), 0, 3);
$concat .= $object;
$concat .= pack('@'.$this->posmod(-$l, 4));
}
@ -299,8 +308,8 @@ trait TL
if (!$this->is_array($object)) {
throw new Exception("You didn't provide a valid array");
}
$concat = \danog\PHP\Struct::pack('<i', $this->constructors->find_by_predicate('vector')['id']);
$concat .= \danog\PHP\Struct::pack('<i', count($object));
$concat = $this->constructors->find_by_predicate('vector')['id'];
$concat .= $this->pack_unsigned_int(count($object));
foreach ($object as $current_object) {
$concat .= $this->serialize_object(['type' => $type['subtype']], $current_object);
}
@ -346,7 +355,7 @@ trait TL
$constructorData = $this->constructors->find_by_predicate('inputMessageEntityMentionName');
}
if (!$bare) {
$concat .= \danog\PHP\Struct::pack('<i', $constructorData['id']);
$concat .= $constructorData['id'];
}
return $concat.$this->serialize_params($constructorData, $object, $layer);
@ -359,7 +368,7 @@ trait TL
throw new Exception('Could not find method: '.$method);
}
return \danog\PHP\Struct::pack('<i', $tl['id']).$this->serialize_params($tl, $arguments);
return $tl['id'].$this->serialize_params($tl, $arguments);
}
public function serialize_params($tl, $arguments, $layer = -1)
@ -413,8 +422,8 @@ trait TL
continue 2;
case 'Vector t':
if (isset($arguments['id'])) {
$serialized .= \danog\PHP\Struct::pack('<i', $this->constructors->find_by_predicate('vector')['id']);
$serialized .= \danog\PHP\Struct::pack('<i', count($arguments['id']));
$serialized .= $this->constructors->find_by_predicate('vector')['id'];
$serialized .= $this->pack_unsigned_int(count($arguments['id']));
$serialized .= $this->random(8 * count($arguments['id']));
continue 2;
}
@ -464,17 +473,22 @@ trait TL
} elseif (!is_resource($bytes_io)) {
throw new Exception('An invalid bytes_io handle was provided.');
}
switch ($type['type']) {
case 'Bool':
return $this->deserialize_bool(stream_get_contents($bytes_io, 4));
case 'int':
return \danog\PHP\Struct::unpack('<i', stream_get_contents($bytes_io, 4))[0];
return $this->unpack_signed_int(stream_get_contents($bytes_io, 4));
case '#':
return unpack('V', stream_get_contents($bytes_io, 4))[1];
case 'long':
return $this->bigint || isset($type['strlong']) ? stream_get_contents($bytes_io, 8) : \danog\PHP\Struct::unpack('<q', stream_get_contents($bytes_io, 8))[0];
if (isset($type['idstrlong'])) {
return 'a'.stream_get_contents($bytes_io, 8);
}
return \danog\MadelineProto\Logger::$bigint || isset($type['strlong']) ? stream_get_contents($bytes_io, 8) : $this->unpack_signed_long(stream_get_contents($bytes_io, 8));
case 'double':
return \danog\PHP\Struct::unpack('<d', stream_get_contents($bytes_io, 8))[0];
return $this->unpack_double(stream_get_contents($bytes_io, 8));
case 'int128':
return stream_get_contents($bytes_io, 16);
case 'int256':
@ -509,7 +523,7 @@ trait TL
case 'true':
return true;
case 'Vector t':
$id = \danog\PHP\Struct::unpack('<i', stream_get_contents($bytes_io, 4))[0];
$id = stream_get_contents($bytes_io, 4);
$constructorData = $this->constructors->find_by_id($id);
if ($constructorData === false) {
throw new Exception('Could not extract type: '.$type['type'].' with id '.$id);
@ -524,7 +538,7 @@ trait TL
throw new Exception('Invalid vector constructor: '.$constructorData['predicate']);
}
case 'vector':
$count = \danog\PHP\Struct::unpack('<i', stream_get_contents($bytes_io, 4))[0];
$count = unpack('V', stream_get_contents($bytes_io, 4))[1];
$result = [];
$type['type'] = $type['subtype'];
for ($i = 0; $i < $count; $i++) {
@ -542,7 +556,7 @@ trait TL
} else {
$constructorData = $this->constructors->find_by_predicate($type['type']);
if ($constructorData === false) {
$id = \danog\PHP\Struct::unpack('<i', stream_get_contents($bytes_io, 4))[0];
$id = stream_get_contents($bytes_io, 4);
$constructorData = $this->constructors->find_by_id($id);
if ($constructorData === false) {
throw new Exception('Could not extract type: '.$type['type'].' with id '.$id);
@ -585,7 +599,10 @@ trait TL
break;
}
}
if ($this->in_array($arg['name'], ['msg_ids', 'msg_id', 'bad_msg_id', 'req_msg_id', 'answer_msg_id', 'first_msg_id', 'key_fingerprint', 'server_salt', 'new_server_salt', 'server_public_key_fingerprints', 'ping_id', 'exchange_id'])) {
if ($this->in_array($arg['name'], ['msg_ids', 'msg_id', 'bad_msg_id', 'req_msg_id', 'answer_msg_id', 'first_msg_id'])) {
$arg['idstrlong'] = true;
}
if ($this->in_array($arg['name'], ['key_fingerprint', 'server_salt', 'new_server_salt', 'server_public_key_fingerprints', 'ping_id', 'exchange_id'])) {
$arg['strlong'] = true;
}
@ -612,6 +629,14 @@ trait TL
return json_decode($x['data'], true);
}
if ($x['_'] === 'message' && isset($x['reply_markup']['rows'])) {
foreach ($x['reply_markup']['rows'] as $key => $row) {
foreach ($row['buttons'] as $bkey => $button) {
$x['reply_markup']['rows'][$key]['buttons'][$bkey] = new \danog\MadelineProto\Button($this, $x, $button);
}
}
}
return $x;
}
}

View File

@ -12,8 +12,12 @@ If not, see <http://www.gnu.org/licenses/>.
namespace danog\MadelineProto\TL;
class TLConstructor extends TLParams
class TLConstructor extends \Volatile
{
use \danog\Serializable;
use \danog\MadelineProto\Tools;
use TLParams;
public $id = [];
public $predicate = [];
public $type = [];
@ -23,7 +27,7 @@ class TLConstructor extends TLParams
public function add($json_dict, $scheme_type)
{
$this->id[$this->key] = (int) $json_dict['id'];
$this->id[$this->key] = $json_dict['id'];
$this->predicate[$this->key] = (string) ((($scheme_type === 'mtproto' && $json_dict['predicate'] === 'message') ? 'MT' : '').$json_dict['predicate']);
$this->type[$this->key] = (($scheme_type === 'mtproto' && $json_dict['type'] === 'Message') ? 'MT' : '').$json_dict['type'];
$this->params[$this->key] = $json_dict['params'];
@ -36,13 +40,13 @@ class TLConstructor extends TLParams
public function find_by_type($type)
{
$key = array_search($type, $this->type);
$key = array_search($type, (array) $this->type);
return ($key === false) ? false : [
'id' => $this->id[$key],
'predicate' => $this->predicate[$key],
'type' => $this->type[$key],
'params' => $this->params[$key],
'params' => $this->array_cast_recursive($this->params[$key]),
];
}
@ -50,7 +54,7 @@ class TLConstructor extends TLParams
{
if ($layer !== -1) {
$newlayer = -1;
$keys = array_keys($this->predicate, $predicate);
$keys = array_keys((array) $this->predicate, $predicate);
foreach ($keys as $k) {
if ($this->layer[$k] <= $layer && $this->layer[$k] > $newlayer) {
$key = $k;
@ -61,26 +65,26 @@ class TLConstructor extends TLParams
}
}
} else {
$key = array_search($predicate, $this->predicate);
$key = array_search($predicate, (array) $this->predicate);
}
return ($key === false) ? false : [
'id' => $this->id[$key],
'predicate' => $this->predicate[$key],
'type' => $this->type[$key],
'params' => $this->params[$key],
'params' => $this->array_cast_recursive($this->params[$key]),
];
}
public function find_by_id($id)
{
$key = array_search($id, $this->id);
$key = array_search($id, (array) $this->id);
return ($key === false) ? false : [
'id' => $this->id[$key],
'predicate' => $this->predicate[$key],
'type' => $this->type[$key],
'params' => $this->params[$key],
'params' => $this->array_cast_recursive($this->params[$key]),
];
}
}

View File

@ -12,8 +12,11 @@ If not, see <http://www.gnu.org/licenses/>.
namespace danog\MadelineProto\TL;
class TLMethod extends TLParams
class TLMethod extends \Volatile
{
use \danog\Serializable;
use \danog\MadelineProto\Tools;
use TLParams;
public $id = [];
public $method = [];
public $type = [];
@ -23,7 +26,7 @@ class TLMethod extends TLParams
public function add($json_dict)
{
$this->id[$this->key] = (int) $json_dict['id'];
$this->id[$this->key] = $json_dict['id'];
$this->method[$this->key] = $json_dict['method'];
$this->type[$this->key] = $json_dict['type'];
$this->params[$this->key] = $json_dict['params'];
@ -38,25 +41,25 @@ class TLMethod extends TLParams
public function find_by_method($method)
{
$key = array_search($method, $this->method);
$key = array_search($method, (array) $this->method);
return ($key === false) ? false : [
'id' => $this->id[$key],
'method' => $this->method[$key],
'type' => $this->type[$key],
'params' => $this->params[$key],
'params' => $this->array_cast_recursive($this->params[$key]),
];
}
public function find_by_id($id)
{
$key = array_search($id, $this->id);
$key = array_search($id, (array) $this->id);
return ($key === false) ? false : [
'id' => $this->id[$key],
'method' => $this->method[$key],
'type' => $this->type[$key],
'params' => $this->params[$key],
'params' => $this->array_cast_recursive($this->params[$key]),
];
}
}

View File

@ -12,7 +12,7 @@ If not, see <http://www.gnu.org/licenses/>.
namespace danog\MadelineProto\TL;
class TLParams
trait TLParams
{
public function parse_params($key, $mtproto = false)
{

View File

@ -28,18 +28,12 @@ class SocketHandler extends \Threaded implements \Collectable
*/
public function run()
{
require_once __DIR__.'/../SecurityException.php';
require_once __DIR__.'/../RPCErrorException.php';
require_once __DIR__.'/../ResponseException.php';
require_once __DIR__.'/../TL/Conversion/Exception.php';
require_once __DIR__.'/../TL/Exception.php';
require_once __DIR__.'/../NothingInTheSocketException.php';
require_once __DIR__.'/../Exception.php';
$this->API->handle_messages($current);
require __DIR__.'/../../../../vendor/autoload.php';
$this->API->handle_messages($this->current);
$this->setGarbage();
}
private $garbage = false;
protected $garbage = false;
public function setGarbage():void
{

View File

@ -21,11 +21,8 @@ class SocketReader extends \Threaded implements \Collectable
public function __construct($me, $current)
{
return;
$this->API = $me;
$this->current = $current;
var_dump('OK');
}
public function __sleep()
@ -43,27 +40,23 @@ class SocketReader extends \Threaded implements \Collectable
*/
public function run()
{
var_dump('BLOCK');
while (true);
require_once __DIR__.'/../SecurityException.php';
require_once __DIR__.'/../RPCErrorException.php';
require_once __DIR__.'/../ResponseException.php';
require_once __DIR__.'/../TL/Conversion/Exception.php';
require_once __DIR__.'/../TL/Exception.php';
require_once __DIR__.'/../NothingInTheSocketException.php';
require_once __DIR__.'/../Exception.php';
require __DIR__.'/../../../../vendor/autoload.php';
$handler_pool = new \Pool($this->API->settings['threading']['handler_workers']);
$this->ready = true;
//while ($this->API->run_workers) {
//try {
//var_dump('READING');
// $this->API->recv_message($this->current);
// $handler_pool->submit(new SocketHandler($this->API, $this->current));
//} catch (\danog\MadelineProto\NothingInTheSocketException $e) { \danog\MadelineProto\Logger::log(['Nothing in the socket for dc '.$this->current], \danog\MadelineProto\Logger::VERBOSE); }
//}
while ($this->API->run_workers) {
try {
$this->API->datacenter->sockets[$this->current]->reading = true;
$this->API->recv_message($this->current);
var_dump('NOW HANDLE');
$handler_pool->submit(new SocketHandler($this->API, $this->current));
$this->API->datacenter->sockets[$this->current]->reading = false;
} catch (\danog\MadelineProto\NothingInTheSocketException $e) {
\danog\MadelineProto\Logger::log(['Nothing in the socket for dc '.$this->current], \danog\MadelineProto\Logger::VERBOSE);
}
}
while ($number = $handler_pool->collect()) {
\danog\MadelineProto\Logger::log(['Shutting down handler pool for dc '.$this->current.', '.$number.' jobs left'], \danog\MadelineProto\Logger::NOTICE);
}

View File

@ -19,11 +19,7 @@ trait Tools
{
public function random($length)
{
if ($length === 0) {
return '';
}
return \phpseclib\Crypt\Random::string($length);
return $length === 0 ? '' : \phpseclib\Crypt\Random::string($length);
}
/**
@ -33,18 +29,15 @@ trait Tools
public function posmod($a, $b)
{
$resto = $a % $b;
if ($resto < 0) {
$resto += abs($b);
}
return $resto;
return $resto < 0 ? ($resto + abs($b)) : $resto;
}
public function utf8ize($d)
{
if (is_array($d)) {
if ($this->is_array($d)) {
foreach ($d as $k => $v) {
if ($k === 'bytes') {
if ($k === 'bytes' || $this->is_array($v)) {
$d[$k] = $this->utf8ize($v);
}
}
@ -62,11 +55,14 @@ trait Tools
public function __call($method, $params)
{
return $method(...$this->array_cast_recursive($params));
return \danog\MadelineProto\Logger::$has_thread ? $method(...$this->array_cast_recursive($params)) : $method(...$params);
}
public function array_cast_recursive($array)
{
if (!\danog\MadelineProto\Logger::$has_thread) {
return $array;
}
if ($this->is_array($array)) {
if (!is_array($array)) {
$array = (array) $array;
@ -78,4 +74,79 @@ trait Tools
return $array;
}
public function unpack_signed_int($value)
{
if (strlen($value) !== 4) {
throw new TL\Exception('Length is not equal to 4');
}
return unpack('l', \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($value) : $value)[1];
}
public function unpack_signed_long($value)
{
if (strlen($value) !== 8) {
throw new TL\Exception('Length is not equal to 8');
}
return unpack('q', \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($value) : $value)[1];
}
public function pack_signed_int($value)
{
if ($value > 2147483647) {
throw new TL\Exception('Provided value '.$value.' is bigger than 2147483647');
}
if ($value < -2147483648) {
throw new TL\Exception('Provided value '.$value.' is smaller than -2147483648');
}
$res = pack('l', $value);
return \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($res) : $res;
}
public function pack_signed_long($value)
{
if ($value > 9223372036854775807) {
throw new TL\Exception('Provided value '.$value.' is bigger than 9223372036854775807');
}
if ($value < -9223372036854775808) {
throw new TL\Exception('Provided value '.$value.' is smaller than -9223372036854775808');
}
$res = \danog\MadelineProto\Logger::$bigint ? ($this->pack_signed_int($value)."\0\0\0\0") : (\danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev(pack('q', $value)) : pack('q', $value));
return $res;
}
public function pack_unsigned_int($value)
{
if ($value > 4294967295) {
throw new TL\Exception('Provided value '.$value.' is bigger than 4294967296');
}
if ($value < 0) {
throw new TL\Exception('Provided value '.$value.' is smaller than 0');
}
return pack('V', $value);
}
public function pack_signed_double($value)
{
$res = pack('d', $value);
if (strlen($res) !== 8) {
throw new TL\Exception('Could not properly encode double');
}
return \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($res) : $res;
}
public function unpack_double($value)
{
if (strlen($value) !== 8) {
throw new TL\Exception('Length is not equal to 8');
}
return unpack('d', \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($value) : $value)[1];
}
}

View File

@ -20,12 +20,12 @@ namespace danog\MadelineProto\VoIP;
*/
trait AuthKeyHandler
{
private $calls = [];
protected $calls = [];
public $REQUESTED = 0;
public $ACCEPTED = 1;
public $CONFIRMED = 2;
public $READY = 3;
private $emojis = ['๐Ÿ˜‰', '๐Ÿ˜', '๐Ÿ˜›', '๐Ÿ˜ญ', '๐Ÿ˜ฑ', '๐Ÿ˜ก', '๐Ÿ˜Ž', '๐Ÿ˜ด', '๐Ÿ˜ต', '๐Ÿ˜ˆ', '๐Ÿ˜ฌ', '๐Ÿ˜‡', '๐Ÿ˜', '๐Ÿ‘ฎ', '๐Ÿ‘ท', '๐Ÿ’‚', '๐Ÿ‘ถ', '๐Ÿ‘จ', '๐Ÿ‘ฉ', '๐Ÿ‘ด', '๐Ÿ‘ต', '๐Ÿ˜ป', '๐Ÿ˜ฝ', '๐Ÿ™€', '๐Ÿ‘บ', '๐Ÿ™ˆ', '๐Ÿ™‰', '๐Ÿ™Š', '๐Ÿ’€', '๐Ÿ‘ฝ', '๐Ÿ’ฉ', '๐Ÿ”ฅ', '๐Ÿ’ฅ', '๐Ÿ’ค', '๐Ÿ‘‚', '๐Ÿ‘€', '๐Ÿ‘ƒ', '๐Ÿ‘…', '๐Ÿ‘„', '๐Ÿ‘', '๐Ÿ‘Ž', '๐Ÿ‘Œ', '๐Ÿ‘Š', 'โœŒ', 'โœ‹', '๐Ÿ‘', '๐Ÿ‘†', '๐Ÿ‘‡', '๐Ÿ‘‰', '๐Ÿ‘ˆ', '๐Ÿ™', '๐Ÿ‘', '๐Ÿ’ช', '๐Ÿšถ', '๐Ÿƒ', '๐Ÿ’ƒ', '๐Ÿ‘ซ', '๐Ÿ‘ช', '๐Ÿ‘ฌ', '๐Ÿ‘ญ', '๐Ÿ’…', '๐ŸŽฉ', '๐Ÿ‘‘', '๐Ÿ‘’', '๐Ÿ‘Ÿ', '๐Ÿ‘ž', '๐Ÿ‘ ', '๐Ÿ‘•', '๐Ÿ‘—', '๐Ÿ‘–', '๐Ÿ‘™', '๐Ÿ‘œ', '๐Ÿ‘“', '๐ŸŽ€', '๐Ÿ’„', '๐Ÿ’›', '๐Ÿ’™', '๐Ÿ’œ', '๐Ÿ’š', '๐Ÿ’', '๐Ÿ’Ž', '๐Ÿถ', '๐Ÿบ', '๐Ÿฑ', '๐Ÿญ', '๐Ÿน', '๐Ÿฐ', '๐Ÿธ', '๐Ÿฏ', '๐Ÿจ', '๐Ÿป', '๐Ÿท', '๐Ÿฎ', '๐Ÿ—', '๐Ÿด', '๐Ÿ‘', '๐Ÿ˜', '๐Ÿผ', '๐Ÿง', '๐Ÿฅ', '๐Ÿ”', '๐Ÿ', '๐Ÿข', '๐Ÿ›', '๐Ÿ', '๐Ÿœ', '๐Ÿž', '๐ŸŒ', '๐Ÿ™', '๐Ÿš', '๐ŸŸ', '๐Ÿฌ', '๐Ÿ‹', '๐Ÿ', '๐ŸŠ', '๐Ÿซ', '๐Ÿ€', '๐ŸŒน', '๐ŸŒป', '๐Ÿ', '๐ŸŒพ', '๐Ÿ„', '๐ŸŒต', '๐ŸŒด', '๐ŸŒณ', '๐ŸŒž', '๐ŸŒš', '๐ŸŒ™', '๐ŸŒŽ', '๐ŸŒ‹', 'โšก', 'โ˜”', 'โ„', 'โ›„', '๐ŸŒ€', '๐ŸŒˆ', '๐ŸŒŠ', '๐ŸŽ“', '๐ŸŽ†', '๐ŸŽƒ', '๐Ÿ‘ป', '๐ŸŽ…', '๐ŸŽ„', '๐ŸŽ', '๐ŸŽˆ', '๐Ÿ”ฎ', '๐ŸŽฅ', '๐Ÿ“ท', '๐Ÿ’ฟ', '๐Ÿ’ป', 'โ˜Ž', '๐Ÿ“ก', '๐Ÿ“บ', '๐Ÿ“ป', '๐Ÿ”‰', '๐Ÿ””', 'โณ', 'โฐ', 'โŒš', '๐Ÿ”’', '๐Ÿ”‘', '๐Ÿ”Ž', '๐Ÿ’ก', '๐Ÿ”ฆ', '๐Ÿ”Œ', '๐Ÿ”‹', '๐Ÿšฟ', '๐Ÿšฝ', '๐Ÿ”ง', '๐Ÿ”จ', '๐Ÿšช', '๐Ÿšฌ', '๐Ÿ’ฃ', '๐Ÿ”ซ', '๐Ÿ”ช', '๐Ÿ’Š', '๐Ÿ’‰', '๐Ÿ’ฐ', '๐Ÿ’ต', '๐Ÿ’ณ', 'โœ‰', '๐Ÿ“ซ', '๐Ÿ“ฆ', '๐Ÿ“…', '๐Ÿ“', 'โœ‚', '๐Ÿ“Œ', '๐Ÿ“Ž', 'โœ’', 'โœ', '๐Ÿ“', '๐Ÿ“š', '๐Ÿ”ฌ', '๐Ÿ”ญ', '๐ŸŽจ', '๐ŸŽฌ', '๐ŸŽค', '๐ŸŽง', '๐ŸŽต', '๐ŸŽน', '๐ŸŽป', '๐ŸŽบ', '๐ŸŽธ', '๐Ÿ‘พ', '๐ŸŽฎ', '๐Ÿƒ', '๐ŸŽฒ', '๐ŸŽฏ', '๐Ÿˆ', '๐Ÿ€', 'โšฝ', 'โšพ', '๐ŸŽพ', '๐ŸŽฑ', '๐Ÿ‰', '๐ŸŽณ', '๐Ÿ', '๐Ÿ‡', '๐Ÿ†', '๐ŸŠ', '๐Ÿ„', 'โ˜•', '๐Ÿผ', '๐Ÿบ', '๐Ÿท', '๐Ÿด', '๐Ÿ•', '๐Ÿ”', '๐ŸŸ', '๐Ÿ—', '๐Ÿฑ', '๐Ÿš', '๐Ÿœ', '๐Ÿก', '๐Ÿณ', '๐Ÿž', '๐Ÿฉ', '๐Ÿฆ', '๐ŸŽ‚', '๐Ÿฐ', '๐Ÿช', '๐Ÿซ', '๐Ÿญ', '๐Ÿฏ', '๐ŸŽ', '๐Ÿ', '๐ŸŠ', '๐Ÿ‹', '๐Ÿ’', '๐Ÿ‡', '๐Ÿ‰', '๐Ÿ“', '๐Ÿ‘', '๐ŸŒ', '๐Ÿ', '๐Ÿ', '๐Ÿ†', '๐Ÿ…', '๐ŸŒฝ', '๐Ÿก', '๐Ÿฅ', '๐Ÿฆ', 'โ›ช', '๐Ÿฐ', 'โ›บ', '๐Ÿญ', '๐Ÿ—ป', '๐Ÿ—ฝ', '๐ŸŽ ', '๐ŸŽก', 'โ›ฒ', '๐ŸŽข', '๐Ÿšข', '๐Ÿšค', 'โš“', '๐Ÿš€', 'โœˆ', '๐Ÿš', '๐Ÿš‚', '๐Ÿš‹', '๐ŸšŽ', '๐ŸšŒ', '๐Ÿš™', '๐Ÿš—', '๐Ÿš•', '๐Ÿš›', '๐Ÿšจ', '๐Ÿš”', '๐Ÿš’', '๐Ÿš‘', '๐Ÿšฒ', '๐Ÿš ', '๐Ÿšœ', '๐Ÿšฆ', 'โš ', '๐Ÿšง', 'โ›ฝ', '๐ŸŽฐ', '๐Ÿ—ฟ', '๐ŸŽช', '๐ŸŽญ', '๐Ÿ‡ฏ๐Ÿ‡ต', '๐Ÿ‡ฐ๐Ÿ‡ท', '๐Ÿ‡ฉ๐Ÿ‡ช', '๐Ÿ‡จ๐Ÿ‡ณ', '๐Ÿ‡บ๐Ÿ‡ธ', '๐Ÿ‡ซ๐Ÿ‡ท', '๐Ÿ‡ช๐Ÿ‡ธ', '๐Ÿ‡ฎ๐Ÿ‡น', '๐Ÿ‡ท๐Ÿ‡บ', '๐Ÿ‡ฌ๐Ÿ‡ง', '1โƒฃ', '2โƒฃ', '3โƒฃ', '4โƒฃ', '5โƒฃ', '6โƒฃ', '7โƒฃ', '8โƒฃ', '9โƒฃ', '0โƒฃ', '๐Ÿ”Ÿ', 'โ—', 'โ“', 'โ™ฅ', 'โ™ฆ', '๐Ÿ’ฏ', '๐Ÿ”—', '๐Ÿ”ฑ', '๐Ÿ”ด', '๐Ÿ”ต', '๐Ÿ”ถ', '๐Ÿ”ท'];
protected $emojis = ['๐Ÿ˜‰', '๐Ÿ˜', '๐Ÿ˜›', '๐Ÿ˜ญ', '๐Ÿ˜ฑ', '๐Ÿ˜ก', '๐Ÿ˜Ž', '๐Ÿ˜ด', '๐Ÿ˜ต', '๐Ÿ˜ˆ', '๐Ÿ˜ฌ', '๐Ÿ˜‡', '๐Ÿ˜', '๐Ÿ‘ฎ', '๐Ÿ‘ท', '๐Ÿ’‚', '๐Ÿ‘ถ', '๐Ÿ‘จ', '๐Ÿ‘ฉ', '๐Ÿ‘ด', '๐Ÿ‘ต', '๐Ÿ˜ป', '๐Ÿ˜ฝ', '๐Ÿ™€', '๐Ÿ‘บ', '๐Ÿ™ˆ', '๐Ÿ™‰', '๐Ÿ™Š', '๐Ÿ’€', '๐Ÿ‘ฝ', '๐Ÿ’ฉ', '๐Ÿ”ฅ', '๐Ÿ’ฅ', '๐Ÿ’ค', '๐Ÿ‘‚', '๐Ÿ‘€', '๐Ÿ‘ƒ', '๐Ÿ‘…', '๐Ÿ‘„', '๐Ÿ‘', '๐Ÿ‘Ž', '๐Ÿ‘Œ', '๐Ÿ‘Š', 'โœŒ', 'โœ‹', '๐Ÿ‘', '๐Ÿ‘†', '๐Ÿ‘‡', '๐Ÿ‘‰', '๐Ÿ‘ˆ', '๐Ÿ™', '๐Ÿ‘', '๐Ÿ’ช', '๐Ÿšถ', '๐Ÿƒ', '๐Ÿ’ƒ', '๐Ÿ‘ซ', '๐Ÿ‘ช', '๐Ÿ‘ฌ', '๐Ÿ‘ญ', '๐Ÿ’…', '๐ŸŽฉ', '๐Ÿ‘‘', '๐Ÿ‘’', '๐Ÿ‘Ÿ', '๐Ÿ‘ž', '๐Ÿ‘ ', '๐Ÿ‘•', '๐Ÿ‘—', '๐Ÿ‘–', '๐Ÿ‘™', '๐Ÿ‘œ', '๐Ÿ‘“', '๐ŸŽ€', '๐Ÿ’„', '๐Ÿ’›', '๐Ÿ’™', '๐Ÿ’œ', '๐Ÿ’š', '๐Ÿ’', '๐Ÿ’Ž', '๐Ÿถ', '๐Ÿบ', '๐Ÿฑ', '๐Ÿญ', '๐Ÿน', '๐Ÿฐ', '๐Ÿธ', '๐Ÿฏ', '๐Ÿจ', '๐Ÿป', '๐Ÿท', '๐Ÿฎ', '๐Ÿ—', '๐Ÿด', '๐Ÿ‘', '๐Ÿ˜', '๐Ÿผ', '๐Ÿง', '๐Ÿฅ', '๐Ÿ”', '๐Ÿ', '๐Ÿข', '๐Ÿ›', '๐Ÿ', '๐Ÿœ', '๐Ÿž', '๐ŸŒ', '๐Ÿ™', '๐Ÿš', '๐ŸŸ', '๐Ÿฌ', '๐Ÿ‹', '๐Ÿ', '๐ŸŠ', '๐Ÿซ', '๐Ÿ€', '๐ŸŒน', '๐ŸŒป', '๐Ÿ', '๐ŸŒพ', '๐Ÿ„', '๐ŸŒต', '๐ŸŒด', '๐ŸŒณ', '๐ŸŒž', '๐ŸŒš', '๐ŸŒ™', '๐ŸŒŽ', '๐ŸŒ‹', 'โšก', 'โ˜”', 'โ„', 'โ›„', '๐ŸŒ€', '๐ŸŒˆ', '๐ŸŒŠ', '๐ŸŽ“', '๐ŸŽ†', '๐ŸŽƒ', '๐Ÿ‘ป', '๐ŸŽ…', '๐ŸŽ„', '๐ŸŽ', '๐ŸŽˆ', '๐Ÿ”ฎ', '๐ŸŽฅ', '๐Ÿ“ท', '๐Ÿ’ฟ', '๐Ÿ’ป', 'โ˜Ž', '๐Ÿ“ก', '๐Ÿ“บ', '๐Ÿ“ป', '๐Ÿ”‰', '๐Ÿ””', 'โณ', 'โฐ', 'โŒš', '๐Ÿ”’', '๐Ÿ”‘', '๐Ÿ”Ž', '๐Ÿ’ก', '๐Ÿ”ฆ', '๐Ÿ”Œ', '๐Ÿ”‹', '๐Ÿšฟ', '๐Ÿšฝ', '๐Ÿ”ง', '๐Ÿ”จ', '๐Ÿšช', '๐Ÿšฌ', '๐Ÿ’ฃ', '๐Ÿ”ซ', '๐Ÿ”ช', '๐Ÿ’Š', '๐Ÿ’‰', '๐Ÿ’ฐ', '๐Ÿ’ต', '๐Ÿ’ณ', 'โœ‰', '๐Ÿ“ซ', '๐Ÿ“ฆ', '๐Ÿ“…', '๐Ÿ“', 'โœ‚', '๐Ÿ“Œ', '๐Ÿ“Ž', 'โœ’', 'โœ', '๐Ÿ“', '๐Ÿ“š', '๐Ÿ”ฌ', '๐Ÿ”ญ', '๐ŸŽจ', '๐ŸŽฌ', '๐ŸŽค', '๐ŸŽง', '๐ŸŽต', '๐ŸŽน', '๐ŸŽป', '๐ŸŽบ', '๐ŸŽธ', '๐Ÿ‘พ', '๐ŸŽฎ', '๐Ÿƒ', '๐ŸŽฒ', '๐ŸŽฏ', '๐Ÿˆ', '๐Ÿ€', 'โšฝ', 'โšพ', '๐ŸŽพ', '๐ŸŽฑ', '๐Ÿ‰', '๐ŸŽณ', '๐Ÿ', '๐Ÿ‡', '๐Ÿ†', '๐ŸŠ', '๐Ÿ„', 'โ˜•', '๐Ÿผ', '๐Ÿบ', '๐Ÿท', '๐Ÿด', '๐Ÿ•', '๐Ÿ”', '๐ŸŸ', '๐Ÿ—', '๐Ÿฑ', '๐Ÿš', '๐Ÿœ', '๐Ÿก', '๐Ÿณ', '๐Ÿž', '๐Ÿฉ', '๐Ÿฆ', '๐ŸŽ‚', '๐Ÿฐ', '๐Ÿช', '๐Ÿซ', '๐Ÿญ', '๐Ÿฏ', '๐ŸŽ', '๐Ÿ', '๐ŸŠ', '๐Ÿ‹', '๐Ÿ’', '๐Ÿ‡', '๐Ÿ‰', '๐Ÿ“', '๐Ÿ‘', '๐ŸŒ', '๐Ÿ', '๐Ÿ', '๐Ÿ†', '๐Ÿ…', '๐ŸŒฝ', '๐Ÿก', '๐Ÿฅ', '๐Ÿฆ', 'โ›ช', '๐Ÿฐ', 'โ›บ', '๐Ÿญ', '๐Ÿ—ป', '๐Ÿ—ฝ', '๐ŸŽ ', '๐ŸŽก', 'โ›ฒ', '๐ŸŽข', '๐Ÿšข', '๐Ÿšค', 'โš“', '๐Ÿš€', 'โœˆ', '๐Ÿš', '๐Ÿš‚', '๐Ÿš‹', '๐ŸšŽ', '๐ŸšŒ', '๐Ÿš™', '๐Ÿš—', '๐Ÿš•', '๐Ÿš›', '๐Ÿšจ', '๐Ÿš”', '๐Ÿš’', '๐Ÿš‘', '๐Ÿšฒ', '๐Ÿš ', '๐Ÿšœ', '๐Ÿšฆ', 'โš ', '๐Ÿšง', 'โ›ฝ', '๐ŸŽฐ', '๐Ÿ—ฟ', '๐ŸŽช', '๐ŸŽญ', '๐Ÿ‡ฏ๐Ÿ‡ต', '๐Ÿ‡ฐ๐Ÿ‡ท', '๐Ÿ‡ฉ๐Ÿ‡ช', '๐Ÿ‡จ๐Ÿ‡ณ', '๐Ÿ‡บ๐Ÿ‡ธ', '๐Ÿ‡ซ๐Ÿ‡ท', '๐Ÿ‡ช๐Ÿ‡ธ', '๐Ÿ‡ฎ๐Ÿ‡น', '๐Ÿ‡ท๐Ÿ‡บ', '๐Ÿ‡ฌ๐Ÿ‡ง', '1โƒฃ', '2โƒฃ', '3โƒฃ', '4โƒฃ', '5โƒฃ', '6โƒฃ', '7โƒฃ', '8โƒฃ', '9โƒฃ', '0โƒฃ', '๐Ÿ”Ÿ', 'โ—', 'โ“', 'โ™ฅ', 'โ™ฆ', '๐Ÿ’ฏ', '๐Ÿ”—', '๐Ÿ”ฑ', '๐Ÿ”ด', '๐Ÿ”ต', '๐Ÿ”ถ', '๐Ÿ”ท'];
public function request_call($user)
{
@ -55,7 +55,7 @@ trait AuthKeyHandler
if ($this->settings['calls']['accept_calls'] === false) {
return false;
}
if (is_array($this->settings['calls']['accept_calls']) && !in_array($this->settings['calls']['accept_calls'])) {
if ($this->is_array($this->settings['calls']['accept_calls']) && !$this->in_array($this->settings['calls']['accept_calls'])) {
return false;
}
if ($params['protocol']['udp_p2p'] && !$this->settings['calls']['allow_p2p']) {

View File

@ -1,25 +0,0 @@
<?php
/*
Copyright 2016-2017 Daniil Gentili
(https://daniil.it)
This file is part of MadelineProto.
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with the MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto;
class VolatileSerializer
{
public function wakeup()
{
$elements = $this;
$elements['_'] = 'pony';
$class = '\\'.str_replace('Serializable', '', get_class($this));
return new $class($elements);
}
}

View File

@ -41,6 +41,7 @@ if ($MadelineProto === false) {
'phone_number' => getenv('MTPROTO_NUMBER'),
]
);
\danog\MadelineProto\Logger::log([$checkedPhone], \danog\MadelineProto\Logger::NOTICE);
$sentCode = $MadelineProto->phone_login(getenv('MTPROTO_NUMBER'));
\danog\MadelineProto\Logger::log([$sentCode], \danog\MadelineProto\Logger::NOTICE);