2019-12-28 20:28:47 +01:00
|
|
|
#!/usr/bin/env php
|
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* Example bot.
|
|
|
|
*
|
2020-02-17 14:13:46 +01:00
|
|
|
* Copyright 2016-2020 Daniil Gentili
|
2019-12-28 20:28:47 +01:00
|
|
|
* (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/>.
|
|
|
|
*
|
|
|
|
* @author Daniil Gentili <daniil@daniil.it>
|
2020-02-17 14:13:46 +01:00
|
|
|
* @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
|
2019-12-28 20:28:47 +01:00
|
|
|
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
|
|
|
*
|
|
|
|
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
|
|
|
*/
|
|
|
|
|
2020-04-29 14:43:43 +02:00
|
|
|
|
2020-06-16 17:52:55 +02:00
|
|
|
if (\function_exists('memprof_enable')) {
|
|
|
|
\memprof_enable();
|
2020-04-29 14:43:43 +02:00
|
|
|
}
|
|
|
|
|
2020-02-09 16:24:57 +01:00
|
|
|
use Amp\Http\Server\HttpServer;
|
|
|
|
use danog\MadelineProto\API;
|
2020-02-25 19:10:49 +01:00
|
|
|
use danog\MadelineProto\APIWrapper;
|
2020-02-09 17:29:39 +01:00
|
|
|
use danog\MadelineProto\MTProtoTools\Files;
|
2019-12-31 12:45:49 +01:00
|
|
|
use danog\MadelineProto\RPCErrorException;
|
2020-02-09 17:29:39 +01:00
|
|
|
use danog\MadelineProto\Tools;
|
2019-12-29 16:30:15 +01:00
|
|
|
use League\Uri\Contracts\UriException;
|
2019-12-28 20:28:47 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Various ways to load MadelineProto
|
|
|
|
*/
|
|
|
|
if (\file_exists('vendor/autoload.php')) {
|
|
|
|
include 'vendor/autoload.php';
|
|
|
|
} else {
|
|
|
|
if (!\file_exists('madeline.php')) {
|
|
|
|
\copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
|
|
|
|
}
|
|
|
|
include 'madeline.php';
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Event handler class.
|
|
|
|
*/
|
2020-02-28 14:25:47 +01:00
|
|
|
class MyEventHandler extends \danog\MadelineProto\EventHandler
|
2019-12-28 20:28:47 +01:00
|
|
|
{
|
|
|
|
const START = "Send me a file URL and I will download it and send it to you!\n\n".
|
|
|
|
"Usage: `https://example.com`\n".
|
|
|
|
"Usage: `https://example.com file name.ext`\n\n".
|
|
|
|
"I can also rename Telegram files, just send me any file and I will rename it!\n\n".
|
|
|
|
"Max 1.5GB, parallel upload and download powered by @MadelineProto.";
|
2020-02-23 19:28:42 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @var int|string Username or ID of bot admin
|
|
|
|
*/
|
|
|
|
const ADMIN = 'danogentili'; // Change this
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get peer(s) where to report errors.
|
|
|
|
*
|
|
|
|
* @return int|string|array
|
|
|
|
*/
|
|
|
|
public function getReportPeers()
|
|
|
|
{
|
|
|
|
return [self::ADMIN];
|
|
|
|
}
|
2019-12-28 20:28:47 +01:00
|
|
|
|
2020-02-09 16:24:57 +01:00
|
|
|
/**
|
|
|
|
* Whether to allow uploads.
|
|
|
|
*/
|
|
|
|
private $UPLOAD;
|
|
|
|
|
2019-12-28 20:28:47 +01:00
|
|
|
/**
|
|
|
|
* Array of media objects.
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
private $states = [];
|
2020-02-09 16:24:57 +01:00
|
|
|
/**
|
|
|
|
* Constructor.
|
|
|
|
*
|
2020-02-23 19:28:42 +01:00
|
|
|
* @param ?API $API API
|
2020-02-09 16:24:57 +01:00
|
|
|
*/
|
2020-02-25 19:10:49 +01:00
|
|
|
public function __construct(?APIWrapper $API)
|
2020-02-09 16:24:57 +01:00
|
|
|
{
|
|
|
|
$this->UPLOAD = \class_exists(HttpServer::class);
|
|
|
|
parent::__construct($API);
|
|
|
|
}
|
2020-04-29 14:43:43 +02:00
|
|
|
public function onStart()
|
|
|
|
{
|
|
|
|
$this->adminId = yield $this->getInfo(self::ADMIN)['bot_api_id'];
|
|
|
|
}
|
2020-02-23 19:28:42 +01:00
|
|
|
/**
|
|
|
|
* Handle updates from channels and supergroups.
|
|
|
|
*
|
|
|
|
* @param array $update Update
|
|
|
|
*
|
|
|
|
* @return \Generator
|
|
|
|
*/
|
|
|
|
public function onUpdateNewChannelMessage(array $update)
|
2019-12-28 20:28:47 +01:00
|
|
|
{
|
2019-12-30 18:27:28 +01:00
|
|
|
//yield $this->onUpdateNewMessage($update);
|
2019-12-28 20:28:47 +01:00
|
|
|
}
|
2020-02-23 19:28:42 +01:00
|
|
|
/**
|
|
|
|
* Handle updates from users.
|
|
|
|
*
|
|
|
|
* @param array $update Update
|
|
|
|
*
|
|
|
|
* @return \Generator
|
|
|
|
*/
|
|
|
|
public function onUpdateNewMessage(array $update): \Generator
|
2019-12-28 20:28:47 +01:00
|
|
|
{
|
|
|
|
if ($update['message']['out'] ?? false) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ($update['message']['_'] !== 'message') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
$peer = yield $this->getInfo($update);
|
|
|
|
$peerId = $peer['bot_api_id'];
|
|
|
|
$messageId = $update['message']['id'];
|
|
|
|
|
2020-02-09 16:24:57 +01:00
|
|
|
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;
|
2020-02-09 17:29:39 +01:00
|
|
|
return;
|
2020-02-09 16:24:57 +01:00
|
|
|
}
|
2019-12-28 20:28:47 +01:00
|
|
|
if ($update['message']['message'] === '/start') {
|
2019-12-30 18:27:28 +01:00
|
|
|
return $this->messages->sendMessage(['peer' => $peerId, 'message' => self::START, 'parse_mode' => 'Markdown', 'reply_to_msg_id' => $messageId]);
|
2019-12-28 20:28:47 +01:00
|
|
|
}
|
2020-04-29 14:43:43 +02:00
|
|
|
if ($update['message']['message'] === '/report' && $peerId === $this->adminId) {
|
2020-06-16 17:52:55 +02:00
|
|
|
\memprof_dump_callgrind($stm = \fopen("php://memory", "w"));
|
|
|
|
\fseek($stm, 0);
|
2020-04-29 14:43:43 +02:00
|
|
|
yield $this->messages->sendMedia(['peer' => $peerId, 'media' => ['_' => 'inputMediaUploadedDocument', 'file' => $stm, 'attributes' => [['_' => 'documentAttributeFilename', 'file_name' => 'callgrind.out']]]]);
|
2020-06-16 17:52:55 +02:00
|
|
|
\fclose($stm);
|
2020-04-29 14:43:43 +02:00
|
|
|
return;
|
|
|
|
}
|
2019-12-29 02:41:06 +01:00
|
|
|
if (isset($update['message']['media']['_']) && $update['message']['media']['_'] !== 'messageMediaWebPage') {
|
2020-02-09 16:24:57 +01:00
|
|
|
if ($this->UPLOAD && ($this->states[$peerId] ?? false) === $this->UPLOAD) {
|
|
|
|
unset($this->states[$peerId]);
|
2020-02-09 17:29:39 +01:00
|
|
|
$update = Files::extractBotAPIFile(yield $this->MTProtoToBotAPI($update));
|
|
|
|
$file = [$update['file_size'], $update['mime_type']];
|
2020-02-23 19:28:42 +01:00
|
|
|
\var_dump($update['file_id'].'.'.Tools::base64urlEncode(\json_encode($file))."/".$update['file_name']);
|
2020-02-09 16:24:57 +01:00
|
|
|
return;
|
|
|
|
}
|
2019-12-30 18:27:28 +01:00
|
|
|
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'];
|
2019-12-28 20:28:47 +01:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (isset($this->states[$peerId])) {
|
|
|
|
$name = $update['message']['message'];
|
2019-12-30 18:27:28 +01:00
|
|
|
$url = $this->states[$peerId];
|
2019-12-28 20:28:47 +01:00
|
|
|
unset($this->states[$peerId]);
|
|
|
|
} else {
|
|
|
|
$url = \explode(' ', $update['message']['message'], 2);
|
|
|
|
$name = \trim($url[1] ?? \basename($update['message']['message']));
|
|
|
|
$url = \trim($url[0]);
|
|
|
|
if (!$url) {
|
|
|
|
return;
|
|
|
|
}
|
2020-01-03 16:53:08 +01:00
|
|
|
if (\stripos($url, 'http') !== 0) {
|
2019-12-28 20:28:47 +01:00
|
|
|
$url = "http://$url";
|
|
|
|
}
|
|
|
|
}
|
2019-12-30 18:27:28 +01:00
|
|
|
$id = yield $this->messages->sendMessage(['peer' => $peerId, 'message' => 'Preparing...', 'reply_to_msg_id' => $messageId]);
|
|
|
|
if (!isset($id['id'])) {
|
|
|
|
$this->report(\json_encode($id));
|
|
|
|
foreach ($id['updates'] as $updat) {
|
|
|
|
if (isset($updat['id'])) {
|
|
|
|
$id = $updat['id'];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$id = $id['id'];
|
|
|
|
}
|
|
|
|
|
2019-12-29 14:21:25 +01:00
|
|
|
$url = new \danog\MadelineProto\FileCallback(
|
2019-12-28 21:07:50 +01:00
|
|
|
$url,
|
2019-12-29 15:20:46 +01:00
|
|
|
function ($progress, $speed, $time) use ($peerId, $id) {
|
|
|
|
$this->logger("Upload progress: $progress%");
|
|
|
|
|
2019-12-30 18:27:28 +01:00
|
|
|
static $prev = 0;
|
|
|
|
$now = \time();
|
|
|
|
if ($now - $prev < 10 && $progress < 100) {
|
2019-12-28 21:07:50 +01:00
|
|
|
return;
|
|
|
|
}
|
2019-12-30 18:27:28 +01:00
|
|
|
$prev = $now;
|
2019-12-28 21:07:50 +01:00
|
|
|
try {
|
2019-12-30 18:27:28 +01:00
|
|
|
yield $this->messages->editMessage(['peer' => $peerId, 'id' => $id, 'message' => "Upload progress: $progress%\nSpeed: $speed mbps\nTime elapsed since start: $time"], ['FloodWaitLimit' => 0]);
|
2019-12-28 21:07:50 +01:00
|
|
|
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
|
|
|
}
|
|
|
|
}
|
2019-12-29 14:21:25 +01:00
|
|
|
);
|
2019-12-28 20:28:47 +01:00
|
|
|
yield $this->messages->sendMedia(
|
|
|
|
[
|
|
|
|
'peer' => $peerId,
|
|
|
|
'reply_to_msg_id' => $messageId,
|
|
|
|
'media' => [
|
|
|
|
'_' => 'inputMediaUploadedDocument',
|
2019-12-29 14:23:41 +01:00
|
|
|
'file' => $url,
|
2019-12-28 20:28:47 +01:00
|
|
|
'attributes' => [
|
|
|
|
['_' => 'documentAttributeFilename', 'file_name' => $name]
|
|
|
|
]
|
|
|
|
],
|
|
|
|
'message' => 'Powered by @MadelineProto!',
|
|
|
|
'parse_mode' => 'Markdown'
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
if (\in_array($peer['type'], ['channel', 'supergroup'])) {
|
|
|
|
yield $this->channels->deleteMessages(['channel' => $peerId, 'id' => [$id]]);
|
|
|
|
} else {
|
|
|
|
yield $this->messages->deleteMessages(['revoke' => true, 'id' => [$id]]);
|
|
|
|
}
|
|
|
|
} catch (\Throwable $e) {
|
2019-12-30 18:27:28 +01:00
|
|
|
if (\strpos($e->getMessage(), 'Could not connect to URI') === false && !($e instanceof UriException) && \strpos($e->getMessage(), 'URI') === false) {
|
2019-12-28 20:28:47 +01:00
|
|
|
$this->report((string) $e);
|
|
|
|
$this->logger((string) $e, \danog\MadelineProto\Logger::FATAL_ERROR);
|
|
|
|
}
|
2019-12-31 12:45:49 +01:00
|
|
|
if ($e instanceof RPCErrorException && $e->rpc === 'FILE_PARTS_INVALID') {
|
2020-01-03 13:09:42 +01:00
|
|
|
$this->report(\json_encode($url));
|
2019-12-31 12:45:49 +01:00
|
|
|
}
|
2019-12-28 20:28:47 +01:00
|
|
|
try {
|
|
|
|
yield $this->messages->editMessage(['peer' => $peerId, 'id' => $id, 'message' => 'Error: '.$e->getMessage()]);
|
2019-12-28 20:32:51 +01:00
|
|
|
} catch (\Throwable $e) {
|
2019-12-28 20:28:47 +01:00
|
|
|
$this->logger((string) $e, \danog\MadelineProto\Logger::FATAL_ERROR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$settings = [
|
|
|
|
'logger' => [
|
2019-12-29 14:53:03 +01:00
|
|
|
'logger_level' => 4
|
2019-12-28 20:28:47 +01:00
|
|
|
],
|
|
|
|
'serialization' => [
|
|
|
|
'serialization_interval' => 30
|
|
|
|
],
|
|
|
|
'connection_settings' => [
|
|
|
|
'media_socket_count' => [
|
|
|
|
'min' => 20,
|
|
|
|
'max' => 1000,
|
|
|
|
]
|
2019-12-29 14:21:25 +01:00
|
|
|
],
|
|
|
|
'upload' => [
|
2020-02-09 16:24:57 +01:00
|
|
|
'allow_automatic_upload' => false // IMPORTANT: for security reasons, upload by URL will still be allowed
|
2019-12-29 21:45:52 +01:00
|
|
|
],
|
2019-12-28 20:28:47 +01:00
|
|
|
];
|
|
|
|
|
2019-12-30 18:27:28 +01:00
|
|
|
$MadelineProto = new \danog\MadelineProto\API(($argv[1] ?? 'bot').'.madeline', $settings);
|
2020-02-23 19:28:42 +01:00
|
|
|
|
|
|
|
// Reduce boilerplate with new wrapper method.
|
|
|
|
// Also initializes error reporting, catching and reporting all errors surfacing from the event loop.
|
|
|
|
$MadelineProto->startAndLoop(MyEventHandler::class);
|