From 3b861dcd3417e6d2ba34853824985926f0ff7fe2 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 14 Jan 2017 12:53:50 +0100 Subject: [PATCH] Bugfixes --- bot.php | 1 + src/danog/MadelineProto/MTProto.php | 10 +- .../MTProtoTools/PeerHandler.php | 244 +++++++++++++++--- .../MTProtoTools/UpdateHandler.php | 12 +- src/danog/MadelineProto/TL/TL.php | 2 +- .../MadelineProto/Wrappers/FilesHandler.php | 23 +- .../MadelineProto/Wrappers/PeerHandler.php | 10 + 7 files changed, 251 insertions(+), 51 deletions(-) diff --git a/bot.php b/bot.php index 19c41fd1..c94c008a 100755 --- a/bot.php +++ b/bot.php @@ -3,6 +3,7 @@ require 'vendor/autoload.php'; $settings = []; + $MadelineProto = \danog\MadelineProto\Serialization::deserialize('bot.madeline'); if (file_exists('token.php') && $MadelineProto === false) { diff --git a/src/danog/MadelineProto/MTProto.php b/src/danog/MadelineProto/MTProto.php index c1af692a..584b4082 100644 --- a/src/danog/MadelineProto/MTProto.php +++ b/src/danog/MadelineProto/MTProto.php @@ -162,8 +162,8 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB ], ], 'app_info' => [ // obtained in https://my.telegram.org - 'api_id' => 25628, - 'api_hash' => '1fe17cda7d355166cdaa71f04122873c', + 'api_id' => 65536, + 'api_hash' => '4251a2777e179232705e2462706f4143', 'device_model' => $device_model, 'system_version' => $system_version, 'app_version' => 'Unicorn', // 🌚 @@ -201,6 +201,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB 'handle_updates' => true, // Should I handle updates? 'callback' => [$this, 'get_updates_update_handler'], // A callable function that will be called every time an update is received, must accept an array (for the update) as the only parameter ], + 'pwr' => ['pwr' => false, 'db_token' => false, 'strict' => false] ]; foreach ($default_settings as $key => $param) { if (!isset($settings[$key])) { @@ -247,18 +248,20 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB // Switches to a new datacenter and if necessary creates authorization keys, binds them and writes client info public function switch_dc($new_dc, $allow_nearest_dc_switch = false) { - \danog\MadelineProto\Logger::log('Switching to DC '.$new_dc.'...'); $old_dc = $this->datacenter->curdc; + \danog\MadelineProto\Logger::log('Switching from DC '.$old_dc.' to DC '.$new_dc.'...'); if (!isset($this->datacenter->sockets[$new_dc])) { $this->datacenter->dc_connect($new_dc); $this->init_authorization(); $this->get_config($this->write_client_info('help.getConfig')); $this->get_nearest_dc($allow_nearest_dc_switch); } + $this->datacenter->curdc = $new_dc; if ( (isset($this->datacenter->sockets[$old_dc]->authorized) && $this->datacenter->sockets[$old_dc]->authorized) && !(isset($this->datacenter->sockets[$new_dc]->authorized) && $this->datacenter->sockets[$new_dc]->authorized && $this->datacenter->sockets[$new_dc]->authorization['user']['id'] === $this->datacenter->sockets[$old_dc]->authorization['user']['id']) ) { + \danog\MadelineProto\Logger::log('Copying authorization...'); $this->should_serialize = true; $this->datacenter->curdc = $old_dc; $exported_authorization = $this->method_call('auth.exportAuthorization', ['dc_id' => $new_dc]); @@ -269,6 +272,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB $this->datacenter->authorization = $this->method_call('auth.importAuthorization', $exported_authorization); $this->datacenter->authorized = true; } + \danog\MadelineProto\Logger::log('Done! Current DC is '.$this->datacenter->curdc); } // Creates authorization keys diff --git a/src/danog/MadelineProto/MTProtoTools/PeerHandler.php b/src/danog/MadelineProto/MTProtoTools/PeerHandler.php index 03e59c43..3faeebee 100644 --- a/src/danog/MadelineProto/MTProtoTools/PeerHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/PeerHandler.php @@ -18,16 +18,18 @@ namespace danog\MadelineProto\MTProtoTools; trait PeerHandler { public $chats = []; + public $last_stored = 0; + public $qres = []; public function add_users($users) { foreach ($users as $key => $user) { switch ($user['_']) { case 'user': - if (!isset($this->chats[$user['id']]) || $this->chats[$user['id']]['user'] !== $user) { - //$this->method_call('users.getFullUser', ['id' => $user]); - $this->chats[$user['id']] = ['_' => 'userFull', 'user' => $user]; + if (!isset($this->chats[$user['id']]) || $this->chats[$user['id']] !== $user) { + $this->chats[$user['id']] = $user; $this->should_serialize = true; + $this->get_pwr_chat($user['id'], false, true); } case 'userEmpty': break; @@ -45,20 +47,20 @@ trait PeerHandler case 'chat': case 'chatEmpty': case 'chatForbidden': - if (!isset($this->chats[-$chat['id']]) || $this->chats[-$chat['id']]['chat'] !== $chat) { - //$this->method_call('messages.getFullChat', ['chat_id' => $chat['id']]); - $this->chats[-$chat['id']] = ['_' => 'chatFull', 'chat' => $chat]; + if (!isset($this->chats[-$chat['id']]) || $this->chats[-$chat['id']] !== $chat) { + $this->chats[-$chat['id']] = $chat; $this->should_serialize = true; + $this->get_pwr_chat(-$chat['id'], true, true); } case 'channelEmpty': break; case 'channel': case 'channelForbidden': - if (!isset($this->chats[(int) ('-100'.$chat['id'])]) || $this->chats[(int) ('-100'.$chat['id'])]['channel'] !== $chat) { - $this->chats[(int) ('-100'.$chat['id'])] = ['_' => 'channelFull', 'channel' => $chat]; + if (!isset($this->chats[(int) ('-100'.$chat['id'])]) || $this->chats[(int) ('-100'.$chat['id'])] !== $chat) { + $this->chats[(int) ('-100'.$chat['id'])] = $chat; $this->should_serialize = true; - //$this->method_call('channels.getFullChannel', ['channel' => $chat]); + $this->get_pwr_chat('-100'.$chat['id'], true, true); } break; default: @@ -134,7 +136,7 @@ trait PeerHandler $id = -$id['id']; break; case 'chatFull': - $id = -$id['chat']['id']; + $id = -$id['id']; break; case 'inputPeerChat': case 'peerChat': @@ -145,7 +147,7 @@ trait PeerHandler $id = '-100'.$id['id']; break; case 'channelFull': - $id = '-100'.$id['channel']['id']; + $id = '-100'.$id['id']; break; case 'inputPeerChannel': @@ -174,14 +176,18 @@ trait PeerHandler if (isset($this->chats[$id])) { return $this->gen_all($this->chats[$id]); } + if ($id < 0 && !preg_match('/^-100/', $id)) { + $this->method_call('messages.getFullChat', ['chat_id' => $id]); + if (isset($this->chats[$id])) { + return $this->gen_all($this->chats[$id]); + } + } throw new \danog\MadelineProto\Exception("Couldn't find peer by provided chat id ".$id); } $id = str_replace('@', '', $id); foreach ($this->chats as $chat) { - foreach (['user', 'chat', 'channel'] as $wut) { - if (isset($chat[$wut]['username']) && strtolower($chat[$wut]['username']) == strtolower($id)) { - return $this->gen_all($chat); - } + if (isset($chat['username']) && strtolower($chat['username']) == strtolower($id)) { + return $this->gen_all($chat); } } if ($recursive) { @@ -196,35 +202,35 @@ trait PeerHandler { $res = [$this->constructors->find_by_predicate($constructor['_'])['type'] => $constructor]; switch ($constructor['_']) { - case 'userFull': - $res['User'] = $constructor['user']; - if ($constructor['user']['self']) { + case 'user': + if ($constructor['self']) { $res['InputPeer'] = ['_' => 'inputPeerSelf']; $res['InputUser'] = ['_' => 'inputUserSelf']; - } elseif (isset($constructor['user']['access_hash'])) { - $res['InputPeer'] = ['_' => 'inputPeerUser', 'user_id' => $constructor['user']['id'], 'access_hash' => $constructor['user']['access_hash']]; - $res['InputUser'] = ['_' => 'inputUser', 'user_id' => $constructor['user']['id'], 'access_hash' => $constructor['user']['access_hash']]; + } elseif (isset($constructor['access_hash'])) { + $res['InputPeer'] = ['_' => 'inputPeerUser', 'user_id' => $constructor['id'], 'access_hash' => $constructor['access_hash']]; + $res['InputUser'] = ['_' => 'inputUser', 'user_id' => $constructor['id'], 'access_hash' => $constructor['access_hash']]; } - $res['Peer'] = ['_' => 'peerUser', 'user_id' => $constructor['user']['id']]; - $res['user_id'] = $constructor['user']['id']; - $res['bot_api_id'] = $constructor['user']['id']; + $res['Peer'] = ['_' => 'peerUser', 'user_id' => $constructor['id']]; + $res['user_id'] = $constructor['id']; + $res['bot_api_id'] = $constructor['id']; + $res['type'] = $constructor['bot'] ? 'bot' : 'user'; break; - case 'chatFull': - $res['Chat'] = $constructor['chat']; - $res['InputPeer'] = ['_' => 'inputPeerChat', 'chat_id' => $constructor['chat']['id']]; - $res['Peer'] = ['_' => 'peerChat', 'chat_id' => $constructor['chat']['id']]; - $res['chat_id'] = $constructor['chat']['id']; - $res['bot_api_id'] = -$constructor['chat']['id']; + case 'chat': + $res['InputPeer'] = ['_' => 'inputPeerChat', 'chat_id' => $constructor['id']]; + $res['Peer'] = ['_' => 'peerChat', 'chat_id' => $constructor['id']]; + $res['chat_id'] = $constructor['id']; + $res['bot_api_id'] = -$constructor['id']; + $res['type'] = 'chat'; break; - case 'channelFull': - $res['Channel'] = $constructor['channel']; - if (isset($constructor['channel']['access_hash'])) { - $res['InputPeer'] = ['_' => 'inputPeerChannel', 'channel_id' => $constructor['channel']['id'], 'access_hash' => $constructor['channel']['access_hash']]; - $res['InputChannel'] = ['_' => 'inputChannel', 'channel_id' => $constructor['channel']['id'], 'access_hash' => $constructor['channel']['access_hash']]; + case 'channel': + if (isset($constructor['access_hash'])) { + $res['InputPeer'] = ['_' => 'inputPeerChannel', 'channel_id' => $constructor['id'], 'access_hash' => $constructor['access_hash']]; + $res['InputChannel'] = ['_' => 'inputChannel', 'channel_id' => $constructor['id'], 'access_hash' => $constructor['access_hash']]; } - $res['Peer'] = ['_' => 'peerChannel', 'channel_id' => $constructor['channel']['id']]; - $res['channel_id'] = $constructor['channel']['id']; - $res['bot_api_id'] = (int) ('-100'.$constructor['channel']['id']); + $res['Peer'] = ['_' => 'peerChannel', 'channel_id' => $constructor['id']]; + $res['channel_id'] = $constructor['id']; + $res['bot_api_id'] = (int) ('-100'.$constructor['id']); + $res['type'] = $constructor['megagroup'] ? 'supergroup' : 'channel'; break; default: throw new \danog\MadelineProto\Exception('Invalid constructor given '.var_export($constructor, true)); @@ -233,6 +239,168 @@ trait PeerHandler return $res; } + public function get_full_info($id) { + $partial = $this->get_info($id); + switch ($partial['type']) { + case 'user': + case 'bot': + $full = $this->method_call('users.getFullUser', ['id' => $partial['InputUser']]); + break; + + case 'chat': + $full = $this->method_call('messages.getFullChat', $partial)['full_chat']; + break; + + case 'channel': + case 'supergroup': + $full = $this->method_call('channels.getFullChannel', ['channel' => $partial['InputChannel']])['full_chat']; + break; + } + $partial = $this->get_info($id); + $partial['full'] = $full; + return $partial; + } + + public function get_pwr_chat($id, $fullfetch = true, $send = true) { + $full = $fullfetch ? $this->get_full_info($id) : $this->get_info($id); + $res = ['id' => $full['bot_api_id'], 'type' => $full['type']]; + switch ($full['type']) { + case 'user': + case 'bot': + foreach (['first_name', 'last_name', 'username', 'verified', 'restricted', 'restriction_reason', 'status', 'bot_inline_placeholder', 'access_hash', 'phone'] as $key) { + if (isset($full['User'][$key])) $res[$key] = $full['User'][$key]; + } + if (isset($full['full']['about'])) $res['about'] = $full['full']['about']; + if (isset($full['full']['bot_info'])) $res['bot_info'] = $full['full']['bot_info']; + if (isset($full['full']['profile_photo']['sizes'])) $res['photo'] = end($full['full']['profile_photo']['sizes']); + $bio = ''; + if ($full['type'] == 'user' && isset($res['username']) && !isset($res['about']) && $fullfetch) { + if (preg_match('/meta property="og:description" content=".+/', file_get_contents('https://telegram.me/'.$res['username']), $biores)) { + $bio = html_entity_decode(preg_replace_callback('/(&#[0-9]+;)/', function ($m) { + return mb_convert_encoding($m[1], 'UTF-8', 'HTML-ENTITIES'); + }, str_replace(['meta property="og:description" content="', '">'], '', $biores[0]))); + } + if ($bio != '' && $bio != 'You can contact @'.$res['username'].' right away.') { + $res['about'] = $bio; + } + } + break; + case 'chat': + foreach (['title', 'participants_count', 'admin', 'admins_enabled'] as $key) { + if (isset($full['Chat'][$key])) $res[$key] = $full['Chat'][$key]; + } + + if (isset($full['full']['chat_photo']['sizes'])) $res['photo'] = end($full['full']['chat_photo']['sizes']); + if (isset($full['full']['exported_invite']['link'])) $res['invite'] = $full['full']['exported_invite']['link']; + if (isset($full['full']['participants']['participants'])) $res['participants'] = $full['full']['participants']['participants']; + break; + case 'channel': + case 'supergroup': + foreach (['title', 'democracy', 'restricted', 'restriction_reason', 'access_hash', 'username', 'signatures'] as $key) { + if (isset($full['Chat'][$key])) $res[$key] = $full['Chat'][$key]; + } + foreach (['can_view_participants', 'can_set_username', 'participants_count', 'admins_count', 'kicked_count', 'migrated_from_chat_id', 'migrated_from_max_id', 'pinned_msg_id', 'about'] as $key) { + if (isset($full['full'][$key])) $res[$key] = $full['full'][$key]; + } + + if (isset($full['full']['chat_photo']['sizes'])) $res['photo'] = end($full['full']['chat_photo']['sizes']); + if (isset($full['full']['exported_invite']['link'])) $res['invite'] = $full['full']['exported_invite']['link']; + if (isset($full['full']['participants']['participants'])) $res['participants'] = $full['full']['participants']['participants']; + break; + } + if (isset($res['participants'])) { + foreach ($res['participants'] as $key => $participant) { + $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['date'])) $newres['date'] = $participant['date']; + switch ($participant['_']) { + case 'chatParticipant': + $newres['role'] = 'user'; + break; + + case 'chatParticipantAdmin': + $newres['role'] = 'admin'; + break; + + case 'chatParticipantCreator': + $newres['role'] = 'creator'; + break; + } + $res['participants'][$key] = $newres; + } + } + if (!isset($res['participants']) && isset($res['can_view_participants']) && $res['can_view_participants']) { + $res['participants'] = []; + $limit = 200; + $offset = -$limit; + $gres = $this->method_call('channels.getParticipants', ['channel' => $full['InputChannel'], 'filter' => ['_' => 'channelParticipantsRecent'], 'offset' => $offset += $limit, 'limit' => 200]); + $count = $gres['count']; + $key = 0; + while ($offset <= $count) { + foreach ($gres['participants'] as $participant) { + $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); + if (isset($participant['date'])) $newres['date'] = $participant['date']; + switch ($participant['_']) { + case 'channelParticipant': + $newres['role'] = 'user'; + break; + + case 'channelParticipantModerator': + $newres['role'] = 'moderator'; + break; + + case 'channelParticipantEditor': + $newres['role'] = 'moderator'; + 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' => 200]); + } + } + if ($fullfetch || $send) { + $this->store_db($res); + } + + return $res; + } + + public function store_db($res, $force = false) { + if (!isset($this->settings['pwr']) || $this->settings['pwr']['pwr'] === false) { + try { + if (isset($res['username'])) shell_exec('curl '.escapeshellarg('https://api.pwrtelegram.xyz/getchat?chat_id=@'.$res['username']).' -s -o /dev/null >/dev/null 2>/dev/null & '); + } catch (\danog\MadelineProto\Exception $e) { \danog\MadelineProto\Logger::log($e->getMessage()); }; + return; + } + if (!empty($res)) { + if (isset($res['participants'])) unset($res['participants']); + $this->qres []= $res; + } + if ($this->last_stored < time() && !$force) { + return false; + } + if (empty($this->qres)) return false; + try { + $payload = json_encode($this->qres); + $path = '/tmp/ids'.hash('sha256', $payload); + file_put_contents($path, $payload); + $result = shell_exec('curl '.escapeshellarg('https://id.pwrtelegram.xyz/db'.$this->settings['pwr']['db_token'].'/addnewmadeline?d=pls').' -d '.escapeshellarg('@'.$path).' -s -o '.escapeshellarg($path.'.log').' >/dev/null 2>/dev/null & '); + \danog\MadelineProto\Logger::log($result); + } catch (\danog\MadelineProto\Exception $e) { \danog\MadelineProto\Logger::log($e->getMessage()); }; + $this->qres = []; + $this->last_stored = time() + 5; + } public function resolve_username($username) { diff --git a/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php b/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php index 26812764..80bb5a86 100644 --- a/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php @@ -23,6 +23,16 @@ trait UpdateHandler public $updates_key = 0; private $getting_state = false; + public function pwr_update_handler($update) { + if (isset($update['message']['to_id'])) { + $full_chat = $this->get_pwr_chat($update['message']['to_id']); + } + if (isset($update['message']['from_id'])) { + $full_chat = $this->get_pwr_chat($update['message']['from_id']); + } + } + + public function get_updates_update_handler($update) { if (!$this->settings['updates']['handle_updates']) { @@ -421,6 +431,6 @@ trait UpdateHandler $update['message']['out'] = true; } \danog\MadelineProto\Logger::log('Saving an update of type '.$update['_'].'...'); - $this->settings['updates']['callback']($update); + if ($this->settings['pwr']['strict']) $this->pwr_update_handler($update); else $this->settings['updates']['callback']($update); } } diff --git a/src/danog/MadelineProto/TL/TL.php b/src/danog/MadelineProto/TL/TL.php index c46fd6aa..0dc53f17 100644 --- a/src/danog/MadelineProto/TL/TL.php +++ b/src/danog/MadelineProto/TL/TL.php @@ -388,7 +388,7 @@ trait TL switch ($arg['type']) { case 'true': case 'false': - $x[$arg['name']] = ($x['flags'] & $arg['pow']) == 1; + $x[$arg['name']] = ($x['flags'] & $arg['pow']) !== 0; continue 2; break; case 'Bool': diff --git a/src/danog/MadelineProto/Wrappers/FilesHandler.php b/src/danog/MadelineProto/Wrappers/FilesHandler.php index 92bc05b0..f10d2837 100644 --- a/src/danog/MadelineProto/Wrappers/FilesHandler.php +++ b/src/danog/MadelineProto/Wrappers/FilesHandler.php @@ -467,13 +467,23 @@ trait FilesHandler return $res; + case 'photoSize': + case 'photoCachedSize': + $res['ext'] = '.jpg'; + $res['name'] = $message_media['location']['volume_id'].'_'.$message_media['location']['local_id']; + $res['size'] = $message_media['size']; + $res['InputFileLocation'] = ['_' => 'inputFileLocation', 'volume_id' => $message_media['location']['volume_id'], 'local_id' => $message_media['location']['local_id'], 'secret' => $message_media['location']['secret']]; + $res['mime'] = 'image/jpeg'; + + return $res; + case 'messageMediaDocument': $res['caption'] = $message_media['caption']; foreach ($message_media['document']['attributes'] as $attribute) { switch ($attribute['_']) { case 'documentAttributeFilename': $pathinfo = pathinfo($attribute['file_name']); - $res['ext'] = '.'.$pathinfo['extension']; + $res['ext'] = isset($pathinfo['extension']) ? '.'.$pathinfo['extension'] : ''; $res['name'] = $pathinfo['filename']; break; @@ -515,14 +525,11 @@ trait FilesHandler public function download_to_file($message_media, $file, $cb = null) { - if (!file_exists($file)) { - touch($file); - } - $stream = fopen($file, 'w'); + $file = str_replace('//', '/', $file); + $stream = fopen($file, 'wb'); $info = $this->get_download_info($message_media); - $this->download_to_stream($info, $stream, $cb, filesize($file), $info['size']); - + fclose($stream); return $file; } @@ -546,7 +553,7 @@ trait FilesHandler while ($percent < 100) { $real_part_size = ($offset + $part_size > $end) ? $part_size - (($offset + $part_size) - $end) : $part_size; \danog\MadelineProto\Logger::log($real_part_size, $offset); - fwrite($stream, $this->API->method_call('upload.getFile', ['location' => $info['InputFileLocation'], 'offset' => $offset, 'limit' => $real_part_size], null, true)['bytes']); + \danog\MadelineProto\Logger::log(fwrite($stream, $this->API->method_call('upload.getFile', ['location' => $info['InputFileLocation'], 'offset' => $offset, 'limit' => $real_part_size], null, true)['bytes'])); \danog\MadelineProto\Logger::log($offset, $size, ftell($stream)); $cb($percent = ($offset += $real_part_size) * 100 / $size); } diff --git a/src/danog/MadelineProto/Wrappers/PeerHandler.php b/src/danog/MadelineProto/Wrappers/PeerHandler.php index dba78d6d..b43ce241 100644 --- a/src/danog/MadelineProto/Wrappers/PeerHandler.php +++ b/src/danog/MadelineProto/Wrappers/PeerHandler.php @@ -22,6 +22,16 @@ trait PeerHandler return $this->API->get_info($id, $recursive); } + public function get_pwr_chat($id) + { + return $this->API->get_pwr_chat($id); + } + + public function peer_isset($id) + { + return $this->API->peer_isset($id); + } + public function gen_all($constructor) { return $this->API->gen_all($constructor);