mirror of
https://github.com/danog/MadelineProto.git
synced 2024-11-26 21:14:43 +01:00
Improve tests
This commit is contained in:
parent
3b21304d8b
commit
47228712a6
@ -31,7 +31,7 @@
|
||||
"amphp/http-client-cookies": "^1",
|
||||
"amphp/uri": "^0.1",
|
||||
"danog/tg-file-decoder": "^0.1",
|
||||
"danog/magicalserializer": "^1.0",
|
||||
"danog/magicalserializer": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"vlucas/phpdotenv": "^3",
|
||||
@ -41,7 +41,7 @@
|
||||
"amphp/php-cs-fixer-config": "dev-master",
|
||||
"haydenpierce/class-finder": "^0.4",
|
||||
"amphp/http-server": "dev-master",
|
||||
"amphp/http": "^1.6"
|
||||
"amphp/http": "^1.6",
|
||||
"amphp/websocket-client": "dev-master as 1",
|
||||
"amphp/websocket": "dev-master as 1",
|
||||
"ext-ctype": "*",
|
||||
|
@ -19,6 +19,8 @@
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
use Amp\Http\Server\HttpServer;
|
||||
use danog\MadelineProto\API;
|
||||
use danog\MadelineProto\Logger;
|
||||
use danog\MadelineProto\RPCErrorException;
|
||||
use League\Uri\Contracts\UriException;
|
||||
@ -47,17 +49,32 @@ class EventHandler extends \danog\MadelineProto\EventHandler
|
||||
"Max 1.5GB, parallel upload and download powered by @MadelineProto.";
|
||||
const ADMIN = 'danogentili';
|
||||
|
||||
/**
|
||||
* Whether to allow uploads.
|
||||
*/
|
||||
private $UPLOAD;
|
||||
|
||||
/**
|
||||
* Array of media objects.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $states = [];
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param API $API API
|
||||
*/
|
||||
public function __construct($API)
|
||||
{
|
||||
$this->UPLOAD = \class_exists(HttpServer::class);
|
||||
parent::__construct($API);
|
||||
}
|
||||
public function onUpdateNewChannelMessage($update)
|
||||
{
|
||||
//yield $this->onUpdateNewMessage($update);
|
||||
}
|
||||
public function report($message)
|
||||
public function report(string $message)
|
||||
{
|
||||
try {
|
||||
$this->messages->sendMessage(['peer' => self::ADMIN, 'message' => $message]);
|
||||
@ -79,10 +96,21 @@ class EventHandler extends \danog\MadelineProto\EventHandler
|
||||
$peerId = $peer['bot_api_id'];
|
||||
$messageId = $update['message']['id'];
|
||||
|
||||
if ($this->UPLOAD && $update['message']['message'] === '/getUrl') {
|
||||
yield $this->messages->sendMessage(['peer' => $peerId, 'message' => 'Give me a file: ', 'reply_to_msg_id' => $messageId]);
|
||||
$this->states[$peerId] = $this->UPLOAD;
|
||||
}
|
||||
if ($update['message']['message'] === '/start') {
|
||||
return $this->messages->sendMessage(['peer' => $peerId, 'message' => self::START, 'parse_mode' => 'Markdown', 'reply_to_msg_id' => $messageId]);
|
||||
}
|
||||
if (isset($update['message']['media']['_']) && $update['message']['media']['_'] !== 'messageMediaWebPage') {
|
||||
if ($this->UPLOAD && ($this->states[$peerId] ?? false) === $this->UPLOAD) {
|
||||
$media = yield $this->getDownloadInfo($this->states[$peerId]);
|
||||
unset($media['MessageMedia']);
|
||||
$media = yield
|
||||
unset($this->states[$peerId]);
|
||||
return;
|
||||
}
|
||||
yield $this->messages->sendMessage(['peer' => $peerId, 'message' => 'Give me a new name for this file: ', 'reply_to_msg_id' => $messageId]);
|
||||
$this->states[$peerId] = $update['message']['media'];
|
||||
|
||||
@ -184,7 +212,7 @@ $settings = [
|
||||
]
|
||||
],
|
||||
'upload' => [
|
||||
'allow_automatic_upload' => false, // IMPORTANT: for security reasons, upload by URL will still be allowed
|
||||
'allow_automatic_upload' => false // IMPORTANT: for security reasons, upload by URL will still be allowed
|
||||
],
|
||||
];
|
||||
|
||||
|
@ -25,7 +25,7 @@ class Exception extends \Exception
|
||||
public static $rollbar = true;
|
||||
public function __toString()
|
||||
{
|
||||
return $this->file === 'MadelineProto' ? $this->message : '\\danog\\MadelineProto\\Exception' . ($this->message !== '' ? ': ' : '') . $this->message . ' in ' . $this->file . ':' . $this->line . PHP_EOL . \danog\MadelineProto\Magic::$revision . PHP_EOL . 'TL Trace:' . PHP_EOL . $this->getTLTrace();
|
||||
return $this->file === 'MadelineProto' ? $this->message : '\\danog\\MadelineProto\\Exception'.($this->message !== '' ? ': ' : '').$this->message.' in '.$this->file.':'.$this->line.PHP_EOL.\danog\MadelineProto\Magic::$revision.PHP_EOL.'TL Trace:'.PHP_EOL.$this->getTLTrace();
|
||||
}
|
||||
public function __construct($message = null, $code = 0, self $previous = null, $file = null, $line = null)
|
||||
{
|
||||
@ -37,8 +37,10 @@ class Exception extends \Exception
|
||||
$this->line = $line;
|
||||
}
|
||||
parent::__construct($message, $code, $previous);
|
||||
if (\strpos($message, 'socket_accept') === false) {
|
||||
\danog\MadelineProto\Logger::log($message . ' in ' . \basename($this->file) . ':' . $this->line, \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
if (\strpos($message, 'socket_accept') === false
|
||||
&& !\in_array(\basename($this->file), ['PKCS8.php', 'PSS.php'])
|
||||
) {
|
||||
\danog\MadelineProto\Logger::log($message.' in '.\basename($this->file).':'.$this->line, \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
}
|
||||
if (\in_array($message, ['The session is corrupted!', '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', 'Please install this fork of phpseclib: https://github.com/danog/tgseclib'])) {
|
||||
return;
|
||||
@ -50,17 +52,24 @@ class Exception extends \Exception
|
||||
\Rollbar\Rollbar::log(\Rollbar\Payload\Level::error(), $this, \debug_backtrace(0));
|
||||
}
|
||||
}
|
||||
public static function extension(string $extensionName)
|
||||
/**
|
||||
* Complain about missing extensions
|
||||
*
|
||||
* @param string $extensionName Extension name
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function extension(string $extensionName): self
|
||||
{
|
||||
$additional = 'Try running sudo apt-get install php' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '-' . $extensionName . '.';
|
||||
$additional = 'Try running sudo apt-get install php'.PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION.'-'.$extensionName.'.';
|
||||
if ($extensionName === 'libtgvoip') {
|
||||
$additional = 'Follow the instructions @ https://voip.madelineproto.xyz to install it.';
|
||||
} elseif ($extensionName === 'prime') {
|
||||
$additional = 'Follow the instructions @ https://prime.madelineproto.xyz to install it.';
|
||||
}
|
||||
$message = 'MadelineProto requires the ' . $extensionName . ' extension to run. ' . $additional;
|
||||
$message = 'MadelineProto requires the '.$extensionName.' extension to run. '.$additional;
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
echo $message . '<br>';
|
||||
echo $message.'<br>';
|
||||
}
|
||||
$file = 'MadelineProto';
|
||||
$line = 1;
|
||||
|
@ -194,6 +194,13 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
'msg_resend_ans_req',
|
||||
];
|
||||
const DEFAULT_GETUPDATES_PARAMS = ['offset' => 0, 'limit' => null, 'timeout' => 0];
|
||||
|
||||
|
||||
const POWERED_BY = "<p><small>Powered by <a href='https://docs.madelineproto.xyz'>MadelineProto</a></small></p>";
|
||||
const NO_CACHE = [
|
||||
'Cache-Control' => ['no-store, no-cache, must-revalidate, max-age=0', 'post-check=0, pre-check=0'],
|
||||
'Pragma' => 'no-cache'
|
||||
];
|
||||
/**
|
||||
* Instance of wrapper API.
|
||||
*
|
||||
|
@ -513,7 +513,7 @@ trait ResponseHandler
|
||||
$r = isset($response['_']) ? $response['_'] : \json_encode($response);
|
||||
$this->logger->logger("Deferred: sent {$r} to deferred", Logger::ULTRA_VERBOSE);
|
||||
if ($botAPI) {
|
||||
$response = (yield from $this->MTProtoToBotAPI($response));
|
||||
$response = (yield from $this->API->MTProtoToBotAPI($response));
|
||||
}
|
||||
if (isset($this->outgoing_messages[$request_id]['promise'])) {
|
||||
// This should not happen but happens, should debug
|
||||
|
@ -549,9 +549,8 @@ trait Files
|
||||
$constructor = $constructor['MessageMedia'];
|
||||
} elseif (isset($constructor['InputMedia'])) {
|
||||
return $constructor;
|
||||
} else {
|
||||
$constructor = (yield from $this->getPwrChat($constructor['Chat'] ?? $constructor['User']));
|
||||
$constructor = $constructor['photo'];
|
||||
} else if (isset($constructor['Chat']) || isset($constructor['User'])) {
|
||||
throw new Exception("Chat photo file IDs can't be reused to resend chat photos, please use getPwrChat()['photo'], instead");
|
||||
}
|
||||
}
|
||||
switch ($constructor['_']) {
|
||||
@ -800,9 +799,6 @@ trait Files
|
||||
throw new \danog\MadelineProto\Exception('Invalid constructor provided: '.$messageMedia['_']);
|
||||
}
|
||||
}
|
||||
|
||||
private const POWERED_BY = "<p><small>Powered by <a href='https://docs.madelineproto.xyz'>MadelineProto</a></small></p>";
|
||||
|
||||
/**
|
||||
* Download file to browser.
|
||||
*
|
||||
@ -841,7 +837,7 @@ trait Files
|
||||
\header("$key: $subValue");
|
||||
}
|
||||
}
|
||||
http_response_code($result['code']);
|
||||
\http_response_code($result['code']);
|
||||
|
||||
if (!\in_array($result['code'], [Status::OK, Status::PARTIAL_CONTENT])) {
|
||||
yield Tools::echo(self::getExplanation($result['code']));
|
||||
@ -913,10 +909,6 @@ trait Files
|
||||
$body .= "</body></html>";
|
||||
return $body;
|
||||
}
|
||||
private const NO_CACHE = [
|
||||
'Cache-Control' => ['no-store, no-cache, must-revalidate, max-age=0', 'post-check=0, pre-check=0'],
|
||||
'Pragma' => 'no-cache'
|
||||
];
|
||||
/**
|
||||
* Parse headers.
|
||||
*
|
||||
|
@ -20,6 +20,11 @@
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
use Amp\Http\Client\Request;
|
||||
use danog\Decoder\FileId;
|
||||
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceDialogPhoto;
|
||||
|
||||
use const danog\Decoder\PHOTO;
|
||||
use const danog\Decoder\PROFILE_PHOTO;
|
||||
|
||||
/**
|
||||
* Manages peers.
|
||||
@ -705,7 +710,7 @@ trait PeerHandler
|
||||
*
|
||||
* @return \Generator<array> Chat object
|
||||
*/
|
||||
public function getPwrChat($id, $fullfetch = true, $send = true): \Generator
|
||||
public function getPwrChat($id, bool $fullfetch = true, bool $send = true): \Generator
|
||||
{
|
||||
$full = $fullfetch ? yield from $this->getFullInfo($id) : (yield from $this->getInfo($id));
|
||||
$res = ['id' => $full['bot_api_id'], 'type' => $full['type']];
|
||||
@ -837,6 +842,34 @@ trait PeerHandler
|
||||
if ($fullfetch || $send) {
|
||||
$this->storeDb($res);
|
||||
}
|
||||
if (isset($res['photo'])) {
|
||||
$photo = [];
|
||||
foreach ([
|
||||
'small' => $res['photo']['sizes'][0],
|
||||
'big' => end($res['photo']['sizes']),
|
||||
] as $type => $size) {
|
||||
$fileId = new FileId;
|
||||
$fileId->setId($res['photo']['id'] ?? 0);
|
||||
$fileId->setAccessHash($res['photo']['access_hash'] ?? 0);
|
||||
$fileId->setFileReference($res['photo']['file_reference'] ?? '');
|
||||
$fileId->setDcId($res['photo']['dc_id']);
|
||||
$fileId->setType(PROFILE_PHOTO);
|
||||
|
||||
$fileId->setLocalId($size['location']['local_id']);
|
||||
$fileId->setVolumeId($size['location']['volume_id']);
|
||||
|
||||
$photoSize = new PhotoSizeSourceDialogPhoto;
|
||||
$photoSize->setDialogId($res['id']);
|
||||
$photoSize->setDialogPhotoSmall($type === 'small');
|
||||
$photoSize->setDialogAccessHash($res['access_hash'] ?? 0);
|
||||
|
||||
$fileId->setPhotoSizeSource($photoSize);
|
||||
|
||||
$photo[$type.'_file_id'] = (string) $fileId;
|
||||
$photo[$type.'_file_unique_id'] = $fileId->getUniqueBotAPI();
|
||||
}
|
||||
$res['photo'] += $photo;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
private function recurseAlphabetSearchParticipants($channel, $filter, $q, $total_count, &$res): \Generator
|
||||
|
@ -83,7 +83,6 @@ class RSA
|
||||
*/
|
||||
public function encrypt($data): string
|
||||
{
|
||||
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['rsa_encrypting'], Logger::VERBOSE);
|
||||
return (new \tgseclib\Math\BigInteger((string) $data, 256))->powMod($this->e, $this->n)->toBytes();
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,11 @@
|
||||
|
||||
namespace danog\MadelineProto\TL\Conversion;
|
||||
|
||||
use danog\Decoder\FileId;
|
||||
use danog\MadelineProto\Logger;
|
||||
|
||||
use const danog\Decoder\TYPES_IDS;
|
||||
|
||||
trait BotAPI
|
||||
{
|
||||
private function htmlEntityDecode(string $stuff): string
|
||||
@ -173,17 +176,16 @@ trait BotAPI
|
||||
/**
|
||||
* Convert MTProto parameters to bot API parameters.
|
||||
*
|
||||
* @param array $data Data
|
||||
* @param array $sent_arguments Sent arguments
|
||||
* @param array $data Data
|
||||
*
|
||||
* @return \Generator<array>
|
||||
*/
|
||||
public function MTProtoToBotAPI(array $data, array $sent_arguments = []): \Generator
|
||||
public function MTProtoToBotAPI(array $data): \Generator
|
||||
{
|
||||
$newd = [];
|
||||
if (!isset($data['_'])) {
|
||||
foreach ($data as $key => $element) {
|
||||
$newd[$key] = (yield from $this->MTProtoToBotAPI($element, $sent_arguments));
|
||||
$newd[$key] = (yield from $this->MTProtoToBotAPI($element));
|
||||
}
|
||||
return $newd;
|
||||
}
|
||||
@ -191,18 +193,20 @@ trait BotAPI
|
||||
case 'updateShortSentMessage':
|
||||
$newd['message_id'] = $data['id'];
|
||||
$newd['date'] = $data['date'];
|
||||
$newd['text'] = $sent_arguments['message'];
|
||||
$newd['text'] = $data['request']['message'];
|
||||
if ($data['out']) {
|
||||
$newd['from'] = (yield from $this->getPwrChat($this->authorization['user']));
|
||||
}
|
||||
$newd['chat'] = (yield from $this->getPwrChat($sent_arguments['peer']));
|
||||
$newd['chat'] = yield from $this->getPwrChat($data['request']['peer']);
|
||||
if (isset($data['entities'])) {
|
||||
$newd['entities'] = (yield from $this->MTProtoToBotAPI($data['entities'], $sent_arguments));
|
||||
$newd['entities'] = yield from $this->MTProtoToBotAPI($data['entities']);
|
||||
}
|
||||
if (isset($data['media'])) {
|
||||
$newd = \array_merge($newd, yield from $this->MTProtoToBotAPI($data['media'], $sent_arguments));
|
||||
$newd += yield from $this->MTProtoToBotAPI($data['media']);
|
||||
}
|
||||
return $newd;
|
||||
case 'updates':
|
||||
$data = array_values(array_filter($data['updates'], fn(array $update) => $update['_'] !== 'updateMessageID'))[0];
|
||||
case 'updateNewChannelMessage':
|
||||
case 'updateNewMessage':
|
||||
return yield from $this->MTProtoToBotAPI($data['message']);
|
||||
@ -217,7 +221,7 @@ trait BotAPI
|
||||
}
|
||||
$newd['chat'] = (yield from $this->getPwrChat($data['to_id']));
|
||||
if (isset($data['entities'])) {
|
||||
$newd['entities'] = (yield from $this->MTProtoToBotAPI($data['entities'], $sent_arguments));
|
||||
$newd['entities'] = (yield from $this->MTProtoToBotAPI($data['entities']));
|
||||
}
|
||||
if (isset($data['views'])) {
|
||||
$newd['views'] = $data['views'];
|
||||
@ -241,7 +245,7 @@ trait BotAPI
|
||||
$newd['forward_from_message_id'] = $data['fwd_from']['channel_post'];
|
||||
}
|
||||
if (isset($data['media'])) {
|
||||
$newd = \array_merge($newd, yield from $this->MTProtoToBotAPI($data['media'], $sent_arguments));
|
||||
$newd = \array_merge($newd, yield from $this->MTProtoToBotAPI($data['media']));
|
||||
}
|
||||
return $newd;
|
||||
case 'messageEntityMention':
|
||||
@ -297,7 +301,7 @@ trait BotAPI
|
||||
$res['photo'] = [];
|
||||
foreach ($data['photo']['sizes'] as $key => $photo) {
|
||||
if (\in_array($photo['_'], ['photoCachedSize', 'photoSize'])) {
|
||||
$res['photo'][$key] = (yield from $this->photosizeToBotAPI($photo, $data['photo']));
|
||||
$res['photo'][$key] = $this->photosizeToBotAPI($photo, $data['photo']);
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
@ -307,21 +311,18 @@ trait BotAPI
|
||||
$type_name = 'document';
|
||||
$res = [];
|
||||
if (isset($data['document']['thumbs']) && $data['document']['thumbs'] && \in_array(\end($data['document']['thumbs'])['_'], ['photoCachedSize', 'photoSize'])) {
|
||||
$res['thumb'] = (yield from $this->photosizeToBotAPI(\end($data['document']['thumbs']), [], true));
|
||||
$res['thumb'] = $this->photosizeToBotAPI(\end($data['document']['thumbs']), $data['document'], true);
|
||||
}
|
||||
foreach ($data['document']['attributes'] as $attribute) {
|
||||
switch ($attribute['_']) {
|
||||
case 'documentAttributeFilename':
|
||||
$pathinfo = \pathinfo($attribute['file_name']);
|
||||
$res['ext'] = isset($pathinfo['extension']) ? '.' . $pathinfo['extension'] : '';
|
||||
$res['ext'] = isset($pathinfo['extension']) ? '.'.$pathinfo['extension'] : '';
|
||||
$res['file_name'] = $pathinfo['filename'];
|
||||
break;
|
||||
case 'documentAttributeAudio':
|
||||
$audio = $attribute;
|
||||
$type_name = 'audio';
|
||||
if ($attribute['voice']) {
|
||||
$type_name = 'voice';
|
||||
}
|
||||
$type_name = $attribute['voice'] ? 'voice' :'audio';
|
||||
$res['duration'] = $attribute['duration'];
|
||||
if (isset($attribute['performer'])) {
|
||||
$res['performer'] = $attribute['performer'];
|
||||
@ -344,7 +345,7 @@ trait BotAPI
|
||||
$res['height'] = $attribute['h'];
|
||||
break;
|
||||
case 'documentAttributeAnimated':
|
||||
$type_name = 'gif';
|
||||
$type_name = 'animation';
|
||||
$res['animated'] = true;
|
||||
break;
|
||||
case 'documentAttributeHasStickers':
|
||||
@ -364,24 +365,32 @@ trait BotAPI
|
||||
if (isset($audio) && isset($audio['title']) && !isset($res['file_name'])) {
|
||||
$res['file_name'] = $audio['title'];
|
||||
if (isset($audio['performer'])) {
|
||||
$res['file_name'] .= ' - ' . $audio['performer'];
|
||||
$res['file_name'] .= ' - '.$audio['performer'];
|
||||
}
|
||||
}
|
||||
if (!isset($res['file_name'])) {
|
||||
$res['file_name'] = $data['document']['access_hash'];
|
||||
}
|
||||
$res['file_name'] .= '_' . $data['document']['id'];
|
||||
$res['file_name'] .= '_'.$data['document']['id'];
|
||||
if (isset($res['ext'])) {
|
||||
$res['file_name'] .= $res['ext'];
|
||||
unset($res['ext']);
|
||||
} else {
|
||||
$res['file_name'] .= $this->getExtensionFromMime($data['document']['mime_type']);
|
||||
}
|
||||
$data['document']['_'] = 'bot_' . $type_name;
|
||||
$res['file_size'] = $data['document']['size'];
|
||||
$res['mime_type'] = $data['document']['mime_type'];
|
||||
$res['file_id'] = \danog\MadelineProto\Tools::base64urlEncode(\danog\MadelineProto\Tools::rleEncode(yield from $this->TL->serializeObject(['type' => 'File'], $data['document'], 'File') . \chr(2)));
|
||||
return [$type_name => $res, 'caption' => isset($data['caption']) ? $data['caption'] : ''];
|
||||
|
||||
$fileId = new FileId;
|
||||
$fileId->setId($data['document']['id']);
|
||||
$fileId->setAccessHash($data['document']['access_hash']);
|
||||
$fileId->setFileReference($data['document']['file_reference'] ?? '');
|
||||
$fileId->setDcId($data['document']['dc_id']);
|
||||
$fileId->setType(TYPES_IDS[$type_name]);
|
||||
|
||||
$res['file_id'] = (string) $fileId;
|
||||
$res['file_unique_id'] = $fileId->getUniqueBotAPI();
|
||||
return [$type_name => $res, 'caption' => $data['caption'] ?? ''];
|
||||
default:
|
||||
throw new Exception(\sprintf(\danog\MadelineProto\Lang::$current_lang['botapi_conversion_error'], $data['_']));
|
||||
}
|
||||
@ -586,7 +595,7 @@ trait BotAPI
|
||||
$multiple_args = [$multiple_args_base];
|
||||
$i = 0;
|
||||
foreach ($text_arr as $word) {
|
||||
if ($this->mbStrlen($multiple_args[$i]['message'] . $word) <= $max_length) {
|
||||
if ($this->mbStrlen($multiple_args[$i]['message'].$word) <= $max_length) {
|
||||
$multiple_args[$i]['message'] .= $word;
|
||||
} else {
|
||||
$i++;
|
||||
@ -670,7 +679,7 @@ trait BotAPI
|
||||
foreach ($initialArray as $item) {
|
||||
$delimOffset += $this->mbStrlen($item);
|
||||
//if ($this->mbStrlen($item) > 0) {
|
||||
$finalArray[] = $item . ($delimOffset < $this->mbStrlen($string) ? $string[$delimOffset] : '');
|
||||
$finalArray[] = $item.($delimOffset < $this->mbStrlen($string) ? $string[$delimOffset] : '');
|
||||
//}
|
||||
$delimOffset++;
|
||||
}
|
||||
|
@ -39,17 +39,38 @@ use const danog\Decoder\VOICE;
|
||||
|
||||
trait BotAPIFiles
|
||||
{
|
||||
private function photosizeToBotAPI($photoSize, $photo, $thumbnail = false): \Generator
|
||||
private function photosizeToBotAPI($photoSize, $photo, $thumbnail = false): array
|
||||
{
|
||||
$ext = '.jpg';
|
||||
//$this->getExtensionFromLocation(['_' => 'inputFileLocation', 'volume_id' => $photoSize['location']['volume_id'], 'local_id' => $photoSize['location']['local_id'], 'secret' => $photoSize['location']['secret'], 'dc_id' => $photoSize['location']['dc_id']], '.jpg');
|
||||
$photoSize['location']['access_hash'] = $photo['access_hash'] ?? 0;
|
||||
$photoSize['location']['id'] = $photo['id'] ?? 0;
|
||||
$photoSize['location']['secret'] = $photo['location']['secret'] ?? 0;
|
||||
$photoSize['location']['dc_id'] = $photo['dc_id'] ?? 0;
|
||||
$photoSize['location']['_'] = $thumbnail ? 'bot_thumbnail' : 'bot_photo';
|
||||
$data = (yield from $this->TL->serializeObject(['type' => 'File'], $photoSize['location'], 'File')).\chr(2);
|
||||
return ['file_id' => \danog\MadelineProto\Tools::base64urlEncode(\danog\MadelineProto\Tools::rleEncode($data)), 'width' => $photoSize['w'], 'height' => $photoSize['h'], 'file_size' => isset($photoSize['size']) ? $photoSize['size'] : \strlen($photoSize['bytes']), 'mime_type' => 'image/jpeg', 'file_name' => $photoSize['location']['volume_id'].'_'.$photoSize['location']['local_id'].$ext];
|
||||
$fileId = new FileId;
|
||||
$fileId->setId($photo['id'] ?? 0);
|
||||
$fileId->setAccessHash($photo['access_hash'] ?? 0);
|
||||
$fileId->setFileReference($photo['file_reference'] ?? '');
|
||||
$fileId->setDcId($photo['dc_id'] ?? 0);
|
||||
|
||||
$fileId->setLocalId($photoSize['location']['local_id']);
|
||||
$fileId->setVolumeId($photoSize['location']['volume_id']);
|
||||
|
||||
$photoSizeSource = new PhotoSizeSourceThumbnail;
|
||||
$photoSizeSource->setThumbType($photoSize['type']);
|
||||
|
||||
if ($photo['_'] === 'photo') {
|
||||
$photoSizeSource->setThumbFileType(PHOTO);
|
||||
$fileId->setType(PHOTO);
|
||||
} else {
|
||||
$photoSizeSource->setThumbFileType(THUMBNAIL);
|
||||
$fileId->setType(THUMBNAIL);
|
||||
}
|
||||
$fileId->setPhotoSizeSource($photoSizeSource);
|
||||
|
||||
return [
|
||||
'file_id' => (string) $fileId,
|
||||
'file_unique_id' => $fileId->getUniqueBotAPI(),
|
||||
'width' => $photoSize['w'],
|
||||
'height' => $photoSize['h'],
|
||||
'file_size' => $photoSize['size'] ?? \strlen($photoSize['bytes']),
|
||||
'mime_type' => 'image/jpeg',
|
||||
'file_name' => $photoSize['location']['volume_id'].'_'.$photoSize['location']['local_id'].'.jpg'
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Unpack bot API file ID.
|
||||
|
@ -40,6 +40,54 @@ class FileIdTest extends TestCase
|
||||
self::$MadelineProto->botLogin(\getenv('BOT_TOKEN'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip file reference from file ID.
|
||||
*
|
||||
* @param string $fileId File ID
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function stripFileReference(string $fileId): string
|
||||
{
|
||||
return FileId::fromBotAPI($fileId)->setFileReference('');
|
||||
}
|
||||
/**
|
||||
* Strip access hash (and possibly ID) from file ID.
|
||||
*
|
||||
* @param string $fileId File ID
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function stripForChat(string $fileId): string
|
||||
{
|
||||
$file = FileId::fromBotAPI($fileId)->setAccessHash(0);
|
||||
if ($file->getPhotoSizeSource()->getDialogId() < 0) {
|
||||
$file->setId(0);
|
||||
}
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that two file IDs are equal.
|
||||
*
|
||||
* @param string $fileIdAstr File ID A
|
||||
* @param string $fileIdBstr File ID B
|
||||
* @param string $message Message
|
||||
*
|
||||
* @throws PHPUnit\Framework\AssertionFailedError
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function assertFileIdEquals(string $fileIdAstr, string $fileIdBstr, $message = '')
|
||||
{
|
||||
$fileIdAstr = self::stripFileReference($fileIdAstr);
|
||||
$fileIdBstr = self::stripFileReference($fileIdBstr);
|
||||
if ($fileIdAstr !== $fileIdBstr) {
|
||||
\var_dump(FileId::fromBotAPI($fileIdAstr), FileId::fromBotAPI($fileIdBstr));
|
||||
}
|
||||
self::assertEquals($fileIdAstr, $fileIdBstr, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileId File ID
|
||||
* @param string $type Expected type
|
||||
@ -47,7 +95,7 @@ class FileIdTest extends TestCase
|
||||
*
|
||||
* @dataProvider provideFileIdsAndType
|
||||
*/
|
||||
public function testDownload(string $type, string $fileIdStr, string $origType)
|
||||
public function testDownload(string $type, string $fileIdStr, string $uniqueFileIdStr, array $fullInfo)
|
||||
{
|
||||
self::$MadelineProto->logger("Trying to download $fileIdStr");
|
||||
self::$MadelineProto->downloadToFile($fileIdStr, '/dev/null');
|
||||
@ -60,16 +108,62 @@ class FileIdTest extends TestCase
|
||||
*
|
||||
* @dataProvider provideFileIdsAndType
|
||||
*/
|
||||
public function testResend(string $type, string $fileIdStr, string $origType)
|
||||
public function testResendConvert(string $type, string $fileIdStr, string $uniqueFileIdStr, array $fullInfo)
|
||||
{
|
||||
self::$MadelineProto->logger("Trying to resend $fileIdStr");
|
||||
self::$MadelineProto->messages->sendMedia(
|
||||
self::$MadelineProto->logger("Trying to resend and then reconvert $fileIdStr");
|
||||
if ($type === 'profile_photo') {
|
||||
$chat = self::$MadelineProto->getPwrChat($fullInfo['chat']);
|
||||
$this->assertArrayHasKey('photo', $chat);
|
||||
$chat = $chat['photo'];
|
||||
$this->assertArrayHasKey($fullInfo['type'].'_file_id', $chat);
|
||||
$this->assertArrayHasKey($fullInfo['type'].'_file_unique_id', $chat);
|
||||
|
||||
$chat[$fullInfo['type'].'_file_id'] = self::stripForChat($chat[$fullInfo['type'].'_file_id']);
|
||||
|
||||
$this->assertFileIdEquals($fileIdStr, $chat[$fullInfo['type'].'_file_id']);
|
||||
$this->assertEquals($uniqueFileIdStr, $chat[$fullInfo['type'].'_file_unique_id']);
|
||||
|
||||
$this->expectExceptionMessage("Chat photo file IDs can't be reused to resend chat photos, please use getPwrChat()['photo'], instead");
|
||||
}
|
||||
$res = self::$MadelineProto->messages->sendMedia(
|
||||
[
|
||||
'peer' => \getenv('DEST'),
|
||||
'media' => $fileIdStr
|
||||
],
|
||||
[
|
||||
'botAPI' => true
|
||||
]
|
||||
);
|
||||
$this->assertTrue(true);
|
||||
if ($type === 'thumbnail') {
|
||||
$this->assertArrayHasKey($fullInfo[0], $res);
|
||||
$res = $res[$fullInfo[0]];
|
||||
$this->assertArrayHasKey('thumb', $res);
|
||||
$this->assertFileIdEquals($fileIdStr, $res['thumb']['file_id']);
|
||||
$this->assertEquals($uniqueFileIdStr, $res['thumb']['file_unique_id']);
|
||||
|
||||
list($type, $fileIdStr, $uniqueFileIdStr) = $fullInfo;
|
||||
} else {
|
||||
$this->assertArrayHasKey($type, $res);
|
||||
$res = $res[$type];
|
||||
}
|
||||
|
||||
$hasFileId = false;
|
||||
$hasFileUniqueId = false;
|
||||
$res = $type === 'photo' ? $res : [$res];
|
||||
foreach ($res as $subRes) {
|
||||
$this->assertArrayHasKey('file_id', $subRes);
|
||||
$this->assertArrayHasKey('file_unique_id', $subRes);
|
||||
$hasFileId |= self::stripFileReference($fileIdStr) === self::stripFileReference($subRes['file_id']);
|
||||
$hasFileUniqueId |= $uniqueFileIdStr === $subRes['file_unique_id'];
|
||||
}
|
||||
|
||||
if (\count($res) === 1) {
|
||||
$this->assertFileIdEquals($fileIdStr, $res[0]['file_id']);
|
||||
$this->assertEquals($uniqueFileIdStr, $res[0]['file_unique_id']);
|
||||
} else {
|
||||
$this->assertTrue((bool) $hasFileUniqueId);
|
||||
$this->assertTrue((bool) $hasFileId);
|
||||
}
|
||||
}
|
||||
|
||||
public function provideFileIdsAndType(): \Generator
|
||||
@ -77,16 +171,21 @@ class FileIdTest extends TestCase
|
||||
$dest = \getenv('DEST');
|
||||
$token = \getenv('BOT_TOKEN');
|
||||
foreach ($this->provideChats() as $chat) {
|
||||
$result = \json_decode(\file_get_contents("https://api.telegram.org/bot$token/getChat?chat_id=$chat"), true)['result']['photo'];
|
||||
$result = \json_decode(\file_get_contents("https://api.telegram.org/bot$token/getChat?chat_id=$chat"), true)['result']['photo'] ?? [];
|
||||
if (!$result) {
|
||||
continue;
|
||||
}
|
||||
yield [
|
||||
'profile_photo',
|
||||
$result['small_file_id'],
|
||||
'profile_photo',
|
||||
$result['small_file_unique_id'],
|
||||
['chat' => $chat, 'type' => 'small'],
|
||||
];
|
||||
yield [
|
||||
'profile_photo',
|
||||
$result['big_file_id'],
|
||||
'profile_photo',
|
||||
$result['big_file_unique_id'],
|
||||
['chat' => $chat, 'type' => 'big'],
|
||||
];
|
||||
}
|
||||
foreach ($this->provideUrls() as $type => $url) {
|
||||
@ -111,16 +210,18 @@ class FileIdTest extends TestCase
|
||||
$botResult = [$botResult];
|
||||
}
|
||||
foreach ($botResult as $subResult) {
|
||||
yield [
|
||||
yield $full = [
|
||||
$type,
|
||||
$subResult['file_id'],
|
||||
$type,
|
||||
$subResult['file_unique_id'],
|
||||
[],
|
||||
];
|
||||
if (isset($subResult['thumb'])) {
|
||||
yield [
|
||||
'thumbnail',
|
||||
$subResult['thumb']['file_id'],
|
||||
$type,
|
||||
$subResult['thumb']['file_unique_id'],
|
||||
$full,
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -129,11 +230,11 @@ class FileIdTest extends TestCase
|
||||
}
|
||||
public function provideChats(): array
|
||||
{
|
||||
return [\getenv('DEST'), '@MadelineProto'];
|
||||
return [\getenv('DEST'), '@MadelineProto', -382346236];
|
||||
}
|
||||
public function provideUrls(): array
|
||||
{
|
||||
return [
|
||||
$res = [
|
||||
'sticker' => 'https://github.com/danog/MadelineProto/blob/master/tests/lel.webp?raw=true',
|
||||
'photo' => 'https://github.com/danog/MadelineProto/blob/master/tests/faust.jpg',
|
||||
'audio' => 'https://github.com/danog/MadelineProto/blob/master/tests/mosconi.mp3?raw=true',
|
||||
@ -141,7 +242,10 @@ class FileIdTest extends TestCase
|
||||
'animation' => 'https://github.com/danog/MadelineProto/blob/master/tests/pony.mp4?raw=true',
|
||||
'document' => 'https://github.com/danog/danog.github.io/raw/master/lol/index_htm_files/0.gif',
|
||||
'voice' => 'https://daniil.it/audio_2020-02-01_18-09-08.ogg',
|
||||
'video_note' => 'https://daniil.it/round.mp4'
|
||||
];
|
||||
if (\getenv('TRAVIS_COMMIT')) {
|
||||
$res['video_note'] = 'https://daniil.it/round.mp4';
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user