1
0
mirror of https://github.com/danog/MadelineProto.git synced 2024-12-02 13:37:47 +01:00
MadelineProto/examples/downloadRenameBot.php

241 lines
8.7 KiB
PHP
Raw Normal View History

2023-11-23 14:16:57 +01:00
<?php declare(strict_types=1);
2019-12-28 20:28:47 +01:00
/**
* 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>
2023-01-04 12:43:01 +01:00
* @copyright 2016-2023 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
*/
use Amp\ByteStream\ReadableStream;
use Amp\Process\Process;
2023-11-23 14:16:57 +01:00
use danog\MadelineProto\API;
use danog\MadelineProto\EventHandler\Attributes\Handler;
2023-12-14 16:05:46 +01:00
use danog\MadelineProto\EventHandler\CommandType;
use danog\MadelineProto\EventHandler\Filter\Combinator\FilterNot;
use danog\MadelineProto\EventHandler\Filter\FilterCommand;
use danog\MadelineProto\EventHandler\Media;
2023-11-23 14:33:58 +01:00
use danog\MadelineProto\EventHandler\Message\PrivateMessage;
2023-11-23 14:16:57 +01:00
use danog\MadelineProto\EventHandler\SimpleFilter\HasMedia;
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming;
use danog\MadelineProto\EventHandler\SimpleFilter\IsNotEdited;
2023-12-14 16:05:46 +01:00
use danog\MadelineProto\FileCallback;
use danog\MadelineProto\Logger;
use danog\MadelineProto\ParseMode;
use danog\MadelineProto\RemoteUrl;
use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\Settings;
use danog\MadelineProto\SimpleEventHandler;
use League\Uri\Contracts\UriException;
use League\Uri\Uri;
2019-12-28 20:28:47 +01:00
use function Amp\async;
use function Amp\ByteStream\buffer;
use function Amp\ByteStream\getStderr;
use function Amp\ByteStream\pipe;
2023-11-23 14:16:57 +01:00
// MadelineProto is already loaded
if (class_exists(API::class)) {
// Otherwise, if a stable version of MadelineProto was installed via composer, load composer autoloader
} elseif (file_exists('vendor/autoload.php')) {
require_once 'vendor/autoload.php';
2019-12-28 20:28:47 +01:00
} else {
2023-11-23 14:16:57 +01:00
// Otherwise download an !!! alpha !!! version of MadelineProto via madeline.php
2022-12-08 20:16:40 +01:00
if (!file_exists('madeline.php')) {
copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
2019-12-28 20:28:47 +01:00
}
2023-11-23 14:16:57 +01:00
require_once 'madeline.php';
2019-12-28 20:28:47 +01:00
}
/**
* Event handler class.
2023-11-23 14:16:57 +01:00
*
* All properties returned by __sleep are automatically stored in the database.
2019-12-28 20:28:47 +01:00
*/
2023-11-23 14:16:57 +01:00
class MyEventHandler extends SimpleEventHandler
2019-12-28 20:28:47 +01:00
{
2023-11-11 16:55:29 +01:00
public const START = "Send me a file URL and I will download it and send it to you!\n\n".
2023-11-23 14:16:57 +01:00
"Usage: `/upload https://example.com`\n".
"Usage: `/upload https://example.com file name.ext`\n\n".
2019-12-28 20:28:47 +01:00
"I can also rename Telegram files, just send me any file and I will rename it!\n\n".
2023-11-23 14:16:57 +01:00
"Max 2.0GB, parallel upload and download powered by @MadelineProto.";
/**
* @var int|string Username or ID of bot admin
*/
2023-11-11 16:55:29 +01:00
public 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
2023-11-23 14:16:57 +01:00
/**
* Returns a list of names for properties that will be automatically saved to the session database (MySQL/postgres/redis if configured, the session file otherwise).
*/
public function __sleep(): array
{
return ['states'];
}
2019-12-28 20:28:47 +01:00
/**
* Array of media objects.
*/
2023-11-23 14:16:57 +01:00
private array $states = [];
/**
2023-12-14 16:05:46 +01:00
* Start.
*/
2023-11-23 14:16:57 +01:00
#[FilterCommand('start', [CommandType::SLASH])]
2023-12-14 16:05:46 +01:00
public function cmdStart(PrivateMessage&Incoming&IsNotEdited $message): void
2019-12-28 20:28:47 +01:00
{
2023-11-23 14:16:57 +01:00
$message->reply(self::START, ParseMode::MARKDOWN);
}
2019-12-28 20:28:47 +01:00
2023-11-23 14:16:57 +01:00
/**
2023-12-14 16:05:46 +01:00
* Save user file.
2023-11-23 14:16:57 +01:00
*/
#[Handler]
2023-12-14 16:05:46 +01:00
public function cmdSaveState(PrivateMessage&Incoming&HasMedia&IsNotEdited $message): void
2023-11-23 14:16:57 +01:00
{
$message->reply('Give me a new name for this file: ', ParseMode::MARKDOWN);
2023-11-23 16:17:13 +01:00
$this->states[$message->chatId] = $message->media;
2023-11-23 14:16:57 +01:00
}
2019-12-28 20:28:47 +01:00
2023-11-23 14:16:57 +01:00
/**
* Upload a url or a youtube video.
2023-11-23 14:16:57 +01:00
*/
#[FilterCommand('upload', [CommandType::SLASH])]
public function cmdYt(PrivateMessage&Incoming&IsNotEdited $message): void
2023-11-23 14:16:57 +01:00
{
$url = $message->commandArgs[0];
$name = $message->commandArgs[1] ?? null;
$process = Process::start([
'yt-dlp',
$url,
'-J',
]);
$info = json_decode(
buffer($process->getStdout()),
true,
);
$process->join();
if (isset($info['title'])) {
$name ??= $info['title'].".mp4";
2024-02-13 21:57:14 +01:00
$url = escapeshellarg($url);
$process = Process::start([
'bash',
'-c',
"yt-dlp $url -f bestvideo*+bestaudio/best -o - | ffmpeg -i - -f mp4 -acodec copy -vcodec copy -movflags isml+frag_keyframe pipe:1",
]);
2024-02-13 21:57:14 +01:00
async(pipe(...), $process->getStderr(), getStderr())->ignore();
$finally = $process->join(...);
$url = $process->getStdout();
} else {
$name ??= $url;
if (Uri::new($url)->getScheme() === null) {
$url = "http://$url";
}
$url = new RemoteUrl($url);
$finally = static fn () => null;
2023-11-23 14:16:57 +01:00
}
async(
$this->cmdUpload(...),
$url,
$name,
$message
)->finally($finally);
2023-11-23 14:16:57 +01:00
}
2019-12-30 18:27:28 +01:00
2023-11-23 14:16:57 +01:00
/**
2023-12-14 16:05:46 +01:00
* Change file name.
2023-11-23 14:16:57 +01:00
*/
#[FilterNot(new FilterCommand('upload', [CommandType::SLASH]))]
2023-12-14 16:05:46 +01:00
public function cmdNameFile(PrivateMessage&Incoming&IsNotEdited $message): void
2023-11-23 14:16:57 +01:00
{
2023-11-23 14:33:58 +01:00
if (isset($this->states[$message->chatId])) {
2023-11-23 14:16:57 +01:00
$name = $message->message;
2023-11-23 16:17:13 +01:00
$url = $this->states[$message->chatId];
2023-11-23 14:16:57 +01:00
unset($this->states[$message->chatId]);
$this->cmdUpload($url, $name, $message);
}
}
2019-12-29 15:20:46 +01:00
private function cmdUpload(Media|RemoteUrl|ReadableStream $file, string $name, PrivateMessage $message): void
2023-11-23 14:16:57 +01:00
{
try {
$sent = $message->reply('Preparing...');
$file = new FileCallback(
$file,
2023-12-14 16:05:46 +01:00
static function ($progress, $speed, $time) use ($sent): void {
2019-12-30 18:27:28 +01:00
static $prev = 0;
2022-12-08 20:16:40 +01:00
$now = time();
2023-12-14 16:05:46 +01:00
if ($now - $prev < 10 && $progress < 100) {
2019-12-28 21:07:50 +01:00
return;
2023-12-14 16:05:46 +01:00
}
2023-11-23 14:16:57 +01:00
2019-12-30 18:27:28 +01:00
$prev = $now;
2019-12-28 21:07:50 +01:00
try {
2023-11-23 14:16:57 +01:00
$sent->editText("Upload progress: $progress%\nSpeed: $speed mbps\nTime elapsed since start: $time");
2023-12-14 16:05:46 +01:00
} catch (RPCErrorException $e) {
}
2022-12-30 21:54:44 +01:00
},
2019-12-29 14:21:25 +01:00
);
2023-07-08 17:46:45 +02:00
$this->messages->sendMedia(
2023-11-23 14:16:57 +01:00
peer : $message->chatId,
reply_to_msg_id: $message->id,
media : [
'_' => 'inputMediaUploadedDocument',
'file' => $file,
2023-07-08 17:46:45 +02:00
'attributes' => [
['_' => 'documentAttributeFilename', 'file_name' => $name],
2019-12-28 20:28:47 +01:00
],
2022-12-30 21:54:44 +01:00
],
2023-11-23 14:16:57 +01:00
message : 'Powered by @MadelineProto!'
2019-12-28 20:28:47 +01:00
);
2023-11-23 14:16:57 +01:00
$sent->delete();
2022-12-30 21:54:44 +01:00
} catch (Throwable $e) {
2023-11-11 16:55:29 +01:00
if (!str_contains($e->getMessage(), 'Could not connect to URI') && !($e instanceof UriException) && !str_contains($e->getMessage(), 'URI')) {
2019-12-28 20:28:47 +01:00
$this->report((string) $e);
2022-12-30 21:54:44 +01:00
$this->logger((string) $e, Logger::FATAL_ERROR);
2019-12-28 20:28:47 +01:00
}
2019-12-31 12:45:49 +01:00
if ($e instanceof RPCErrorException && $e->rpc === 'FILE_PARTS_INVALID') {
2023-11-23 14:16:57 +01:00
$this->report(json_encode($file));
2019-12-31 12:45:49 +01:00
}
2019-12-28 20:28:47 +01:00
try {
2023-11-23 14:16:57 +01:00
$sent->editText('Error: ' . $e->getMessage());
2022-12-30 21:54:44 +01:00
} catch (Throwable $e) {
$this->logger((string) $e, Logger::FATAL_ERROR);
2019-12-28 20:28:47 +01:00
}
}
}
}
2021-12-09 13:25:14 +01:00
$settings = new Settings;
2023-11-23 14:33:58 +01:00
$settings->getConnection()->setMaxMediaSocketCount(1000);
// In this case we don't need full caching
$settings->getPeer()->setFullFetch(false)->setCacheAllPeersOnStartup(false);
2021-12-09 13:25:14 +01:00
// IMPORTANT: for security reasons, upload by URL will still be allowed
$settings->getFiles()->setAllowAutomaticUpload(true);
// Reduce boilerplate with new wrapper method.
// Also initializes error reporting, catching and reporting all errors surfacing from the event loop.
2023-11-23 14:16:57 +01:00
MyEventHandler::startAndLoop('bot.madeline', $settings);