├── Procfile ├── WebStreamer ├── utils │ ├── __init__.py │ ├── keepalive.py │ ├── time_format.py │ └── custom_dl.py ├── server │ ├── __init__.py │ └── stream_routes.py ├── __init__.py ├── bot │ ├── __init__.py │ └── plugins │ │ ├── start.py │ │ └── stream.py ├── vars.py └── __main__.py ├── requirements.txt ├── app.json └── README.md /Procfile: -------------------------------------------------------------------------------- 1 | web: python -m WebStreamer -------------------------------------------------------------------------------- /WebStreamer/utils/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp 2 | apscheduler 3 | pyrogram 4 | python-dotenv 5 | tgcrypto -------------------------------------------------------------------------------- /WebStreamer/server/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from aiohttp import web 4 | from .stream_routes import routes 5 | 6 | 7 | async def web_server(): 8 | web_app = web.Application(client_max_size=30000000) 9 | web_app.add_routes(routes) 10 | return web_app 11 | -------------------------------------------------------------------------------- /WebStreamer/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | import time 5 | from WebStreamer.bot import StreamBot 6 | 7 | print('\n') 8 | print('------------------- loading Telegram Bot -------------------') 9 | 10 | StreamBot.start() 11 | bot_info = StreamBot.get_me() 12 | __version__ = 1.03 13 | StartTime = time.time() 14 | 15 | 16 | -------------------------------------------------------------------------------- /WebStreamer/bot/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from pyrogram import Client 4 | from ..vars import Var 5 | 6 | StreamBot = Client( 7 | session_name= 'Web Streamer', 8 | api_id=Var.API_ID, 9 | api_hash=Var.API_HASH, 10 | bot_token=Var.BOT_TOKEN, 11 | sleep_threshold=Var.SLEEP_THRESHOLD, 12 | workers=Var.WORKERS 13 | ) 14 | -------------------------------------------------------------------------------- /WebStreamer/bot/plugins/start.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from pyrogram import filters, emoji 4 | from WebStreamer.bot import StreamBot 5 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Message 6 | 7 | @StreamBot.on_message(filters.command(['start', 'help'])) 8 | async def start(_, m: Message): 9 | await m.reply(f'Hi {m.from_user.mention(style="md")}, Send me a file to get an instant stream link.', 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /WebStreamer/utils/keepalive.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import aiohttp 3 | from WebStreamer.vars import Var 4 | 5 | async def ping_server(): 6 | try: 7 | async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session: 8 | async with session.get(Var.URL) as resp: 9 | logging.info("Pinged server with response: {}".format(resp.status)) 10 | except TimeoutError: 11 | logging.warning("Couldn't connect to the site URL..!") -------------------------------------------------------------------------------- /WebStreamer/utils/time_format.py: -------------------------------------------------------------------------------- 1 | def get_readable_time(seconds: int) -> str: 2 | count = 0 3 | readable_time = "" 4 | time_list = [] 5 | time_suffix_list = ["s", "m", "h", " days"] 6 | while count < 4: 7 | count += 1 8 | if count < 3: 9 | remainder, result = divmod(seconds, 60) 10 | else: 11 | remainder, result = divmod(seconds, 24) 12 | if seconds == 0 and remainder == 0: 13 | break 14 | time_list.append(int(result)) 15 | seconds = int(remainder) 16 | for x in range(len(time_list)): 17 | time_list[x] = str(time_list[x]) + time_suffix_list[x] 18 | if len(time_list) == 4: 19 | readable_time += time_list.pop() + ", " 20 | time_list.reverse() 21 | readable_time += ": ".join(time_list) 22 | return readable_time -------------------------------------------------------------------------------- /WebStreamer/vars.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from os import getenv, environ 4 | from dotenv import load_dotenv 5 | 6 | load_dotenv() 7 | 8 | class Var(object): 9 | API_ID = int(getenv('API_ID')) 10 | API_HASH = str(getenv('API_HASH')) 11 | BOT_TOKEN = str(getenv('BOT_TOKEN')) 12 | SLEEP_THRESHOLD = int(getenv('SLEEP_THRESHOLD', '60')) 13 | WORKERS = int(getenv('WORKERS', '3')) 14 | BIN_CHANNEL = int(getenv('BIN_CHANNEL', None)) 15 | PORT = int(getenv('PORT', 8080)) 16 | BIND_ADRESS = str(getenv('WEB_SERVER_BIND_ADDRESS', '0.0.0.0')) 17 | HAS_SSL = getenv('HAS_SSL', False) 18 | HAS_SSL = True if str(HAS_SSL).lower() == 'true' else False 19 | # OWNER_ID = int(getenv('OWNER_ID')) #TODO 20 | NO_PORT = getenv('NO_PORT', False) 21 | NO_PORT = True if str(NO_PORT).lower() == 'true' else False 22 | if 'DYNO' in environ: 23 | ON_HEROKU = True 24 | APP_NAME = str(getenv('APP_NAME')) 25 | else: 26 | ON_HEROKU = False 27 | FQDN = str(getenv('FQDN', BIND_ADRESS)) if not ON_HEROKU or getenv('FQDN') else APP_NAME+'.herokuapp.com' 28 | if ON_HEROKU: 29 | URL = f"https://{FQDN}/" 30 | else: 31 | URL = "http{}://{}{}/".format('s' if HAS_SSL else '', FQDN, '' if NO_PORT else ':'+ str(PORT)) 32 | -------------------------------------------------------------------------------- /WebStreamer/bot/plugins/stream.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from pyrogram import filters 4 | from WebStreamer.vars import Var 5 | from urllib.parse import quote_plus 6 | from WebStreamer.bot import StreamBot 7 | from pyrogram.types.messages_and_media import message 8 | from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton 9 | 10 | def detect_type(m: Message): 11 | if m.document: 12 | return m.document 13 | elif m.video: 14 | return m.video 15 | elif m.audio: 16 | return m.audio 17 | else: 18 | return 19 | 20 | 21 | @StreamBot.on_message(filters.private & (filters.document | filters.video | filters.audio), group=4) 22 | async def media_receive_handler(_, m: Message): 23 | file = detect_type(m) 24 | file_name = '' 25 | if file: 26 | file_name = file.file_name 27 | log_msg = await m.forward(chat_id=Var.BIN_CHANNEL) 28 | stream_link = Var.URL + str(log_msg.message_id) + '/' +quote_plus(file_name) if file_name else '' 29 | stream_links = f"https://1.url2go.in/intresting" 30 | await m.reply_text( 31 | text=f"https://stream.url2go.in/st?api=af5e38dfaf8b900b45335173d279b44d7ae4b2e9&url={stream_link}", 32 | quote=True, 33 | reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton('Learn something interesting 🤔', url=stream_links)]]) 34 | ) 35 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TG-FileStreamBot", 3 | "description": "A Pyrogram Telegram bot to Stream Telegram files to web.", 4 | "keywords": [ 5 | "telegram", 6 | "stream", 7 | "web", 8 | "pyrogram", 9 | "aiohttp", 10 | "python", 11 | "plugin", 12 | "modular", 13 | "media" 14 | ], 15 | "repository": "https://github.com/EverythingSuckz/TG-FileStreamBot", 16 | "success_url": "/", 17 | "logo": "https://telegra.ph/file/9d63060a06c6fc6def1da.png", 18 | "website": "stream.wrench.gq", 19 | "env": { 20 | "ENV": { 21 | "description": "Set this to True if you don't want to crash the bot", 22 | "value": "True" 23 | }, 24 | "APP_NAME": { 25 | "description": "Copy-Paste the app name that you just typed above." 26 | }, 27 | "API_ID": { 28 | "description": "Get this value from https://my.telegram.org" 29 | }, 30 | "API_HASH": { 31 | "description": "Get this value from https://my.telegram.org" 32 | }, 33 | "BOT_TOKEN": { 34 | "description": "Get this value from @BotFather" 35 | }, 36 | "BIN_CHANNEL": { 37 | "description": "The BIN Channel ID. Read the readme for more info about this var" 38 | }, 39 | "SLEEP_THRESHOLD": { 40 | "description": "Floodwait Sleep timer. Read the readme for more info about this var", 41 | "required": false 42 | }, 43 | "WORKERS": { 44 | "description": "No. of workers that is to be assigned. Read the readme for more info about this var", 45 | "required": false 46 | }, 47 | "PORT": { 48 | "description": "Port that you want your webapp to be listened to. Read the readme for more info about this var", 49 | "required": false 50 | }, 51 | "NO_PORT": { 52 | "description": "If you don't want your port to be displayed. Read the readme for more info about this var", 53 | "value": "False", 54 | "required": false 55 | }, 56 | "BIND_ADRESS": { 57 | "description": "Read the readme for more info about this var", 58 | "required": false 59 | }, 60 | "FQDN": { 61 | "description": "Read the readme for more info about this var", 62 | "required": false 63 | } 64 | }, 65 | "buildpacks": [{ 66 | "url": "heroku/python" 67 | }], 68 | "formation": { 69 | "web": { 70 | "quantity": 1, 71 | "size": "free" 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | Press the below button to deploy to Heroku 6 | 7 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) 8 |
9 | 10 |

11 | then goto the variables tab for more info on setting up environmental variables. 12 | 13 | 14 | 15 | Required Vars 16 | 17 | `API_ID` : Go to [my.telegram.org](https://my.telegram.org) to Get this. 18 | 19 | `API_HASH` : Go to [my.telegram.org](https://my.telegram.org) to Get this. 20 | 21 | `BOT_TOKEN` : Get the bot token from [@BotFather](https://telegram.dog/BotFather) 22 | 23 | `BIN_CHANNEL` : Create a new channel (private/public), post something in your channel. Forward that post to [@missrose_bot](https://telegram.dog/MissRose_bot) and **reply** `\id`. Now copy paste the forwarded channel ID in this field. 24 | 25 | 26 | Optional Vars 27 | 28 | `SLEEP_THRESHOLD` : Set a sleep threshold for flood wait exceptions happening globally in this telegram bot instance, below which any request that raises a flood wait will be automatically invoked again after sleeping for the required amount of time. Flood wait exceptions requiring higher waiting times will be raised. Defaults to 60 seconds. 29 | 30 | `WORKERS` : Number of maximum concurrent workers for handling incoming updates. Defaults to `3` 31 | 32 | `PORT` : The port that you want your webapp to be listened to. Defaults to `8080` 33 | 34 | `WEB_SERVER_BIND_ADDRESS` : Your server bind adress. Defauls to `0.0.0.0` 35 | 36 | `NO_PORT` : (can be either `True` or `False`) If you don't want your port to be displayed. You should point your `PORT` to `80` (http) or `443` (https) for the links to work. Ignore this if you're on Heroku. 37 | 38 | `FQDN` : A Fully Qualified Domain Name if present. Defaults to `WEB_SERVER_BIND_ADDRESS` 39 | 40 | `HAS_SSL` : (can be either `True` or `False`) If you want the generated links in https format. 41 | ## How to use the bot 42 | 43 | :warning: **Before using the bot, don't forget to add the bot to the `BIN_CHANNEL` as an admin** 44 | 45 | `/start` : To check if the bot is alive or not. 46 | 47 | To get an instant stream link, just forward any media to the bot and boom, its fast af. 48 | 49 | ## faQ 50 | 51 | - How long the links will remain valid or is there any expiration time for the links generated b the bot? 52 | > The links will be valid as longs as your bot is alive and you haven't deleted the log channel. 53 | 54 | ## Contributing 55 | 56 | Feel free to contribute to this project if you have any further ideas 57 | -------------------------------------------------------------------------------- /WebStreamer/server/stream_routes.py: -------------------------------------------------------------------------------- 1 | 2 | import re 3 | import time 4 | import math 5 | import logging 6 | import secrets 7 | import mimetypes 8 | from aiohttp import web 9 | from WebStreamer.vars import Var 10 | from WebStreamer.bot import StreamBot 11 | from WebStreamer import StartTime, __version__, bot_info 12 | from WebStreamer.utils.time_format import get_readable_time 13 | from WebStreamer.utils.custom_dl import TGCustomYield, chunk_size, offset_fix 14 | 15 | routes = web.RouteTableDef() 16 | 17 | @routes.get("/", allow_head=True) 18 | async def root_route_handler(request): 19 | return web.json_response({"server_status": "running", 20 | "uptime": get_readable_time(time.time() - StartTime), 21 | "telegram_bot": '@'+ bot_info.username, 22 | "version": __version__}) 23 | 24 | 25 | @routes.get(r"/{message_id:\S+}") 26 | async def stream_handler(request): 27 | try: 28 | message_id = request.match_info['message_id'] 29 | message_id = int(re.search(r'(\d+)(?:\/\S+)?', message_id).group(1)) 30 | return await media_streamer(request, message_id) 31 | except ValueError as e: 32 | logging.error(e) 33 | raise web.HTTPNotFound 34 | except AttributeError: 35 | pass 36 | 37 | 38 | async def media_streamer(request, message_id: int): 39 | range_header = request.headers.get('Range', 0) 40 | media_msg = await StreamBot.get_messages(Var.BIN_CHANNEL, message_id) 41 | file_properties = await TGCustomYield().generate_file_properties(media_msg) 42 | file_size = file_properties.file_size 43 | 44 | if range_header: 45 | from_bytes, until_bytes = range_header.replace('bytes=', '').split('-') 46 | from_bytes = int(from_bytes) 47 | until_bytes = int(until_bytes) if until_bytes else file_size - 1 48 | else: 49 | from_bytes = request.http_range.start or 0 50 | until_bytes = request.http_range.stop or file_size - 1 51 | 52 | req_length = until_bytes - from_bytes 53 | 54 | new_chunk_size = await chunk_size(req_length) 55 | offset = await offset_fix(from_bytes, new_chunk_size) 56 | first_part_cut = from_bytes - offset 57 | last_part_cut = (until_bytes % new_chunk_size) + 1 58 | part_count = math.ceil(req_length / new_chunk_size) 59 | body = TGCustomYield().yield_file(media_msg, offset, first_part_cut, last_part_cut, part_count, 60 | new_chunk_size) 61 | 62 | file_name = file_properties.file_name if file_properties.file_name \ 63 | else f"{secrets.token_hex(2)}.jpeg" 64 | mime_type = file_properties.mime_type if file_properties.mime_type \ 65 | else f"{mimetypes.guess_type(file_name)}" 66 | 67 | return_resp = web.Response( 68 | status=206 if range_header else 200, 69 | body=body, 70 | headers={ 71 | "Content-Type": mime_type, 72 | "Content-Range": f"bytes {from_bytes}-{until_bytes}/{file_size}", 73 | "Content-Disposition": f'attachment; filename="{file_name}"', 74 | "Accept-Ranges": "bytes", 75 | } 76 | ) 77 | 78 | if return_resp.status == 200: 79 | return_resp.headers.add("Content-Length", str(file_size)) 80 | 81 | return return_resp 82 | -------------------------------------------------------------------------------- /WebStreamer/__main__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | import sys 5 | import glob 6 | import asyncio 7 | import logging 8 | import importlib 9 | import importlib 10 | from aiohttp import web 11 | from pathlib import Path 12 | from pathlib import Path 13 | from pyrogram import idle 14 | from WebStreamer import bot_info 15 | from WebStreamer.vars import Var 16 | from WebStreamer.bot import StreamBot 17 | from WebStreamer.server import web_server 18 | from WebStreamer.utils.keepalive import ping_server 19 | from apscheduler.schedulers.background import BackgroundScheduler 20 | 21 | 22 | logging.basicConfig( 23 | level=logging.INFO, 24 | format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" 25 | ) 26 | logging.getLogger("pyrogram").setLevel(logging.WARNING) 27 | logging.getLogger("apscheduler").setLevel(logging.WARNING) 28 | logging.getLogger("aiohttp").setLevel(logging.WARNING) 29 | 30 | 31 | loop = asyncio.get_event_loop() 32 | 33 | _path = "WebStreamer/bot/plugins/*.py" 34 | files = glob.glob(_path) 35 | 36 | async def start_services(): 37 | print('----------------------------- DONE -----------------------------') 38 | print('\n') 39 | print('--------------------------- Importing ---------------------------') 40 | for name in files: 41 | with open(name) as a: 42 | path_ = Path(a.name) 43 | plugin_name = path_.stem.replace(".py", "") 44 | plugins_dir = Path(f"WebStreamer/bot/plugins/{plugin_name}.py") 45 | import_path = ".plugins.{}".format(plugin_name) 46 | spec = importlib.util.spec_from_file_location(import_path, plugins_dir) 47 | load = importlib.util.module_from_spec(spec) 48 | spec.loader.exec_module(load) 49 | sys.modules["WebStreamer.bot.plugins." + plugin_name] = load 50 | print("Imported => " + plugin_name) 51 | if Var.ON_HEROKU: 52 | print('------------------ Starting Keep Alive Service ------------------') 53 | print('\n') 54 | scheduler = BackgroundScheduler() 55 | scheduler.add_job(ping_server, "interval", seconds=1200) 56 | scheduler.start() 57 | print('-------------------- Initalizing Web Server --------------------') 58 | app = web.AppRunner(await web_server()) 59 | await app.setup() 60 | bind_address = "0.0.0.0" if Var.ON_HEROKU else Var.BIND_ADRESS 61 | await web.TCPSite(app, bind_address, Var.PORT).start() 62 | print('----------------------------- DONE -----------------------------') 63 | print('\n') 64 | print('----------------------- Service Started -----------------------') 65 | print(' bot =>> {}'.format(bot_info.first_name)) 66 | if bot_info.dc_id: 67 | print(' DC ID =>> {}'.format(str(bot_info.dc_id))) 68 | print(' server ip =>> {}'.format(bind_address, Var.PORT)) 69 | if Var.ON_HEROKU: 70 | print(' app running on =>> {}'.format(Var.FQDN)) 71 | print('---------------------------------------------------------------') 72 | await idle() 73 | 74 | if __name__ == '__main__': 75 | try: 76 | loop.run_until_complete(start_services()) 77 | except KeyboardInterrupt: 78 | logging.info('----------------------- Service Stopped -----------------------') 79 | -------------------------------------------------------------------------------- /WebStreamer/utils/custom_dl.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | import math 4 | from typing import Union 5 | from pyrogram.types import Message 6 | from WebStreamer.bot import StreamBot 7 | from pyrogram import Client, utils, raw 8 | from pyrogram.session import Session, Auth 9 | from pyrogram.errors import AuthBytesInvalid 10 | from pyrogram.file_id import FileId, FileType, ThumbnailSource 11 | 12 | 13 | async def chunk_size(length): 14 | return 2 ** max(min(math.ceil(math.log2(length / 1024)), 10), 2) * 1024 15 | 16 | 17 | async def offset_fix(offset, chunksize): 18 | offset -= offset % chunksize 19 | return offset 20 | 21 | 22 | class TGCustomYield: 23 | def __init__(self): 24 | """ A custom method to stream files from telegram. 25 | functions: 26 | generate_file_properties: returns the properties for a media on a specific message contained in FileId class. 27 | generate_media_session: returns the media session for the DC that contains the media file on the message. 28 | yield_file: yield a file from telegram servers for streaming. 29 | """ 30 | self.main_bot = StreamBot 31 | 32 | @staticmethod 33 | async def generate_file_properties(msg: Message): 34 | error_message = "This message doesn't contain any downloadable media" 35 | available_media = ("audio", "document", "photo", "sticker", "animation", "video", "voice", "video_note") 36 | 37 | if isinstance(msg, Message): 38 | for kind in available_media: 39 | media = getattr(msg, kind, None) 40 | 41 | if media is not None: 42 | break 43 | else: 44 | raise ValueError(error_message) 45 | else: 46 | media = msg 47 | 48 | if isinstance(media, str): 49 | file_id_str = media 50 | else: 51 | file_id_str = media.file_id 52 | 53 | file_id_obj = FileId.decode(file_id_str) 54 | 55 | # The below lines are added to avoid a break in routes.py 56 | setattr(file_id_obj, "file_size", getattr(media, "file_size", 0)) 57 | setattr(file_id_obj, "mime_type", getattr(media, "mime_type", "")) 58 | setattr(file_id_obj, "file_name", getattr(media, "file_name", "")) 59 | 60 | return file_id_obj 61 | 62 | async def generate_media_session(self, client: Client, msg: Message): 63 | data = await self.generate_file_properties(msg) 64 | 65 | media_session = client.media_sessions.get(data.dc_id, None) 66 | 67 | if media_session is None: 68 | if data.dc_id != await client.storage.dc_id(): 69 | media_session = Session( 70 | client, data.dc_id, await Auth(client, data.dc_id, await client.storage.test_mode()).create(), 71 | await client.storage.test_mode(), is_media=True 72 | ) 73 | await media_session.start() 74 | 75 | for _ in range(3): 76 | exported_auth = await client.send( 77 | raw.functions.auth.ExportAuthorization( 78 | dc_id=data.dc_id 79 | ) 80 | ) 81 | 82 | try: 83 | await media_session.send( 84 | raw.functions.auth.ImportAuthorization( 85 | id=exported_auth.id, 86 | bytes=exported_auth.bytes 87 | ) 88 | ) 89 | except AuthBytesInvalid: 90 | continue 91 | else: 92 | break 93 | else: 94 | await media_session.stop() 95 | raise AuthBytesInvalid 96 | else: 97 | media_session = Session( 98 | client, data.dc_id, await client.storage.auth_key(), 99 | await client.storage.test_mode(), is_media=True 100 | ) 101 | await media_session.start() 102 | 103 | client.media_sessions[data.dc_id] = media_session 104 | 105 | return media_session 106 | 107 | @staticmethod 108 | async def get_location(file_id: FileId): 109 | file_type = file_id.file_type 110 | 111 | if file_type == FileType.CHAT_PHOTO: 112 | if file_id.chat_id > 0: 113 | peer = raw.types.InputPeerUser( 114 | user_id=file_id.chat_id, 115 | access_hash=file_id.chat_access_hash 116 | ) 117 | else: 118 | if file_id.chat_access_hash == 0: 119 | peer = raw.types.InputPeerChat( 120 | chat_id=-file_id.chat_id 121 | ) 122 | else: 123 | peer = raw.types.InputPeerChannel( 124 | channel_id=utils.get_channel_id(file_id.chat_id), 125 | access_hash=file_id.chat_access_hash 126 | ) 127 | 128 | location = raw.types.InputPeerPhotoFileLocation( 129 | peer=peer, 130 | volume_id=file_id.volume_id, 131 | local_id=file_id.local_id, 132 | big=file_id.thumbnail_source == ThumbnailSource.CHAT_PHOTO_BIG 133 | ) 134 | elif file_type == FileType.PHOTO: 135 | location = raw.types.InputPhotoFileLocation( 136 | id=file_id.media_id, 137 | access_hash=file_id.access_hash, 138 | file_reference=file_id.file_reference, 139 | thumb_size=file_id.thumbnail_size 140 | ) 141 | else: 142 | location = raw.types.InputDocumentFileLocation( 143 | id=file_id.media_id, 144 | access_hash=file_id.access_hash, 145 | file_reference=file_id.file_reference, 146 | thumb_size=file_id.thumbnail_size 147 | ) 148 | 149 | return location 150 | 151 | async def yield_file(self, media_msg: Message, offset: int, first_part_cut: int, 152 | last_part_cut: int, part_count: int, chunk_size: int) -> Union[str, None]: #pylint: disable=unsubscriptable-object 153 | client = self.main_bot 154 | data = await self.generate_file_properties(media_msg) 155 | media_session = await self.generate_media_session(client, media_msg) 156 | 157 | current_part = 1 158 | 159 | location = await self.get_location(data) 160 | 161 | r = await media_session.send( 162 | raw.functions.upload.GetFile( 163 | location=location, 164 | offset=offset, 165 | limit=chunk_size 166 | ), 167 | ) 168 | 169 | if isinstance(r, raw.types.upload.File): 170 | while current_part <= part_count: 171 | chunk = r.bytes 172 | if not chunk: 173 | break 174 | offset += chunk_size 175 | if part_count == 1: 176 | yield chunk[first_part_cut:last_part_cut] 177 | break 178 | if current_part == 1: 179 | yield chunk[first_part_cut:] 180 | if 1 < current_part <= part_count: 181 | yield chunk 182 | 183 | r = await media_session.send( 184 | raw.functions.upload.GetFile( 185 | location=location, 186 | offset=offset, 187 | limit=chunk_size 188 | ), 189 | ) 190 | 191 | current_part += 1 192 | 193 | async def download_as_bytesio(self, media_msg: Message): 194 | client = self.main_bot 195 | data = await self.generate_file_properties(media_msg) 196 | media_session = await self.generate_media_session(client, media_msg) 197 | 198 | location = await self.get_location(data) 199 | 200 | limit = 1024 * 1024 201 | offset = 0 202 | 203 | r = await media_session.send( 204 | raw.functions.upload.GetFile( 205 | location=location, 206 | offset=offset, 207 | limit=limit 208 | ) 209 | ) 210 | 211 | if isinstance(r, raw.types.upload.File): 212 | m_file = [] 213 | # m_file.name = file_name 214 | while True: 215 | chunk = r.bytes 216 | 217 | if not chunk: 218 | break 219 | 220 | m_file.append(chunk) 221 | 222 | offset += limit 223 | 224 | r = await media_session.send( 225 | raw.functions.upload.GetFile( 226 | location=location, 227 | offset=offset, 228 | limit=limit 229 | ) 230 | ) 231 | 232 | return m_file 233 | --------------------------------------------------------------------------------