Fix bot API file IDs

This commit is contained in:
Daniil Gentili 2020-02-04 17:34:02 +01:00
parent dcd6500737
commit e4ba475970
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
5 changed files with 207 additions and 141 deletions

View File

@ -30,7 +30,8 @@
"amphp/byte-stream": "^1", "amphp/byte-stream": "^1",
"danog/dns-over-https": "^0.2", "danog/dns-over-https": "^0.2",
"amphp/http-client-cookies": "^1", "amphp/http-client-cookies": "^1",
"amphp/uri": "^0.1" "amphp/uri": "^0.1",
"danog/tg-file-decoder": "^0.1"
}, },
"require-dev": { "require-dev": {
"vlucas/phpdotenv": "^3", "vlucas/phpdotenv": "^3",

View File

@ -19,7 +19,6 @@
namespace danog\MadelineProto\Stream; namespace danog\MadelineProto\Stream;
use Amp\Promise;
use Amp\Socket\EncryptableSocket; use Amp\Socket\EncryptableSocket;
use Amp\Socket\Socket; use Amp\Socket\Socket;

View File

@ -19,9 +19,23 @@
namespace danog\MadelineProto\TL\Conversion; namespace danog\MadelineProto\TL\Conversion;
use danog\Decoder\FileId;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceDialogPhoto;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceLegacy;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceStickersetThumbnail;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceThumbnail;
use danog\MadelineProto\MTProtoTools\PeerHandler; use danog\MadelineProto\MTProtoTools\PeerHandler;
use danog\MadelineProto\Tools;
use tgseclib\Math\BigInteger; use const danog\Decoder\ANIMATION;
use const danog\Decoder\AUDIO;
use const danog\Decoder\DOCUMENT;
use const danog\Decoder\PHOTO;
use const danog\Decoder\PROFILE_PHOTO;
use const danog\Decoder\STICKER;
use const danog\Decoder\THUMBNAIL;
use const danog\Decoder\VIDEO;
use const danog\Decoder\VIDEO_NOTE;
use const danog\Decoder\VOICE;
trait BotAPIFiles trait BotAPIFiles
{ {
@ -40,114 +54,159 @@ trait BotAPIFiles
/** /**
* Unpack bot API file ID. * Unpack bot API file ID.
* *
* @param string $file_id Bot API file ID * @param string $fileId Bot API file ID
* *
* @return array Unpacked file ID * @return array Unpacked file ID
*/ */
public function unpackFileId(string $file_id): array public function unpackFileId(string $fileId): array
{ {
$file_id = Tools::rleDecode(Tools::base64urlDecode($file_id)); $fileId = FileId::fromBotAPI($fileId);
$version = \ord($file_id[\strlen($file_id) - 1]);
$subVersion = $version === 4 ? \ord($file_id[\strlen($file_id) - 2]) : 0; $this->logger("Got file ID with version {$fileId->getVersion()}.{$fileId->getSubVersion()}");
$this->logger("Got file ID with version {$version}.{$subVersion}"); if (!\in_array($fileId->getVersion(), [2, 4])) {
if (!\in_array($version, [2, 4])) {
throw new Exception("Invalid bot API file ID version {$version}"); throw new Exception("Invalid bot API file ID version {$version}");
} }
$res = \fopen('php://memory', 'rw+b');
\fwrite($res, $file_id); $photoSize = $fileId->hasPhotoSizeSource() ? $fileId->getPhotoSizeSource() : null;
\fseek($res, 0);
$file_id = $res; switch ($fileId->getType()) {
$deserialized = $this->TL->deserialize($file_id); case PROFILE_PHOTO:
$res = ['type' => \str_replace('bot_', '', $deserialized['_'])]; /**
if (\in_array($res['type'], ['profile_photo', 'thumbnail', 'photo'])) { * @var $photoSize PhotoSizeSourceDialogPhoto
$deserialized['secret'] = 0; */
$deserialized['photosize_source'] = $version >= 4 ? Tools::unpackSignedInt(\stream_get_contents($file_id, 4)) : 0; if ($photoSize->getDialogId() < 0) {
// Legacy, Thumbnail, DialogPhotoSmall, DialogPhotoBig, StickerSetThumbnail $res['Chat'] = [
switch ($deserialized['photosize_source']) { '_' => $photoSize->getDialogId() < -1000000000000 ? 'channel' : 'chat',
case 0: 'id' => $photoSize->getDialogId() < -1000000000000 ? PeerHandler::fromSupergroup($photoSize->getDialogId()) : -$photoSize->getDialogId(),
$deserialized['secret'] = \stream_get_contents($file_id, 8); 'access_hash' => $photoSize->getDialogAccessHash(),
break; 'photo' => [
case 1: '_' => 'chatPhoto',
$deserialized['file_type'] = Tools::unpackSignedInt(\stream_get_contents($file_id, 4)); 'dc_id' => $fileId->getDcId(),
$deserialized['thumbnail_type'] = \chr(Tools::unpackSignedInt(\stream_get_contents($file_id, 4))); $photoSize->isSmallDialogPhoto() ? 'photo_small' : 'photo_big' => [
break; '_' => 'fileLocationToBeDeprecated',
case 2: 'volume_id' => $fileId->getVolumeId(),
case 3: 'local_id' => $fileId->getLocalId()
$deserialized['photo_size'] = $deserialized['photosize_source'] === 2 ? 'photo_small' : 'photo_big'; ]
$deserialized['dialog_id'] = (string) new BigInteger(\strrev(\stream_get_contents($file_id, 8)), -256); ],
$deserialized['dialog_access_hash'] = \stream_get_contents($file_id, 8); 'min' => true
break; ];
case 4:
$deserialized['sticker_set_id'] = Tools::unpackSignedInt(\stream_get_contents($file_id, 4));
$deserialized['sticker_set_access_hash'] = \stream_get_contents($file_id, 8);
break;
}
$deserialized['local_id'] = Tools::unpackSignedInt(\stream_get_contents($file_id, 4));
}
switch ($deserialized['_']) {
case 'bot_profile_photo':
if ($deserialized['dialog_id'] < 0) {
$res['Chat'] = ['_' => $deserialized['dialog_id'] < -1000000000000 ? 'channel' : 'chat', 'id' => $deserialized['dialog_id'] < -1000000000000 ? PeerHandler::fromSupergroup($deserialized['dialog_id']) : -$deserialized['dialog_id'], 'access_hash' => $deserialized['dialog_access_hash'], 'photo' => ['_' => 'chatPhoto', 'dc_id' => $deserialized['dc_id'], $deserialized['photo_size'] => ['_' => 'fileLocationToBeDeprecated', 'volume_id' => $deserialized['volume_id'], 'local_id' => $deserialized['local_id']]], 'min' => true];
return $res; return $res;
} }
$res['User'] = ['_' => 'user', 'id' => $deserialized['dialog_id'], 'access_hash' => $deserialized['dialog_access_hash'], 'photo' => ['_' => 'userProfilePhoto', 'dc_id' => $deserialized['dc_id'], 'photo_id' => $deserialized['id'], $deserialized['photo_size'] => ['_' => 'fileLocationToBeDeprecated', 'volume_id' => $deserialized['volume_id'], 'local_id' => $deserialized['local_id']]], 'min' => true]; $res['User'] = [
'_' => 'user',
'id' => $photoSize->getDialogId(),
'access_hash' => $photoSize->getDialogAccessHash(),
'photo' => [
'_' => 'userProfilePhoto',
'dc_id' => $fileId->getDcId(),
'photo_id' => $fileId->getId(),
$photoSize->isSmallDialogPhoto() ? 'photo_small' : 'photo_big' => [
'_' => 'fileLocationToBeDeprecated',
'volume_id' => $fileId->getVolumeId(),
'local_id' => $fileId->getLocalId()
]
],
'min' => true
];
return $res; return $res;
case 'bot_thumbnail': case THUMBNAIL:
$res['InputFileLocation'] = ['_' => $deserialized['file_type'] >= 3 ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation', 'id' => $deserialized['id'], 'access_hash' => $deserialized['access_hash'], 'file_reference' => '', 'thumb_size' => (string) $deserialized['thumbnail_type']]; $res['InputFileLocation'] = [
$res['name'] = $deserialized['id'] . '_' . $deserialized['thumbnail_type']; '_' => $photoSize->getThumbFileType() <= PHOTO ? 'inputPhotoFileLocation' : 'inputDocumentFileLocation',
'id' => $fileId->getId(),
'access_hash' => $fileId->getAccessHash(),
'file_reference' => $fileId->getFileReference(),
'thumb_size' => $photoSize->getThumbType()
];
$res['name'] = $fileId->getId().'_'.$photoSize->getThumbType();
$res['ext'] = 'jpg'; $res['ext'] = 'jpg';
$res['mime'] = 'image/jpeg'; $res['mime'] = 'image/jpeg';
$res['InputMedia'] = ['_' => $deserialized['file_type'] >= 3 ? 'inputMediaDocument' : 'inputMediaPhoto', 'id' => ['_' => $deserialized['file_type'] >= 3 ? 'inputDocument' : 'inputPhoto', 'id' => $deserialized['id'], 'access_hash' => $deserialized['access_hash']]]; $res['InputMedia'] = [
'_' => $photoSize->getThumbFileType() <= PHOTO ? 'inputMediaPhoto' : 'inputMediaDocument',
'id' => [
'_' => $photoSize->getThumbFileType() <= PHOTO ? 'inputPhoto' : 'inputDocument',
'id' => $fileId->getId(),
'access_hash' => $fileId->getAccessHash(),
'file_reference' => $fileId->getFileReference(),
]
];
return $res; return $res;
case 'bot_photo': case PHOTO:
if ($deserialized['photosize_source'] === 0) { $constructor = [
$constructor['id'] = $deserialized['id']; '_' => 'photo',
$constructor['access_hash'] = $deserialized['access_hash']; 'id' => $fileId->getId(),
unset($deserialized['id'], $deserialized['access_hash']); 'access_hash' => $fileId->getAccessHash(),
$deserialized['_'] = $deserialized['secret'] ? 'fileLocation' : 'fileLocationToBeDeprecated'; 'file_reference' => $fileId->getFileReference(),
$constructor['sizes'][0] = ['_' => 'photoSize', 'type' => '', 'location' => $deserialized]; 'dc_id' => $fileId->getDcId(),
$res['MessageMedia'] = ['_' => 'messageMediaPhoto', 'photo' => $constructor, 'caption' => '']; 'sizes' => []
];
$constructor['sizes'][] = [
'_' => 'photoSize',
'type' => $photoSize instanceof PhotoSizeSourceThumbnail ? $photoSize->getThumbType() : '',
'location' => [
'_' => $photoSize instanceof PhotoSizeSourceLegacy ? 'fileLocation' : 'fileLocationToBeDeprecated',
'dc_id' => $fileId->getDcId(),
'local_id' => $fileId->getLocalId(),
'volume_id' => $fileId->getVolumeId(),
'secret' => $photoSize instanceof PhotoSizeSourceLegacy ? $photoSize->getSecret() : ''
]
];
$res['MessageMedia'] = [
'_' => 'messageMediaPhoto',
'photo' => $constructor,
'caption' => ''
];
return $res; return $res;
case VOICE:
$attribute = [
'_' => 'documentAttributeAudio',
'voice' => true
];
break;
case VIDEO:
$attribute = [
'_' => 'documentAttributeVideo',
'round_message' => false
];
break;
case DOCUMENT:
$attribute = [];
break;
case STICKER:
$attribute = [
'_' => 'documentAttributeSticker',
'alt' => ''
];
if ($photoSize instanceof PhotoSizeSourceStickersetThumbnail) {
$attribute['stickerset'] = [
'_' => 'inputStickerSetID',
'id' => $photoSize->getStickerSetId(),
'access_hash' => $photoSize->getStickerSetAccessHash()
];
} }
$res['MessageMedia'] = ['_' => 'photo', 'id' => $deserialized['id'], 'access_hash' => $deserialized['access_hash'], 'sizes' => [['_' => 'photoSize', 'type' => $deserialized['thumbnail_type'], 'location' => ['_' => 'fileLocationToBeDeprecated', 'local_id' => $deserialized['local_id'], 'volume_id' => $deserialized['local_id']]]], 'dc_id' => $deserialized['dc_id']]; break;
return $res; case AUDIO:
case 'bot_voice': $attribute = ['_' => 'documentAttributeAudio', 'voice' => false];
unset($deserialized['_']); break;
$constructor = \array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => true]]]); case ANIMATION:
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => '']; $attribute = ['_' => 'documentAttributeAnimated'];
return $res; break;
case 'bot_video': case VIDEO_NOTE:
unset($deserialized['_']); $attribute = ['_' => 'documentAttributeVideo', 'round_message' => true];
$constructor = \array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeVideo', 'round_message' => false]]]); break;
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 'bot_document':
unset($deserialized['_']);
$constructor = \array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => []]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 'bot_sticker':
unset($deserialized['_']);
$constructor = \array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeSticker']]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 'bot_audio':
unset($deserialized['_']);
$constructor = \array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => false]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 'bot_gif':
unset($deserialized['_']);
$constructor = \array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAnimated']]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 'bot_video_note':
unset($deserialized['_']);
$constructor = \array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeVideo', 'round_message' => true]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
default: default:
throw new Exception(\sprintf(\danog\MadelineProto\Lang::$current_lang['file_type_invalid'], $type)); throw new Exception(\sprintf(\danog\MadelineProto\Lang::$current_lang['file_type_invalid'], $fileId->getTypeName()));
} }
$constructor = [
'_' => 'document',
'id' => $fileId->getId(),
'access_hash' => $fileId->getAccessHash(),
'file_reference' => $fileId->getFileReference(),
'dc_id' => $fileId->getDcId(),
'mime_type' => '',
'attributes' => [$attribute]
];
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
} }
} }

View File

@ -420,6 +420,9 @@ class TL
if (\is_string($object) && \strlen($object) === 9 && $object[0] === 'a') { if (\is_string($object) && \strlen($object) === 9 && $object[0] === 'a') {
return \substr($object, 1); return \substr($object, 1);
} }
if (\is_array($object) && \count($object) === 2) {
return \pack('l2', ...$object); // For bot API on 32bit
}
if (!\is_numeric($object)) { if (!\is_numeric($object)) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['not_numeric']); throw new Exception(\danog\MadelineProto\Lang::$current_lang['not_numeric']);
} }
@ -454,7 +457,7 @@ class TL
if (!\is_string($object)) { if (!\is_string($object)) {
throw new Exception("You didn't provide a valid string"); throw new Exception("You didn't provide a valid string");
} }
$object = \pack('C*', ...\unpack('C*', $object)); //$object = \pack('C*', ...\unpack('C*', $object));
$l = \strlen($object); $l = \strlen($object);
$concat = ''; $concat = '';
if ($l <= 253) { if ($l <= 253) {

View File

@ -114,28 +114,12 @@ export TEST_USERNAME=danogentili
export TEST_DESTINATION_GROUPS='["@danogentili"]' export TEST_DESTINATION_GROUPS='["@danogentili"]'
export MTPROTO_SETTINGS='{"logger":{"logger_level":5}}' export MTPROTO_SETTINGS='{"logger":{"logger_level":5}}'
php tools/makephar.php $HOME/phar5 "madeline$php$branch.phar" $TRAVIS_COMMIT runTestSimple()
{
cp tests/testing.php tests/testingBackup.php tests/testing.php
#set +e }
#tests/testing.php <<EOF runTest()
#m {
#$API_ID
#$API_HASH
#b
#$BOT_TOKEN
#n
#n
#n
#
#EOF
#export TRAVIS_PHAR="madeline$php$branch.phar"
#set -e
#tests/testing.php
#cp tests/testingBackup.php tests/testing.php
#rm testing.madeline
tests/testing.php <<EOF tests/testing.php <<EOF
m m
$API_ID $API_ID
@ -147,8 +131,28 @@ n
n n
EOF EOF
}
echo "Testing with previous version..."
cp tests/testing.php tests/testingBackup.php
runTest
echo "Testing with new version (upgrade)..."
php tools/makephar.php $HOME/phar5 "madeline$php$branch.phar" $TRAVIS_COMMIT
export TRAVIS_PHAR="madeline$php$branch.phar"
runTestSimple
echo "Testing with new version (restart)"
cp tests/testingBackup.php tests/testing.php cp tests/testingBackup.php tests/testing.php
tests/testing.php rm testing.madeline
runTest
echo "Testing with new version (reload)"
cp tests/testingBackup.php tests/testing.php
runTestSimple
eval "$(ssh-agent -s)" eval "$(ssh-agent -s)"
echo -e "$private_key" > madeline_rsa echo -e "$private_key" > madeline_rsa