Fixes to dialog fetching, participant list fetching, exceptions, documentation, and lotsa other stuff

This commit is contained in:
Daniil Gentili 2017-07-26 18:38:10 +02:00
parent 4b1af4d274
commit d79ffc1349
18 changed files with 263 additions and 62 deletions

View File

@ -579,6 +579,7 @@ var_dump($authorization);
See tests/testing.php for more examples.
Use `get_pwr_chat` to get chat info, see [here for the parameters and the result](https://daniil.it/MadelineProto/get_pwr_chat.html).
You can also use `get_info` to get chat info, see [here for the parameters and the result](https://daniil.it/MadelineProto/get_info.html)You can also use `get_full_info` to get chat info, see [here for the parameters and the result](https://daniil.it/MadelineProto/get_full_info.html).
Methods that allow sending message entities (messages.sendMessage for example) also have an additional parse_mode parameter that enables or disables html/markdown parsing of the message to be sent. See the method-specific documentation for more info.

View File

@ -27,6 +27,7 @@ try {
die;
}
}
var_dump($a);
//var_dump($MadelineProto->API->get_config([], ['datacenter' => $MadelineProto->API->datacenter->curdc]));
//var_dump($MadelineProto->API->settings['connection']);
echo 'Wrote '.\danog\MadelineProto\Serialization::serialize('bot.madeline', $MadelineProto).' bytes'.PHP_EOL;

View File

@ -22,6 +22,7 @@ description: chat attributes, type and example
|restriction\_reason|[string](API_docs/types/string.md) | Optional|Restriction reason|
|status|[UserStatus](API_docs/types/UserStatus.md) | Optional|Describes last time user was online|
|bot\_inline\_placeholder|[string](API_docs/types/string.md) | Optional|Inline placeholder of inline bot|
|bot\_nochats|[Bool](API_docs/types/Bool.md) | Optional|Can the bot be added to a group|
|about|[string](API_docs/types/string.md) | Optional|Description of supergroups/channels or bios of users|
|bot\_info|[BotInfo](API_docs/types/BotInfo.md) | Optional|Bot info of bots|
|phone\_calls\_available|[Bool](API_docs/types/Bool.md) | Optional|Are phone calls available for that user?|

15
docs/FullInfo.md Normal file
View File

@ -0,0 +1,15 @@
---
title: full chat info
description: chat attributes, type and example
---
## Constructor: FullInfo
### Attributes:
| Name | Type | Required |Description|
|----------|:-------------:|:--------:|----------:|
|partial|[Info](Info.md) | Yes|Info object|
|last_update|[int](API_docs/types/int.md) | Yes|unixdate|
|full|[ChatFull](API_docs/types/ChatFull.md) or [UserFull](API_docs/types/UserFull.md) | Yes|full info|

23
docs/Info.md Normal file
View File

@ -0,0 +1,23 @@
---
title: chat info
description: chat attributes, type and example
---
## Constructor: Info
### Attributes:
| Name | Type | Required |Description|
|----------|:-------------:|:--------:|----------:|
|type|[string](API_docs/types/string.md) | Yes|bot, user, channel, supergroup or chat|
|bot\_api\_id|[long](API_docs/types/long.md) | Yes|bot API chat id|
|InputPeer|[InputPeer](API_docs/types/InputPeer.md) | Yes|InputPeer object|
|Peer|[Peer](API_docs/types/Peer.md) | Optional|Peer object|
|user\_id|[int](API_docs/types/int.md) | Optional|MTProto user id|
|chat\_id|[int](API_docs/types/int.md) | Optional|MTProto chat id|
|channel\_id|[int](API_docs/types/int.md) | Optional|MTProto channel id|
|InputUser|[InputUser](API_docs/types/InputUser.md) | Optional|InputUser object|
|InputChannel|[InputChannel](API_docs/types/InputChannel.md) | Optional|InputChannel object|

View File

@ -12,6 +12,11 @@ description: PWRTelegram participant attributes, type and example
|----------|:-------------:|:--------:|-----------:|
|user|[Chat](Chat.md) | Yes| The participant|
|inviter|[Chat](Chat.md) | Optional|The user that invited this participant|
|promoted_by|[Chat](Chat.md) | Optional|The user that promoted this participant|
|kicked_by|[Chat](Chat.md) | Optional|The user that kicked this participant|
|date|[int](API_docs/types/int.md) | Yes|When was the user invited|
|role|[string](API_docs/types/int.md) | Yes|user, admin, creator, moderator, editor, creator, kicked|
|role|[string](API_docs/types/string.md) | Yes|user, admin, creator, moderator, editor, creator, kicked|
|can_edit|[Bool](API_docs/types/Bool.md) | Optional|Can the user edit messages in the channel|
|left|[Bool](API_docs/types/Bool.md) | Optional|Has this user left|
|admin_rights|[ChannelAdminRights](API_docs/types/ChannelAdminRights.md) | Optional|Admin rights|
|banned_rights|[ChannelBannedRights](API_docs/types/ChannelBannedRights.md) | Optional|Banned rights|

42
docs/get_full_info.md Normal file
View File

@ -0,0 +1,42 @@
---
title: get_full_info
description: get_full_info parameters, return type and example
---
## Method: get_full_info
### Parameters:
| Name | Type |
|----------|:-------------:|
|id| A username, a bot API chat id, a tg-cli chat id, a [Chat](API_docs/types/Chat.md), a [User](API_docs/types/User.md), an [InputPeer](API_docs/types/InputPeer.md), an [InputUser](API_docs/types/InputUser.md), an [InputChannel](API_docs/types/InputChannel.md), a [Peer](API_docs/types/Peer.md), or a [Chat](API_docs/types/Chat.md) object|
### Return type: [FullInfo](FullInfo.md)
### Example:
```
$MadelineProto = new \danog\MadelineProto\API();
if (isset($token)) {
$this->bot_login($token);
}
if (isset($number)) {
$sentCode = $MadelineProto->phone_login($number);
echo 'Enter the code you received: ';
$code = '';
for ($x = 0; $x < $sentCode['type']['length']; $x++) {
$code .= fgetc(STDIN);
}
$MadelineProto->complete_phone_login($code);
}
$Chat = $MadelineProto->get_full_info($id);
```
Or, if you're into Lua:
```
Chat = get_full_info(id)
```

42
docs/get_info.md Normal file
View File

@ -0,0 +1,42 @@
---
title: get_info
description: get_info parameters, return type and example
---
## Method: get_info
### Parameters:
| Name | Type |
|----------|:-------------:|
|id| A username, a bot API chat id, a tg-cli chat id, a [Chat](API_docs/types/Chat.md), a [User](API_docs/types/User.md), an [InputPeer](API_docs/types/InputPeer.md), an [InputUser](API_docs/types/InputUser.md), an [InputChannel](API_docs/types/InputChannel.md), a [Peer](API_docs/types/Peer.md), or a [Chat](API_docs/types/Chat.md) object|
### Return type: [Info](Info.md)
### Example:
```
$MadelineProto = new \danog\MadelineProto\API();
if (isset($token)) {
$this->bot_login($token);
}
if (isset($number)) {
$sentCode = $MadelineProto->phone_login($number);
echo 'Enter the code you received: ';
$code = '';
for ($x = 0; $x < $sentCode['type']['length']; $x++) {
$code .= fgetc(STDIN);
}
$MadelineProto->complete_phone_login($code);
}
$Chat = $MadelineProto->get_info($id);
```
Or, if you're into Lua:
```
Chat = get_info(id)
```

View File

@ -583,6 +583,7 @@ var_dump($authorization);
See tests/testing.php for more examples.
Use `get_pwr_chat` to get chat info, see [here for the parameters and the result](https://daniil.it/MadelineProto/get_pwr_chat.html).
You can also use `get_info` to get chat info, see [here for the parameters and the result](https://daniil.it/MadelineProto/get_info.html)You can also use `get_full_info` to get chat info, see [here for the parameters and the result](https://daniil.it/MadelineProto/get_full_info.html).
Methods that allow sending message entities (messages.sendMessage for example) also have an additional parse_mode parameter that enables or disables html/markdown parsing of the message to be sent. See the method-specific documentation for more info.

View File

@ -36,16 +36,16 @@ class Exception extends \Exception
}
parent::__construct($message, $code, $previous);
/* if (\danog\MadelineProto\Logger::$constructed) {
if (\danog\MadelineProto\Logger::$constructed) {
\danog\MadelineProto\Logger::log([$message.' in '.basename($this->file).':'.$this->line], \danog\MadelineProto\Logger::FATAL_ERROR);
}*/
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', 'The php-libtgvoip extension is required to accept and manage calls. See daniil.it/MadelineProto for more info.'])) {
}
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', 'The php-libtgvoip extension is required to accept and manage calls. See daniil.it/MadelineProto for more info.', 'File does not exist'])) {
return;
}
if (strpos($message, 'socket_write') !== false || strpos($message, 'socket_read') !== false || strpos($message, 'Received request to switch to DC ') !== false || strpos($message, "Couldn't get response") !== false || strpos($message, 'Re-executing query...') !== false || strpos($message, "Couldn't find peer by provided") !== false || strpos($message, 'id.pwrtelegram.xyz') !== false || strpos($message, 'Please update ') !== false || strpos($message, 'posix_isatty') !== false) {
return;
}
// \Rollbar\Rollbar::log(\Rollbar\Payload\Level::error(), $this, debug_backtrace(0));
\Rollbar\Rollbar::log(\Rollbar\Payload\Level::error(), $this, debug_backtrace(0));
}
/**

View File

@ -81,8 +81,15 @@ class Lua
$cb($result, $cb_extra);
}
array_walk_recursive($result, function (&$value, $key) {
if ($value instanceof \danog\MadelineProto\TL\Types\Button) {
$value = $value->jsonSerialize();
if (is_object($value)) {
$newval = [];
foreach (get_class_methods($value) as $key => $name) {
$newval[$key] = [$value, $name];
}
foreach ($value as $key => $name) {
$newval[$key] = $name;
}
$value = $newval;
}
});

View File

@ -44,7 +44,7 @@ class MTProto extends \Volatile
use \danog\MadelineProto\Wrappers\DialogHandler;
use \danog\MadelineProto\Wrappers\Login;
const V = 61;
const V = 63;
const NOT_LOGGED_IN = 0;
const WAITING_CODE = 1;
@ -315,7 +315,7 @@ class MTProto extends \Volatile
$this->updates_state['sync_loading'] = isset($trace['args'][1]) && $trace['args'][1];
}
}
$force = false;
$this->reset_session();
if (!isset($this->v) || $this->v !== self::V) {
\danog\MadelineProto\Logger::log(['Serialization is out of date, reconstructing object!'], Logger::WARNING);
@ -347,6 +347,7 @@ class MTProto extends \Volatile
$this->reset_session(true, true);
$this->config = ['expires' => -1];
$this->__construct($settings);
$force = true;
}
$this->setup_threads();
$this->datacenter->__construct($this->settings['connection'], $this->settings['connection_settings']);
@ -355,7 +356,7 @@ class MTProto extends \Volatile
$this->get_cdn_config($this->datacenter->curdc);
}
if ($this->authorized === self::LOGGED_IN && !$this->authorization['user']['bot']) {
$this->get_dialogs();
$this->get_dialogs($force);
}
if ($this->authorized === self::LOGGED_IN && $this->settings['updates']['handle_updates'] && !$this->updates_state['sync_loading']) {
\danog\MadelineProto\Logger::log(['Getting updates after deserialization...'], Logger::NOTICE);

View File

@ -287,10 +287,15 @@ trait Files
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]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->rpc === 'OFFSET_INVALID') {
switch ($e->rpc) {
case 'OFFSET_INVALID':
\Rollbar\Rollbar::log(\Rollbar\Payload\Level::error(), $e->rpc, ['info' => $message_media, 'offset' => $offset]);
break;
} else {
case 'FILE_TOKEN_INVALID':
$cdn = false;
continue 2;
default:
throw $e;
}
}
@ -306,7 +311,20 @@ trait Files
}
if ($res['_'] === 'upload.cdnFileReuploadNeeded') {
\danog\MadelineProto\Logger::log(['File is not stored on CDN, requesting reupload!'], \danog\MadelineProto\Logger::NOTICE);
$this->add_cdn_hashes($message_media['file_token'], $this->method_call('upload.reuploadCdnFile', ['file_token' => $message_media['file_token'], 'request_token' => $res['request_token']], ['heavy' => true, 'datacenter' => $old_dc]));
try {
$this->add_cdn_hashes($message_media['file_token'], $this->method_call('upload.reuploadCdnFile', ['file_token' => $message_media['file_token'], 'request_token' => $res['request_token']], ['heavy' => true, 'datacenter' => $old_dc]));
} catch (\danog\MadelineProto\RPCErrorException $e) {
switch ($e->rpc) {
case 'FILE_TOKEN_INVALID':
case 'REQUEST_TOKEN_INVALID':
$cdn = false;
continue 2;
default:
throw $e;
}
}
continue;
}
if ($cdn === false && $res['type']['_'] === 'storage.fileUnknown' && $res['bytes'] === '') {

View File

@ -326,7 +326,7 @@ trait PeerHandler
switch ($full['type']) {
case 'user':
case 'bot':
foreach (['first_name', 'last_name', 'username', 'verified', 'restricted', 'restriction_reason', 'status', 'bot_inline_placeholder', 'access_hash', 'phone', 'lang_code'] as $key) {
foreach (['first_name', 'last_name', 'username', 'verified', 'restricted', 'restriction_reason', 'status', 'bot_inline_placeholder', 'access_hash', 'phone', 'lang_code', 'bot_nochats'] as $key) {
if (isset($full['User'][$key])) {
$res[$key] = $full['User'][$key];
}
@ -412,9 +412,27 @@ trait PeerHandler
if (isset($participant['inviter_id'])) {
$newres['inviter'] = $this->get_pwr_chat($participant['inviter_id'], false, false);
}
if (isset($participant['promoted_by'])) {
$newres['promoted_by'] = $this->get_pwr_chat($participant['promoted_by'], false, false);
}
if (isset($participant['kicked_by'])) {
$newres['kicked_by'] = $this->get_pwr_chat($participant['kicked_by'], false, false);
}
if (isset($participant['date'])) {
$newres['date'] = $participant['date'];
}
if (isset($participant['admin_rights'])) {
$newres['admin_rights'] = $participant['admin_rights'];
}
if (isset($participant['banned_rights'])) {
$newres['banned_rights'] = $participant['banned_rights'];
}
if (isset($participant['can_edit'])) {
$newres['can_edit'] = $participant['can_edit'];
}
if (isset($participant['left'])) {
$newres['left'] = $participant['left'];
}
switch ($participant['_']) {
case 'chatParticipant':
$newres['role'] = 'user';
@ -435,46 +453,64 @@ trait PeerHandler
$res['participants'] = [];
$limit = 200;
$offset = -$limit;
$filters = ['channelParticipantsBanned', 'channelParticipantsAdmins', 'channelParticipantsKicked', 'channelParticipantsBots', 'channelParticipantsRecent'];
$gres = $this->method_call('channels.getParticipants', ['channel' => $full['InputChannel'], 'filter' => ['_' => 'channelParticipantsRecent'], 'offset' => $offset += $limit, 'limit' => $limit], ['datacenter' => $this->datacenter->curdc]);
$count = $gres['count'];
$key = -1;
while ($offset <= $count) {
foreach ($gres['participants'] as $participant) {
$newres = [];
$newres['user'] = $this->get_pwr_chat($participant['user_id'], false, false);
$key++;
if (isset($participant['inviter_id'])) {
$newres['inviter'] = $this->get_pwr_chat($participant['inviter_id'], false, false);
while (count($filters)) {
$filter = array_pop($filters);
while ($offset <= $count) {
foreach ($gres['participants'] as $participant) {
$newres = [];
$newres['user'] = $this->get_pwr_chat($participant['user_id'], false, false);
if (isset($participant['inviter_id'])) {
$newres['inviter'] = $this->get_pwr_chat($participant['inviter_id'], false, false);
}
if (isset($participant['kicked_by'])) {
$newres['kicked_by'] = $this->get_pwr_chat($participant['kicked_by'], false, false);
}
if (isset($participant['promoted_by'])) {
$newres['promoted_by'] = $this->get_pwr_chat($participant['promoted_by'], false, false);
}
if (isset($participant['date'])) {
$newres['date'] = $participant['date'];
}
switch ($participant['_']) {
case 'channelParticipantSelf':
$newres['role'] = 'user';
if (isset($newres['admin_rights'])) {
$newres['admin_rights'] = $full['Chat']['admin_rights'];
}
if (isset($newres['banned_rights'])) {
$newres['banned_rights'] = $full['Chat']['banned_rights'];
}
break;
case 'channelParticipant':
$newres['role'] = 'user';
break;
case 'channelParticipantCreator':
$newres['role'] = 'creator';
break;
case 'channelParticipantAdmin':
$newres['role'] = 'admin';
break;
case 'channelParticipantBanned':
$newres['role'] = 'banned';
break;
}
$res['participants'][$participant['user_id']] = $newres;
}
if (isset($participant['date'])) {
$newres['date'] = $participant['date'];
$gres = $this->method_call('channels.getParticipants', ['channel' => $full['InputChannel'], 'filter' => ['_' => $filter, 'q' => ''], 'offset' => $offset += $limit, 'limit' => $limit], ['datacenter' => $this->datacenter->curdc]);
if (empty($gres['participants'])) {
break;
}
switch ($participant['_']) {
case 'channelParticipant':
$newres['role'] = 'user';
break;
case 'channelParticipantModerator':
$newres['role'] = 'moderator';
break;
case 'channelParticipantEditor':
$newres['role'] = 'editor';
break;
case 'channelParticipantCreator':
$newres['role'] = 'creator';
break;
case 'channelParticipantKicked':
$newres['role'] = 'kicked';
break;
}
$res['participants'][$key] = $newres;
}
$gres = $this->method_call('channels.getParticipants', ['channel' => $full['InputChannel'], 'filter' => ['_' => 'channelParticipantsRecent'], 'offset' => $offset += $limit, 'limit' => $limit], ['datacenter' => $this->datacenter->curdc]);
if (empty($gres['participants'])) {
if ($offset >= $count) {
break;
}
}

View File

@ -408,7 +408,10 @@ trait BotAPI
$text = $this->html_entity_decode($node->textContent);
$href = $node->getAttribute('href');
if (preg_match('|mention:|', $href)) {
$entities[] = ['_' => 'inputMessageEntityMentionName', 'offset' => mb_strlen($nmessage), 'length' => mb_strlen($text), 'user_id' => $this->get_info(str_replace('mention:', '', $href))['InputUser']];
if (!isset(($mention = $this->get_info(str_replace('mention:', '', $href)))['InputUser'])) {
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
}
$entities[] = ['_' => 'inputMessageEntityMentionName', 'offset' => mb_strlen($nmessage), 'length' => mb_strlen($text), 'user_id' => $mention['InputUser']];
} elseif (preg_match('|buttonurl:|', $href)) {
if (!isset($entities['buttons'])) {
$entities['buttons'] = [];

View File

@ -27,17 +27,17 @@ trait PrettyException
foreach (array_reverse($this->getTrace()) as $k => $frame) {
if (isset($frame['function']) && in_array($frame['function'], ['serialize_params', 'serialize_object'])) {
if ($frame['args'][2] !== '') {
$this->tl_trace .= $tl ? "['".$frame['args'][2]."']" : "While serializing: \t".$frame['args'][2];
$this->tl_trace .= $tl ? "['".$frame['args'][2]."']" : "While serializing: \t".$frame['args'][2];
$tl = true;
}
} else {
if ($tl) {
$this->tl_trace .= PHP_EOL;
}
if (isset($frame['function']) && $frame['function'] === 'handle_rpc_error' && $k === count($this->getTrace()) - 1) {
if (isset($frame['function']) && ($frame['function'] === 'handle_rpc_error' && $k === count($this->getTrace()) - 1) || $frame['function'] === 'unserialize') {
continue;
}
$this->tl_trace .= isset($frame['file']) ? str_pad(basename($frame['file']).'('.$frame['line'].'):', 16)."\t" : '';
$this->tl_trace .= isset($frame['file']) ? str_pad(basename($frame['file']).'('.$frame['line'].'):', 20)."\t" : '';
$this->tl_trace .= isset($frame['function']) ? $frame['function'].'(' : '';
$this->tl_trace .= isset($frame['args']) ? substr(json_encode($frame['args']), 1, -1) : '';
$this->tl_trace .= ')';

View File

@ -73,7 +73,14 @@ trait AuthKeyHandler
$b = \phpseclib\Math\BigInteger::randomRange($this->two, $dh_config['p']->subtract($this->two));
$g_b = $dh_config['g']->powMod($b, $dh_config['p']);
$this->check_G($g_b, $dh_config['p']);
$res = $this->method_call('phone.acceptCall', ['peer' => $call, 'g_b' => $g_b->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'udp_p2p' => true, 'min_layer' => 65, 'max_layer' => 65]], ['datacenter' => $this->datacenter->curdc]);
try {
$res = $this->method_call('phone.acceptCall', ['peer' => $call, 'g_b' => $g_b->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'udp_p2p' => true, 'min_layer' => 65, 'max_layer' => 65]], ['datacenter' => $this->datacenter->curdc]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->RPC === 'CALL_ALREADY_ACCEPTED') {
return true;
}
throw $e;
}
$this->calls[$res['phone_call']['id']]->storage['b'] = $b;
$this->handle_pending_updates();

View File

@ -16,22 +16,20 @@ trait DialogHandler
{
public function get_dialogs($force = false)
{
if (!isset($this->dialog_params['offset_date']) || $force) {
$this->dialog_params = ['limit' => 0, 'offset_date' => 0, 'offset_id' => 0, 'offset_peer' => ['_' => 'inputPeerEmpty']];
if (!isset($this->dialog_params['offset_date']) || $force || is_null($this->dialog_params['offset_date'])) {
$this->dialog_params = ['limit' => 0, 'offset_date' => 0, 'offset_id' => 0, 'offset_peer' => ['_' => 'inputPeerEmpty'], 'count' => 0];
}
$this->updates_state['sync_loading'] = true;
$res = ['dialogs' => [0], 'count' => 1];
$datacenter = $this->datacenter->curdc;
$count = 0;
while ($count < $res['count']) {
while ($this->dialog_params['count'] < $res['count']) {
\danog\MadelineProto\Logger::log(['Getting dialogs...']);
$res = $this->method_call('messages.getDialogs', $this->dialog_params, ['datacenter' => $datacenter, 'FloodWaitLimit' => 100]);
$count += count($res['dialogs']);
$old_params = $this->dialog_params;
$this->dialog_params['count'] += count($res['dialogs']);
$this->dialog_params['offset_date'] = end($res['messages'])['date'];
$this->dialog_params['offset_peer'] = end($res['dialogs'])['peer'];
$this->dialog_params['offset_id'] = end($res['messages'])['id'];
if ($this->dialog_params === $old_params) {
if (!isset($res['count'])) {
break;
}
}