Implemented bot API object conversion, file locking for serialization, bot API file ids

This commit is contained in:
Daniil Gentili 2017-02-16 04:55:10 +01:00
parent f93d956d0e
commit 38d6ee07b3
17 changed files with 1363 additions and 622 deletions

1
.gitignore vendored
View File

@ -86,3 +86,4 @@ docs_md
.env
composer.lock
b.php

View File

@ -236,7 +236,7 @@ To specify a custom callback change the correct value in the settings. The speci
### Uploading and downloading files
MadelineProto provides wrapper methods to upload and download files.
MadelineProto provides wrapper methods to upload and download files that support bot API file ids.
Every method described in this section accepts a last optional paramater with a callable function that will be called during the upload/download using the first parameter to pass a floating point number indicating the upload/download status in percentage.
@ -249,12 +249,13 @@ $inputFile = $MadelineProto->upload('file', 'optional new file name.ext');
$MadelineProto->messages->sendMedia(['peer' => '@pwrtelegramgroup', 'media' => $inputMedia]);
```
To convert the result of sendMedia to a bot API file id select the messageMedia object from the output of the method and pass it to `$MadelineProto->API->MTProto_to_botAPI()`.
See tests/testing.php for more examples.
There are multiple download methods that allow you to download a file to a directory, to a file or to a stream.
The first parameter of these functions must always be a [messageMediaPhoto](https://daniil.it/MadelineProto/API_docs/constructors/messageMediaPhoto.html) or a [messageMediaDocument](https://daniil.it/MadelineProto/API_docs/constructors/messageMediaDocument.html) object. These objects are usually received in updates, see `bot.php` for examples
The first parameter of these functions must always be either a [messageMediaPhoto](https://daniil.it/MadelineProto/API_docs/constructors/messageMediaPhoto.html) or a [messageMediaDocument](https://daniil.it/MadelineProto/API_docs/constructors/messageMediaDocument.html) object or a bot API file id. These objects are usually received in updates, see `bot.php` for examples
```
@ -305,6 +306,8 @@ See tests/testing.php for more examples.
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.
To convert the results of methods to bot API objects you must provide a second parameter to method wrappers, containing an array with the `botAPI` key set to true.
Note that when you login as a bot, MadelineProto also logins using the [PWRTelegram](https://pwrtelegram.xyz) API, to allow persistant storage of peers, even after a logout and another login.
### Storing sessions

54
id.php
View File

@ -1,54 +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/>.
*/
require 'vendor/autoload.php';
//require '../db:id.php';
//$select = $pdo->prepare('SELECT * FROM ul');
//$select->execute();
//$pdores = $select->fetchAll(PDO::FETCH_ASSOC);
function foreach_offset_length($string)
{
$res = [];
$strlen = strlen($string);
for ($offset = 0; $offset < strlen($string); $offset++) {
for ($length = $strlen - $offset; $length > 0; $length--) {
$s = substr($string, $offset, $length);
$number = (string) (new \phpseclib\Math\BigInteger(strrev($s), 256));
//$number = ord($s);
$res[] = ['number' => $number, 'offset' => $offset, 'length' => $length];
}
}
return $res;
}
$res = [];
$pdores = [['file_id' => 'AwADBAADiQEAAo_aCgYAAc-fglzxcY0C']];
foreach ($pdores as $r) {
$base256 = base64url_decode($r['file_id']);
$res = foreach_offset_length($base256);
if (!isset($same)) {
$same = $res;
} else {
foreach ($same as $key => $s) {
if (!in_array($s, $res)) {
unset($same[$key]);
}
}
}
}
var_dump($res);
function base64url_decode($data)
{
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}

169
pwrtelegram_debug_bot.php Executable file
View File

@ -0,0 +1,169 @@
#!/usr/bin/env php
<?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/>.
*/
require 'vendor/autoload.php';
$settings = [];
$token='';
try {
$MadelineProto = \danog\MadelineProto\Serialization::deserialize('b.madeline');
} catch (\danog\MadelineProto\Exception $e) {
$MadelineProto = new \danog\MadelineProto\API($settings);
$authorization = $MadelineProto->bot_login($token);
\danog\MadelineProto\Logger::log([$authorization], \danog\MadelineProto\Logger::NOTICE);
}
function base64url_decode($data)
{
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}
function rle_decode($string) {
$base256 = '';
$last = '';
foreach (str_split($string) as $cur) {
if ($last === chr(0)) {
$base256 .= str_repeat($last, ord($cur));
$last = '';
} else {
$base256 .= $last;
$last = $cur;
}
}
$string = $base256.$last;
return $string;
}
function foreach_offset_length($string)
{
/* $a = [];
$b = [];
foreach ([2, 3, 4] as $r) {
$a []= chr(0).chr($r);
$b []= str_repeat(chr(0), $r);
}
$string = str_replace($a, $b, $string);*/
$res = [];
$strlen = strlen($string);
for ($offset = 0; $offset < strlen($string); $offset++) {
// for ($length = $strlen - $offset; $length > 0; $length--) {
foreach (['i' => 4, 'q' => 8] as $c => $length) {
$s = substr($string, $offset, $length);
if (strlen($s) === $length) {
$number = \danog\PHP\Struct::unpack('<'.$c, $s)[0];
//$number = ord($s);
$res[] = ['number' => $number, 'offset' => $offset, 'length' => $length];
}
}
}
return $res;
}
$res = ['offset' => 0, 'files' => []];
function getfiles($token, &$params) {
foreach (json_decode(file_get_contents('https://api.telegram.org/bot'.$token.'/getupdates?offset='.$params['offset']), true)['result'] as $update) {
$params['offset'] = $update['update_id']+1;
if (isset($update['message']['audio'])) {
$params['files'][$update['message']['message_id']] = $update['message']['audio']['file_id'];
}
if (isset($update['message']['document'])) {
$params['files'][$update['message']['message_id']] = $update['message']['document']['file_id'];
}
if (isset($update['message']['video'])) {
$params['files'][$update['message']['message_id']] = $update['message']['video']['file_id'];
}
if (isset($update['message']['sticker'])) {
$params['files'][$update['message']['message_id']] = $update['message']['sticker']['file_id'];
}
if (isset($update['message']['voice'])) {
$params['files'][$update['message']['message_id']] = $update['message']['voice']['file_id'];
}
if (isset($update['message']['photo'])) {
$params['files'][$update['message']['message_id']] = end($update['message']['photo'])['file_id'];
}
}
}
$offset = 0;
while (true) {
$updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
//var_dump($updates);
foreach ($updates as $update) {
$offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id
//var_dump($update);
switch ($update['update']['_']) {
case 'updateNewMessage':
if (isset($update['update']['message']['out']) && $update['update']['message']['out']) {
continue;
}
try {
if (isset($update['update']['message']['media'])) {
getfiles($token, $res);
$bot_api_id = $message = $res['files'][$update['update']['message']['id']];
$bot_api_id_b256 = base64url_decode($bot_api_id);
$bot_api_id_rledecoded = rle_decode($bot_api_id_b256);
$message .= PHP_EOL.PHP_EOL.
'First 4 bytes: '.ord($bot_api_id_rledecoded[0]).' '.ord($bot_api_id_rledecoded[1]).' '.ord($bot_api_id_rledecoded[2]).' '.ord($bot_api_id_rledecoded[3]).PHP_EOL.
'First 4 bytes (single integer): '.(\danog\PHP\Struct::unpack('<i', substr($bot_api_id_rledecoded, 0, 4))[0]).PHP_EOL.
'bytes 8-16: '.(\danog\PHP\Struct::unpack('<q', substr($bot_api_id_rledecoded, 8, 8))[0]).PHP_EOL.
'bytes 16-24: '.(\danog\PHP\Struct::unpack('<q', substr($bot_api_id_rledecoded, 16, 8))[0]).PHP_EOL.
'Last byte: '.ord(substr($bot_api_id_rledecoded, -1)).PHP_EOL.
'Total length: '.strlen($bot_api_id_b256).PHP_EOL.
'Total length (rledecoded): '.strlen($bot_api_id_rledecoded).PHP_EOL.
PHP_EOL.'<b>param (value): start-end (length)</b><br>'.PHP_EOL;
//var_dump($update);
$bot_api = foreach_offset_length($bot_api_id_rledecoded);
$mtproto = $MadelineProto->get_download_info($update['update']['message']['media'])['InputFileLocation'];
$m = [];
unset($mtproto['_']);
if (isset($mtproto['version']))unset($mtproto['version']);
if (isset($update['update']['message']['media']['photo'])) $mtproto['id'] = $update['update']['message']['media']['photo']['id'];
if (isset($update['update']['message']['media']['photo'])) $mtproto['access_hash'] = $update['update']['message']['media']['photo']['access_hash'];
if (isset($update['update']['message']['media']['document'])) $mtproto['id'] = $update['update']['message']['media']['document']['id'];
if (isset($update['update']['message']['media']['document'])) $mtproto['access_hash'] = $update['update']['message']['media']['document']['access_hash'];
//var_dump($mtproto);
foreach ($mtproto as $key => $n) {
foreach ($bot_api as $bn) {
if ($bn['number'] === $n) {
$m [$bn['offset']+$bn['length']]= $key." (".$n."): ".$bn['offset'].'-'.($bn['offset']+$bn['length']).' ('.$bn['length'].')'.PHP_EOL;
unset($mtproto[$key]);
}
}
}
ksort($m);
foreach ($m as $key => $bn) {
$message .= $bn;
}
foreach ($mtproto as $key => $n) {
$message .= $key." (".$n."): not found".PHP_EOL;
}
$MadelineProto->messages->sendMessage(['peer' => $update['update']['message']['from_id'], 'message' => $message, 'reply_to_msg_id' => $update['update']['message']['id'], 'parse_mode' => 'html']);
}
} catch (\danog\MadelineProto\RPCErrorException $e) {
$MadelineProto->messages->sendMessage(['peer' => '@danogentili', 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
} catch (\danog\MadelineProto\Exception $e) {
$MadelineProto->messages->sendMessage(['peer' => '@danogentili', 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
}
try {
if (isset($update['update']['message']['media']) && $update['update']['message']['media'] == 'messageMediaPhoto' && $update['update']['message']['media'] == 'messageMediaDocument') {
$time = time();
// $file = $MadelineProto->download_to_dir($update['update']['message']['media'], '/tmp');
// $MadelineProto->messages->sendMessage(['peer' => $update['update']['message']['from_id'], 'message' => 'Downloaded to '.$file.' in '.(time() - $time).' seconds', 'reply_to_msg_id' => $update['update']['message']['id'], 'entities' => [['_' => 'messageEntityPre', 'offset' => 0, 'length' => strlen($res), 'language' => 'json']]]);
}
} catch (\danog\MadelineProto\RPCErrorException $e) {
$MadelineProto->messages->sendMessage(['peer' => '@danogentili', 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
}
}
}
echo 'Wrote '.\danog\MadelineProto\Serialization::serialize('b.madeline', $MadelineProto).' bytes'.PHP_EOL;
}

View File

@ -37,10 +37,10 @@ class API extends APIFactory
$this->APIFactory();
\danog\MadelineProto\Logger::log(['Ping...'], Logger::ULTRA_VERBOSE);
$pong = $this->ping([3]);
$pong = $this->ping(['ping_id' => 3]);
\danog\MadelineProto\Logger::log(['Pong: '.$pong['ping_id']], Logger::ULTRA_VERBOSE);
\danog\MadelineProto\Logger::log(['Getting future salts...'], Logger::ULTRA_VERBOSE);
$this->future_salts = $this->get_future_salts([3]);
//\danog\MadelineProto\Logger::log(['Getting future salts...'], Logger::ULTRA_VERBOSE);
//$this->future_salts = $this->get_future_salts(['num' => 3]);
$this->API->v = $this->API->getV();
\danog\MadelineProto\Logger::log(['MadelineProto is ready!'], Logger::NOTICE);
}

View File

@ -93,6 +93,6 @@ class APIFactory
{
$this->API->get_config();
return $this->API->method_call($this->namespace.$name, (isset($arguments[0]) && is_array($arguments[0])) ? $arguments[0] : []);
return $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] : []);
}
}

View File

@ -29,6 +29,7 @@ class MTProto extends PrimeModule
use \danog\MadelineProto\MTProtoTools\SeqNoHandler;
use \danog\MadelineProto\MTProtoTools\UpdateHandler;
use \danog\MadelineProto\TL\TL;
use \danog\MadelineProto\TL\Files;
use \danog\MadelineProto\Tools;
public $settings = [];

View File

@ -508,7 +508,7 @@ trait AuthKeyHandler
$padding = $this->random($this->posmod(-strlen($encrypted_data), 16));
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->auth_key['auth_key']);
$encrypted_message = $this->datacenter->auth_key['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
$res = $this->method_call('auth.bindTempAuthKey', ['perm_auth_key_id' => $perm_auth_key_id, 'nonce' => $nonce, 'expires_at' => $expires_at, 'encrypted_message' => $encrypted_message], $int_message_id);
$res = $this->method_call('auth.bindTempAuthKey', ['perm_auth_key_id' => $perm_auth_key_id, 'nonce' => $nonce, 'expires_at' => $expires_at, 'encrypted_message' => $encrypted_message], ['message_id' => $int_message_id]);
if ($res === true) {
\danog\MadelineProto\Logger::log(['Successfully binded temporary and permanent authorization keys.'], \danog\MadelineProto\Logger::NOTICE);

View File

@ -17,17 +17,22 @@ namespace danog\MadelineProto\MTProtoTools;
*/
trait CallHandler
{
public function method_call($method, $args = [], $message_id = null, $heavy = false)
public function method_call($method, $args = [], $aargs = ['message_id' => null, 'heavy' => false])
{
if (!is_array($args)) {
throw new \danog\MadelineProto\Exception("Arguments aren't an array.");
}
if (!is_array($aargs)) {
throw new \danog\MadelineProto\Exception("Additonal arguments aren't an array.");
}
$args = $this->botAPI_to_MTProto($args);
$serialized = $this->serialize_method($method, $args);
$content_related = $this->content_related($method);
for ($count = 1; $count <= $this->settings['max_tries']['query']; $count++) {
try {
\danog\MadelineProto\Logger::log(['Calling method (try number '.$count.' for '.$method.')...'], \danog\MadelineProto\Logger::VERBOSE);
$args = $this->get_named_method_args($method, $args);
$int_message_id = $this->send_message($this->serialize_method($method, $args), $this->content_related($method), $message_id);
$int_message_id = $this->send_message($serialized, $content_related, $aargs);
if ($method === 'http_wait') {
return true;
}
@ -53,7 +58,7 @@ trait CallHandler
continue;
}
$server_answer = $this->datacenter->incoming_messages[$this->datacenter->outgoing_messages[$int_message_id]['response']]['content']; // continue was not called, so I got a response
if ($heavy) {
if (isset($aargs['heavy']) && $aargs['heavy']) {
$this->datacenter->incoming_messages[$this->datacenter->outgoing_messages[$int_message_id]['response']]['content'] = [];
}
} catch (\danog\MadelineProto\Exception $e) {
@ -125,6 +130,9 @@ trait CallHandler
$server_answer = $server_answer['_'] === 'boolTrue';
break;
}
if (isset($aargs['botAPI']) && $aargs['botAPI']) {
$server_answer = $this->MTProto_to_botAPI($server_answer, $args);
}
} 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);
@ -136,7 +144,7 @@ trait CallHandler
sleep(1); // To avoid flooding
continue;
} finally {
if ($heavy && isset($int_message_id)) {
if (isset($aargs['heavy']) && $aargs['heavy'] && isset($int_message_id)) {
$this->datacenter->outgoing_messages[$int_message_id]['args'] = [];
}
}

View File

@ -21,10 +21,12 @@ trait MessageHandler
* Forming the message frame and sending message to server
* :param message: byte string to send.
*/
public function send_message($message_data, $content_related, $int_message_id = null)
public function send_message($message_data, $content_related, $aargs = [])
{
if ($int_message_id === null) {
if (!isset($aargs['message_id']) || $aargs['message_id'] === null) {
$int_message_id = $this->generate_message_id();
} else {
$int_message_id = $aargs['message_id'];
}
if (!is_int($int_message_id)) {
throw new \danog\MadelineProto\Exception("Specified message id isn't an integer");

View File

@ -316,7 +316,7 @@ trait PeerHandler
$res['bot_info'] = $full['full']['bot_info'];
}
if (isset($full['full']['profile_photo']['sizes'])) {
$res['photo'] = end($full['full']['profile_photo']['sizes']);
$res['photo'] = $this->photosize_to_botapi(end($full['full']['profile_photo']['sizes']), $full['User']);
}
$bio = '';
if ($full['type'] === 'user' && isset($res['username']) && !isset($res['about']) && $fullfetch) {
@ -338,7 +338,7 @@ trait PeerHandler
}
if (isset($full['full']['chat_photo']['sizes'])) {
$res['photo'] = end($full['full']['chat_photo']['sizes']);
$res['photo'] = $this->photosize_to_botapi(end($full['full']['chat_photo']['sizes']), $full['Chat']);
}
if (isset($full['full']['exported_invite']['link'])) {
$res['invite'] = $full['full']['exported_invite']['link'];
@ -361,7 +361,7 @@ trait PeerHandler
}
if (isset($full['full']['chat_photo']['sizes'])) {
$res['photo'] = end($full['full']['chat_photo']['sizes']);
$res['photo'] = $this->photosize_to_botapi(end($full['full']['chat_photo']['sizes']), $full['Chat']);
}
if (isset($full['full']['exported_invite']['link'])) {
$res['invite'] = $full['full']['exported_invite']['link'];

View File

@ -51,7 +51,11 @@ class Serialization
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
if (file_exists($filename)) {
$unserialized = unserialize(file_get_contents($filename));
$file = fopen($filename, 'r+');
flock($file, LOCK_EX);
$unserialized = unserialize(stream_get_contents($file));
flock($file, LOCK_UN);
fclose($file);
} else {
throw new Exception('File does not exist');
}

View File

@ -0,0 +1,153 @@
'wmlc' => [
0 => 'application/wmlc',
],
'dcr' => [
0 => 'application/x-director',
],
'dvi' => [
0 => 'application/x-dvi',
],
'gtar' => [
0 => 'application/x-gtar',
],
'php' => [
0 => 'application/x-httpd-php',
1 => 'application/php',
2 => 'application/x-php',
3 => 'text/php',
4 => 'text/x-php',
5 => 'application/x-httpd-php-source',
],
'swf' => [
0 => 'application/x-shockwave-flash',
],
'sit' => [
0 => 'application/x-stuffit',
],
'z' => [
0 => 'application/x-compress',
],
'mid' => [
0 => 'audio/midi',
],
'aif' => [
0 => 'audio/x-aiff',
1 => 'audio/aiff',
],
'ram' => [
0 => 'audio/x-pn-realaudio',
],
'rpm' => [
0 => 'audio/x-pn-realaudio-plugin',
],
'ra' => [
0 => 'audio/x-realaudio',
],
'rv' => [
0 => 'video/vnd.rn-realvideo',
],
'jp2' => [
0 => 'image/jp2',
1 => 'video/mj2',
2 => 'image/jpx',
3 => 'image/jpm',
],
'tiff' => [
0 => 'image/tiff',
],
'eml' => [
0 => 'message/rfc822',
],
'pem' => [
0 => 'application/x-x509-user-cert',
1 => 'application/x-pem-file',
],
'p10' => [
0 => 'application/x-pkcs10',
1 => 'application/pkcs10',
],
'p12' => [
0 => 'application/x-pkcs12',
],
'p7a' => [
0 => 'application/x-pkcs7-signature',
],
'p7c' => [
0 => 'application/pkcs7-mime',
1 => 'application/x-pkcs7-mime',
],
'p7r' => [
0 => 'application/x-pkcs7-certreqresp',
],
'p7s' => [
0 => 'application/pkcs7-signature',
],
'crt' => [
0 => 'application/x-x509-ca-cert',
1 => 'application/pkix-cert',
],
'crl' => [
0 => 'application/pkix-crl',
1 => 'application/pkcs-crl',
],
'pgp' => [
0 => 'application/pgp',
],
'gpg' => [
0 => 'application/gpg-keys',
],
'rsa' => [
0 => 'application/x-pkcs7',
],
'ics' => [
0 => 'text/calendar',
],
'zsh' => [
0 => 'text/x-scriptzsh',
],
'cdr' => [
0 => 'application/cdr',
1 => 'application/coreldraw',
2 => 'application/x-cdr',
3 => 'application/x-coreldraw',
4 => 'image/cdr',
5 => 'image/x-cdr',
6 => 'zz-application/zz-winassoc-cdr',
],
'wma' => [
0 => 'audio/x-ms-wma',
],
'vcf' => [
0 => 'text/x-vcard',
],
'srt' => [
0 => 'text/srt',
],
'vtt' => [
0 => 'text/vtt',
],
'ico' => [
0 => 'image/x-icon',
1 => 'image/x-ico',
2 => 'image/vnd.microsoft.icon',
],
'csv' => [
0 => 'text/x-comma-separated-values',
1 => 'text/comma-separated-values',
2 => 'application/vnd.msexcel',
],
'json' => [
0 => 'application/json',
1 => 'text/json',
],
];
public function upload($file, $file_name = '', $cb = null)
{
if (!file_exists($file)) {
throw new \danog\MadelineProto\Exception('Given file does not exist!');
}
if (empty($file_name)) {
$file_name = basename($file);
}
$file_size = filesize($file);

View File

@ -0,0 +1,705 @@
<?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\TL;
/**
* Manages serialization of file ids
*/
trait Files
{
public $all_mimes = [
'png' => [
0 => 'image/png',
1 => 'image/x-png',
],
'bmp' => [
0 => 'image/bmp',
1 => 'image/x-bmp',
2 => 'image/x-bitmap',
3 => 'image/x-xbitmap',
4 => 'image/x-win-bitmap',
5 => 'image/x-windows-bmp',
6 => 'image/ms-bmp',
7 => 'image/x-ms-bmp',
8 => 'application/bmp',
9 => 'application/x-bmp',
10 => 'application/x-win-bitmap',
],
'gif' => [
0 => 'image/gif',
],
'jpeg' => [
0 => 'image/jpeg',
1 => 'image/pjpeg',
],
'xspf' => [
0 => 'application/xspf+xml',
],
'vlc' => [
0 => 'application/videolan',
],
'wmv' => [
0 => 'video/x-ms-wmv',
1 => 'video/x-ms-asf',
],
'au' => [
0 => 'audio/x-au',
],
'ac3' => [
0 => 'audio/ac3',
],
'flac' => [
0 => 'audio/x-flac',
],
'ogg' => [
0 => 'audio/ogg',
1 => 'video/ogg',
2 => 'application/ogg',
],
'kmz' => [
0 => 'application/vnd.google-earth.kmz',
],
'kml' => [
0 => 'application/vnd.google-earth.kml+xml',
],
'rtx' => [
0 => 'text/richtext',
],
'rtf' => [
0 => 'text/rtf',
],
'jar' => [
0 => 'application/java-archive',
1 => 'application/x-java-application',
2 => 'application/x-jar',
],
'zip' => [
0 => 'application/x-zip',
1 => 'application/zip',
2 => 'application/x-zip-compressed',
3 => 'application/s-compressed',
4 => 'multipart/x-zip',
],
'7zip' => [
0 => 'application/x-compressed',
],
'xml' => [
0 => 'application/xml',
1 => 'text/xml',
],
'svg' => [
0 => 'image/svg+xml',
],
'3g2' => [
0 => 'video/3gpp2',
],
'3gp' => [
0 => 'video/3gp',
1 => 'video/3gpp',
],
'mp4' => [
0 => 'video/mp4',
],
'm4a' => [
0 => 'audio/x-m4a',
],
'f4v' => [
0 => 'video/x-f4v',
],
'flv' => [
0 => 'video/x-flv',
],
'webm' => [
0 => 'video/webm',
],
'aac' => [
0 => 'audio/x-acc',
],
'm4u' => [
0 => 'application/vnd.mpegurl',
],
'pdf' => [
0 => 'application/pdf',
1 => 'application/octet-stream',
],
'pptx' => [
0 => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
],
'ppt' => [
0 => 'application/powerpoint',
1 => 'application/vnd.ms-powerpoint',
2 => 'application/vnd.ms-office',
3 => 'application/msword',
],
'docx' => [
0 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
],
'xlsx' => [
0 => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
1 => 'application/vnd.ms-excel',
],
'xl' => [
0 => 'application/excel',
],
'xls' => [
0 => 'application/msexcel',
1 => 'application/x-msexcel',
2 => 'application/x-ms-excel',
3 => 'application/x-excel',
4 => 'application/x-dos_ms_excel',
5 => 'application/xls',
6 => 'application/x-xls',
],
'xsl' => [
0 => 'text/xsl',
],
'mpeg' => [
0 => 'video/mpeg',
],
'mov' => [
0 => 'video/quicktime',
],
'avi' => [
0 => 'video/x-msvideo',
1 => 'video/msvideo',
2 => 'video/avi',
3 => 'application/x-troff-msvideo',
],
'movie' => [
0 => 'video/x-sgi-movie',
],
'log' => [
0 => 'text/x-log',
],
'txt' => [
0 => 'text/plain',
],
'css' => [
0 => 'text/css',
],
'html' => [
0 => 'text/html',
],
'wav' => [
0 => 'audio/x-wav',
1 => 'audio/wave',
2 => 'audio/wav',
],
'xhtml' => [
0 => 'application/xhtml+xml',
],
'tar' => [
0 => 'application/x-tar',
],
'tgz' => [
0 => 'application/x-gzip-compressed',
],
'psd' => [
0 => 'application/x-photoshop',
1 => 'image/vnd.adobe.photoshop',
],
'exe' => [
0 => 'application/x-msdownload',
],
'js' => [
0 => 'application/x-javascript',
],
'mp3' => [
0 => 'audio/mpeg',
1 => 'audio/mpg',
2 => 'audio/mpeg3',
3 => 'audio/mp3',
],
'rar' => [
0 => 'application/x-rar',
1 => 'application/rar',
2 => 'application/x-rar-compressed',
],
'gzip' => [
0 => 'application/x-gzip',
],
'hqx' => [
0 => 'application/mac-binhex40',
1 => 'application/mac-binhex',
2 => 'application/x-binhex40',
3 => 'application/x-mac-binhex40',
],
'cpt' => [
0 => 'application/mac-compactpro',
],
'bin' => [
0 => 'application/macbinary',
1 => 'application/mac-binary',
2 => 'application/x-binary',
3 => 'application/x-macbinary',
],
'oda' => [
0 => 'application/oda',
],
'ai' => [
0 => 'application/postscript',
],
'smil' => [
0 => 'application/smil',
],
'mif' => [
0 => 'application/vnd.mif',
],
'wbxml' => [
0 => 'application/wbxml',
],
'wmlc' => [
0 => 'application/wmlc',
],
'dcr' => [
0 => 'application/x-director',
],
'dvi' => [
0 => 'application/x-dvi',
],
'gtar' => [
0 => 'application/x-gtar',
],
'php' => [
0 => 'application/x-httpd-php',
1 => 'application/php',
2 => 'application/x-php',
3 => 'text/php',
4 => 'text/x-php',
5 => 'application/x-httpd-php-source',
],
'swf' => [
0 => 'application/x-shockwave-flash',
],
'sit' => [
0 => 'application/x-stuffit',
],
'z' => [
0 => 'application/x-compress',
],
'mid' => [
0 => 'audio/midi',
],
'aif' => [
0 => 'audio/x-aiff',
1 => 'audio/aiff',
],
'ram' => [
0 => 'audio/x-pn-realaudio',
],
'rpm' => [
0 => 'audio/x-pn-realaudio-plugin',
],
'ra' => [
0 => 'audio/x-realaudio',
],
'rv' => [
0 => 'video/vnd.rn-realvideo',
],
'jp2' => [
0 => 'image/jp2',
1 => 'video/mj2',
2 => 'image/jpx',
3 => 'image/jpm',
],
'tiff' => [
0 => 'image/tiff',
],
'eml' => [
0 => 'message/rfc822',
],
'pem' => [
0 => 'application/x-x509-user-cert',
1 => 'application/x-pem-file',
],
'p10' => [
0 => 'application/x-pkcs10',
1 => 'application/pkcs10',
],
'p12' => [
0 => 'application/x-pkcs12',
],
'p7a' => [
0 => 'application/x-pkcs7-signature',
],
'p7c' => [
0 => 'application/pkcs7-mime',
1 => 'application/x-pkcs7-mime',
],
'p7r' => [
0 => 'application/x-pkcs7-certreqresp',
],
'p7s' => [
0 => 'application/pkcs7-signature',
],
'crt' => [
0 => 'application/x-x509-ca-cert',
1 => 'application/pkix-cert',
],
'crl' => [
0 => 'application/pkix-crl',
1 => 'application/pkcs-crl',
],
'pgp' => [
0 => 'application/pgp',
],
'gpg' => [
0 => 'application/gpg-keys',
],
'rsa' => [
0 => 'application/x-pkcs7',
],
'ics' => [
0 => 'text/calendar',
],
'zsh' => [
0 => 'text/x-scriptzsh',
],
'cdr' => [
0 => 'application/cdr',
1 => 'application/coreldraw',
2 => 'application/x-cdr',
3 => 'application/x-coreldraw',
4 => 'image/cdr',
5 => 'image/x-cdr',
6 => 'zz-application/zz-winassoc-cdr',
],
'wma' => [
0 => 'audio/x-ms-wma',
],
'vcf' => [
0 => 'text/x-vcard',
],
'srt' => [
0 => 'text/srt',
],
'vtt' => [
0 => 'text/vtt',
],
'ico' => [
0 => 'image/x-icon',
1 => 'image/x-ico',
2 => 'image/vnd.microsoft.icon',
],
'csv' => [
0 => 'text/x-comma-separated-values',
1 => 'text/comma-separated-values',
2 => 'application/vnd.msexcel',
],
'json' => [
0 => 'application/json',
1 => 'text/json',
],
];
public function upload($file, $file_name = '', $cb = null)
{
if (!file_exists($file)) {
throw new \danog\MadelineProto\Exception('Given file does not exist!');
}
if (empty($file_name)) {
$file_name = basename($file);
}
$file_size = filesize($file);
if ($file_size > 1500 * 1024 * 1024) {
throw new \danog\MadelineProto\Exception('Given file is too big!');
}
if ($cb === null) {
$cb = function ($percent) {
\danog\MadelineProto\Logger::log(['Upload status: '.$percent.'%'], \danog\MadelineProto\Logger::NOTICE);
};
}
$part_size = 512 * 1024;
$part_total_num = (int) ceil($file_size / $part_size);
$part_num = 0;
$method = $file_size > 10 * 1024 * 1024 ? 'upload.saveBigFilePart' : 'upload.saveFilePart';
$constructor = $file_size > 10 * 1024 * 1024 ? 'inputFileBig' : 'inputFile';
$file_id = \danog\PHP\Struct::unpack('<q', $this->random(8))[0];
$f = fopen($file, 'r');
fseek($f, 0);
while (ftell($f) !== $file_size) {
if (!$this->method_call($method, ['file_id' => $file_id, 'file_part' => $part_num++, 'file_total_parts' => $part_total_num, 'bytes' => stream_get_contents($f, $part_size)], ['heavy' => true])) {
throw new \danog\MadelineProto\Exception('An error occurred while uploading file part '.$part_num);
}
$cb(ftell($f) * 100 / $file_size);
}
fclose($f);
return ['_' => $constructor, 'id' => $file_id, 'parts' => $part_total_num, 'name' => $file_name, 'md5_checksum' => md5_file($file)];
}
public function get_extension_from_mime($mime)
{
foreach ($this->all_mimes as $key => $value) {
if (array_search($mime, $value) !== false) {
return '.'.$key;
}
}
return '';
}
public function base64url_decode($data)
{
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}
public function base64url_encode($data)
{
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
function rle_decode($string) {
$new = '';
$last = '';
$null = chr(0);
foreach (str_split($string) as $cur) {
if ($last === $null) {
$new .= str_repeat($last, ord($cur));
$last = '';
} else {
$new .= $last;
$last = $cur;
}
}
$string = $new.$last;
return $string;
}
function rle_encode($string) {
$new = '';
$count = 0;
$null = chr(0);
foreach (str_split($string) as $cur) {
if ($cur === $null) {
$count++;
} else {
if ($count > 0) {
$new .= $null.chr($count);
$count = 0;
}
$new .= $cur;
}
}
return $new;
}
public function photosize_to_botapi($photo, $message_media, $thumbnail = false) {
$ext = $this->get_extension_from_location(['_' => 'inputFileLocation', 'volume_id' => $photo['location']['volume_id'], 'local_id' => $photo['location']['local_id'], 'secret' => $photo['location']['secret'], 'dc_id' => $photo['location']['dc_id']], '.jpg');
$data = \danog\PHP\Struct::pack('<iiqqqqib', $thumbnail ? 0 : 2, $photo['location']['dc_id'], $thumbnail ? 0 : $message_media['id'], $thumbnail ? 0 : $message_media['access_hash'],$photo['location']['volume_id'],$photo['location']['secret'], $photo['location']['local_id'], 2);
return [
'file_id' => $this->base64url_encode($this->rle_encode($data)),
'width' => $photo['w'],
'height' => $photo['h'],
'file_size' => isset($photo['size']) ? $photo['size'] : strlen($photo['bytes']),
'mime_type' => 'image/jpeg',
'file_name' => $photo['location']['volume_id'].'_'.$photo['location']['local_id'].$ext
];
}
public function unpack_file_id($file_id) {
$file_id = $this->rle_decode($this->base64url_decode($file_id));
$res = [];
$type = \danog\PHP\Struct::unpack('<i', substr($file_id, 0, 4))[0];
switch ($type) {
case 0:
$constructor = ['_' => 'photo', 'sizes' => []];
list($type, $constructor['sizes'][0]['location']['dc_id'], $constructor['id'], $constructor['access_hash'], $constructor['sizes'][0]['location']['volume_id'],$constructor['sizes'][0]['location']['secret'],$constructor['sizes'][0]['location']['local_id'], $verify) = \danog\PHP\Struct::unpack('<iiqqqqib', $file_id);
if ($verify !== 2) {
throw new Exception('Invalid last byte');
}
$res['type'] = 'photo';
$res['MessageMedia'] = ['_' => 'messageMediaPhoto', 'photo' => $constructor, 'caption' => ''];
return $res;
case 2:
$constructor = ['_' => 'photo', 'sizes' => []];
list($type, $constructor['sizes'][0]['location']['dc_id'], $constructor['id'], $constructor['access_hash'], $constructor['sizes'][0]['location']['volume_id'],$constructor['sizes'][0]['location']['secret'],$constructor['sizes'][0]['location']['local_id'], $verify) = \danog\PHP\Struct::unpack('<iiqqqqib', $file_id);
if ($verify !== 2) {
throw new Exception('Invalid last byte');
}
$res['type'] = 'photo';
$res['MessageMedia'] = ['_' => 'messageMediaPhoto', 'photo' => $constructor, 'caption' => ''];
return $res;
case 3:
$constructor = ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => true]]];
list($type, $constructor['dc_id'], $constructor['id'], $constructor['access_hash'], $verify) = \danog\PHP\Struct::unpack('<iiqqb', $file_id);
if ($verify !== 2) {
throw new Exception('Invalid last byte');
}
$res['type'] = 'voice';
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 4:
$res['type'] = 'videos';
$constructor = ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeVideo']]];
list($type, $constructor['dc_id'], $constructor['id'], $constructor['access_hash'], $verify) = \danog\PHP\Struct::unpack('<iiqqb', $file_id);
if ($verify !== 2) {
throw new Exception('Invalid last byte');
}
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 5:
$res['type'] = 'document';
$constructor = ['_' => 'document', 'mime_type' => '', 'attributes' => []];
list($type, $constructor['dc_id'], $constructor['id'], $constructor['access_hash'], $verify) = \danog\PHP\Struct::unpack('<iiqqb', $file_id);
if ($verify !== 2) {
throw new Exception('Invalid last byte');
}
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
default:
throw new Exception("Invalid file type detected (".$type.')');
}
}
public function get_extension_from_location($location, $default) {
$this->switch_dc($location['dc_id']);
$res = $this->method_call('upload.getFile', ['location' => $location, 'offset' => 0, 'limit' => 1],['heavy' => true]);
switch ($res['type']['_']) {
case 'storage.fileJpeg': return '.jpg';
case 'storage.fileGif': return '.gif';
case 'storage.filePng': return '.png';
case 'storage.filePdf': return '.pdf';
case 'storage.fileMp3': return '.mp3';
case 'storage.fileMov': return '.mov';
case 'storage.fileMp4': return '.mp4';
case 'storage.fileWebp': return '.webp';
default: return $default;
}
}
public function get_download_info($message_media)
{
if (is_string($message_media)) {
$message_media = $this->unpack_file_id($message_media)['MessageMedia'];
}
if (!isset($message_media['_'])) {
return $message_media;
}
$res = [];
switch ($message_media['_']) {
case 'messageMediaPhoto':
$photo = end($message_media['photo']['sizes']);
$res['name'] = $photo['location']['volume_id'].'_'.$photo['location']['local_id'];
$res['InputFileLocation'] = ['_' => 'inputFileLocation', 'volume_id' => $photo['location']['volume_id'], 'local_id' => $photo['location']['local_id'], 'secret' => $photo['location']['secret'], 'dc_id' => $photo['location']['dc_id']];
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], '.jpg');
$res['mime'] = 'image/jpeg';
return $res;
case 'photoSize':
case 'photoCachedSize':
$res['name'] = $message_media['location']['volume_id'].'_'.$message_media['location']['local_id'];
$res['InputFileLocation'] = ['_' => 'inputFileLocation', 'volume_id' => $message_media['location']['volume_id'], 'local_id' => $message_media['location']['local_id'], 'secret' => $message_media['location']['secret'], 'dc_id' => $message_media['location']['dc_id']];
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], '.jpg');
$res['mime'] = 'image/jpeg';
return $res;
case 'messageMediaDocument':
foreach ($message_media['document']['attributes'] as $attribute) {
switch ($attribute['_']) {
case 'documentAttributeFilename':
$pathinfo = pathinfo($attribute['file_name']);
if (isset($pathinfo['extension'])) $res['ext'] = '.'.$pathinfo['extension'];
$res['name'] = $pathinfo['filename'];
break;
case 'documentAttributeAudio':
$audio = $attribute;
break;
}
}
if (isset($audio) && isset($audio['title']) && !isset($res['name'])) {
$res['name'] = $audio['title'];
if (isset($audio['performer'])) {
$res['name'] .= ' - '.$audio['performer'];
}
}
$res ['InputFileLocation'] = ['_' => 'inputDocumentFileLocation', 'id' => $message_media['document']['id'], 'access_hash' => $message_media['document']['access_hash'], 'version' => isset($message_media['document']['version']) ? $message_media['document']['version'] : 0, 'dc_id' => $message_media['document']['dc_id']];
if (!isset($res['ext'])) {
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], $this->get_extension_from_mime($message_media['document']['mime_type']));
}
if (!isset($res['name'])) {
$res['name'] = $message_media['document']['access_hash'];
}
$res['name'] .= '_'.$message_media['document']['id'];
$res['mime'] = $message_media['document']['mime_type'];
return $res;
default:
throw new \danog\MadelineProto\Exception('Invalid constructor provided: '.$message_media['_']);
}
}
public function download_to_dir($message_media, $dir, $cb = null)
{
$info = $this->get_download_info($message_media);
return $this->download_to_file($info, $dir.'/'.$info['name'].$info['ext'], $cb);
}
public function download_to_file($message_media, $file, $cb = null)
{
$file = str_replace('//', '/', $file);
$info = $this->get_download_info($message_media);
if (!file_exists($file)) touch ($file);
$stream = fopen($file, 'r+b');
flock($stream, LOCK_EX);
$this->download_to_stream($info, $stream, $cb, filesize($file), -1);
flock($stream, LOCK_UN);
fclose($stream);
clearstatcache();
return $file;
}
public function download_to_stream($message_media, $stream, $cb = null, $offset = 0, $end = -1)
{
if ($cb === null) {
$cb = function ($percent) {
\danog\MadelineProto\Logger::log(['Download status: '.$percent.'%'], \danog\MadelineProto\Logger::NOTICE);
};
}
$info = $this->get_download_info($message_media);
if (stream_get_meta_data($stream)['seekable']) {
fseek($stream, $offset);
}
$downloaded_size = 0;
$size = $end - $offset;
$part_size = 512 * 1024;
$percent = 0;
if (isset($info['InputFileLocation']['dc_id'])) {
$this->switch_dc($info['InputFileLocation']['dc_id']);
}
$end = false;
while (true) {
//$real_part_size = (($offset + $part_size > $end) && $end !== -1) ? $part_size - (($offset + $part_size) - $end) : $part_size;
try {
$res = $this->method_call('upload.getFile', ['location' => $info['InputFileLocation'], 'offset' => $offset, 'limit' => $part_size],['heavy' => true]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->getMessage() === 'OFFSET_INVALID') break; else throw $e;
}
while ($res['type']['_'] === 'storage.fileUnknown' && $res['bytes'] === '') {
$dc = 1;
$this->switch_dc($dc);
$res = $this->method_call('upload.getFile', ['location' => $info['InputFileLocation'], 'offset' => $offset, 'limit' => $real_part_size], ['heavy' => true]);
$dc++;
}
if ($res['bytes'] === '') {
break;
}
if ($end !== -1 && strlen($res['bytes']) + $downloaded_size > $size) {
$res['bytes'] = substr($res['bytes'], 0, (strlen($res['bytes']) + $downloaded_size) - $size);
$end = true;
}
$offset += strlen($res['bytes']);
$downloaded_size += strlen($res['bytes']);
\danog\MadelineProto\Logger::log([fwrite($stream, $res['bytes'])], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
if ($end) break;
//\danog\MadelineProto\Logger::log([$offset, $size, ftell($stream)], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
if ($end !== -1) $cb($percent = $downloaded_size * 100 / $size);
}
if ($end === -1) $cb(100);
return true;
}
}

View File

@ -230,11 +230,289 @@ trait TL
{
return html_entity_decode(str_replace('<br />', "\n", $stuff));
}
public function parse_buttons($rows) {
$newrows = [];
$key = 0;
$button_key = 0;
foreach ($rows as $row) {
$newrows[$key] = ['_' => 'keyboardButtonRow', 'buttons' => []];
foreach ($row as $button) {
$newrows[$key]['buttons'][$button_key] = ['_' => 'keyboardButton', 'text' => $button['text']];
if (isset($button['url'])) {
$newrows[$key]['buttons'][$button_key]['_'] = 'keyboardButtonUrl';
$newrows[$key]['buttons'][$button_key]['url'] = $button['url'];
} else if (isset($button['callback_data'])) {
$newrows[$key]['buttons'][$button_key]['_'] = 'keyboardButtonCallback';
$newrows[$key]['buttons'][$button_key]['data'] = $button['callback_data'];
} else if (isset($button['switch_inline_query'])) {
$newrows[$key]['buttons'][$button_key]['_'] = 'keyboardButtonSwitchInline';
$newrows[$key]['buttons'][$button_key]['same_peer'] = false;
$newrows[$key]['buttons'][$button_key]['query'] = $button['switch_inline_query'];
} else if (isset($button['switch_inline_query_current_chat'])) {
$newrows[$key]['buttons'][$button_key]['_'] = 'keyboardButtonSwitchInline';
$newrows[$key]['buttons'][$button_key]['same_peer'] = true;
$newrows[$key]['buttons'][$button_key]['query'] = $button['switch_inline_query_current_chat'];
} else if (isset($button['callback_game'])) {
$newrows[$key]['buttons'][$button_key]['_'] = 'keyboardButtonGame';
$newrows[$key]['buttons'][$button_key]['text'] = $button['callback_game'];
} else if (isset($button['request_contact'])) {
$newrows[$key]['buttons'][$button_key]['_'] = 'keyboardButtonRequestPhone';
} else if (isset($button['request_location'])) {
$newrows[$key]['buttons'][$button_key]['_'] = 'keyboardButtonRequestGeoLocation';
}
$button_key++;
}
$key++;
}
return $newrows;
}
public function serialize_params($tl, $arguments)
{
$serialized = '';
public function parse_reply_markup($markup) {
if (isset($markup['force_reply']) && $markup['force_reply']) {
$markup['_'] = 'replyKeyboardForceReply';
unset($markup['force_reply']);
}
if (isset($markup['remove_keyboard']) && $markup['remove_keyboard']) {
$markup['_'] = 'replyKeyboardHide';
unset($markup['remove_keyboard']);
}
if (isset($markup['keyboard'])) {
$markup['_'] = 'replyKeyboardMarkup';
if (isset($markup['resize_keyboard'])) {
$markup['resize'] = $markup['resize_keyboard'];
unset($markup['resize_keyboard']);
}
if (isset($markup['one_time_keyboard'])) {
$markup['single_use'] = $markup['one_time_keyboard'];
unset($markup['one_time_keyboard']);
}
$markup['rows'] = $this->parse_buttons($markup['keyboard']);
unset($markup['keyboard']);
}
if (isset($markup['inline_keyboard'])) {
$markup['_'] = 'replyInlineMarkup';
$markup['rows'] = $this->parse_buttons($markup['inline_keyboard']);
unset($markup['inline_keyboard']);
}
return $markup;
}
public function MTProto_to_botAPI($data, $sent_arguments = []) {
$newd = [];
if (!isset($data['_'])) {
foreach ($data as $key => $element) {
$newd[$key] = $this->MTProto_to_botAPI($element, $sent_arguments);
}
return $newd;
}
switch ($data['_']) {
case 'updateShortSentMessage':
$newd['message_id'] = $data['id'];
$newd['date'] = $data['date'];
$newd['text'] = $sent_arguments['message'];
if ($data['out']) $newd['from'] = $this->get_pwr_chat($this->datacenter->authorization['user']);
$newd['chat'] = $this->get_pwr_chat($sent_arguments['peer']);
if (isset($data['entities'])) $newd['entities'] = $this->MTProto_to_botAPI($data['entities'], $sent_arguments);
if (isset($data['media'])) $newd = array_merge($newd, $this->MTProto_to_botAPI($data['media'], $sent_arguments));
return $newd;
case 'updateNewChannelMessage':
case 'updateNewMessage':
return $this->MTProto_to_botAPI($data['message']);
case 'message':
$newd['message_id'] = $data['id'];
$newd['date'] = $data['date'];
$newd['text'] = $data['message'];
$newd['post'] = $data['post'];
$newd['silent'] = $data['silent'];
if (isset($data['from_id'])) $newd['from'] = $this->get_pwr_chat($data['from_id']);
$newd['chat'] = $this->get_pwr_chat($data['to_id']);
if (isset($data['entities'])) $newd['entities'] = $this->MTProto_to_botAPI($data['entities'], $sent_arguments);
if (isset($data['views'])) $newd['views'] = $data['views'];
if (isset($data['edit_date'])) $newd['edit_date'] = $data['edit_date'];
if (isset($data['via_bot_id'])) $newd['via_bot'] = $this->get_pwr_chat($data['via_bot_id']);
if (isset($data['fwd_from']['from_id'])) $newd['froward_from'] = $this->get_pwr_chat($data['fwd_from']['from_id']);
if (isset($data['fwd_from']['channel_id'])) $newd['forward_from_chat'] = $this->get_pwr_chat($data['fwd_from']['channel_id']);
if (isset($data['fwd_from']['date'])) $newd['forward_date'] = $data['fwd_from']['date'];
if (isset($data['fwd_from']['channel_post'])) $newd['forward_from_message_id'] = $data['fwd_from']['channel_post'];
if (isset($data['media'])) $newd = array_merge($newd, $this->MTProto_to_botAPI($data['media'], $sent_arguments));
return $newd;
case 'messageEntityMention':
unset($data['_']);
$data['type'] = 'mention';
return $data;
case 'messageEntityHashtag':
unset($data['_']);
$data['type'] = 'hashtag';
return $data;
case 'messageEntityBotCommand':
unset($data['_']);
$data['type'] = 'bot_command';
return $data;
case 'messageEntityUrl':
unset($data['_']);
$data['type'] = 'url';
return $data;
case 'messageEntityEmail':
unset($data['_']);
$data['type'] = 'email';
return $data;
case 'messageEntityBold':
unset($data['_']);
$data['type'] = 'bold';
return $data;
case 'messageEntityItalic':
unset($data['_']);
$data['type'] = 'italic';
return $data;
case 'messageEntityCode':
unset($data['_']);
$data['type'] = 'code';
return $data;
case 'messageEntityPre':
unset($data['_']);
$data['type'] = 'pre';
return $data;
case 'messageEntityTextUrl':
unset($data['_']);
$data['type'] = 'text_url';
return $data;
case 'messageEntityMentionName':
unset($data['_']);
$data['type'] = 'text_mention';
$data['user'] = $this->get_pwr_chat($data['user_id']);
unset($data['user_id']);
return $data;
case 'messageMediaPhoto':
$res['caption'] = $data['caption'];
$res['photo'] = [];
foreach ($data['photo']['sizes'] as $key => $photo) {
$res['photo'][$key] = $this->photosize_to_botapi($photo, $data['photo']);
}
return $res;
case 'messageMediaEmpty':
return [];
case 'messageMediaDocument':
$type = 5;
$type_name = 'document';
$res = [];
if ($data['document']['thumb']['_'] === 'photoSize') $res['thumb'] = $this->photosize_to_botapi($data['document']['thumb'], $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['file_name'] = $pathinfo['filename'];
break;
case 'documentAttributeAudio':
$audio = $attribute;
$type_name = 'audio';
if ($attribute['voice']) {
$type = 3;
$type_name = 'voice';
}
$res['duration'] = $attribute['duration'];
if (isset($attribute['performer'])) $res['performer'] = $attribute['performer'];
if (isset($attribute['title'])) $res['title'] = $attribute['title'];
if (isset($attribute['waveform'])) $res['title'] = $attribute['waveform'];
break;
case 'documentAttributeVideo':
$type = 4;
$type_name = 'video';
$res['width'] = $attribute['w'];
$res['height'] = $attribute['h'];
$res['duration'] = $attribute['duration'];
break;
case 'documentAttributeImageSize':
$res['width'] = $attribute['w'];
$res['height'] = $attribute['h'];
break;
case 'documentAttributeAnimated':
$res['animated'] = true;
break;
case 'documentAttributeHasStickers':
$res['has_stickers'] = true;
break;
case 'documentAttributeSticker':
$type_name = 'sticker';
$res['mask'] = $attribute['mask'];
$res['emoji'] = $attribute['alt'];
$res['sticker_set'] = $attribute['stickerset'];
if (isset($attribute['mask_coords'])) $res['mask_coords'] = $attribute['mask_coords'];
break;
}
}
if (isset($audio) && isset($audio['title']) && !isset($res['file_name'])) {
$res['file_name'] = $audio['title'];
if (isset($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'];
if (isset($res['ext'])) {
$res['file_name'] .= $res['ext'];
unset($res['ext']);
} else {
$res['file_name'] .= $this->get_extension_from_mime($data['document']['mime_type']);
}
$res['file_size'] = $data['document']['size'];
$res['mime_type'] = $data['document']['mime_type'];
$res['file_id'] = $this->base64url_encode($this->rle_encode(\danog\PHP\Struct::pack('<iiqqb', $type, $data['document']['dc_id'], $data['document']['id'], $data['document']['access_hash'], 2)));
return [$type_name => $res, 'caption' => $data['caption']];
default:
throw new Exception("Can't convert ".$data['_'].' to a bot API object');
}
}
public function botAPI_to_MTProto($arguments) {
if (isset($arguments['disable_web_page_preview'])) {
$arguments['no_webpage'] = $arguments['disable_web_page_preview'];
}
if (isset($arguments['disable_notification'])) {
$arguments['silent'] = $arguments['disable_notification'];
}
if (isset($arguments['reply_to_message_id'])) {
$arguments['reply_to_msg_id'] = $arguments['reply_to_message_id'];
}
if (isset($arguments['chat_id'])) {
$arguments['peer'] = $arguments['chat_id'];
}
if (isset($arguments['text'])) {
$arguments['message'] = $arguments['text'];
}
if (isset($arguments['reply_markup'])) {
$arguments['reply_markup'] = $this->parse_reply_markup($arguments['reply_markup']);
}
if (isset($arguments['parse_mode'])) {
$arguments = $this->parse_mode($arguments);
}
return $arguments;
}
public function parse_mode($arguments) {
if (preg_match('/markdown/i', $arguments['parse_mode'])) {
$arguments['message'] = str_replace("\n", '', \Parsedown::instance()->line($arguments['message']));
$arguments['parse_mode'] = 'HTML';
@ -299,8 +577,13 @@ trait TL
}
}
$arguments['message'] = $nmessage;
unset($arguments['parse_mode']);
}
}
return $arguments;
}
public function serialize_params($tl, $arguments)
{
$serialized = '';
$flags = 0;
foreach ($tl['params'] as $cur_flag) {
if ($cur_flag['flag']) {

View File

@ -17,561 +17,27 @@ namespace danog\MadelineProto\Wrappers;
*/
trait FilesHandler
{
public $all_mimes = [
'png' => [
0 => 'image/png',
1 => 'image/x-png',
],
'bmp' => [
0 => 'image/bmp',
1 => 'image/x-bmp',
2 => 'image/x-bitmap',
3 => 'image/x-xbitmap',
4 => 'image/x-win-bitmap',
5 => 'image/x-windows-bmp',
6 => 'image/ms-bmp',
7 => 'image/x-ms-bmp',
8 => 'application/bmp',
9 => 'application/x-bmp',
10 => 'application/x-win-bitmap',
],
'gif' => [
0 => 'image/gif',
],
'jpeg' => [
0 => 'image/jpeg',
1 => 'image/pjpeg',
],
'xspf' => [
0 => 'application/xspf+xml',
],
'vlc' => [
0 => 'application/videolan',
],
'wmv' => [
0 => 'video/x-ms-wmv',
1 => 'video/x-ms-asf',
],
'au' => [
0 => 'audio/x-au',
],
'ac3' => [
0 => 'audio/ac3',
],
'flac' => [
0 => 'audio/x-flac',
],
'ogg' => [
0 => 'audio/ogg',
1 => 'video/ogg',
2 => 'application/ogg',
],
'kmz' => [
0 => 'application/vnd.google-earth.kmz',
],
'kml' => [
0 => 'application/vnd.google-earth.kml+xml',
],
'rtx' => [
0 => 'text/richtext',
],
'rtf' => [
0 => 'text/rtf',
],
'jar' => [
0 => 'application/java-archive',
1 => 'application/x-java-application',
2 => 'application/x-jar',
],
'zip' => [
0 => 'application/x-zip',
1 => 'application/zip',
2 => 'application/x-zip-compressed',
3 => 'application/s-compressed',
4 => 'multipart/x-zip',
],
'7zip' => [
0 => 'application/x-compressed',
],
'xml' => [
0 => 'application/xml',
1 => 'text/xml',
],
'svg' => [
0 => 'image/svg+xml',
],
'3g2' => [
0 => 'video/3gpp2',
],
'3gp' => [
0 => 'video/3gp',
1 => 'video/3gpp',
],
'mp4' => [
0 => 'video/mp4',
],
'm4a' => [
0 => 'audio/x-m4a',
],
'f4v' => [
0 => 'video/x-f4v',
],
'flv' => [
0 => 'video/x-flv',
],
'webm' => [
0 => 'video/webm',
],
'aac' => [
0 => 'audio/x-acc',
],
'm4u' => [
0 => 'application/vnd.mpegurl',
],
'pdf' => [
0 => 'application/pdf',
1 => 'application/octet-stream',
],
'pptx' => [
0 => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
],
'ppt' => [
0 => 'application/powerpoint',
1 => 'application/vnd.ms-powerpoint',
2 => 'application/vnd.ms-office',
3 => 'application/msword',
],
'docx' => [
0 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
],
'xlsx' => [
0 => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
1 => 'application/vnd.ms-excel',
],
'xl' => [
0 => 'application/excel',
],
'xls' => [
0 => 'application/msexcel',
1 => 'application/x-msexcel',
2 => 'application/x-ms-excel',
3 => 'application/x-excel',
4 => 'application/x-dos_ms_excel',
5 => 'application/xls',
6 => 'application/x-xls',
],
'xsl' => [
0 => 'text/xsl',
],
'mpeg' => [
0 => 'video/mpeg',
],
'mov' => [
0 => 'video/quicktime',
],
'avi' => [
0 => 'video/x-msvideo',
1 => 'video/msvideo',
2 => 'video/avi',
3 => 'application/x-troff-msvideo',
],
'movie' => [
0 => 'video/x-sgi-movie',
],
'log' => [
0 => 'text/x-log',
],
'txt' => [
0 => 'text/plain',
],
'css' => [
0 => 'text/css',
],
'html' => [
0 => 'text/html',
],
'wav' => [
0 => 'audio/x-wav',
1 => 'audio/wave',
2 => 'audio/wav',
],
'xhtml' => [
0 => 'application/xhtml+xml',
],
'tar' => [
0 => 'application/x-tar',
],
'tgz' => [
0 => 'application/x-gzip-compressed',
],
'psd' => [
0 => 'application/x-photoshop',
1 => 'image/vnd.adobe.photoshop',
],
'exe' => [
0 => 'application/x-msdownload',
],
'js' => [
0 => 'application/x-javascript',
],
'mp3' => [
0 => 'audio/mpeg',
1 => 'audio/mpg',
2 => 'audio/mpeg3',
3 => 'audio/mp3',
],
'rar' => [
0 => 'application/x-rar',
1 => 'application/rar',
2 => 'application/x-rar-compressed',
],
'gzip' => [
0 => 'application/x-gzip',
],
'hqx' => [
0 => 'application/mac-binhex40',
1 => 'application/mac-binhex',
2 => 'application/x-binhex40',
3 => 'application/x-mac-binhex40',
],
'cpt' => [
0 => 'application/mac-compactpro',
],
'bin' => [
0 => 'application/macbinary',
1 => 'application/mac-binary',
2 => 'application/x-binary',
3 => 'application/x-macbinary',
],
'oda' => [
0 => 'application/oda',
],
'ai' => [
0 => 'application/postscript',
],
'smil' => [
0 => 'application/smil',
],
'mif' => [
0 => 'application/vnd.mif',
],
'wbxml' => [
0 => 'application/wbxml',
],
'wmlc' => [
0 => 'application/wmlc',
],
'dcr' => [
0 => 'application/x-director',
],
'dvi' => [
0 => 'application/x-dvi',
],
'gtar' => [
0 => 'application/x-gtar',
],
'php' => [
0 => 'application/x-httpd-php',
1 => 'application/php',
2 => 'application/x-php',
3 => 'text/php',
4 => 'text/x-php',
5 => 'application/x-httpd-php-source',
],
'swf' => [
0 => 'application/x-shockwave-flash',
],
'sit' => [
0 => 'application/x-stuffit',
],
'z' => [
0 => 'application/x-compress',
],
'mid' => [
0 => 'audio/midi',
],
'aif' => [
0 => 'audio/x-aiff',
1 => 'audio/aiff',
],
'ram' => [
0 => 'audio/x-pn-realaudio',
],
'rpm' => [
0 => 'audio/x-pn-realaudio-plugin',
],
'ra' => [
0 => 'audio/x-realaudio',
],
'rv' => [
0 => 'video/vnd.rn-realvideo',
],
'jp2' => [
0 => 'image/jp2',
1 => 'video/mj2',
2 => 'image/jpx',
3 => 'image/jpm',
],
'tiff' => [
0 => 'image/tiff',
],
'eml' => [
0 => 'message/rfc822',
],
'pem' => [
0 => 'application/x-x509-user-cert',
1 => 'application/x-pem-file',
],
'p10' => [
0 => 'application/x-pkcs10',
1 => 'application/pkcs10',
],
'p12' => [
0 => 'application/x-pkcs12',
],
'p7a' => [
0 => 'application/x-pkcs7-signature',
],
'p7c' => [
0 => 'application/pkcs7-mime',
1 => 'application/x-pkcs7-mime',
],
'p7r' => [
0 => 'application/x-pkcs7-certreqresp',
],
'p7s' => [
0 => 'application/pkcs7-signature',
],
'crt' => [
0 => 'application/x-x509-ca-cert',
1 => 'application/pkix-cert',
],
'crl' => [
0 => 'application/pkix-crl',
1 => 'application/pkcs-crl',
],
'pgp' => [
0 => 'application/pgp',
],
'gpg' => [
0 => 'application/gpg-keys',
],
'rsa' => [
0 => 'application/x-pkcs7',
],
'ics' => [
0 => 'text/calendar',
],
'zsh' => [
0 => 'text/x-scriptzsh',
],
'cdr' => [
0 => 'application/cdr',
1 => 'application/coreldraw',
2 => 'application/x-cdr',
3 => 'application/x-coreldraw',
4 => 'image/cdr',
5 => 'image/x-cdr',
6 => 'zz-application/zz-winassoc-cdr',
],
'wma' => [
0 => 'audio/x-ms-wma',
],
'vcf' => [
0 => 'text/x-vcard',
],
'srt' => [
0 => 'text/srt',
],
'vtt' => [
0 => 'text/vtt',
],
'ico' => [
0 => 'image/x-icon',
1 => 'image/x-ico',
2 => 'image/vnd.microsoft.icon',
],
'csv' => [
0 => 'text/x-comma-separated-values',
1 => 'text/comma-separated-values',
2 => 'application/vnd.msexcel',
],
'json' => [
0 => 'application/json',
1 => 'text/json',
],
];
public function upload($file, $file_name = '', $cb = null)
{
if (!file_exists($file)) {
throw new \danog\MadelineProto\Exception('Given file does not exist!');
}
if (empty($file_name)) {
$file_name = basename($file);
}
$file_size = filesize($file);
if ($file_size > 1500 * 1024 * 1024) {
throw new \danog\MadelineProto\Exception('Given file is too big!');
}
if ($cb === null) {
$cb = function ($percent) {
\danog\MadelineProto\Logger::log(['Upload status: '.$percent.'%'], \danog\MadelineProto\Logger::NOTICE);
};
}
$part_size = 512 * 1024;
$part_total_num = (int) ceil($file_size / $part_size);
$part_num = 0;
$method = $file_size > 10 * 1024 * 1024 ? 'upload.saveBigFilePart' : 'upload.saveFilePart';
$constructor = $file_size > 10 * 1024 * 1024 ? 'inputFileBig' : 'inputFile';
$file_id = \danog\PHP\Struct::unpack('<q', $this->API->random(8))[0];
$f = fopen($file, 'r');
fseek($f, 0);
while (ftell($f) !== $file_size) {
if (!$this->API->method_call($method, ['file_id' => $file_id, 'file_part' => $part_num++, 'file_total_parts' => $part_total_num, 'bytes' => stream_get_contents($f, $part_size)], null, true)) {
throw new \danog\MadelineProto\Exception('An error occurred while uploading file part '.$part_num);
}
$cb(ftell($f) * 100 / $file_size);
}
fclose($f);
return ['_' => $constructor, 'id' => $file_id, 'parts' => $part_total_num, 'name' => $file_name, 'md5_checksum' => md5_file($file)];
public function upload($file, $file_name = '', $cb = null) {
return $this->API->upload($file, $file_name, $cb);
}
public function get_extension_from_mime($mime)
{
foreach ($this->all_mimes as $key => $value) {
if (array_search($mime, $value) !== false) {
return '.'.$key;
}
}
return '';
return $this->API->get_extension_from_mime($mime);
}
public function get_download_info($message_media)
{
if (!isset($message_media['_']) && isset($message_media['InputFileLocation']) && isset($message_media['size'])) {
return $message_media;
}
$res = [];
switch ($message_media['_']) {
case 'messageMediaPhoto':
$res['caption'] = $message_media['caption'];
$res['ext'] = '.jpg';
$photo = end($message_media['photo']['sizes']);
$res['name'] = $photo['location']['volume_id'].'_'.$photo['location']['local_id'];
$res['size'] = $photo['size'];
$res['InputFileLocation'] = ['_' => 'inputFileLocation', 'volume_id' => $photo['location']['volume_id'], 'local_id' => $photo['location']['local_id'], 'secret' => $photo['location']['secret'], 'dc_id' => $photo['location']['dc_id']];
$res['mime'] = 'image/jpeg';
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'], 'dc_id' => $message_media['location']['dc_id']];
$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'] = isset($pathinfo['extension']) ? '.'.$pathinfo['extension'] : '';
$res['name'] = $pathinfo['filename'];
break;
case 'documentAttributeAudio':
$audio = $attribute;
break;
}
}
if (isset($audio) && isset($audio['title']) && !isset($res['name'])) {
$res['name'] = $audio['title'];
if (isset($audio['performer'])) {
$res['name'] .= ' - '.$audio['performer'];
}
}
if (!isset($res['ext'])) {
$res['ext'] = $this->get_extension_from_mime($message_media['document']['mime_type']);
}
if (!isset($res['name'])) {
$res['name'] = $message_media['document']['id'];
}
$res['name'] .= '_'.$message_media['document']['id'];
$res['size'] = $message_media['document']['size'];
$res['mime'] = $message_media['document']['mime_type'];
$res['InputFileLocation'] = ['_' => 'inputDocumentFileLocation', 'id' => $message_media['document']['id'], 'access_hash' => $message_media['document']['access_hash'], 'version' => $message_media['document']['version'], 'dc_id' => $message_media['document']['dc_id']];
return $res;
default:
throw new \danog\MadelineProto\Exception('Invalid constructor provided: '.$message_media['_']);
}
return $this->API->get_download_info($message_media);
}
public function download_to_dir($message_media, $dir, $cb = null)
{
$info = $this->get_download_info($message_media);
return $this->download_to_file($info, $dir.'/'.$info['name'].$info['ext'], $cb);
return $this->API->download_to_dir($message_media, $dir, $cb);
}
public function download_to_file($message_media, $file, $cb = null)
{
$file = str_replace('//', '/', $file);
$info = $this->get_download_info($message_media);
$stream = fopen($file, 'wb');
$this->download_to_stream($info, $stream, $cb, filesize($file), $info['size']);
fclose($stream);
clearstatcache();
return $file;
return $this->API->download_to_file($message_media, $file, $cb);
}
public function download_to_stream($message_media, $stream, $cb = null, $offset = 0, $end = -1)
{
if ($cb === null) {
$cb = function ($percent) {
\danog\MadelineProto\Logger::log(['Download status: '.$percent.'%'], \danog\MadelineProto\Logger::NOTICE);
};
}
$info = $this->get_download_info($message_media);
if ($end === -1) {
$end = $info['size'];
}
if (stream_get_meta_data($stream)['seekable']) {
fseek($stream, $offset);
}
$size = $end - $offset;
$part_size = 512 * 1024;
$percent = 0;
if (isset($info['InputFileLocation']['dc_id'])) {
$this->API->switch_dc($info['InputFileLocation']['dc_id']);
}
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], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
$res = $this->API->method_call('upload.getFile', ['location' => $info['InputFileLocation'], 'offset' => $offset, 'limit' => $real_part_size], null, true);
$dc = 1;
while ($res['type']['_'] === 'storage.fileUnknown' && $res['bytes'] === '') {
$this->API->switch_dc($dc);
$res = $this->API->method_call('upload.getFile', ['location' => $info['InputFileLocation'], 'offset' => $offset, 'limit' => $real_part_size], null, true);
$dc++;
}
\danog\MadelineProto\Logger::log([fwrite($stream, $res['bytes'])], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
\danog\MadelineProto\Logger::log([$offset, $size, ftell($stream)], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
$cb($percent = ($offset += $real_part_size) * 100 / $size);
}
return true;
return $this->API->download_to_stream($message_media, $stream, $cb);
}
}

View File

@ -60,7 +60,7 @@ if ($MadelineProto === false) {
$MadelineProto->bot_login(getenv('BOT_TOKEN'));
}
}
$message = (getenv('TRAVIS_COMMIT') == '') ? 'I iz works always (io laborare sembre) (yo lavorar siempre)' : ('Travis ci tests in progress: commit '.getenv('TRAVIS_COMMIT').', job '.getenv('TRAVIS_JOB_NUMBER').', PHP version: '.getenv('TRAVIS_PHP_VERSION'));
$message = (getenv('TRAVIS_COMMIT') == '') ? 'I iz works always (io laborare sembre) (yo lavorar siempre) (mi labori ĉiam) (я всегда работать) (Ik werkuh altijd)' : ('Travis ci tests in progress: commit '.getenv('TRAVIS_COMMIT').', job '.getenv('TRAVIS_JOB_NUMBER').', PHP version: '.getenv('TRAVIS_PHP_VERSION'));
$flutter = 'https://storage.pwrtelegram.xyz/pwrtelegrambot/document/file_6570.mp4';
$mention = $MadelineProto->get_info(getenv('TEST_USERNAME')); // Returns an array with all of the constructors that can be extracted from a username or an id
@ -99,12 +99,12 @@ $time = time();
$inputFile = $MadelineProto->upload('tests/60', 'magic'); // This gets an inputFile object with file name magic
var_dump(time() - $time);
$media['document'] = ['_' => 'inputMediaUploadedDocument', 'file' => $inputFile, 'mime_type' => 'magic/magic', 'caption' => 'This file was uploaded using MadelineProto', 'attributes' => [['_' => 'documentAttributeFilename', 'file_name' => 'magic.magic']]];
foreach (json_decode(getenv('TEST_DESTINATION_GROUPS'), true) as $peer) {
$sentMessage = $MadelineProto->messages->sendMessage(['peer' => $peer, 'message' => $message, 'entities' => [['_' => 'inputMessageEntityMentionName', 'offset' => 0, 'length' => strlen($message), 'user_id' => $mention]]]);
$sentMessage = $MadelineProto->messages->sendMessage(['peer' => $peer, 'message' => $message, 'entities' => [['_' => 'inputMessageEntityMentionName', 'offset' => 0, 'length' => mb_strlen($message), 'user_id' => $mention]]]);
\danog\MadelineProto\Logger::log([$sentMessage], \danog\MadelineProto\Logger::NOTICE);
foreach ($media as $type => $inputMedia) {
\danog\MadelineProto\Logger::log([$MadelineProto->messages->sendMedia(['peer' => $peer, 'media' => $inputMedia])], \danog\MadelineProto\Logger::NOTICE);
$type = $MadelineProto->invokeWithoutUpdates(['query' => $MadelineProto->API->serialize_method('messages.sendMedia', ['peer' => $peer, 'media' => $inputMedia])]);
var_dump($MadelineProto->API->MTProto_to_botAPI(end($type['updates'])));
}
}
@ -125,7 +125,7 @@ $mention = $MadelineProto->get_info(getenv('TEST_USERNAME')); // Returns an arra
$mention = $mention['user_id']; // Selects only the numeric user id
foreach (json_decode(getenv('TEST_DESTINATION_GROUPS'), true) as $peer) {
$sentMessage = $MadelineProto->messages->sendMessage(['peer' => $peer, 'message' => $message, 'entities' => [['_' => 'inputMessageEntityMentionName', 'offset' => 0, 'length' => strlen($message), 'user_id' => $mention]]]);
$sentMessage = $MadelineProto->messages->sendMessage(['peer' => $peer, 'message' => $message, 'entities' => [['_' => 'inputMessageEntityMentionName', 'offset' => 0, 'length' => mb_strlen($message), 'user_id' => $mention]]]);
\danog\MadelineProto\Logger::log([$sentMessage], \danog\MadelineProto\Logger::NOTICE);
}
sleep(5);