├── .gitattributes ├── .gitignore ├── Dockerfile ├── Procfile ├── README.md ├── bot.py ├── cache └── a.txt ├── config.py ├── html_gen.py ├── requirements.txt ├── static ├── logo.png ├── no-image.png ├── screenshot1.jpg └── screenshot2.jpg ├── streamer.py ├── templates ├── home.html └── stream.html ├── utils ├── __init__.py ├── custom_dl.py ├── file_properties.py └── time_format.py └── web.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.pyc 3 | *.pyc 4 | *.pyc 5 | minify.py 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10 2 | 3 | WORKDIR /app 4 | 5 | COPY ./requirements.txt /app/requirements.txt 6 | 7 | RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt 8 | 9 | COPY . /app 10 | 11 | CMD ["uvicorn", "web:app", "--host", "0.0.0.0", "--port", "80"] -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: uvicorn web:app --host=0.0.0.0 --port=${PORT:-5000} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

Index Telegram Channels Over Internet

3 | 4 |
5 | 6 | ### Join For Latest Updates 7 | 8 | [![Telegram Channel](https://img.shields.io/static/v1?label=Join&message=Telegram%20Channel&color=blueviolet&style=for-the-badge&logo=telegram&logoColor=violet)](https://telegram.me/TechZBots) [![Telegram Group](https://img.shields.io/static/v1?label=Join&message=Telegram%20Group&color=blueviolet&style=for-the-badge&logo=telegram&logoColor=violet)](https://telegram.me/TechZBots_Support) 9 | 10 |
11 | 12 | - Built With Flask In Python 13 | - Fast And Responsive 14 | - Multiple Channel Support 15 | > Index As Many Public Channels As You Want 16 | - Improved Cache System 17 | > Your channel's files on first visit get cached on server, so that userbot doest have to fetch your channel again and again ( to avoid floodwait ) 18 | 19 | > You can request the cache to be removed manually by admins of bot 20 | 21 |
22 | 23 | ### ♻️ How To Use ? 24 | 25 | - Make Bot admin in your channel 26 | - Your channel must be public 27 | - Now open this link ```BASE_URL/channel/``` 28 | 29 |
30 | 31 | ### Screenshots 32 | 33 | 34 | 35 | 36 | 37 |
38 | 39 | ### Config Vars 40 | 41 | ``` 42 | API_ID = "" 43 | API_HASH = "" 44 | BOT_TOKEN = "" 45 | STRING_SESSION = "" 46 | OWNER_ID = "" 47 | ADMINS = [1693701096] 48 | BASE_URL = "" 49 | HOME_PAGE_REDIRECT = "https://techzbots.t.me" 50 | ``` 51 | 52 |
53 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | from pyrogram.client import Client 2 | from pyrogram.types import Message 3 | import os 4 | import json 5 | 6 | 7 | def rm_cache(channel=None): 8 | print("Cleaning Cache...") 9 | if not channel: 10 | global image_cache 11 | image_cache = {} 12 | try: 13 | for file in os.listdir("downloads"): 14 | try: 15 | os.remove(f"downloads/{file}") 16 | print(f"Removed {file}") 17 | except Exception as e: 18 | print(e) 19 | except Exception as e: 20 | print(e) 21 | try: 22 | for file in os.listdir("cache"): 23 | try: 24 | if file.endswith(".json"): 25 | if channel: 26 | if file.startswith(channel): 27 | os.remove(f"cache/{file}") 28 | print(f"Removed {file}") 29 | else: 30 | os.remove(f"cache/{file}") 31 | print(f"Removed {file}") 32 | except Exception as e: 33 | print(e) 34 | except Exception as e: 35 | print(e) 36 | 37 | 38 | def get_cache(channel, page): 39 | if os.path.exists(f"cache/{channel}-{page}.json"): 40 | with open(f"cache/{channel}-{page}.json", "r") as f: 41 | return json.load(f)["posts"] 42 | else: 43 | return None 44 | 45 | 46 | def save_cache(channel, cache, page): 47 | with open(f"cache/{channel}-{page}.json", "w") as f: 48 | json.dump(cache, f) 49 | 50 | 51 | async def get_posts(user: Client, channel, page=1): 52 | page = int(page) 53 | cache = get_cache(channel, page) 54 | if cache: 55 | return cache 56 | else: 57 | posts = [] 58 | async for post in user.get_chat_history( 59 | chat_id=channel, limit=50, offset=(page - 1) * 50 60 | ): 61 | post: Message 62 | if post.video: 63 | if post.video.file_name: 64 | file_name = post.video.file_name 65 | elif post.caption: 66 | file_name = post.caption 67 | else: 68 | file_name = post.video.file_id 69 | 70 | file_name = file_name[:200] 71 | 72 | title = " ".join(file_name.split(".")[:-1]) 73 | posts.append({"msg-id": post.id, "title": title}) 74 | 75 | save_cache(channel, {"posts": posts}, page) 76 | return posts 77 | 78 | 79 | image_cache = {} 80 | 81 | 82 | async def get_image(bot: Client, file, channel): 83 | global image_cache 84 | 85 | cache = image_cache.get(f"{channel}-{file}") 86 | if cache: 87 | print(f"Returning img from cache - {channel}-{file}") 88 | return cache 89 | 90 | else: 91 | print(f"Downloading img from Telegram - {channel}-{file}") 92 | msg = await bot.get_messages(channel, int(file)) 93 | img = await bot.download_media(str(msg.video.thumbs[0].file_id)) 94 | image_cache[f"{channel}-{file}"] = img 95 | return img 96 | -------------------------------------------------------------------------------- /cache/a.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechShreyash/TechZIndex/39c5cf92678bca46ac923f1ca3f9db445e807358/cache/a.txt -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | API_ID = "" 2 | API_HASH = "" 3 | BOT_TOKEN = "" 4 | STRING_SESSION = "" 5 | OWNER_ID = "" 6 | ADMINS = [1693701096] 7 | BASE_URL = "" 8 | HOME_PAGE_REDIRECT = "https://techzbots.t.me" -------------------------------------------------------------------------------- /html_gen.py: -------------------------------------------------------------------------------- 1 | def posts_html(posts, channel): 2 | html = "" 3 | phtml = """
4 |
5 | {title} 6 | 7 |
8 |
{title}
9 |
10 |
11 |
""" 12 | for post in posts: 13 | html += phtml.format( 14 | id=post["msg-id"], 15 | img=f"/api/thumb/{channel}/{post['msg-id']}", 16 | title=post["title"], 17 | channel=channel, 18 | ) 19 | return html 20 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi 2 | uvicorn 3 | pyrogram 4 | requests 5 | TgCrypto -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechShreyash/TechZIndex/39c5cf92678bca46ac923f1ca3f9db445e807358/static/logo.png -------------------------------------------------------------------------------- /static/no-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechShreyash/TechZIndex/39c5cf92678bca46ac923f1ca3f9db445e807358/static/no-image.png -------------------------------------------------------------------------------- /static/screenshot1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechShreyash/TechZIndex/39c5cf92678bca46ac923f1ca3f9db445e807358/static/screenshot1.jpg -------------------------------------------------------------------------------- /static/screenshot2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechShreyash/TechZIndex/39c5cf92678bca46ac923f1ca3f9db445e807358/static/screenshot2.jpg -------------------------------------------------------------------------------- /streamer.py: -------------------------------------------------------------------------------- 1 | import math 2 | import logging 3 | import mimetypes 4 | import utils 5 | from fastapi.responses import StreamingResponse, Response 6 | 7 | logger = logging.getLogger("streamer") 8 | 9 | 10 | class_cache = {} 11 | 12 | 13 | async def media_streamer(bot, channel, message_id: int, request): 14 | range_header = request.headers.get("Range", 0) 15 | 16 | faster_client = bot 17 | 18 | if faster_client in class_cache: 19 | tg_connect = class_cache[faster_client] 20 | logger.debug(f"Using cached ByteStreamer object for client") 21 | else: 22 | logger.debug(f"Creating new ByteStreamer object for client") 23 | tg_connect = utils.ByteStreamer(faster_client) 24 | class_cache[faster_client] = tg_connect 25 | 26 | logger.debug("before calling get_file_properties") 27 | file_id = await tg_connect.get_file_properties(channel, message_id) 28 | logger.debug("after calling get_file_properties") 29 | 30 | file_size = file_id.file_size 31 | 32 | if range_header: 33 | from_bytes, until_bytes = range_header.replace("bytes=", "").split("-") 34 | from_bytes = int(from_bytes) 35 | until_bytes = int(until_bytes) if until_bytes else file_size - 1 36 | else: 37 | from_bytes = 0 38 | until_bytes = file_size - 1 39 | 40 | if (until_bytes > file_size) or (from_bytes < 0) or (until_bytes < from_bytes): 41 | return Response( 42 | status_code=416, 43 | content="416: Range not satisfiable", 44 | headers={"Content-Range": f"bytes */{file_size}"}, 45 | ) 46 | 47 | chunk_size = 1024 * 1024 48 | until_bytes = min(until_bytes, file_size - 1) 49 | 50 | offset = from_bytes - (from_bytes % chunk_size) 51 | first_part_cut = from_bytes - offset 52 | last_part_cut = until_bytes % chunk_size + 1 53 | 54 | req_length = until_bytes - from_bytes + 1 55 | part_count = math.ceil(until_bytes / chunk_size) - math.floor(offset / chunk_size) 56 | body = tg_connect.yield_file( 57 | file_id, offset, first_part_cut, last_part_cut, part_count, chunk_size 58 | ) 59 | mime_type = file_id.mime_type 60 | file_name = utils.get_name(file_id) 61 | disposition = "attachment" 62 | 63 | if not mime_type: 64 | mime_type = mimetypes.guess_type(file_name)[0] or "application/octet-stream" 65 | 66 | if "video/" in mime_type or "audio/" in mime_type or "/html" in mime_type: 67 | disposition = "inline" 68 | 69 | return StreamingResponse( 70 | status_code=206 if range_header else 200, 71 | content=body, 72 | headers={ 73 | "Content-Type": f"{mime_type}", 74 | "Content-Range": f"bytes {from_bytes}-{until_bytes}/{file_size}", 75 | "Content-Length": str(req_length), 76 | "Content-Disposition": f'{disposition}; filename="{file_name}"', 77 | "Accept-Ranges": "bytes", 78 | }, 79 | media_type=mime_type, 80 | ) 81 | -------------------------------------------------------------------------------- /templates/home.html: -------------------------------------------------------------------------------- 1 | TechZ Index

Latest Uploads

POSTS
-------------------------------------------------------------------------------- /templates/stream.html: -------------------------------------------------------------------------------- 1 | TechZ Index
Download
-------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | # This file is a part of TG-FileStreamBot 2 | # Coding : Jyothis Jayanth [@EverythingSuckz] 3 | 4 | from .time_format import get_readable_time 5 | from .file_properties import get_hash, get_name 6 | from .custom_dl import ByteStreamer -------------------------------------------------------------------------------- /utils/custom_dl.py: -------------------------------------------------------------------------------- 1 | import math 2 | import asyncio 3 | import logging 4 | from typing import Dict, Union 5 | from pyrogram import Client, utils, raw 6 | from .file_properties import get_file_ids 7 | from pyrogram.session import Session, Auth 8 | from pyrogram.errors import AuthBytesInvalid 9 | from pyrogram.file_id import FileId, FileType, ThumbnailSource 10 | 11 | logger = logging.getLogger("streamer") 12 | 13 | 14 | class ByteStreamer: 15 | def __init__(self, client: Client): 16 | """A custom class that holds the cache of a specific client and class functions. 17 | attributes: 18 | client: the client that the cache is for. 19 | cached_file_ids: a dict of cached file IDs. 20 | cached_file_properties: a dict of cached file properties. 21 | 22 | functions: 23 | generate_file_properties: returns the properties for a media of a specific message contained in Tuple. 24 | generate_media_session: returns the media session for the DC that contains the media file. 25 | yield_file: yield a file from telegram servers for streaming. 26 | 27 | This is a modified version of the 28 | Thanks to Eyaadh 29 | """ 30 | self.clean_timer = 30 * 60 31 | self.client: Client = client 32 | self.cached_file_ids: Dict[int, FileId] = {} 33 | asyncio.create_task(self.clean_cache()) 34 | 35 | async def get_file_properties(self, channel, message_id: int) -> FileId: 36 | """ 37 | Returns the properties of a media of a specific message in a FIleId class. 38 | if the properties are cached, then it'll return the cached results. 39 | or it'll generate the properties from the Message ID and cache them. 40 | """ 41 | if message_id not in self.cached_file_ids: 42 | await self.generate_file_properties(channel, message_id) 43 | logger.debug(f"Cached file properties for message with ID {message_id}") 44 | return self.cached_file_ids[message_id] 45 | 46 | async def generate_file_properties(self, channel, message_id: int) -> FileId: 47 | """ 48 | Generates the properties of a media file on a specific message. 49 | returns ths properties in a FIleId class. 50 | """ 51 | file_id = await get_file_ids(self.client, channel, message_id) 52 | logger.debug( 53 | f"Generated file ID and Unique ID for message with ID {message_id}" 54 | ) 55 | if not file_id: 56 | logger.debug(f"Message with ID {message_id} not found") 57 | raise Exception("FileNotFound") 58 | self.cached_file_ids[message_id] = file_id 59 | logger.debug(f"Cached media message with ID {message_id}") 60 | return self.cached_file_ids[message_id] 61 | 62 | async def generate_media_session(self, client: Client, file_id: FileId) -> Session: 63 | """ 64 | Generates the media session for the DC that contains the media file. 65 | This is required for getting the bytes from Telegram servers. 66 | """ 67 | 68 | media_session = client.media_sessions.get(file_id.dc_id, None) 69 | 70 | if media_session is None: 71 | if file_id.dc_id != await client.storage.dc_id(): 72 | media_session = Session( 73 | client, 74 | file_id.dc_id, 75 | await Auth( 76 | client, file_id.dc_id, await client.storage.test_mode() 77 | ).create(), 78 | await client.storage.test_mode(), 79 | is_media=True, 80 | ) 81 | await media_session.start() 82 | 83 | for _ in range(6): 84 | exported_auth = await client.invoke( 85 | raw.functions.auth.ExportAuthorization(dc_id=file_id.dc_id) 86 | ) 87 | 88 | try: 89 | await media_session.invoke( 90 | raw.functions.auth.ImportAuthorization( 91 | id=exported_auth.id, bytes=exported_auth.bytes 92 | ) 93 | ) 94 | break 95 | except AuthBytesInvalid: 96 | logger.debug( 97 | f"Invalid authorization bytes for DC {file_id.dc_id}" 98 | ) 99 | continue 100 | else: 101 | await media_session.stop() 102 | raise AuthBytesInvalid 103 | else: 104 | media_session = Session( 105 | client, 106 | file_id.dc_id, 107 | await client.storage.auth_key(), 108 | await client.storage.test_mode(), 109 | is_media=True, 110 | ) 111 | await media_session.start() 112 | logger.debug(f"Created media session for DC {file_id.dc_id}") 113 | client.media_sessions[file_id.dc_id] = media_session 114 | else: 115 | logger.debug(f"Using cached media session for DC {file_id.dc_id}") 116 | return media_session 117 | 118 | @staticmethod 119 | async def get_location( 120 | file_id: FileId, 121 | ) -> Union[ 122 | raw.types.InputPhotoFileLocation, 123 | raw.types.InputDocumentFileLocation, 124 | raw.types.InputPeerPhotoFileLocation, 125 | ]: 126 | """ 127 | Returns the file location for the media file. 128 | """ 129 | file_type = file_id.file_type 130 | 131 | if file_type == FileType.CHAT_PHOTO: 132 | if file_id.chat_id > 0: 133 | peer = raw.types.InputPeerUser( 134 | user_id=file_id.chat_id, access_hash=file_id.chat_access_hash 135 | ) 136 | else: 137 | if file_id.chat_access_hash == 0: 138 | peer = raw.types.InputPeerChat(chat_id=-file_id.chat_id) 139 | else: 140 | peer = raw.types.InputPeerChannel( 141 | channel_id=utils.get_channel_id(file_id.chat_id), 142 | access_hash=file_id.chat_access_hash, 143 | ) 144 | 145 | location = raw.types.InputPeerPhotoFileLocation( 146 | peer=peer, 147 | volume_id=file_id.volume_id, 148 | local_id=file_id.local_id, 149 | big=file_id.thumbnail_source == ThumbnailSource.CHAT_PHOTO_BIG, 150 | ) 151 | elif file_type == FileType.PHOTO: 152 | location = raw.types.InputPhotoFileLocation( 153 | id=file_id.media_id, 154 | access_hash=file_id.access_hash, 155 | file_reference=file_id.file_reference, 156 | thumb_size=file_id.thumbnail_size, 157 | ) 158 | else: 159 | location = raw.types.InputDocumentFileLocation( 160 | id=file_id.media_id, 161 | access_hash=file_id.access_hash, 162 | file_reference=file_id.file_reference, 163 | thumb_size=file_id.thumbnail_size, 164 | ) 165 | return location 166 | 167 | async def yield_file( 168 | self, 169 | file_id: FileId, 170 | offset: int, 171 | first_part_cut: int, 172 | last_part_cut: int, 173 | part_count: int, 174 | chunk_size: int, 175 | ) -> Union[str, None]: 176 | """ 177 | Custom generator that yields the bytes of the media file. 178 | Modded from 179 | Thanks to Eyaadh 180 | """ 181 | client = self.client 182 | logger.debug(f"Starting to yielding file with client.") 183 | media_session = await self.generate_media_session(client, file_id) 184 | 185 | current_part = 1 186 | location = await self.get_location(file_id) 187 | 188 | try: 189 | r = await media_session.invoke( 190 | raw.functions.upload.GetFile( 191 | location=location, offset=offset, limit=chunk_size 192 | ), 193 | ) 194 | if isinstance(r, raw.types.upload.File): 195 | while True: 196 | chunk = r.bytes 197 | if not chunk: 198 | break 199 | elif part_count == 1: 200 | yield chunk[first_part_cut:last_part_cut] 201 | elif current_part == 1: 202 | yield chunk[first_part_cut:] 203 | elif current_part == part_count: 204 | yield chunk[:last_part_cut] 205 | else: 206 | yield chunk 207 | 208 | current_part += 1 209 | offset += chunk_size 210 | 211 | if current_part > part_count: 212 | break 213 | 214 | r = await media_session.invoke( 215 | raw.functions.upload.GetFile( 216 | location=location, offset=offset, limit=chunk_size 217 | ), 218 | ) 219 | except (TimeoutError, AttributeError): 220 | pass 221 | finally: 222 | logger.debug(f"Finished yielding file with {current_part} parts.") 223 | 224 | async def clean_cache(self) -> None: 225 | """ 226 | function to clean the cache to reduce memory usage 227 | """ 228 | while True: 229 | await asyncio.sleep(self.clean_timer) 230 | self.cached_file_ids.clear() 231 | logger.debug("Cleaned the cache") 232 | -------------------------------------------------------------------------------- /utils/file_properties.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | from pyrogram import Client 3 | from pyrogram.types import Message 4 | from pyrogram.file_id import FileId 5 | from typing import Any, Optional, Union 6 | from pyrogram.raw.types.messages import Messages 7 | from datetime import datetime 8 | 9 | 10 | async def parse_file_id(message: "Message") -> Optional[FileId]: 11 | media = get_media_from_message(message) 12 | if media: 13 | return FileId.decode(media.file_id) 14 | 15 | 16 | async def parse_file_unique_id(message: "Messages") -> Optional[str]: 17 | media = get_media_from_message(message) 18 | if media: 19 | return media.file_unique_id 20 | 21 | 22 | async def get_file_ids(client: Client, chat_id, message_id) -> Optional[FileId]: 23 | message = await client.get_messages(chat_id, int(message_id)) 24 | if message.empty: 25 | raise Exception("FileNotFound") 26 | media = get_media_from_message(message) 27 | file_unique_id = await parse_file_unique_id(message) 28 | file_id = await parse_file_id(message) 29 | setattr(file_id, "file_size", getattr(media, "file_size", 0)) 30 | setattr(file_id, "mime_type", getattr(media, "mime_type", "")) 31 | setattr(file_id, "file_name", getattr(media, "file_name", "")) 32 | setattr(file_id, "unique_id", file_unique_id) 33 | return file_id 34 | 35 | 36 | def get_media_from_message(message: "Message") -> Any: 37 | media_types = ( 38 | "audio", 39 | "document", 40 | "photo", 41 | "sticker", 42 | "animation", 43 | "video", 44 | "voice", 45 | "video_note", 46 | ) 47 | for attr in media_types: 48 | media = getattr(message, attr, None) 49 | if media: 50 | return media 51 | 52 | 53 | def get_hash(media_msg: Union[str, Message], length: int) -> str: 54 | if isinstance(media_msg, Message): 55 | media = get_media_from_message(media_msg) 56 | unique_id = getattr(media, "file_unique_id", "") 57 | else: 58 | unique_id = media_msg 59 | long_hash = hashlib.sha256(unique_id.encode("UTF-8")).hexdigest() 60 | return long_hash[:length] 61 | 62 | 63 | def get_name(media_msg: Union[Message, FileId]) -> str: 64 | if isinstance(media_msg, Message): 65 | media = get_media_from_message(media_msg) 66 | file_name = getattr(media, "file_name", "") 67 | 68 | elif isinstance(media_msg, FileId): 69 | file_name = getattr(media_msg, "file_name", "") 70 | 71 | if not file_name: 72 | if isinstance(media_msg, Message) and media_msg.media: 73 | media_type = media_msg.media.value 74 | elif media_msg.file_type: 75 | media_type = media_msg.file_type.name.lower() 76 | else: 77 | media_type = "file" 78 | 79 | formats = { 80 | "photo": "jpg", 81 | "audio": "mp3", 82 | "voice": "ogg", 83 | "video": "mp4", 84 | "animation": "mp4", 85 | "video_note": "mp4", 86 | "sticker": "webp", 87 | } 88 | 89 | ext = formats.get(media_type) 90 | ext = "." + ext if ext else "" 91 | 92 | date = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") 93 | file_name = f"{media_type}-{date}{ext}" 94 | 95 | return file_name 96 | -------------------------------------------------------------------------------- /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 23 | -------------------------------------------------------------------------------- /web.py: -------------------------------------------------------------------------------- 1 | from streamer import media_streamer 2 | from fastapi import FastAPI, Request 3 | from fastapi.responses import HTMLResponse, FileResponse, RedirectResponse, Response 4 | from bot import get_image, get_posts, rm_cache 5 | from html_gen import posts_html 6 | from pyrogram.client import Client 7 | from config import * 8 | from pyrogram import filters 9 | from pyrogram.types import Message 10 | 11 | user = Client( 12 | "userbot", 13 | api_id=int(API_ID), 14 | api_hash=API_HASH, 15 | session_string=STRING_SESSION, 16 | ) 17 | bot = Client( 18 | "techzindexbot", 19 | api_id=int(API_ID), 20 | api_hash=API_HASH, 21 | bot_token=BOT_TOKEN, 22 | ) 23 | 24 | app = FastAPI(docs_url=None, redoc_url=None) 25 | 26 | with open("templates/home.html") as f: 27 | HOME_HTML = f.read() 28 | with open("templates/stream.html") as f: 29 | STREAM_HTML = f.read() 30 | 31 | 32 | @app.get("/") 33 | async def home(): 34 | return RedirectResponse(HOME_PAGE_REDIRECT) 35 | 36 | 37 | @app.get("/channel/{channel}") 38 | async def channel(channel): 39 | posts = await get_posts(user, str(channel).lower()) 40 | phtml = posts_html(posts, channel) 41 | return HTMLResponse( 42 | HOME_HTML.replace("POSTS", phtml).replace("CHANNEL_ID", channel) 43 | ) 44 | 45 | 46 | @app.get("/api/posts/{channel}/{page}") 47 | async def get_posts_api(channel, page: str): 48 | posts = await get_posts(user, str(channel).lower(), page) 49 | phtml = posts_html(posts, channel) 50 | return {"html": phtml} 51 | 52 | 53 | @app.get("/static/{file}") 54 | async def static_files(file): 55 | return FileResponse(f"static/{file}") 56 | 57 | 58 | @app.get("/api/thumb/{channel}/{id}") 59 | async def get_thumb(channel, id): 60 | img = await get_image(bot, id, channel) 61 | return FileResponse(img, media_type="image/jpeg") 62 | 63 | 64 | @app.on_event("startup") 65 | async def startup(): 66 | print("Starting TG Clients...") 67 | await bot.start() 68 | await user.start() 69 | print("TG Clients Started") 70 | print("========================================") 71 | print("TechZIndex Started Successfully") 72 | print("Made By TechZBots | TechShreyash") 73 | print("========================================") 74 | 75 | 76 | # Streamer 77 | 78 | 79 | @app.get("/stream/{channel}/{id}") 80 | async def stream(channel, id): 81 | return HTMLResponse( 82 | STREAM_HTML.replace("URL", f"{BASE_URL}/api/stream/{channel}/{id}") 83 | ) 84 | 85 | 86 | @app.get("/api/stream/{channel}/{id}") 87 | async def stream_api(channel, id, request: Request): 88 | return await media_streamer(bot, channel, id, request) 89 | 90 | 91 | # bot commands 92 | 93 | 94 | @bot.on_message(filters.command("start")) 95 | async def start_cmd(_, msg: Message): 96 | await msg.reply_text( 97 | "TechZIndex Up and Running\n\n/clean_cache to clean website cache\n/help to know how to use this bot\n\nMade By @TechZBots | @TechZBots_Support" 98 | ) 99 | 100 | 101 | @bot.on_message(filters.command("help")) 102 | async def help_cmd(_, msg: Message): 103 | await msg.reply_text( 104 | f""" 105 | **How to use this bot?** 106 | 107 | 1. Add me to your channel as admin 108 | 2. Your channel must be public 109 | 3. Now open this link domain/channel/ 110 | 111 | Ex : http://index.techzbots.live/channel/autoairinganimes 112 | 113 | Contact [Owner](tg://user?id={OWNER_ID}) To Get domain of website 114 | Owner Id : {OWNER_ID}""" 115 | ) 116 | 117 | 118 | @bot.on_message(filters.command("clean_cache")) 119 | async def clean_cache(_, msg: Message): 120 | if msg.from_user.id in ADMINS: 121 | x = msg.text.split(" ") 122 | if len(x) == 2: 123 | rm_cache(x[1]) 124 | else: 125 | rm_cache() 126 | await msg.reply_text("Cache cleaned") 127 | else: 128 | await msg.reply_text( 129 | "You are not my owner\n\nContact [Owner](tg://user?id={OWNER_ID}) If You Want To Update Your Site\n\nRead : https://t.me/TechZBots/524" 130 | ) 131 | --------------------------------------------------------------------------------