├── Procfile ├── runtime.txt ├── requirements.txt ├── Dockerfile ├── bot ├── __init__.py ├── modules │ ├── decorators.py │ ├── static.py │ └── telegram.py ├── plugins │ ├── callback.py │ ├── deeplinks.py │ ├── commands.py │ └── files.py ├── __main__.py ├── server │ ├── __init__.py │ ├── error.py │ ├── main.py │ └── templates │ │ └── player.html └── config.py └── README.md /Procfile: -------------------------------------------------------------------------------- 1 | web: python -m bot -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.11.6 -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | telethon 2 | cryptg 3 | quart 4 | uvicorn 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11 2 | 3 | WORKDIR /app 4 | COPY . /app 5 | 6 | RUN pip install -r requirements.txt 7 | 8 | CMD ["python", "-m", "bot"] -------------------------------------------------------------------------------- /bot/__init__.py: -------------------------------------------------------------------------------- 1 | from telethon import TelegramClient 2 | from logging import getLogger 3 | from logging.config import dictConfig 4 | from .config import Telegram, LOGGER_CONFIG_JSON 5 | 6 | dictConfig(LOGGER_CONFIG_JSON) 7 | 8 | version = 1.5 9 | logger = getLogger('bot') 10 | 11 | TelegramBot = TelegramClient( 12 | session='bot', 13 | api_id=Telegram.API_ID, 14 | api_hash=Telegram.API_HASH 15 | ) 16 | -------------------------------------------------------------------------------- /bot/modules/decorators.py: -------------------------------------------------------------------------------- 1 | from telethon.events import NewMessage, CallbackQuery 2 | from telethon.tl.custom import Message 3 | from typing import Callable 4 | from functools import wraps 5 | from bot.config import Telegram 6 | from bot.modules.static import * 7 | 8 | def verify_user(private: bool = False): 9 | 10 | def decorator(func: Callable): 11 | @wraps(func) 12 | async def wrapper(update: NewMessage.Event | CallbackQuery.Event): 13 | if private and not update.is_private: 14 | return 15 | 16 | chat_id = str(update.chat_id) 17 | 18 | if not Telegram.ALLOWED_USER_IDS or chat_id in Telegram.ALLOWED_USER_IDS: 19 | return await func(update) 20 | 21 | return wrapper 22 | return decorator 23 | -------------------------------------------------------------------------------- /bot/plugins/callback.py: -------------------------------------------------------------------------------- 1 | from telethon.events import CallbackQuery 2 | from bot import TelegramBot 3 | from bot.modules.decorators import verify_user 4 | from bot.modules.static import * 5 | from bot.modules.telegram import get_message 6 | 7 | @TelegramBot.on(CallbackQuery(pattern=r'^rm_')) 8 | @verify_user(private=True) 9 | async def delete_file(event: CallbackQuery.Event): 10 | query_data = event.query.data.decode().split('_') 11 | 12 | if len(query_data) != 3: 13 | return await event.answer(InvalidQueryText, alert=True) 14 | 15 | message = await get_message(int(query_data[1])) 16 | 17 | if not message: 18 | return await event.answer(MessageNotExist, alert=True) 19 | 20 | await message.delete() 21 | 22 | return await event.answer(LinkRevokedText, alert=True) 23 | -------------------------------------------------------------------------------- /bot/__main__.py: -------------------------------------------------------------------------------- 1 | from importlib import import_module 2 | from pathlib import Path 3 | from bot import TelegramBot, logger 4 | from bot.config import Telegram 5 | from bot.server import server 6 | 7 | def load_plugins(): 8 | count = 0 9 | for path in Path('bot/plugins').rglob('*.py'): 10 | import_module(f'bot.plugins.{path.stem}') 11 | count += 1 12 | logger.info(f'Loaded {count} {"plugins" if count > 1 else "plugin"}.') 13 | 14 | if __name__ == '__main__': 15 | logger.info('initializing...') 16 | TelegramBot.loop.create_task(server.serve()) 17 | TelegramBot.start(bot_token=Telegram.BOT_TOKEN) 18 | logger.info('Telegram client is now started.') 19 | logger.info('Loading bot plugins...') 20 | load_plugins() 21 | logger.info('Bot is now ready!') 22 | TelegramBot.run_until_disconnected() 23 | -------------------------------------------------------------------------------- /bot/plugins/deeplinks.py: -------------------------------------------------------------------------------- 1 | from telethon.events import NewMessage 2 | from telethon.tl.custom import Message 3 | from bot import TelegramBot 4 | from bot.modules.decorators import verify_user 5 | from bot.modules.telegram import get_message, send_message 6 | from bot.modules.static import * 7 | 8 | @TelegramBot.on(NewMessage(incoming=True, pattern=r'^/start file_')) 9 | @verify_user(private=True) 10 | async def send_file(event: NewMessage.Event | Message): 11 | payload = event.raw_text.split()[-1].split('_') 12 | 13 | if len(payload) != 3: 14 | return await event.reply(InvalidPayloadText) 15 | 16 | message = await get_message(int(payload[1])) 17 | 18 | if not message: 19 | return await event.reply(MessageNotExist) 20 | 21 | message.raw_text = '' 22 | await send_message(message, send_to=event.chat_id) 23 | -------------------------------------------------------------------------------- /bot/server/__init__.py: -------------------------------------------------------------------------------- 1 | from quart import Quart 2 | from uvicorn import Server as UvicornServer, Config 3 | from logging import getLogger 4 | from bot.config import Server, LOGGER_CONFIG_JSON 5 | 6 | from . import main, error 7 | 8 | logger = getLogger('uvicorn') 9 | instance = Quart(__name__) 10 | instance.config['RESPONSE_TIMEOUT'] = None 11 | 12 | @instance.before_serving 13 | async def before_serve(): 14 | logger.info('Web server is started!') 15 | logger.info(f'Server running on {Server.BIND_ADDRESS}:{Server.PORT}') 16 | 17 | instance.register_blueprint(main.bp) 18 | 19 | instance.register_error_handler(400, error.invalid_request) 20 | instance.register_error_handler(404, error.not_found) 21 | instance.register_error_handler(405, error.invalid_method) 22 | instance.register_error_handler(error.HTTPError, error.http_error) 23 | 24 | server = UvicornServer ( 25 | Config ( 26 | app=instance, 27 | host=Server.BIND_ADDRESS, 28 | port=Server.PORT, 29 | log_config=LOGGER_CONFIG_JSON 30 | ) 31 | ) 32 | -------------------------------------------------------------------------------- /bot/server/error.py: -------------------------------------------------------------------------------- 1 | class HTTPError(Exception): 2 | status_code:int = None 3 | description:str = None 4 | def __init__(self, status_code, description): 5 | self.status_code = status_code 6 | self.description = description 7 | super().__init__(self.status_code, self.description) 8 | 9 | error_messages = { 10 | 400: 'Invalid request.', 11 | 401: 'File code is required to download the file.', 12 | 403: 'Invalid file code.', 13 | 404: 'File not found.', 14 | 500: 'Internal server error.' 15 | } 16 | 17 | async def invalid_request(_): 18 | return 'Invalid request.', 400 19 | 20 | async def not_found(_): 21 | return 'Resource not found.', 404 22 | 23 | async def invalid_method(_): 24 | return 'Invalid request method.', 405 25 | 26 | async def http_error(error: HTTPError): 27 | error_message = error_messages.get(error.status_code) 28 | return error.description or error_message, error.status_code 29 | 30 | def abort(status_code: int = 500, description: str = None): 31 | raise HTTPError(status_code, description) 32 | -------------------------------------------------------------------------------- /bot/plugins/commands.py: -------------------------------------------------------------------------------- 1 | from telethon import Button 2 | from telethon.events import NewMessage 3 | from telethon.tl.custom.message import Message 4 | from bot import TelegramBot 5 | from bot.config import Telegram 6 | from bot.modules.static import * 7 | from bot.modules.decorators import verify_user 8 | 9 | @TelegramBot.on(NewMessage(incoming=True, pattern=r'^/start$')) 10 | @verify_user(private=True) 11 | async def welcome(event: NewMessage.Event | Message): 12 | await event.reply( 13 | message=WelcomeText % {'first_name': event.sender.first_name}, 14 | buttons=[ 15 | [ 16 | Button.url('Add to Channel', f'https://t.me/{Telegram.BOT_USERNAME}?startchannel&admin=post_messages+edit_messages+delete_messages') 17 | ] 18 | ] 19 | ) 20 | 21 | @TelegramBot.on(NewMessage(incoming=True, pattern=r'^/info$')) 22 | @verify_user(private=True) 23 | async def user_info(event: Message): 24 | await event.reply(UserInfoText.format(sender=event.sender)) 25 | 26 | @TelegramBot.on(NewMessage(chats=Telegram.OWNER_ID, incoming=True, pattern=r'^/log$')) 27 | async def send_log(event: NewMessage.Event | Message): 28 | await event.reply(file='event-log.txt') 29 | -------------------------------------------------------------------------------- /bot/modules/static.py: -------------------------------------------------------------------------------- 1 | WelcomeText = \ 2 | """ 3 | Hi **%(first_name)s**, send me a file or add me as an admin to any channel to instantly generate file links. 4 | 5 | Add me to your channel to instantly generate links for any downloadable media. Once received, I will automatically attach appropriate buttons to the post containing the URL. If you want me to ignore a given post, you can insert `#pass` in the post. 6 | 7 | - /start to get this message. 8 | - /info to get user info. 9 | - /log to get bot logs. (admin only!) 10 | """ 11 | 12 | UserInfoText = \ 13 | """ 14 | **First Name:** 15 | `{sender.first_name}` 16 | 17 | **Last Name:** 18 | `{sender.last_name}` 19 | 20 | **User ID:** 21 | `{sender.id}` 22 | 23 | **Username:** 24 | `@{sender.username}` 25 | """ 26 | 27 | FileLinksText = \ 28 | """ 29 | **Download Link:** 30 | `%(dl_link)s` 31 | **Telegram File:** 32 | `%(tg_link)s` 33 | """ 34 | 35 | MediaLinksText = \ 36 | """ 37 | **Download Link:** 38 | `%(dl_link)s` 39 | **Stream Link:** 40 | `%(stream_link)s` 41 | **Telegram File:** 42 | `%(tg_link)s` 43 | """ 44 | 45 | InvalidQueryText = \ 46 | """ 47 | Query data mismatched. 48 | """ 49 | 50 | MessageNotExist = \ 51 | """ 52 | File revoked or not exist. 53 | """ 54 | 55 | LinkRevokedText = \ 56 | """ 57 | The link has been revoked. It may take some time for the changes to take effect. 58 | """ 59 | 60 | InvalidPayloadText = \ 61 | """ 62 | Invalid payload. 63 | """ 64 | 65 | MediaTypeNotSupportedText = \ 66 | """ 67 | Sorry, this media type is not supported. 68 | """ 69 | -------------------------------------------------------------------------------- /bot/config.py: -------------------------------------------------------------------------------- 1 | from os import environ as env 2 | 3 | class Telegram: 4 | API_ID = int(env.get("TELEGRAM_API_ID", 1234)) 5 | API_HASH = env.get("TELEGRAM_API_HASH", "xyz") 6 | OWNER_ID = int(env.get("OWNER_ID", 1234567890)) 7 | ALLOWED_USER_IDS = env.get("ALLOWED_USER_IDS", "").split() 8 | BOT_USERNAME = env.get("TELEGRAM_BOT_USERNAME", "BotFather") 9 | BOT_TOKEN = env.get("TELEGRAM_BOT_TOKEN", "1234:abcd") 10 | CHANNEL_ID = int(env.get("TELEGRAM_CHANNEL_ID", -1001234567890)) 11 | SECRET_CODE_LENGTH = int(env.get("SECRET_CODE_LENGTH", 12)) 12 | 13 | class Server: 14 | BASE_URL = env.get("BASE_URL", "http://127.0.0.1:8080") 15 | BIND_ADDRESS = env.get("BIND_ADDRESS", "0.0.0.0") 16 | PORT = int(env.get("PORT", 8080)) 17 | 18 | # LOGGING CONFIGURATION 19 | LOGGER_CONFIG_JSON = { 20 | 'version': 1, 21 | 'formatters': { 22 | 'default': { 23 | 'format': '[%(asctime)s][%(name)s][%(levelname)s] -> %(message)s', 24 | 'datefmt': '%d/%m/%Y %H:%M:%S' 25 | }, 26 | }, 27 | 'handlers': { 28 | 'file_handler': { 29 | 'class': 'logging.FileHandler', 30 | 'filename': 'event-log.txt', 31 | 'formatter': 'default' 32 | }, 33 | 'stream_handler': { 34 | 'class': 'logging.StreamHandler', 35 | 'formatter': 'default' 36 | } 37 | }, 38 | 'loggers': { 39 | 'uvicorn': { 40 | 'level': 'INFO', 41 | 'handlers': ['file_handler', 'stream_handler'] 42 | }, 43 | 'uvicorn.error': { 44 | 'level': 'WARNING', 45 | 'handlers': ['file_handler', 'stream_handler'] 46 | }, 47 | 'bot': { 48 | 'level': 'INFO', 49 | 'handlers': ['file_handler', 'stream_handler'] 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /bot/modules/telegram.py: -------------------------------------------------------------------------------- 1 | from telethon.events import NewMessage 2 | from telethon.tl.custom import Message 3 | from datetime import datetime 4 | from mimetypes import guess_type 5 | from bot import TelegramBot 6 | from bot.config import Telegram 7 | from bot.server.error import abort 8 | 9 | async def get_message(message_id: int) -> Message | None: 10 | message = None 11 | 12 | try: 13 | message = await TelegramBot.get_messages(Telegram.CHANNEL_ID, ids=message_id) 14 | except Exception: 15 | pass 16 | 17 | return message 18 | 19 | async def send_message(message:Message, send_to:int = Telegram.CHANNEL_ID) -> Message: 20 | message.forward 21 | return await TelegramBot.send_message( 22 | entity=send_to, 23 | message=message 24 | ) 25 | 26 | def filter_files(update: NewMessage.Event | Message): 27 | return bool( 28 | ( 29 | update.document 30 | or update.photo 31 | or update.video 32 | or update.video_note 33 | or update.audio 34 | or update.gif 35 | ) 36 | and not update.sticker 37 | ) 38 | 39 | def get_file_properties(message: Message): 40 | file_name = message.file.name 41 | file_size = message.file.size or 0 42 | mime_type = message.file.mime_type 43 | 44 | if not file_name: 45 | attributes = { 46 | 'video': 'mp4', 47 | 'audio': 'mp3', 48 | 'voice': 'ogg', 49 | 'photo': 'jpg', 50 | 'video_note': 'mp4' 51 | } 52 | 53 | for attribute in attributes: 54 | media = getattr(message, attribute, None) 55 | if media: 56 | file_type, file_format = attribute, attributes[attribute] 57 | break 58 | 59 | if not media: 60 | abort(400, 'Invalid media type.') 61 | 62 | date = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") 63 | file_name = f'{file_type}-{date}.{file_format}' 64 | 65 | if not mime_type: 66 | mime_type = guess_type(file_name)[0] or 'application/octet-stream' 67 | 68 | return file_name, file_size, mime_type 69 | -------------------------------------------------------------------------------- /bot/server/main.py: -------------------------------------------------------------------------------- 1 | from quart import Blueprint, Response, request, render_template, redirect 2 | from .error import abort 3 | from bot import TelegramBot 4 | from bot.config import Telegram, Server 5 | from math import ceil, floor 6 | from bot.modules.telegram import get_message, get_file_properties 7 | 8 | bp = Blueprint('main', __name__) 9 | 10 | @bp.route('/') 11 | async def home(): 12 | return redirect(f'https://t.me/{Telegram.BOT_USERNAME}') 13 | 14 | @bp.route('/dl/') 15 | async def transmit_file(file_id): 16 | file = await get_message(message_id=int(file_id)) or abort(404) 17 | code = request.args.get('code') or abort(401) 18 | range_header = request.headers.get('Range', 0) 19 | 20 | if code != file.raw_text: 21 | abort(403) 22 | 23 | file_name, file_size, mime_type = get_file_properties(file) 24 | 25 | if range_header: 26 | from_bytes, until_bytes = range_header.replace("bytes=", "").split("-") 27 | from_bytes = int(from_bytes) 28 | until_bytes = int(until_bytes) if until_bytes else file_size - 1 29 | else: 30 | from_bytes = 0 31 | until_bytes = file_size - 1 32 | 33 | if (until_bytes > file_size) or (from_bytes < 0) or (until_bytes < from_bytes): 34 | abort(416, 'Invalid range.') 35 | 36 | chunk_size = 1024 * 1024 37 | until_bytes = min(until_bytes, file_size - 1) 38 | 39 | offset = from_bytes - (from_bytes % chunk_size) 40 | first_part_cut = from_bytes - offset 41 | last_part_cut = until_bytes % chunk_size + 1 42 | 43 | req_length = until_bytes - from_bytes + 1 44 | part_count = ceil(until_bytes / chunk_size) - floor(offset / chunk_size) 45 | 46 | headers = { 47 | "Content-Type": f"{mime_type}", 48 | "Content-Range": f"bytes {from_bytes}-{until_bytes}/{file_size}", 49 | "Content-Length": str(req_length), 50 | "Content-Disposition": f'attachment; filename="{file_name}"', 51 | "Accept-Ranges": "bytes", 52 | } 53 | 54 | async def file_generator(): 55 | current_part = 1 56 | async for chunk in TelegramBot.iter_download(file, offset=offset, chunk_size=chunk_size, stride=chunk_size, file_size=file_size): 57 | if not chunk: 58 | break 59 | elif part_count == 1: 60 | yield chunk[first_part_cut:last_part_cut] 61 | elif current_part == 1: 62 | yield chunk[first_part_cut:] 63 | elif current_part == part_count: 64 | yield chunk[:last_part_cut] 65 | else: 66 | yield chunk 67 | 68 | current_part += 1 69 | 70 | if current_part > part_count: 71 | break 72 | 73 | return Response(file_generator(), headers=headers, status=206 if range_header else 200) 74 | 75 | @bp.route('/stream/') 76 | async def stream_file(file_id): 77 | code = request.args.get('code') or abort(401) 78 | 79 | return await render_template('player.html', mediaLink=f'{Server.BASE_URL}/dl/{file_id}?code={code}') 80 | 81 | @bp.route('/file/') 82 | async def file_deeplink(file_id): 83 | code = request.args.get('code') or abort(401) 84 | 85 | return redirect(f'https://t.me/{Telegram.BOT_USERNAME}?start=file_{file_id}_{code}') 86 | -------------------------------------------------------------------------------- /bot/plugins/files.py: -------------------------------------------------------------------------------- 1 | from telethon import Button 2 | from telethon.events import NewMessage 3 | from telethon.errors import MessageAuthorRequiredError, MessageNotModifiedError, MessageIdInvalidError 4 | from telethon.tl.custom import Message 5 | from secrets import token_hex 6 | from bot import TelegramBot 7 | from bot.config import Telegram, Server 8 | from bot.modules.decorators import verify_user 9 | from bot.modules.telegram import send_message, filter_files 10 | from bot.modules.static import * 11 | 12 | @TelegramBot.on(NewMessage(incoming=True, func=filter_files)) 13 | @verify_user(private=True) 14 | async def user_file_handler(event: NewMessage.Event | Message): 15 | secret_code = token_hex(Telegram.SECRET_CODE_LENGTH) 16 | event.message.text = f'`{secret_code}`' 17 | message = await send_message(event.message) 18 | message_id = message.id 19 | 20 | dl_link = f'{Server.BASE_URL}/dl/{message_id}?code={secret_code}' 21 | tg_link = f'{Server.BASE_URL}/file/{message_id}?code={secret_code}' 22 | deep_link = f'https://t.me/{Telegram.BOT_USERNAME}?start=file_{message_id}_{secret_code}' 23 | 24 | if (event.document and 'video' in event.document.mime_type) or event.video: 25 | stream_link = f'{Server.BASE_URL}/stream/{message_id}?code={secret_code}' 26 | await event.reply( 27 | message= MediaLinksText % {'dl_link': dl_link, 'tg_link': tg_link, 'tg_link': tg_link, 'stream_link': stream_link}, 28 | buttons=[ 29 | [ 30 | Button.url('Download', dl_link), 31 | Button.url('Stream', stream_link) 32 | ], 33 | [ 34 | Button.url('Get File', deep_link), 35 | Button.inline('Revoke', f'rm_{message_id}_{secret_code}') 36 | ] 37 | ] 38 | ) 39 | else: 40 | await event.reply( 41 | message=FileLinksText % {'dl_link': dl_link, 'tg_link': tg_link}, 42 | buttons=[ 43 | [ 44 | Button.url('Download', dl_link), 45 | Button.url('Get File', deep_link) 46 | ], 47 | [ 48 | Button.inline('Revoke', f'rm_{message_id}_{secret_code}') 49 | ] 50 | ] 51 | ) 52 | 53 | @TelegramBot.on(NewMessage(incoming=True, func=filter_files, forwards=False)) 54 | @verify_user() 55 | async def channel_file_handler(event: NewMessage.Event | Message): 56 | secret_code = token_hex(Telegram.SECRET_CODE_LENGTH) 57 | event.message.text = f"`{secret_code}`" 58 | message = await send_message(event.message) 59 | message_id = message.id 60 | 61 | dl_link = f"{Server.BASE_URL}/dl/{message_id}?code={secret_code}" 62 | tg_link = f"{Server.BASE_URL}/file/{message_id}?code={secret_code}" 63 | 64 | if (event.document and "video" in event.document.mime_type) or event.video: 65 | stream_link = f"{Server.BASE_URL}/stream/{message_id}?code={secret_code}" 66 | 67 | try: 68 | await event.edit( 69 | buttons=[ 70 | [Button.url("Download", dl_link), Button.url("Stream", stream_link)], 71 | [Button.url("Get File", tg_link)], 72 | ] 73 | ) 74 | except ( 75 | MessageAuthorRequiredError, 76 | MessageIdInvalidError, 77 | MessageNotModifiedError, 78 | ): 79 | pass 80 | else: 81 | try: 82 | await event.edit( 83 | buttons=[ 84 | [Button.url("Download", dl_link), Button.url("Get File", tg_link)] 85 | ] 86 | ) 87 | except ( 88 | MessageAuthorRequiredError, 89 | MessageIdInvalidError, 90 | MessageNotModifiedError, 91 | ): 92 | pass 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

🌐File Stream Bot

2 | An open-source Python Telegram bot to transmit Telegram files over HTTP. 3 | 4 | Demo Bot 5 |

6 | 7 | ## **📑 INDEX** 8 | 9 | * [**⚙️ Installation**](#installation) 10 | * [Python & Git](#i-1) 11 | * [Download](#i-2) 12 | * [Requirements](#i-3) 13 | * [**📝 Variables**](#variables) 14 | * [**🕹 Deployment**](#deployment) 15 | * [Locally](#d-1) 16 | * [Docker](#d-2) 17 | * [**⛑️ Need help!**](#help) 18 | * [**❤️ Credits & Thanks**](#credits) 19 | 20 | 21 | 22 | ## ⚙️ Installation 23 | 24 | 25 | 26 | **1.Install Python & Git:** 27 | 28 | For Windows: 29 | ``` 30 | winget install Python.Python.3.11 31 | winget install Git.Git 32 | ``` 33 | For Linux: 34 | ``` 35 | sudo apt-get update && sudo apt-get install -y python3.11 git pip 36 | ``` 37 | For macOS: 38 | ``` 39 | brew install python@3.11 git 40 | ``` 41 | For Termux: 42 | ``` 43 | pkg install python -y 44 | pkg install git -y 45 | ``` 46 | 47 | 48 | 49 | **2.Download repository:** 50 | ``` 51 | git clone https://github.com/TheCaduceus/FileStreamBot.git 52 | ``` 53 | 54 | **3.Change Directory:** 55 | 56 | ``` 57 | cd FileStreamBot 58 | ``` 59 | 60 | 61 | 62 | **4.Install requirements:** 63 | 64 | ``` 65 | pip install -r requirements.txt 66 | ``` 67 | 68 | 69 | 70 | ## 📝 Variables 71 | **The variables provided below should either be completed within the [config.py](https://github.com/TheCaduceus/FileStreamBot/blob/main/bot/config.py) file or configured as environment variables.** 72 | * `API_ID`|`TELEGRAM_API_ID`: API ID of your Telegram account, can be obtained from [My Telegram](https://my.telegram.org). `int` 73 | * `API_HASH`|`TELEGRAM_API_HASH`: API hash of your Telegram account, can be obtained from [My Telegram](https://my.telegram.org). `str` 74 | * `OWNER_ID`: ID of your Telegram account, can be obtained by sending **/info** to [@DrFileStreamBot](https://t.me/DrFileStreamBot). `int` 75 | * `ALLOWED_USER_IDS`: A list of Telegram account IDs (separated by spaces) that are permitted to use the bot. Leave this field empty to allow anyone to use it. `str` 76 | * `BOT_USERNAME`|`TELEGRAM_BOT_USERNAME`: Username of your Telegram bot, create one using [@BotFather](https://t.me/BotFather). `str` 77 | * `BOT_TOKEN`|`TELEGRAM_BOT_TOKEN`: Telegram API token of your bot, can be obtained from [@BotFather](https://t.me/BotFather). `str` 78 | * `CHANNEL_ID`|`TELEGRAM_CHANNEL_ID`: ID of the channel where bot will forward all files received from users, can be obtained by forwarding any message from channel to [@ShowJsonBot](https://t.me/ShowJsonBot) and then looking from `forward_from_chat` key. `int` 79 | * `BOT_WORKERS`: Number of updates bot should process from Telegram at once, by default to 10 updates. `int` 80 | * `SECRET_CODE_LENGTH`: Number of characters that file code should contain, by default to 12 characters. `int` 81 | * `BASE_URL`: Base URL that bot should use while generating file links, can be FQDN and by default to `127.0.0.1`. `str` 82 | * `BIND_ADDRESS`: Bind address for web server, by default to `0.0.0.0` to run on all possible addresses. `str` 83 | * `PORT`: Port for web server to run on, by default to `8080`. `int` 84 | 85 | ## 🕹 Deployment 86 | 87 | 88 | 89 | **1.Running locally:** 90 | ``` 91 | python -m bot 92 | ``` 93 | 94 | 95 | 96 | **2.Using Docker:** *(Recommended)* 97 | * Build own Docker image: 98 | ``` 99 | docker build -t file-stream-bot . 100 | ``` 101 | * Run the Docker container: 102 | ``` 103 | docker run -p 8080:8080 file-stream-bot 104 | ``` 105 | 106 | 107 | 108 | ## ⛑️ Need help! 109 | - Ask questions or doubts [here](https://t.me/DrDiscussion). 110 | 111 | 112 | 113 | ## ❤️ Credits & Thanks 114 | 115 | [**Dr.Caduceus**](https://github.com/TheCaduceus): Owner & developer of File Stream Bot. 116 | -------------------------------------------------------------------------------- /bot/server/templates/player.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Play Files 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 92 | 93 | 94 | 95 | 101 | 102 |
103 | 104 | 152 | 153 | 154 | 155 | --------------------------------------------------------------------------------