mirror of
https://github.com/danog/TelegramApiServer.git
synced 2024-11-30 04:19:13 +01:00
Upload media from POST request
This commit is contained in:
parent
320fd42e7d
commit
dd53cc98bb
151
README.md
151
README.md
@ -12,6 +12,7 @@ Fast, simple, async php telegram api server:
|
||||
* Full access to telegram api: bot and user
|
||||
* Multiple sessions
|
||||
* Stream media (view files in browser)
|
||||
* Upload media
|
||||
* Websocket endpoint for events
|
||||
|
||||
**Architecture Example**
|
||||
@ -63,10 +64,10 @@ Fast, simple, async php telegram api server:
|
||||
|
||||
Also options can be set in .env file (see .env.example)
|
||||
```
|
||||
1. Access telegram api directly with simple GET/POST requests.
|
||||
1. Access Telegram API with simple GET/POST requests.
|
||||
|
||||
Regular and application/json POST supported.
|
||||
Its recommended to use http_build_query when using GET requests.
|
||||
Its recommended to use http_build_query, when using GET requests.
|
||||
|
||||
**Rules:**
|
||||
* All methods from MadelineProto supported: [Methods List](https://docs.madelineproto.xyz/API_docs/methods/)
|
||||
@ -92,49 +93,115 @@ Fast, simple, async php telegram api server:
|
||||
* copy message from one channel to another (not repost): `http://127.0.0.1:9503/api/copyMessages/?data[from_peer]=@xtrime&data[to_peer]=@xtrime&data[id][0]=1`
|
||||
|
||||
## Advanced features
|
||||
#### Uploading files.
|
||||
|
||||
* Multiple sessions support.
|
||||
When running multiple sessions, need to define which session to use for request.
|
||||
Each session is stored in `sessions/{$session}.madeline`. Nested folders supported.
|
||||
**Examples:**
|
||||
* `php server.php --session=bot --session=users/xtrime --session=users/user1`
|
||||
* `http://127.0.0.1:9503/api/bot/getSelf`
|
||||
* `http://127.0.0.1:9503/api/users/xtrime/getSelf`
|
||||
* `http://127.0.0.1:9503/api/users/user1/getSelf`
|
||||
* sessions file paths are: `sessions/bot.madeline`, `sessions/users/xtrime.madeline` and `sessions/users/user1.madeline`
|
||||
* glob syntax for sessions:
|
||||
* `--session=*` to use all `sessions/*.madeline` files.
|
||||
* `--session=users/* --session=bots/*` to use all session files from `sessions/bots` and `sessions/users` folders.
|
||||
* Session management (**Use with caution, can be unstable**)
|
||||
|
||||
**Examples:**
|
||||
* Session list: `http://127.0.0.1:9503/system/getSessionList`
|
||||
* Adding session: `http://127.0.0.1:9503/system/addSession?session=users/xtrime`
|
||||
* [optional] Adding session with custom settings: `http://127.0.0.1:9503/system/addSession?session=users/xtrime&settings[app_info][app_id]=xxx&&settings[app_info][app_hash]=xxx`
|
||||
* Removing session: `http://127.0.0.1:9503/system/removeSession?session=users/xtrime`
|
||||
|
||||
If there is no authorization in session, or session file is blank, authorization required:
|
||||
|
||||
User:
|
||||
* `http://127.0.0.1:9503/api/users/xtrime/phoneLogin?phone=+7123...`
|
||||
* `http://127.0.0.1:9503/api/users/xtrime/completePhoneLogin?code=123456`
|
||||
* (optional) `http://127.0.0.1:9503/api/users/xtrime/complete2falogin?password=123456`
|
||||
* (optional) `http://127.0.0.1:9503/api/users/xtrime/completeSignup?firstName=MyExampleName`
|
||||
|
||||
Bot:
|
||||
* `http://127.0.0.1:9503/api/bot/botLogin?token=34298141894:aflknsaflknLKNFS`
|
||||
|
||||
After authorization eventHandler need to be set, to receive updates for new session in `/events` websocket:
|
||||
* `http://127.0.0.1:9503/api/users/xtrime/setEventHandler`
|
||||
* `http://127.0.0.1:9503/api/bot/setEventHandler`
|
||||
To upload files from POST request use custom `uploadMediaForm` method:
|
||||
|
||||
* EventHandler updates via websocket. Connect to `ws://127.0.0.1:9503/events`. You will get all events in json.
|
||||
Each event is json object in [json-rpc 2.0 format](https://www.jsonrpc.org/specification#response_object). Example:
|
||||
`curl "http://127.0.0.1:9503/api/uploadMediaForm" -g -F "file=@/Users/xtrime/Downloads/test.txt"`
|
||||
Method supports `application/x-www-form-urlencoded` and `multipart/form-data`.
|
||||
|
||||
Send result from `uploadMediaForm` to `messages.sendMedia`:
|
||||
```
|
||||
curl --location --request POST 'http://127.0.0.1:9503/api/sendMedia' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"data":{
|
||||
"peer": "@xtrime",
|
||||
"media": {
|
||||
"_": "inputMediaUploadedDocument",
|
||||
"file": {
|
||||
"_": "inputFile",
|
||||
"id": 1164670976363200575,
|
||||
"parts": 1,
|
||||
"name": "test.txt",
|
||||
"mime_type": "text/plain",
|
||||
"md5_checksum": ""
|
||||
},
|
||||
"attributes": [
|
||||
{
|
||||
"_": "documentAttributeFilename",
|
||||
"file_name": "test.txt"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
Also see: https://docs.madelineproto.xyz/docs/FILES.html#uploading-files
|
||||
|
||||
#### Downloading files
|
||||
|
||||
```
|
||||
curl --location --request POST '127.0.0.1:9503/api/downloadToResponse' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"media": {
|
||||
"_": "messageMediaDocument",
|
||||
"document": {
|
||||
"_": "document",
|
||||
"id": 5470079466401169993,
|
||||
"access_hash": -6754208767885394084,
|
||||
"file_reference": {
|
||||
"_": "bytes",
|
||||
"bytes": "AkKdqJkAACnyXiaBgp3M3DfBh8C0+mGKXwSsGUY="
|
||||
},
|
||||
"date": 1551713685,
|
||||
"mime_type": "video/mp4",
|
||||
"size": 400967,
|
||||
"dc_id": 2
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
Also see: https://docs.madelineproto.xyz/docs/FILES.html#downloading-files
|
||||
|
||||
#### Multiple sessions support.
|
||||
|
||||
When running multiple sessions, need to define which session to use for request.
|
||||
Each session is stored in `sessions/{$session}.madeline`. Nested folders supported.
|
||||
**Examples:**
|
||||
* `php server.php --session=bot --session=users/xtrime --session=users/user1`
|
||||
* `http://127.0.0.1:9503/api/bot/getSelf`
|
||||
* `http://127.0.0.1:9503/api/users/xtrime/getSelf`
|
||||
* `http://127.0.0.1:9503/api/users/user1/getSelf`
|
||||
* sessions file paths are: `sessions/bot.madeline`, `sessions/users/xtrime.madeline` and `sessions/users/user1.madeline`
|
||||
* glob syntax for sessions:
|
||||
* `--session=*` to use all `sessions/*.madeline` files.
|
||||
* `--session=users/* --session=bots/*` to use all session files from `sessions/bots` and `sessions/users` folders.
|
||||
|
||||
#### Session management
|
||||
|
||||
When using CombinedAPI (multiple accounts) name of session can be added to path of websocket endpoint:
|
||||
This endpoint will send events only from `users/xtrime` session: `ws://127.0.0.1:9503/events/users/xtrime`
|
||||
|
||||
PHP websocket client example: [websocket-events.php](https://github.com/xtrime-ru/TelegramApiServer/blob/master/examples/websocket-events.php)
|
||||
**Examples:**
|
||||
* Session list: `http://127.0.0.1:9503/system/getSessionList`
|
||||
* Adding session: `http://127.0.0.1:9503/system/addSession?session=users/xtrime`
|
||||
* [optional] Adding session with custom settings: `http://127.0.0.1:9503/system/addSession?session=users/xtrime&settings[app_info][app_id]=xxx&&settings[app_info][app_hash]=xxx`
|
||||
* Removing session: `http://127.0.0.1:9503/system/removeSession?session=users/xtrime`
|
||||
|
||||
If there is no authorization in session, or session file is blank, authorization required:
|
||||
|
||||
User:
|
||||
* `http://127.0.0.1:9503/api/users/xtrime/phoneLogin?phone=+7123...`
|
||||
* `http://127.0.0.1:9503/api/users/xtrime/completePhoneLogin?code=123456`
|
||||
* (optional) `http://127.0.0.1:9503/api/users/xtrime/complete2falogin?password=123456`
|
||||
* (optional) `http://127.0.0.1:9503/api/users/xtrime/completeSignup?firstName=MyExampleName`
|
||||
|
||||
Bot:
|
||||
* `http://127.0.0.1:9503/api/bot/botLogin?token=34298141894:aflknsaflknLKNFS`
|
||||
|
||||
After authorization eventHandler need to be set, to receive updates for new session in `/events` websocket:
|
||||
* `http://127.0.0.1:9503/api/users/xtrime/setEventHandler`
|
||||
* `http://127.0.0.1:9503/api/bot/setEventHandler`
|
||||
|
||||
#### EventHandler updates via websocket.
|
||||
|
||||
Connect to `ws://127.0.0.1:9503/events`. You will get all events in json.
|
||||
Each event is json object in [json-rpc 2.0 format](https://www.jsonrpc.org/specification#response_object). Example:
|
||||
|
||||
When using CombinedAPI (multiple accounts) name of session can be added to path of websocket endpoint:
|
||||
This endpoint will send events only from `users/xtrime` session: `ws://127.0.0.1:9503/events/users/xtrime`
|
||||
|
||||
PHP websocket client example: [websocket-events.php](https://github.com/xtrime-ru/TelegramApiServer/blob/master/examples/websocket-events.php)
|
||||
|
||||
## Contacts
|
||||
|
||||
|
@ -14,7 +14,8 @@
|
||||
"amphp/websocket-server": "^2",
|
||||
"amphp/websocket-client": "dev-master#53f7883b325b09864095300ec8ff81e84e772c3b",
|
||||
"vlucas/phpdotenv": "^4",
|
||||
"danog/madelineproto":"dev-master"
|
||||
"danog/madelineproto":"dev-master",
|
||||
"amphp/http-server-form-parser": "^1.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"roave/security-advisories": "dev-master"
|
||||
|
78
composer.lock
generated
78
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "fde2f21e919da4755aa6b9f1f24c217e",
|
||||
"content-hash": "adb3f78b0ae79061e4ad9976499da64b",
|
||||
"packages": [
|
||||
{
|
||||
"name": "amphp/amp",
|
||||
@ -676,6 +676,74 @@
|
||||
],
|
||||
"time": "2020-01-04T18:10:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "amphp/http-server-form-parser",
|
||||
"version": "v1.1.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/amphp/http-server-form-parser.git",
|
||||
"reference": "f26313797fb5ffd936c8fa865fde61523b6d05f2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/amphp/http-server-form-parser/zipball/f26313797fb5ffd936c8fa865fde61523b6d05f2",
|
||||
"reference": "f26313797fb5ffd936c8fa865fde61523b6d05f2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"amphp/amp": "^2",
|
||||
"amphp/byte-stream": "^1.3",
|
||||
"amphp/http": "^1",
|
||||
"amphp/http-server": "^2 || ^1 || ^0.8",
|
||||
"php": ">=7"
|
||||
},
|
||||
"require-dev": {
|
||||
"amphp/php-cs-fixer-config": "dev-master",
|
||||
"amphp/phpunit-util": "^1.1.2",
|
||||
"phpunit/phpunit": "^8 || ^7 || ^6"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Amp\\Http\\Server\\FormParser\\": "src"
|
||||
},
|
||||
"files": [
|
||||
"src/functions.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Daniel Lowrey",
|
||||
"email": "rdlowrey@php.net"
|
||||
},
|
||||
{
|
||||
"name": "Bob Weinand"
|
||||
},
|
||||
{
|
||||
"name": "Niklas Keller",
|
||||
"email": "me@kelunik.com"
|
||||
},
|
||||
{
|
||||
"name": "Aaron Piotrowski",
|
||||
"email": "aaron@trowski.com"
|
||||
}
|
||||
],
|
||||
"description": "A form parser for Amp's HTTP parser.",
|
||||
"homepage": "https://github.com/amphp/http-server-form-parser",
|
||||
"keywords": [
|
||||
"amp",
|
||||
"amphp",
|
||||
"async",
|
||||
"form",
|
||||
"http",
|
||||
"non-blocking"
|
||||
],
|
||||
"time": "2019-12-13T15:52:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "amphp/http-server-router",
|
||||
"version": "v1.0.2",
|
||||
@ -1513,12 +1581,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/danog/MadelineProto.git",
|
||||
"reference": "17bacd1389f090fd5a83cf04ff9018dda1bbaf5e"
|
||||
"reference": "bdd86d4efbefe8a0ed840f25d7608df60ed9261a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/danog/MadelineProto/zipball/17bacd1389f090fd5a83cf04ff9018dda1bbaf5e",
|
||||
"reference": "17bacd1389f090fd5a83cf04ff9018dda1bbaf5e",
|
||||
"url": "https://api.github.com/repos/danog/MadelineProto/zipball/bdd86d4efbefe8a0ed840f25d7608df60ed9261a",
|
||||
"reference": "bdd86d4efbefe8a0ed840f25d7608df60ed9261a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1603,7 +1671,7 @@
|
||||
"telegram",
|
||||
"video"
|
||||
],
|
||||
"time": "2020-02-06T01:44:00+00:00"
|
||||
"time": "2020-02-07T20:23:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "danog/magicalserializer",
|
||||
|
@ -3,6 +3,9 @@
|
||||
namespace TelegramApiServer\Controllers;
|
||||
|
||||
use Amp\ByteStream\ResourceInputStream;
|
||||
use Amp\Http\Server\FormParser\BufferingParser;
|
||||
use Amp\Http\Server\FormParser\File;
|
||||
use Amp\Http\Server\FormParser\Form;
|
||||
use Amp\Http\Server\Request;
|
||||
use Amp\Http\Server\RequestHandler\CallableRequestHandler;
|
||||
use Amp\Http\Server\Response;
|
||||
@ -21,6 +24,7 @@ abstract class AbstractApiController
|
||||
|
||||
protected Client $client;
|
||||
protected Request $request;
|
||||
protected ?File $file = null;
|
||||
protected $extensionClass;
|
||||
|
||||
|
||||
@ -68,7 +72,7 @@ abstract class AbstractApiController
|
||||
public function process()
|
||||
{
|
||||
$this->resolvePath($this->request->getAttribute(Router::class));
|
||||
yield from $this->resolveRequest($this->request);
|
||||
yield from $this->resolveRequest();
|
||||
yield from $this->generateResponse();
|
||||
|
||||
return $this->getResponse();
|
||||
@ -77,26 +81,32 @@ abstract class AbstractApiController
|
||||
/**
|
||||
* Получаем параметры из GET и POST
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return AbstractApiController
|
||||
*/
|
||||
private function resolveRequest(Request $request)
|
||||
private function resolveRequest()
|
||||
{
|
||||
$query = $request->getUri()->getQuery();
|
||||
$body = '';
|
||||
while ($chunk = yield $request->getBody()->read()) {
|
||||
$body .= $chunk;
|
||||
}
|
||||
$contentType = $request->getHeader('Content-Type');
|
||||
$query = $this->request->getUri()->getQuery();
|
||||
$contentType = $this->request->getHeader('Content-Type');
|
||||
|
||||
parse_str($query, $get);
|
||||
|
||||
switch ($contentType) {
|
||||
case 'application/json':
|
||||
switch (true) {
|
||||
case $contentType === 'application/x-www-form-urlencoded':
|
||||
case mb_strpos($contentType, 'multipart/form-data') !== false:
|
||||
/** @var Form $form */
|
||||
$form = yield (new BufferingParser())->parseForm($this->request);
|
||||
$post = $form->getValues();
|
||||
$fileName = array_key_first($form->getFiles());
|
||||
if ($fileName) {
|
||||
$this->file = $form->getFile($fileName);
|
||||
}
|
||||
break;
|
||||
case $contentType === 'application/json':
|
||||
$body = yield $this->request->getBody()->buffer();
|
||||
$post = json_decode($body, 1);
|
||||
break;
|
||||
default:
|
||||
$body = yield $this->request->getBody()->buffer();
|
||||
parse_str($body, $post);
|
||||
}
|
||||
|
||||
@ -146,7 +156,7 @@ abstract class AbstractApiController
|
||||
$pathCount = count($this->api);
|
||||
if ($pathCount === 1 && $this->extensionClass && is_callable([$this->extensionClass,$this->api[0]])) {
|
||||
/** @var ApiExtensions|SystemApiExtensions $madelineProtoExtensions */
|
||||
$madelineProtoExtensions = new $this->extensionClass($madelineProto, $this->request);
|
||||
$madelineProtoExtensions = new $this->extensionClass($madelineProto, $this->request, $this->file);
|
||||
$result = $madelineProtoExtensions->{$this->api[0]}(...$this->parameters);
|
||||
} else {
|
||||
//Проверяем нет ли в MadilineProto такого метода.
|
||||
|
@ -4,16 +4,18 @@
|
||||
namespace TelegramApiServer\MadelineProtoExtensions;
|
||||
|
||||
|
||||
use Amp\ByteStream\InMemoryStream;
|
||||
use Amp\ByteStream\IteratorStream;
|
||||
use Amp\Http\Server\FormParser\File;
|
||||
use Amp\Http\Server\Request;
|
||||
use Amp\Producer;
|
||||
use Amp\Promise;
|
||||
use danog\MadelineProto;
|
||||
use danog\MadelineProto\TL\Conversion\BotAPI;
|
||||
use OutOfRangeException;
|
||||
use TelegramApiServer\EventObservers\EventHandler;
|
||||
use UnexpectedValueException;
|
||||
use function Amp\call;
|
||||
use \danog\MadelineProto;
|
||||
|
||||
class ApiExtensions
|
||||
{
|
||||
@ -21,11 +23,13 @@ class ApiExtensions
|
||||
|
||||
private MadelineProto\Api $madelineProto;
|
||||
private Request $request;
|
||||
private ?File $file;
|
||||
|
||||
public function __construct(MadelineProto\Api $madelineProto, Request $request)
|
||||
public function __construct(MadelineProto\Api $madelineProto, Request $request, ?File $file)
|
||||
{
|
||||
$this->madelineProto = $madelineProto;
|
||||
$this->request = $request;
|
||||
$this->file = $file;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -475,6 +479,39 @@ class ApiExtensions
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload file from POST request.
|
||||
* Response can be passed to 'media' field in messages.sendMedia.
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function uploadMediaForm(): Promise
|
||||
{
|
||||
return call(function() {
|
||||
$media = [];
|
||||
if ($this->file !== null) {
|
||||
$inputFile = yield $this->madelineProto->uploadFromStream(
|
||||
new InMemoryStream($this->file->getContents()),
|
||||
strlen($this->file->getContents()),
|
||||
$this->file->getMimeType(),
|
||||
$this->file->getName()
|
||||
);
|
||||
$inputFile['id'] = unpack('P', $inputFile['id'])['1'];
|
||||
$media = [
|
||||
'media' => [
|
||||
'_' => 'inputMediaUploadedDocument',
|
||||
'file' => $inputFile,
|
||||
'attributes' => [
|
||||
['_' => 'documentAttributeFilename', 'file_name' => $this->file->getName()]
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
return $media;
|
||||
});
|
||||
}
|
||||
|
||||
public function setEventHandler(): void
|
||||
{
|
||||
$this->madelineProto->setEventHandler(EventHandler::class);
|
||||
|
@ -40,7 +40,7 @@ class Server
|
||||
try {
|
||||
Amp\Loop::run();
|
||||
} catch (\Throwable $e) {
|
||||
Logger::getInstance()->critical($e->getMessage(), [
|
||||
Logger::getInstance()->alert($e->getMessage(), [
|
||||
'exception' => [
|
||||
'message' => $e->getMessage(),
|
||||
'code' => $e->getCode(),
|
||||
@ -48,6 +48,7 @@ class Server
|
||||
'line' => $e->getLine(),
|
||||
],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,6 +76,7 @@ class Server
|
||||
Logger::getInstance()->emergency('Got SIGINT');
|
||||
Amp\Loop::cancel($watcherId);
|
||||
yield $server->stop();
|
||||
exit;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user