File Name:
49 |{{file_name}}
50 |
File Size:
51 |{{file_size}}
52 |├── .gitattributes ├── .gitignore ├── Dockerfile ├── FileStream ├── __init__.py ├── __main__.py ├── bot │ ├── __init__.py │ ├── clients.py │ └── plugins │ │ ├── admin.py │ │ ├── callback.py │ │ ├── start.py │ │ └── stream.py ├── config.py ├── server │ ├── __init__.py │ ├── exceptions.py │ └── stream_routes.py ├── template │ ├── dl.html │ └── play.html └── utils │ ├── __init__.py │ ├── bot_utils.py │ ├── broadcast_helper.py │ ├── custom_dl.py │ ├── database.py │ ├── file_properties.py │ ├── human_readable.py │ ├── render_template.py │ ├── time_format.py │ └── translation.py ├── Procfile ├── README.md ├── app.json └── requirements.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .nox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | .pytest_cache/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | db.sqlite3 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # IPython 77 | profile_default/ 78 | ipython_config.py 79 | 80 | # pyenv 81 | .python-version 82 | 83 | # celery beat schedule file 84 | celerybeat-schedule 85 | 86 | # SageMath parsed files 87 | *.sage.py 88 | 89 | # Environments 90 | .env 91 | .venv 92 | env/ 93 | venv/ 94 | ENV/ 95 | env.bak/ 96 | venv.bak/ 97 | 98 | # Spyder project settings 99 | .spyderproject 100 | .spyproject 101 | 102 | # Rope project settings 103 | .ropeproject 104 | 105 | # mkdocs documentation 106 | /site 107 | 108 | # mypy 109 | .mypy_cache/ 110 | .dmypy.json 111 | dmypy.json 112 | 113 | # Pyre type checker 114 | .pyre/ 115 | 116 | #session files 117 | *.session 118 | *.session-journal 119 | 120 | .env 121 | test.py 122 | /test 123 | 124 | streambot.log.* 125 | .idea/workspace.xml 126 | *.xml 127 | *.xml 128 | .idea/workspace.xml 129 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11 2 | 3 | WORKDIR /app 4 | COPY . /app 5 | 6 | RUN pip install --upgrade pip 7 | RUN pip install -r requirements.txt 8 | 9 | COPY . . 10 | 11 | CMD ["python", "-m", "FileStream"] -------------------------------------------------------------------------------- /FileStream/__init__.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | __version__ = "1.1.0" 4 | StartTime = time.time() 5 | 6 | -------------------------------------------------------------------------------- /FileStream/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import asyncio 3 | import logging 4 | import traceback 5 | import logging.handlers as handlers 6 | from FileStream.config import Telegram, Server 7 | from aiohttp import web 8 | from pyrogram import idle 9 | 10 | from FileStream.bot import FileStream 11 | from FileStream.server import web_server 12 | from FileStream.bot.clients import initialize_clients 13 | 14 | logging.basicConfig( 15 | level=logging.INFO, 16 | datefmt="%d/%m/%Y %H:%M:%S", 17 | format='[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s', 18 | handlers=[logging.StreamHandler(stream=sys.stdout), 19 | handlers.RotatingFileHandler("streambot.log", mode="a", maxBytes=104857600, backupCount=2, encoding="utf-8")],) 20 | 21 | logging.getLogger("aiohttp").setLevel(logging.ERROR) 22 | logging.getLogger("pyrogram").setLevel(logging.ERROR) 23 | logging.getLogger("aiohttp.web").setLevel(logging.ERROR) 24 | 25 | server = web.AppRunner(web_server()) 26 | 27 | loop = asyncio.get_event_loop() 28 | 29 | async def start_services(): 30 | print() 31 | if Telegram.SECONDARY: 32 | print("------------------ Starting as Secondary Server ------------------") 33 | else: 34 | print("------------------- Starting as Primary Server -------------------") 35 | print() 36 | print("-------------------- Initializing Telegram Bot --------------------") 37 | 38 | 39 | await FileStream.start() 40 | bot_info = await FileStream.get_me() 41 | FileStream.id = bot_info.id 42 | FileStream.username = bot_info.username 43 | FileStream.fname=bot_info.first_name 44 | print("------------------------------ DONE ------------------------------") 45 | print() 46 | print("---------------------- Initializing Clients ----------------------") 47 | await initialize_clients() 48 | print("------------------------------ DONE ------------------------------") 49 | print() 50 | print("--------------------- Initializing Web Server ---------------------") 51 | await server.setup() 52 | await web.TCPSite(server, Server.BIND_ADDRESS, Server.PORT).start() 53 | print("------------------------------ DONE ------------------------------") 54 | print() 55 | print("------------------------- Service Started -------------------------") 56 | print(" bot =>> {}".format(bot_info.first_name)) 57 | if bot_info.dc_id: 58 | print(" DC ID =>> {}".format(str(bot_info.dc_id))) 59 | print(" URL =>> {}".format(Server.URL)) 60 | print("------------------------------------------------------------------") 61 | await idle() 62 | 63 | async def cleanup(): 64 | await server.cleanup() 65 | await FileStream.stop() 66 | 67 | if __name__ == "__main__": 68 | try: 69 | loop.run_until_complete(start_services()) 70 | except KeyboardInterrupt: 71 | pass 72 | except Exception as err: 73 | logging.error(traceback.format_exc()) 74 | finally: 75 | loop.run_until_complete(cleanup()) 76 | loop.stop() 77 | print("------------------------ Stopped Services ------------------------") -------------------------------------------------------------------------------- /FileStream/bot/__init__.py: -------------------------------------------------------------------------------- 1 | from ..config import Telegram 2 | from pyrogram import Client 3 | 4 | if Telegram.SECONDARY: 5 | plugins=None 6 | no_updates=True 7 | else: 8 | plugins={"root": "FileStream/bot/plugins"} 9 | no_updates=None 10 | 11 | FileStream = Client( 12 | name="FileStream", 13 | api_id=Telegram.API_ID, 14 | api_hash=Telegram.API_HASH, 15 | workdir="FileStream", 16 | plugins=plugins, 17 | bot_token=Telegram.BOT_TOKEN, 18 | sleep_threshold=Telegram.SLEEP_THRESHOLD, 19 | workers=Telegram.WORKERS, 20 | no_updates=no_updates 21 | ) 22 | 23 | multi_clients = {} 24 | work_loads = {} 25 | 26 | -------------------------------------------------------------------------------- /FileStream/bot/clients.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | from os import environ 4 | from ..config import Telegram 5 | from pyrogram import Client 6 | from . import multi_clients, work_loads, FileStream 7 | 8 | 9 | async def initialize_clients(): 10 | all_tokens = dict( 11 | (c + 1, t) 12 | for c, (_, t) in enumerate( 13 | filter( 14 | lambda n: n[0].startswith("MULTI_TOKEN"), sorted(environ.items()) 15 | ) 16 | ) 17 | ) 18 | if not all_tokens: 19 | multi_clients[0] = FileStream 20 | work_loads[0] = 0 21 | print("No additional clients found, using default client") 22 | return 23 | 24 | async def start_client(client_id, token): 25 | try: 26 | if len(token) >= 100: 27 | session_string=token 28 | bot_token=None 29 | print(f'Starting Client - {client_id} Using Session String') 30 | else: 31 | session_string=None 32 | bot_token=token 33 | print(f'Starting Client - {client_id} Using Bot Token') 34 | if client_id == len(all_tokens): 35 | await asyncio.sleep(2) 36 | print("This will take some time, please wait...") 37 | client = await Client( 38 | name=str(client_id), 39 | api_id=Telegram.API_ID, 40 | api_hash=Telegram.API_HASH, 41 | bot_token=bot_token, 42 | sleep_threshold=Telegram.SLEEP_THRESHOLD, 43 | no_updates=True, 44 | session_string=session_string, 45 | in_memory=True, 46 | ).start() 47 | client.id = (await client.get_me()).id 48 | work_loads[client_id] = 0 49 | return client_id, client 50 | except Exception: 51 | logging.error(f"Failed starting Client - {client_id} Error:", exc_info=True) 52 | 53 | clients = await asyncio.gather(*[start_client(i, token) for i, token in all_tokens.items()]) 54 | multi_clients.update(dict(clients)) 55 | if len(multi_clients) != 1: 56 | Telegram.MULTI_CLIENT = True 57 | print("Multi-Client Mode Enabled") 58 | else: 59 | print("No additional clients were initialized, using default client") 60 | -------------------------------------------------------------------------------- /FileStream/bot/plugins/admin.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import string 4 | import random 5 | import asyncio 6 | import aiofiles 7 | import datetime 8 | 9 | from FileStream.utils.broadcast_helper import send_msg 10 | from FileStream.utils.database import Database 11 | from FileStream.bot import FileStream 12 | from FileStream.server.exceptions import FIleNotFound 13 | from FileStream.config import Telegram, Server 14 | from pyrogram import filters, Client 15 | from pyrogram.types import Message 16 | from pyrogram.enums.parse_mode import ParseMode 17 | 18 | db = Database(Telegram.DATABASE_URL, Telegram.SESSION_NAME) 19 | broadcast_ids = {} 20 | 21 | 22 | @FileStream.on_message(filters.command("status") & filters.private & filters.user(Telegram.OWNER_ID)) 23 | async def sts(c: Client, m: Message): 24 | await m.reply_text(text=f"""**Total Users in DB:** `{await db.total_users_count()}` 25 | **Banned Users in DB:** `{await db.total_banned_users_count()}` 26 | **Total Links Generated: ** `{await db.total_files()}`""" 27 | , parse_mode=ParseMode.MARKDOWN, quote=True) 28 | 29 | 30 | @FileStream.on_message(filters.command("ban") & filters.private & filters.user(Telegram.OWNER_ID)) 31 | async def sts(b, m: Message): 32 | id = m.text.split("/ban ")[-1] 33 | if not await db.is_user_banned(int(id)): 34 | try: 35 | await db.ban_user(int(id)) 36 | await db.delete_user(int(id)) 37 | await m.reply_text(text=f"`{id}`** is Banned** ", parse_mode=ParseMode.MARKDOWN, quote=True) 38 | if not str(id).startswith('-100'): 39 | await b.send_message( 40 | chat_id=id, 41 | text="**Your Banned to Use The Bot**", 42 | parse_mode=ParseMode.MARKDOWN, 43 | disable_web_page_preview=True 44 | ) 45 | except Exception as e: 46 | await m.reply_text(text=f"**something went wrong: {e}** ", parse_mode=ParseMode.MARKDOWN, quote=True) 47 | else: 48 | await m.reply_text(text=f"`{id}`** is Already Banned** ", parse_mode=ParseMode.MARKDOWN, quote=True) 49 | 50 | 51 | @FileStream.on_message(filters.command("unban") & filters.private & filters.user(Telegram.OWNER_ID)) 52 | async def sts(b, m: Message): 53 | id = m.text.split("/unban ")[-1] 54 | if await db.is_user_banned(int(id)): 55 | try: 56 | await db.unban_user(int(id)) 57 | await m.reply_text(text=f"`{id}`** is Unbanned** ", parse_mode=ParseMode.MARKDOWN, quote=True) 58 | if not str(id).startswith('-100'): 59 | await b.send_message( 60 | chat_id=id, 61 | text="**Your Unbanned now Use can use The Bot**", 62 | parse_mode=ParseMode.MARKDOWN, 63 | disable_web_page_preview=True 64 | ) 65 | except Exception as e: 66 | await m.reply_text(text=f"** something went wrong: {e}**", parse_mode=ParseMode.MARKDOWN, quote=True) 67 | else: 68 | await m.reply_text(text=f"`{id}`** is not Banned** ", parse_mode=ParseMode.MARKDOWN, quote=True) 69 | 70 | 71 | @FileStream.on_message(filters.command("broadcast") & filters.private & filters.user(Telegram.OWNER_ID) & filters.reply) 72 | async def broadcast_(c, m): 73 | all_users = await db.get_all_users() 74 | broadcast_msg = m.reply_to_message 75 | while True: 76 | broadcast_id = ''.join([random.choice(string.ascii_letters) for i in range(3)]) 77 | if not broadcast_ids.get(broadcast_id): 78 | break 79 | out = await m.reply_text( 80 | text=f"Broadcast initiated! You will be notified with log file when all the users are notified." 81 | ) 82 | start_time = time.time() 83 | total_users = await db.total_users_count() 84 | done = 0 85 | failed = 0 86 | success = 0 87 | broadcast_ids[broadcast_id] = dict( 88 | total=total_users, 89 | current=done, 90 | failed=failed, 91 | success=success 92 | ) 93 | async with aiofiles.open('broadcast.txt', 'w') as broadcast_log_file: 94 | async for user in all_users: 95 | sts, msg = await send_msg( 96 | user_id=int(user['id']), 97 | message=broadcast_msg 98 | ) 99 | if msg is not None: 100 | await broadcast_log_file.write(msg) 101 | if sts == 200: 102 | success += 1 103 | else: 104 | failed += 1 105 | if sts == 400: 106 | await db.delete_user(user['id']) 107 | done += 1 108 | if broadcast_ids.get(broadcast_id) is None: 109 | break 110 | else: 111 | broadcast_ids[broadcast_id].update( 112 | dict( 113 | current=done, 114 | failed=failed, 115 | success=success 116 | ) 117 | ) 118 | try: 119 | await out.edit_text(f"Broadcast Status\n\ncurrent: {done}\nfailed:{failed}\nsuccess: {success}") 120 | except: 121 | pass 122 | if broadcast_ids.get(broadcast_id): 123 | broadcast_ids.pop(broadcast_id) 124 | completed_in = datetime.timedelta(seconds=int(time.time() - start_time)) 125 | await asyncio.sleep(3) 126 | await out.delete() 127 | if failed == 0: 128 | await m.reply_text( 129 | text=f"broadcast completed in `{completed_in}`\n\nTotal users {total_users}.\nTotal done {done}, {success} success and {failed} failed.", 130 | quote=True 131 | ) 132 | else: 133 | await m.reply_document( 134 | document='broadcast.txt', 135 | caption=f"broadcast completed in `{completed_in}`\n\nTotal users {total_users}.\nTotal done {done}, {success} success and {failed} failed.", 136 | quote=True 137 | ) 138 | os.remove('broadcast.txt') 139 | 140 | 141 | @FileStream.on_message(filters.command("del") & filters.private & filters.user(Telegram.OWNER_ID)) 142 | async def sts(c: Client, m: Message): 143 | file_id = m.text.split(" ")[-1] 144 | try: 145 | file_info = await db.get_file(file_id) 146 | except FIleNotFound: 147 | await m.reply_text( 148 | text=f"**ꜰɪʟᴇ ᴀʟʀᴇᴀᴅʏ ᴅᴇʟᴇᴛᴇᴅ**", 149 | quote=True 150 | ) 151 | return 152 | await db.delete_one_file(file_info['_id']) 153 | await db.count_links(file_info['user_id'], "-") 154 | await m.reply_text( 155 | text=f"**Fɪʟᴇ Dᴇʟᴇᴛᴇᴅ Sᴜᴄᴄᴇssғᴜʟʟʏ !** ", 156 | quote=True 157 | ) 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /FileStream/bot/plugins/callback.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import math 3 | from FileStream import __version__ 4 | from FileStream.bot import FileStream 5 | from FileStream.config import Telegram, Server 6 | from FileStream.utils.translation import LANG, BUTTON 7 | from FileStream.utils.bot_utils import gen_link 8 | from FileStream.utils.database import Database 9 | from FileStream.utils.human_readable import humanbytes 10 | from FileStream.server.exceptions import FIleNotFound 11 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery 12 | from pyrogram.file_id import FileId, FileType, PHOTO_TYPES 13 | from pyrogram.enums.parse_mode import ParseMode 14 | db = Database(Telegram.DATABASE_URL, Telegram.SESSION_NAME) 15 | 16 | #---------------------[ START CMD ]---------------------# 17 | @FileStream.on_callback_query() 18 | async def cb_data(bot, update: CallbackQuery): 19 | usr_cmd = update.data.split("_") 20 | if usr_cmd[0] == "home": 21 | await update.message.edit_text( 22 | text=LANG.START_TEXT.format(update.from_user.mention, FileStream.username), 23 | disable_web_page_preview=True, 24 | reply_markup=BUTTON.START_BUTTONS 25 | ) 26 | elif usr_cmd[0] == "help": 27 | await update.message.edit_text( 28 | text=LANG.HELP_TEXT.format(Telegram.OWNER_ID), 29 | disable_web_page_preview=True, 30 | reply_markup=BUTTON.HELP_BUTTONS 31 | ) 32 | elif usr_cmd[0] == "about": 33 | await update.message.edit_text( 34 | text=LANG.ABOUT_TEXT.format(FileStream.fname, __version__), 35 | disable_web_page_preview=True, 36 | reply_markup=BUTTON.ABOUT_BUTTONS 37 | ) 38 | 39 | #---------------------[ MY FILES CMD ]---------------------# 40 | 41 | elif usr_cmd[0] == "N/A": 42 | await update.answer("N/A", True) 43 | elif usr_cmd[0] == "close": 44 | await update.message.delete() 45 | elif usr_cmd[0] == "msgdelete": 46 | await update.message.edit_caption( 47 | caption= "**Cᴏɴғɪʀᴍ ʏᴏᴜ ᴡᴀɴᴛ ᴛᴏ ᴅᴇʟᴇᴛᴇ ᴛʜᴇ Fɪʟᴇ**\n\n", 48 | reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("ʏᴇs", callback_data=f"msgdelyes_{usr_cmd[1]}_{usr_cmd[2]}"), InlineKeyboardButton("ɴᴏ", callback_data=f"myfile_{usr_cmd[1]}_{usr_cmd[2]}")]]) 49 | ) 50 | elif usr_cmd[0] == "msgdelyes": 51 | await delete_user_file(usr_cmd[1], int(usr_cmd[2]), update) 52 | return 53 | elif usr_cmd[0] == "msgdelpvt": 54 | await update.message.edit_caption( 55 | caption= "**Cᴏɴғɪʀᴍ ʏᴏᴜ ᴡᴀɴᴛ ᴛᴏ ᴅᴇʟᴇᴛᴇ ᴛʜᴇ Fɪʟᴇ**\n\n", 56 | reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("ʏᴇs", callback_data=f"msgdelpvtyes_{usr_cmd[1]}"), InlineKeyboardButton("ɴᴏ", callback_data=f"mainstream_{usr_cmd[1]}")]]) 57 | ) 58 | elif usr_cmd[0] == "msgdelpvtyes": 59 | await delete_user_filex(usr_cmd[1], update) 60 | return 61 | 62 | elif usr_cmd[0] == "mainstream": 63 | _id = usr_cmd[1] 64 | reply_markup, stream_text = await gen_link(_id=_id) 65 | await update.message.edit_text( 66 | text=stream_text, 67 | parse_mode=ParseMode.HTML, 68 | disable_web_page_preview=True, 69 | reply_markup=reply_markup, 70 | ) 71 | 72 | elif usr_cmd[0] == "userfiles": 73 | file_list, total_files = await gen_file_list_button(int(usr_cmd[1]), update.from_user.id) 74 | await update.message.edit_caption( 75 | caption="Total files: {}".format(total_files), 76 | reply_markup=InlineKeyboardMarkup(file_list) 77 | ) 78 | elif usr_cmd[0] == "myfile": 79 | await gen_file_menu(usr_cmd[1], usr_cmd[2], update) 80 | return 81 | elif usr_cmd[0] == "sendfile": 82 | myfile = await db.get_file(usr_cmd[1]) 83 | file_name = myfile['file_name'] 84 | await update.answer(f"Sending File {file_name}") 85 | await update.message.reply_cached_media(myfile['file_id'], caption=f'**{file_name}**') 86 | else: 87 | await update.message.delete() 88 | 89 | 90 | 91 | #---------------------[ MY FILES FUNC ]---------------------# 92 | 93 | async def gen_file_list_button(file_list_no: int, user_id: int): 94 | 95 | file_range=[file_list_no*10-10+1, file_list_no*10] 96 | user_files, total_files=await db.find_files(user_id, file_range) 97 | 98 | file_list=[] 99 | async for x in user_files: 100 | file_list.append([InlineKeyboardButton(x["file_name"], callback_data=f"myfile_{x['_id']}_{file_list_no}")]) 101 | if total_files > 10: 102 | file_list.append( 103 | [InlineKeyboardButton("◄", callback_data="{}".format("userfiles_"+str(file_list_no-1) if file_list_no > 1 else 'N/A')), 104 | InlineKeyboardButton(f"{file_list_no}/{math.ceil(total_files/10)}", callback_data="N/A"), 105 | InlineKeyboardButton("►", callback_data="{}".format("userfiles_"+str(file_list_no+1) if total_files > file_list_no*10 else 'N/A'))] 106 | ) 107 | if not file_list: 108 | file_list.append( 109 | [InlineKeyboardButton("ᴇᴍᴘᴛʏ", callback_data="N/A")]) 110 | file_list.append([InlineKeyboardButton("ᴄʟᴏsᴇ", callback_data="close")]) 111 | return file_list, total_files 112 | 113 | async def gen_file_menu(_id, file_list_no, update: CallbackQuery): 114 | try: 115 | myfile_info=await db.get_file(_id) 116 | except FIleNotFound: 117 | await update.answer("File Not Found") 118 | return 119 | 120 | file_id=FileId.decode(myfile_info['file_id']) 121 | 122 | if file_id.file_type in PHOTO_TYPES: 123 | file_type = "Image" 124 | elif file_id.file_type == FileType.VOICE: 125 | file_type = "Voice" 126 | elif file_id.file_type in (FileType.VIDEO, FileType.ANIMATION, FileType.VIDEO_NOTE): 127 | file_type = "Video" 128 | elif file_id.file_type == FileType.DOCUMENT: 129 | file_type = "Document" 130 | elif file_id.file_type == FileType.STICKER: 131 | file_type = "Sticker" 132 | elif file_id.file_type == FileType.AUDIO: 133 | file_type = "Audio" 134 | else: 135 | file_type = "Unknown" 136 | 137 | page_link = f"{Server.URL}watch/{myfile_info['_id']}" 138 | stream_link = f"{Server.URL}dl/{myfile_info['_id']}" 139 | if "video" in file_type.lower(): 140 | MYFILES_BUTTONS = InlineKeyboardMarkup( 141 | [ 142 | [InlineKeyboardButton("sᴛʀᴇᴀᴍ", url=page_link), InlineKeyboardButton("ᴅᴏᴡɴʟᴏᴀᴅ", url=stream_link)], 143 | [InlineKeyboardButton("ɢᴇᴛ ғɪʟᴇ", callback_data=f"sendfile_{myfile_info['_id']}"), 144 | InlineKeyboardButton("ʀᴇᴠᴏᴋᴇ ғɪʟᴇ", callback_data=f"msgdelete_{myfile_info['_id']}_{file_list_no}")], 145 | [InlineKeyboardButton("ʙᴀᴄᴋ", callback_data="userfiles_{}".format(file_list_no))] 146 | ] 147 | ) 148 | else: 149 | MYFILES_BUTTONS = InlineKeyboardMarkup( 150 | [ 151 | [InlineKeyboardButton("ᴅᴏᴡɴʟᴏᴀᴅ", url=stream_link)], 152 | [InlineKeyboardButton("ɢᴇᴛ ғɪʟᴇ", callback_data=f"sendfile_{myfile_info['_id']}"), 153 | InlineKeyboardButton("ʀᴇᴠᴏᴋᴇ ғɪʟᴇ", callback_data=f"msgdelete_{myfile_info['_id']}_{file_list_no}")], 154 | [InlineKeyboardButton("ʙᴀᴄᴋ", callback_data="userfiles_{}".format(file_list_no))] 155 | ] 156 | ) 157 | 158 | TiMe = myfile_info['time'] 159 | if type(TiMe) == float: 160 | date = datetime.datetime.fromtimestamp(TiMe) 161 | await update.edit_message_caption( 162 | caption="**File Name :** `{}`\n**File Size :** `{}`\n**File Type :** `{}`\n**Created On :** `{}`".format(myfile_info['file_name'], 163 | humanbytes(int(myfile_info['file_size'])), 164 | file_type, 165 | TiMe if isinstance(TiMe,str) else date.date()), 166 | reply_markup=MYFILES_BUTTONS ) 167 | 168 | 169 | async def delete_user_file(_id, file_list_no: int, update:CallbackQuery): 170 | 171 | try: 172 | myfile_info=await db.get_file(_id) 173 | except FIleNotFound: 174 | await update.answer("File Already Deleted") 175 | return 176 | 177 | await db.delete_one_file(myfile_info['_id']) 178 | await db.count_links(update.from_user.id, "-") 179 | await update.message.edit_caption( 180 | caption= "**Fɪʟᴇ Dᴇʟᴇᴛᴇᴅ Sᴜᴄᴄᴇssғᴜʟʟʏ !**" + update.message.caption.replace("Cᴏɴғɪʀᴍ ʏᴏᴜ ᴡᴀɴᴛ ᴛᴏ ᴅᴇʟᴇᴛᴇ ᴛʜᴇ Fɪʟᴇ", ""), 181 | reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("ʙᴀᴄᴋ", callback_data=f"userfiles_1")]]) 182 | ) 183 | 184 | async def delete_user_filex(_id, update:CallbackQuery): 185 | 186 | try: 187 | myfile_info=await db.get_file(_id) 188 | except FIleNotFound: 189 | await update.answer("File Already Deleted") 190 | return 191 | 192 | await db.delete_one_file(myfile_info['_id']) 193 | await db.count_links(update.from_user.id, "-") 194 | await update.message.edit_caption( 195 | caption= "**Fɪʟᴇ Dᴇʟᴇᴛᴇᴅ Sᴜᴄᴄᴇssғᴜʟʟʏ !**\n\n", 196 | reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("ᴄʟᴏsᴇ", callback_data=f"close")]]) 197 | ) 198 | 199 | -------------------------------------------------------------------------------- /FileStream/bot/plugins/start.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import math 3 | from FileStream import __version__ 4 | from FileStream.bot import FileStream 5 | from FileStream.server.exceptions import FIleNotFound 6 | from FileStream.utils.bot_utils import gen_linkx, verify_user 7 | from FileStream.config import Telegram 8 | from FileStream.utils.database import Database 9 | from FileStream.utils.translation import LANG, BUTTON 10 | from pyrogram import filters, Client 11 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Message 12 | from pyrogram.enums.parse_mode import ParseMode 13 | import asyncio 14 | 15 | db = Database(Telegram.DATABASE_URL, Telegram.SESSION_NAME) 16 | 17 | @FileStream.on_message(filters.command('start') & filters.private) 18 | async def start(bot: Client, message: Message): 19 | if not await verify_user(bot, message): 20 | return 21 | usr_cmd = message.text.split("_")[-1] 22 | 23 | if usr_cmd == "/start": 24 | if Telegram.START_PIC: 25 | await message.reply_photo( 26 | photo=Telegram.START_PIC, 27 | caption=LANG.START_TEXT.format(message.from_user.mention, FileStream.username), 28 | parse_mode=ParseMode.HTML, 29 | reply_markup=BUTTON.START_BUTTONS 30 | ) 31 | else: 32 | await message.reply_text( 33 | text=LANG.START_TEXT.format(message.from_user.mention, FileStream.username), 34 | parse_mode=ParseMode.HTML, 35 | disable_web_page_preview=True, 36 | reply_markup=BUTTON.START_BUTTONS 37 | ) 38 | else: 39 | if "stream_" in message.text: 40 | try: 41 | file_check = await db.get_file(usr_cmd) 42 | file_id = str(file_check['_id']) 43 | if file_id == usr_cmd: 44 | reply_markup, stream_text = await gen_linkx(m=message, _id=file_id, 45 | name=[FileStream.username, FileStream.fname]) 46 | await message.reply_text( 47 | text=stream_text, 48 | parse_mode=ParseMode.HTML, 49 | disable_web_page_preview=True, 50 | reply_markup=reply_markup, 51 | quote=True 52 | ) 53 | 54 | except FIleNotFound as e: 55 | await message.reply_text("File Not Found") 56 | except Exception as e: 57 | await message.reply_text("Something Went Wrong") 58 | logging.error(e) 59 | 60 | elif "file_" in message.text: 61 | try: 62 | file_check = await db.get_file(usr_cmd) 63 | db_id = str(file_check['_id']) 64 | file_id = file_check['file_id'] 65 | file_name = file_check['file_name'] 66 | if db_id == usr_cmd: 67 | filex = await message.reply_cached_media(file_id=file_id, caption=f'**{file_name}**') 68 | await asyncio.sleep(3600) 69 | try: 70 | await filex.delete() 71 | await message.delete() 72 | except Exception: 73 | pass 74 | 75 | except FIleNotFound as e: 76 | await message.reply_text("**File Not Found**") 77 | except Exception as e: 78 | await message.reply_text("Something Went Wrong") 79 | logging.error(e) 80 | 81 | else: 82 | await message.reply_text(f"**Invalid Command**") 83 | 84 | @FileStream.on_message(filters.private & filters.command(["about"])) 85 | async def start(bot, message): 86 | if not await verify_user(bot, message): 87 | return 88 | if Telegram.START_PIC: 89 | await message.reply_photo( 90 | photo=Telegram.START_PIC, 91 | caption=LANG.ABOUT_TEXT.format(FileStream.fname, __version__), 92 | parse_mode=ParseMode.HTML, 93 | reply_markup=BUTTON.ABOUT_BUTTONS 94 | ) 95 | else: 96 | await message.reply_text( 97 | text=LANG.ABOUT_TEXT.format(FileStream.fname, __version__), 98 | disable_web_page_preview=True, 99 | reply_markup=BUTTON.ABOUT_BUTTONS 100 | ) 101 | 102 | @FileStream.on_message((filters.command('help')) & filters.private) 103 | async def help_handler(bot, message): 104 | if not await verify_user(bot, message): 105 | return 106 | if Telegram.START_PIC: 107 | await message.reply_photo( 108 | photo=Telegram.START_PIC, 109 | caption=LANG.HELP_TEXT.format(Telegram.OWNER_ID), 110 | parse_mode=ParseMode.HTML, 111 | reply_markup=BUTTON.HELP_BUTTONS 112 | ) 113 | else: 114 | await message.reply_text( 115 | text=LANG.HELP_TEXT.format(Telegram.OWNER_ID), 116 | parse_mode=ParseMode.HTML, 117 | disable_web_page_preview=True, 118 | reply_markup=BUTTON.HELP_BUTTONS 119 | ) 120 | 121 | # --------------------------------------------------------------------------------------------------- 122 | 123 | @FileStream.on_message(filters.command('files') & filters.private) 124 | async def my_files(bot: Client, message: Message): 125 | if not await verify_user(bot, message): 126 | return 127 | user_files, total_files = await db.find_files(message.from_user.id, [1, 10]) 128 | 129 | file_list = [] 130 | async for x in user_files: 131 | file_list.append([InlineKeyboardButton(x["file_name"], callback_data=f"myfile_{x['_id']}_{1}")]) 132 | if total_files > 10: 133 | file_list.append( 134 | [ 135 | InlineKeyboardButton("◄", callback_data="N/A"), 136 | InlineKeyboardButton(f"1/{math.ceil(total_files / 10)}", callback_data="N/A"), 137 | InlineKeyboardButton("►", callback_data="userfiles_2") 138 | ], 139 | ) 140 | if not file_list: 141 | file_list.append( 142 | [InlineKeyboardButton("ᴇᴍᴘᴛʏ", callback_data="N/A")], 143 | ) 144 | file_list.append([InlineKeyboardButton("ᴄʟᴏsᴇ", callback_data="close")]) 145 | await message.reply_photo(photo=Telegram.FILE_PIC, 146 | caption="Total files: {}".format(total_files), 147 | reply_markup=InlineKeyboardMarkup(file_list)) 148 | 149 | 150 | -------------------------------------------------------------------------------- /FileStream/bot/plugins/stream.py: -------------------------------------------------------------------------------- 1 | 2 | import asyncio 3 | from FileStream.bot import FileStream, multi_clients 4 | from FileStream.utils.bot_utils import is_user_banned, is_user_exist, is_user_joined, gen_link, is_channel_banned, is_channel_exist, is_user_authorized 5 | from FileStream.utils.database import Database 6 | from FileStream.utils.file_properties import get_file_ids, get_file_info 7 | from FileStream.config import Telegram 8 | from pyrogram import filters, Client 9 | from pyrogram.errors import FloodWait 10 | from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton 11 | from pyrogram.enums.parse_mode import ParseMode 12 | db = Database(Telegram.DATABASE_URL, Telegram.SESSION_NAME) 13 | 14 | @FileStream.on_message( 15 | filters.private 16 | & ( 17 | filters.document 18 | | filters.video 19 | | filters.video_note 20 | | filters.audio 21 | | filters.voice 22 | | filters.animation 23 | | filters.photo 24 | ), 25 | group=4, 26 | ) 27 | async def private_receive_handler(bot: Client, message: Message): 28 | if not await is_user_authorized(message): 29 | return 30 | if await is_user_banned(message): 31 | return 32 | 33 | await is_user_exist(bot, message) 34 | if Telegram.FORCE_SUB: 35 | if not await is_user_joined(bot, message): 36 | return 37 | try: 38 | inserted_id = await db.add_file(get_file_info(message)) 39 | await get_file_ids(False, inserted_id, multi_clients, message) 40 | reply_markup, stream_text = await gen_link(_id=inserted_id) 41 | await message.reply_text( 42 | text=stream_text, 43 | parse_mode=ParseMode.HTML, 44 | disable_web_page_preview=True, 45 | reply_markup=reply_markup, 46 | quote=True 47 | ) 48 | except FloodWait as e: 49 | print(f"Sleeping for {str(e.value)}s") 50 | await asyncio.sleep(e.value) 51 | await bot.send_message(chat_id=Telegram.ULOG_CHANNEL, 52 | text=f"Gᴏᴛ FʟᴏᴏᴅWᴀɪᴛ ᴏғ {str(e.value)}s ғʀᴏᴍ [{message.from_user.first_name}](tg://user?id={message.from_user.id})\n\n**ᴜsᴇʀ ɪᴅ :** `{str(message.from_user.id)}`", 53 | disable_web_page_preview=True, parse_mode=ParseMode.MARKDOWN) 54 | 55 | 56 | @FileStream.on_message( 57 | filters.channel 58 | & ~filters.forwarded 59 | & ~filters.media_group 60 | & ( 61 | filters.document 62 | | filters.video 63 | | filters.video_note 64 | | filters.audio 65 | | filters.voice 66 | | filters.photo 67 | ) 68 | ) 69 | async def channel_receive_handler(bot: Client, message: Message): 70 | if await is_channel_banned(bot, message): 71 | return 72 | await is_channel_exist(bot, message) 73 | 74 | try: 75 | inserted_id = await db.add_file(get_file_info(message)) 76 | await get_file_ids(False, inserted_id, multi_clients, message) 77 | reply_markup, stream_link = await gen_link(_id=inserted_id) 78 | await bot.edit_message_reply_markup( 79 | chat_id=message.chat.id, 80 | message_id=message.id, 81 | reply_markup=InlineKeyboardMarkup( 82 | [[InlineKeyboardButton("Dᴏᴡɴʟᴏᴀᴅ ʟɪɴᴋ 📥", 83 | url=f"https://t.me/{FileStream.username}?start=stream_{str(inserted_id)}")]]) 84 | ) 85 | 86 | except FloodWait as w: 87 | print(f"Sleeping for {str(w.x)}s") 88 | await asyncio.sleep(w.x) 89 | await bot.send_message(chat_id=Telegram.ULOG_CHANNEL, 90 | text=f"ɢᴏᴛ ғʟᴏᴏᴅᴡᴀɪᴛ ᴏғ {str(w.x)}s ғʀᴏᴍ {message.chat.title}\n\n**ᴄʜᴀɴɴᴇʟ ɪᴅ :** `{str(message.chat.id)}`", 91 | disable_web_page_preview=True) 92 | except Exception as e: 93 | await bot.send_message(chat_id=Telegram.ULOG_CHANNEL, text=f"**#EʀʀᴏʀTʀᴀᴄᴋᴇʙᴀᴄᴋ:** `{e}`", 94 | disable_web_page_preview=True) 95 | print(f"Cᴀɴ'ᴛ Eᴅɪᴛ Bʀᴏᴀᴅᴄᴀsᴛ Mᴇssᴀɢᴇ!\nEʀʀᴏʀ: **Gɪᴠᴇ ᴍᴇ ᴇᴅɪᴛ ᴘᴇʀᴍɪssɪᴏɴ ɪɴ ᴜᴘᴅᴀᴛᴇs ᴀɴᴅ ʙɪɴ Cʜᴀɴɴᴇʟ!{e}**") 96 | 97 | -------------------------------------------------------------------------------- /FileStream/config.py: -------------------------------------------------------------------------------- 1 | from os import environ as env 2 | from dotenv import load_dotenv 3 | 4 | load_dotenv() 5 | 6 | class Telegram: 7 | API_ID = int(env.get("API_ID")) 8 | API_HASH = str(env.get("API_HASH")) 9 | BOT_TOKEN = str(env.get("BOT_TOKEN")) 10 | OWNER_ID = int(env.get('OWNER_ID', '7978482443')) 11 | WORKERS = int(env.get("WORKERS", "6")) # 6 workers = 6 commands at once 12 | DATABASE_URL = str(env.get('DATABASE_URL')) 13 | UPDATES_CHANNEL = str(env.get('UPDATES_CHANNEL', "Telegram")) 14 | SESSION_NAME = str(env.get('SESSION_NAME', 'FileStream')) 15 | FORCE_SUB_ID = env.get('FORCE_SUB_ID', None) 16 | FORCE_SUB = env.get('FORCE_UPDATES_CHANNEL', False) 17 | FORCE_SUB = True if str(FORCE_SUB).lower() == "true" else False 18 | SLEEP_THRESHOLD = int(env.get("SLEEP_THRESHOLD", "60")) 19 | FILE_PIC = env.get('FILE_PIC', "https://graph.org/file/5bb9935be0229adf98b73.jpg") 20 | START_PIC = env.get('START_PIC', "https://graph.org/file/290af25276fa34fa8f0aa.jpg") 21 | VERIFY_PIC = env.get('VERIFY_PIC', "https://graph.org/file/736e21cc0efa4d8c2a0e4.jpg") 22 | MULTI_CLIENT = False 23 | FLOG_CHANNEL = int(env.get("FLOG_CHANNEL", None)) # Logs channel for file logs 24 | ULOG_CHANNEL = int(env.get("ULOG_CHANNEL", None)) # Logs channel for user logs 25 | MODE = env.get("MODE", "primary") 26 | SECONDARY = True if MODE.lower() == "secondary" else False 27 | AUTH_USERS = list(set(int(x) for x in str(env.get("AUTH_USERS", "")).split())) 28 | 29 | class Server: 30 | PORT = int(env.get("PORT", 8080)) 31 | BIND_ADDRESS = str(env.get("BIND_ADDRESS", "0.0.0.0")) 32 | PING_INTERVAL = int(env.get("PING_INTERVAL", "1200")) 33 | HAS_SSL = str(env.get("HAS_SSL", "0").lower()) in ("1", "true", "t", "yes", "y") 34 | NO_PORT = str(env.get("NO_PORT", "0").lower()) in ("1", "true", "t", "yes", "y") 35 | FQDN = str(env.get("FQDN", BIND_ADDRESS)) 36 | URL = "http{}://{}{}/".format( 37 | "s" if HAS_SSL else "", FQDN, "" if NO_PORT else ":" + str(PORT) 38 | ) 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /FileStream/server/__init__.py: -------------------------------------------------------------------------------- 1 | from aiohttp import web 2 | from .stream_routes import routes 3 | 4 | def web_server(): 5 | web_app = web.Application(client_max_size=30000000) 6 | web_app.add_routes(routes) 7 | return web_app 8 | -------------------------------------------------------------------------------- /FileStream/server/exceptions.py: -------------------------------------------------------------------------------- 1 | class InvalidHash(Exception): 2 | message = "Invalid hash" 3 | 4 | class FIleNotFound(Exception): 5 | message = "File not found" -------------------------------------------------------------------------------- /FileStream/server/stream_routes.py: -------------------------------------------------------------------------------- 1 | import time 2 | import math 3 | import logging 4 | import mimetypes 5 | import traceback 6 | from aiohttp import web 7 | from aiohttp.http_exceptions import BadStatusLine 8 | from FileStream.bot import multi_clients, work_loads, FileStream 9 | from FileStream.config import Telegram, Server 10 | from FileStream.server.exceptions import FIleNotFound, InvalidHash 11 | from FileStream import utils, StartTime, __version__ 12 | from FileStream.utils.render_template import render_page 13 | 14 | routes = web.RouteTableDef() 15 | 16 | @routes.get("/status", allow_head=True) 17 | async def root_route_handler(_): 18 | return web.json_response( 19 | { 20 | "server_status": "running", 21 | "uptime": utils.get_readable_time(time.time() - StartTime), 22 | "telegram_bot": "@" + FileStream.username, 23 | "connected_bots": len(multi_clients), 24 | "loads": dict( 25 | ("bot" + str(c + 1), l) 26 | for c, (_, l) in enumerate( 27 | sorted(work_loads.items(), key=lambda x: x[1], reverse=True) 28 | ) 29 | ), 30 | "version": __version__, 31 | } 32 | ) 33 | 34 | @routes.get("/watch/{path}", allow_head=True) 35 | async def stream_handler(request: web.Request): 36 | try: 37 | path = request.match_info["path"] 38 | return web.Response(text=await render_page(path), content_type='text/html') 39 | except InvalidHash as e: 40 | raise web.HTTPForbidden(text=e.message) 41 | except FIleNotFound as e: 42 | raise web.HTTPNotFound(text=e.message) 43 | except (AttributeError, BadStatusLine, ConnectionResetError): 44 | pass 45 | 46 | 47 | @routes.get("/dl/{path}", allow_head=True) 48 | async def stream_handler(request: web.Request): 49 | try: 50 | path = request.match_info["path"] 51 | return await media_streamer(request, path) 52 | except InvalidHash as e: 53 | raise web.HTTPForbidden(text=e.message) 54 | except FIleNotFound as e: 55 | raise web.HTTPNotFound(text=e.message) 56 | except (AttributeError, BadStatusLine, ConnectionResetError): 57 | pass 58 | except Exception as e: 59 | traceback.print_exc() 60 | logging.critical(e.with_traceback(None)) 61 | logging.debug(traceback.format_exc()) 62 | raise web.HTTPInternalServerError(text=str(e)) 63 | 64 | class_cache = {} 65 | 66 | async def media_streamer(request: web.Request, db_id: str): 67 | range_header = request.headers.get("Range", 0) 68 | 69 | index = min(work_loads, key=work_loads.get) 70 | faster_client = multi_clients[index] 71 | 72 | if Telegram.MULTI_CLIENT: 73 | logging.info(f"Client {index} is now serving {request.headers.get('X-FORWARDED-FOR',request.remote)}") 74 | 75 | if faster_client in class_cache: 76 | tg_connect = class_cache[faster_client] 77 | logging.debug(f"Using cached ByteStreamer object for client {index}") 78 | else: 79 | logging.debug(f"Creating new ByteStreamer object for client {index}") 80 | tg_connect = utils.ByteStreamer(faster_client) 81 | class_cache[faster_client] = tg_connect 82 | logging.debug("before calling get_file_properties") 83 | file_id = await tg_connect.get_file_properties(db_id, multi_clients) 84 | logging.debug("after calling get_file_properties") 85 | 86 | file_size = file_id.file_size 87 | 88 | if range_header: 89 | from_bytes, until_bytes = range_header.replace("bytes=", "").split("-") 90 | from_bytes = int(from_bytes) 91 | until_bytes = int(until_bytes) if until_bytes else file_size - 1 92 | else: 93 | from_bytes = request.http_range.start or 0 94 | until_bytes = (request.http_range.stop or file_size) - 1 95 | 96 | if (until_bytes > file_size) or (from_bytes < 0) or (until_bytes < from_bytes): 97 | return web.Response( 98 | status=416, 99 | body="416: Range not satisfiable", 100 | headers={"Content-Range": f"bytes */{file_size}"}, 101 | ) 102 | 103 | chunk_size = 1024 * 1024 104 | until_bytes = min(until_bytes, file_size - 1) 105 | 106 | offset = from_bytes - (from_bytes % chunk_size) 107 | first_part_cut = from_bytes - offset 108 | last_part_cut = until_bytes % chunk_size + 1 109 | 110 | req_length = until_bytes - from_bytes + 1 111 | part_count = math.ceil(until_bytes / chunk_size) - math.floor(offset / chunk_size) 112 | body = tg_connect.yield_file( 113 | file_id, index, offset, first_part_cut, last_part_cut, part_count, chunk_size 114 | ) 115 | 116 | mime_type = file_id.mime_type 117 | file_name = utils.get_name(file_id) 118 | disposition = "attachment" 119 | 120 | if not mime_type: 121 | mime_type = mimetypes.guess_type(file_name)[0] or "application/octet-stream" 122 | 123 | # if "video/" in mime_type or "audio/" in mime_type: 124 | # disposition = "inline" 125 | 126 | return web.Response( 127 | status=206 if range_header else 200, 128 | body=body, 129 | headers={ 130 | "Content-Type": f"{mime_type}", 131 | "Content-Range": f"bytes {from_bytes}-{until_bytes}/{file_size}", 132 | "Content-Length": str(req_length), 133 | "Content-Disposition": f'{disposition}; filename="{file_name}"', 134 | "Accept-Ranges": "bytes", 135 | }, 136 | ) 137 | -------------------------------------------------------------------------------- /FileStream/template/dl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 |{{file_name}}
{{file_size}}
52 |
79 | This is a Telegram Bot to Stream Movies and Series directly on
80 | Telegram. You can also
81 | download them if you want. This bot is developed by Avi
83 |
If you like this bot, then don't
84 | forget to share it with your friends and family.
85 |
Report Bugs and Contact us on Telegram Below
108 |{}
\n
30 | 📥 Dᴏᴡɴʟᴏᴀᴅ : {}
\n
31 | 🖥 Wᴀᴛᴄʜ : {}
\n
32 | 🔗 Sʜᴀʀᴇ : {}
\n"""
33 |
34 | STREAM_TEXT_X = """
35 | 𝗬𝗼𝘂𝗿 𝗟𝗶𝗻𝗸 𝗚𝗲𝗻𝗲𝗿𝗮𝘁𝗲𝗱 !\n
36 | 📂 Fɪʟᴇ ɴᴀᴍᴇ : {}\n
37 | 📦 Fɪʟᴇ ꜱɪᴢᴇ : {}
\n
38 | 📥 Dᴏᴡɴʟᴏᴀᴅ : {}
\n
39 | 🔗 Sʜᴀʀᴇ : {}
\n"""
40 |
41 |
42 | BAN_TEXT = "__Sᴏʀʀʏ Sɪʀ, Yᴏᴜ ᴀʀᴇ Bᴀɴɴᴇᴅ ᴛᴏ ᴜsᴇ ᴍᴇ.__\n\n**[Cᴏɴᴛᴀᴄᴛ Dᴇᴠᴇʟᴏᴘᴇʀ](tg://user?id={}) Tʜᴇʏ Wɪʟʟ Hᴇʟᴘ Yᴏᴜ**"
43 |
44 |
45 | class BUTTON(object):
46 | START_BUTTONS = InlineKeyboardMarkup(
47 | [[
48 | InlineKeyboardButton('ʜᴇʟᴘ', callback_data='help'),
49 | InlineKeyboardButton('ᴀʙᴏᴜᴛ', callback_data='about'),
50 | InlineKeyboardButton('ᴄʟᴏsᴇ', callback_data='close')
51 | ],
52 | [InlineKeyboardButton("📢 ᴜᴘᴅᴀᴛᴇ ᴄʜᴀɴɴᴇʟ", url=f'https://t.me/{Telegram.UPDATES_CHANNEL}')]
53 | ]
54 | )
55 | HELP_BUTTONS = InlineKeyboardMarkup(
56 | [[
57 | InlineKeyboardButton('ʜᴏᴍᴇ', callback_data='home'),
58 | InlineKeyboardButton('ᴀʙᴏᴜᴛ', callback_data='about'),
59 | InlineKeyboardButton('ᴄʟᴏsᴇ', callback_data='close'),
60 | ],
61 | [InlineKeyboardButton("📢 ᴜᴘᴅᴀᴛᴇ ᴄʜᴀɴɴᴇʟ", url=f'https://t.me/{Telegram.UPDATES_CHANNEL}')]
62 | ]
63 | )
64 | ABOUT_BUTTONS = InlineKeyboardMarkup(
65 | [[
66 | InlineKeyboardButton('ʜᴏᴍᴇ', callback_data='home'),
67 | InlineKeyboardButton('ʜᴇʟᴘ', callback_data='help'),
68 | InlineKeyboardButton('ᴄʟᴏsᴇ', callback_data='close'),
69 | ],
70 | [InlineKeyboardButton("📢 ᴜᴘᴅᴀᴛᴇ ᴄʜᴀɴɴᴇʟ", url=f'https://t.me/{Telegram.UPDATES_CHANNEL}')]
71 | ]
72 | )
73 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: python -m FileStream
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
8 |
9 |
10 | Report a Bug
11 | |
12 | Request Feature
13 |
20 |
21 |
22 |
23 |
25 | This bot provides stream links for Telegram files without the necessity of waiting for the download to complete, offering the ability to store files. 26 |
27 | 28 | 29 | ### ♢ How to Deploy : 30 | 31 | Either you could locally host, VPS, or deploy on [Heroku](https://heroku.com) 32 | 33 | #### ♢ Click on This Drop-down and get more details 34 | 35 |