
commit
fa313ac811
6 changed files with 140 additions and 0 deletions
@ -0,0 +1,21 @@
|
||||
MIT License |
||||
|
||||
Copyright (c) 2021 blank X |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
@ -0,0 +1,15 @@
|
||||
# StreamTG |
||||
|
||||
Stream files from Telegram |
||||
|
||||
### Installation Instructions |
||||
1. Install `python3` |
||||
2. `pip3 install -r requirements.txt` |
||||
3. Copy example-config.yaml to config.yaml and edit it |
||||
|
||||
### Start |
||||
`python3 streamtg.py` |
||||
If you want to set a custom port, have a PORT environment variable |
||||
|
||||
### How to Use |
||||
http://localhost:8080/?chat_id=-1001289824958&message_id=5302&message_id=5304&token=DIi4aXHn440PTPXJE1yVyIoU2L4bLGiyjC1Fd7usKAMZYWVcp5p0P792G4YlbNnIcWCLypXbUFJkVzKqhh0AkJYSWqJbsAy8TjA |
@ -0,0 +1,8 @@
|
||||
telegram: |
||||
api_id: 0 |
||||
api_hash: https://my.telegram.org |
||||
bot_token: https://t.me/BotFather |
||||
# If authorized tokens does not exist, no authorization is required |
||||
authorized_tokens: |
||||
- DIi4aXHn440PTPXJE1yVyIoU2L4bLGiyjC1Fd7usKAMZYWVcp5p0P792G4YlbNnIcWCLypXbUFJkVzKqhh0AkJYSWqJbsAy8TjA |
||||
- RCywhzEkwqWmDjsqhBdNMwO1cQNi72SWsvAKZxdoeFylbioqKCZjEKyjgDXkYn6xeMQLj4dDq6QoonVAU1b1MDyOUX9CvT5W4MP |
@ -0,0 +1,90 @@
|
||||
import logging |
||||
logging.basicConfig(level=logging.INFO) |
||||
|
||||
import os |
||||
import yaml |
||||
from aiohttp import web |
||||
from telethon import TelegramClient |
||||
from telethon.utils import _get_file_info |
||||
|
||||
with open('config.yaml') as file: |
||||
config = yaml.safe_load(file) |
||||
|
||||
client = TelegramClient('streamtg', config['telegram']['api_id'], config['telegram']['api_hash']) |
||||
authorized_tokens = config.get('authorized_tokens') |
||||
port = os.environ.get('PORT', 8080) |
||||
|
||||
async def handler(request): |
||||
query = request.query |
||||
if authorized_tokens: |
||||
if 'token' not in query: |
||||
return web.Response(status=401, text='Unauthorized') |
||||
if query['token'] not in authorized_tokens: |
||||
return web.Response(status=403, text='Forbidden') |
||||
if 'chat_id' not in query: |
||||
return web.Response(status=400, text='Missing chat_id') |
||||
chat_id = query['chat_id'] |
||||
try: |
||||
chat_id = int(chat_id) |
||||
except ValueError: |
||||
pass |
||||
if 'message_id' not in query: |
||||
return web.Response(status=400, text='Missing message_id') |
||||
message_ids = query.getall('message_id') |
||||
if any(True for i in message_ids if not i.isnumeric() or i == '0'): |
||||
return web.Response(status=400, text='Invalid message_id') |
||||
message_ids = list(map(int, message_ids)) |
||||
messages = await client.get_messages(chat_id, ids=message_ids) |
||||
if any(True for i in messages if i is None): |
||||
return web.Response(status=400, text='At least one of the messages does not exist') |
||||
if any(True for i in messages if not i.media): |
||||
return web.Response(status=400, text='At least one of the messages do not contain media') |
||||
http_range = request.http_range |
||||
offset = http_range.start or 0 |
||||
end = http_range.stop |
||||
max_size = 0 |
||||
for i in messages: |
||||
max_size += _get_file_info(i).size |
||||
if end is None: |
||||
end = max_size |
||||
elif end > max_size: |
||||
return web.Response(status=416, text='Range end size is bigger than file sizes', headers={'Content-Range': f'bytes */{max_size}'}) |
||||
length = end - offset |
||||
|
||||
async def download(): |
||||
tmp_offset = offset |
||||
tmp_length = length |
||||
for i in messages: |
||||
if tmp_length < 1: |
||||
break |
||||
size = _get_file_info(i).size |
||||
if tmp_offset > size: |
||||
tmp_offset -= size |
||||
continue |
||||
async for chunk in client._iter_download(i, offset=tmp_offset, msg_data=(chat_id, i.id)): |
||||
yield chunk[:tmp_length] |
||||
tmp_length -= len(chunk) |
||||
if tmp_length < 1: |
||||
break |
||||
|
||||
return web.Response(status=206 if (end - offset != max_size) else 200, |
||||
body=download(), |
||||
headers={ |
||||
'Content-Range': f'bytes {offset}-{end}/{max_size}', |
||||
'Content-Length': str(end - offset), |
||||
'Accept-Ranges': 'bytes' |
||||
} |
||||
) |
||||
|
||||
app = web.Application() |
||||
app.add_routes([web.get('/', handler)]) |
||||
|
||||
async def main(): |
||||
await client.start(bot_token=config['telegram'].get('bot_token')) |
||||
runner = web.AppRunner(app) |
||||
await runner.setup() |
||||
site = web.TCPSite(runner, '127.0.0.1', port) |
||||
await site.start() |
||||
await client.run_until_disconnected() |
||||
|
||||
client.loop.run_until_complete(main()) |
Loading…
Reference in new issue