navpan2 commited on
Commit
157e137
·
1 Parent(s): d9822e2

Upload 29 files

Browse files
Dockerfile ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.8
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 ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ import time
2
+
3
+ __version__ = "1.0.1"
4
+ StartTime = time.time()
5
+
FileStream/__main__.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import asyncio
3
+ import logging
4
+ import traceback
5
+ import logging.handlers as handlers
6
+ from .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.utils import ping_server
13
+ from FileStream.bot.clients import initialize_clients
14
+
15
+ logging.basicConfig(
16
+ level=logging.INFO,
17
+ datefmt="%d/%m/%Y %H:%M:%S",
18
+ format='[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s',
19
+ handlers=[logging.StreamHandler(stream=sys.stdout),
20
+ handlers.RotatingFileHandler("streambot.log", mode="a", maxBytes=104857600, backupCount=2, encoding="utf-8")],)
21
+
22
+ logging.getLogger("aiohttp").setLevel(logging.ERROR)
23
+ logging.getLogger("pyrogram").setLevel(logging.ERROR)
24
+ logging.getLogger("aiohttp.web").setLevel(logging.ERROR)
25
+
26
+ server = web.AppRunner(web_server())
27
+
28
+ loop = asyncio.get_event_loop()
29
+
30
+ async def start_services():
31
+ print()
32
+ if Telegram.SECONDARY:
33
+ print("------------------ Starting as Secondary Server ------------------")
34
+ else:
35
+ print("------------------- Starting as Primary Server -------------------")
36
+ print()
37
+ print("-------------------- Initializing Telegram Bot --------------------")
38
+
39
+
40
+ await FileStream.start()
41
+ bot_info = await FileStream.get_me()
42
+ FileStream.id = bot_info.id
43
+ FileStream.username = bot_info.username
44
+ FileStream.fname=bot_info.first_name
45
+ print("------------------------------ DONE ------------------------------")
46
+ print()
47
+ print("---------------------- Initializing Clients ----------------------")
48
+ await initialize_clients()
49
+ print("------------------------------ DONE ------------------------------")
50
+ if Server.KEEP_ALIVE:
51
+ print("------------------ Starting Keep Alive Service ------------------")
52
+ print()
53
+ asyncio.create_task(ping_server())
54
+ print()
55
+ print("--------------------- Initializing Web Server ---------------------")
56
+ await server.setup()
57
+ await web.TCPSite(server, Server.BIND_ADDRESS, Server.PORT).start()
58
+ print("------------------------------ DONE ------------------------------")
59
+ print()
60
+ print("------------------------- Service Started -------------------------")
61
+ print(" bot =>> {}".format(bot_info.first_name))
62
+ if bot_info.dc_id:
63
+ print(" DC ID =>> {}".format(str(bot_info.dc_id)))
64
+ print(" URL =>> {}".format(Server.URL))
65
+ print("------------------------------------------------------------------")
66
+ await idle()
67
+
68
+ async def cleanup():
69
+ await server.cleanup()
70
+ await FileStream.stop()
71
+
72
+ if __name__ == "__main__":
73
+ try:
74
+ loop.run_until_complete(start_services())
75
+ except KeyboardInterrupt:
76
+ pass
77
+ except Exception as err:
78
+ logging.error(traceback.format_exc())
79
+ finally:
80
+ loop.run_until_complete(cleanup())
81
+ loop.stop()
82
+ print("------------------------ Stopped Services ------------------------")
FileStream/bot/__init__.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
FileStream/bot/clients.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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")
FileStream/bot/plugins/admin.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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"**File Already Deleted**",
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
+
FileStream/bot/plugins/callback.py ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
FileStream/bot/plugins/start.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
14
+ db = Database(Telegram.DATABASE_URL, Telegram.SESSION_NAME)
15
+
16
+ @FileStream.on_message(filters.command('start') & filters.private)
17
+ async def start(bot: Client, message: Message):
18
+ if not await verify_user(bot, message):
19
+ return
20
+ usr_cmd = message.text.split("_")[-1]
21
+
22
+ if usr_cmd == "/start":
23
+ await message.reply_text(
24
+ text=LANG.START_TEXT.format(message.from_user.mention, FileStream.username),
25
+ parse_mode=ParseMode.HTML,
26
+ disable_web_page_preview=True,
27
+ reply_markup=BUTTON.START_BUTTONS
28
+ )
29
+ else:
30
+ if "stream_" in message.text:
31
+ try:
32
+ file_check = await db.get_file(usr_cmd)
33
+ file_id = str(file_check['_id'])
34
+ if file_id == usr_cmd:
35
+ reply_markup, stream_text = await gen_linkx(m=message, _id=file_id,
36
+ name=[FileStream.username, FileStream.fname])
37
+ await message.reply_text(
38
+ text=stream_text,
39
+ parse_mode=ParseMode.HTML,
40
+ disable_web_page_preview=True,
41
+ reply_markup=reply_markup,
42
+ quote=True
43
+ )
44
+
45
+ except FIleNotFound as e:
46
+ await message.reply_text("File Not Found")
47
+ except Exception as e:
48
+ await message.reply_text("Something Went Wrong")
49
+ logging.error(e)
50
+
51
+ elif "file_" in message.text:
52
+ try:
53
+ file_check = await db.get_file(usr_cmd)
54
+ db_id = str(file_check['_id'])
55
+ file_id = file_check['file_id']
56
+ file_name = file_check['file_name']
57
+ if db_id == usr_cmd:
58
+ await message.reply_cached_media(file_id=file_id, caption=f'**{file_name}**')
59
+
60
+ except FIleNotFound as e:
61
+ await message.reply_text("**File Not Found**")
62
+ except Exception as e:
63
+ await message.reply_text("Something Went Wrong")
64
+ logging.error(e)
65
+
66
+ else:
67
+ await message.reply_text(f"**Invalid Command**")
68
+
69
+ @FileStream.on_message(filters.private & filters.command(["about"]))
70
+ async def start(bot, message):
71
+ if not await verify_user(bot, message):
72
+ return
73
+ await message.reply_text(
74
+ text=LANG.ABOUT_TEXT.format(FileStream.fname, __version__),
75
+ disable_web_page_preview=True,
76
+ reply_markup=BUTTON.ABOUT_BUTTONS
77
+ )
78
+
79
+
80
+ @FileStream.on_message((filters.command('help')) & filters.private)
81
+ async def help_handler(bot, message):
82
+ if not await verify_user(bot, message):
83
+ return
84
+ await message.reply_text(
85
+ text=LANG.HELP_TEXT.format(Telegram.OWNER_ID),
86
+ parse_mode=ParseMode.HTML,
87
+ disable_web_page_preview=True,
88
+ reply_markup=BUTTON.HELP_BUTTONS
89
+ )
90
+
91
+ # ---------------------------------------------------------------------------------------------------
92
+
93
+ @FileStream.on_message(filters.command('files') & filters.private)
94
+ async def my_files(bot: Client, message: Message):
95
+ if not await verify_user(bot, message):
96
+ return
97
+ user_files, total_files = await db.find_files(message.from_user.id, [1, 10])
98
+
99
+ file_list = []
100
+ async for x in user_files:
101
+ file_list.append([InlineKeyboardButton(x["file_name"], callback_data=f"myfile_{x['_id']}_{1}")])
102
+ if total_files > 10:
103
+ file_list.append(
104
+ [
105
+ InlineKeyboardButton("◄", callback_data="N/A"),
106
+ InlineKeyboardButton(f"1/{math.ceil(total_files / 10)}", callback_data="N/A"),
107
+ InlineKeyboardButton("►", callback_data="userfiles_2")
108
+ ],
109
+ )
110
+ if not file_list:
111
+ file_list.append(
112
+ [InlineKeyboardButton("ᴇᴍᴘᴛʏ", callback_data="N/A")],
113
+ )
114
+ file_list.append([InlineKeyboardButton("ᴄʟᴏsᴇ", callback_data="close")])
115
+ await message.reply_photo(photo=Telegram.IMAGE_FILEID,
116
+ caption="Total files: {}".format(total_files),
117
+ reply_markup=InlineKeyboardMarkup(file_list))
118
+
119
+
FileStream/bot/plugins/stream.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_UPDATES_CHANNEL:
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.LOG_CHANNEL,
52
+ text=f"Gᴏᴛ FʟᴏᴏᴅWᴀɪᴛ ᴏғ {str(e.value)}s from [{message.from_user.first_name}](tg://user?id={message.from_user.id})\n\n**𝚄𝚜𝚎𝚛 𝙸𝙳 :** `{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.LOG_CHANNEL,
90
+ text=f"ɢᴏᴛ ғʟᴏᴏᴅᴡᴀɪᴛ ᴏғ {str(w.x)}s FROM {message.chat.title}\n\n**CHANNEL ID:** `{str(message.chat.id)}`",
91
+ disable_web_page_preview=True)
92
+ except Exception as e:
93
+ await bot.send_message(chat_id=Telegram.LOG_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}**")
FileStream/config.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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('20295350')
8
+ API_HASH = str('805a0a86f3b382d904617d0a2fd4fb6f')
9
+ BOT_TOKEN = str('6951577014:AAGPEWqTj42ndRcmDDibDqfvBymcrJkg7Ss')
10
+ OWNER_ID = int('1220356966')
11
+ WORKERS = int(env.get("WORKERS", "6")) # 6 workers = 6 commands at once
12
+ DATABASE_URL = str('mongodb+srv://npanchayan:jph2G5RdOuII3gYM@cluster0.vycugvp.mongodb.net/?retryWrites=true&w=majority')
13
+ UPDATES_CHANNEL = str(env.get('UPDATES_CHANNEL', "Telegram"))
14
+ SESSION_NAME = str(env.get('SESSION_NAME', 'FileStream'))
15
+ FORCE_UPDATES_CHANNEL = env.get('FORCE_UPDATES_CHANNEL', False)
16
+ FORCE_UPDATES_CHANNEL = True if str(FORCE_UPDATES_CHANNEL).lower() == "true" else False
17
+ SLEEP_THRESHOLD = int(env.get("SLEEP_THRESHOLD", "60"))
18
+ IMAGE_FILEID = env.get('IMAGE_FILEID', "https://telegra.ph/file/5bb9935be0229adf98b73.jpg")
19
+ MULTI_CLIENT = False
20
+ LOG_CHANNEL = int('-1001642343016')
21
+ MODE = env.get("MODE", "primary")
22
+ SECONDARY = True if MODE.lower() == "secondary" else False
23
+ AUTH_USERS = list(set(int(x) for x in str(env.get("AUTH_USERS", "")).split()))
24
+
25
+ class Server:
26
+ PORT = int(8080)
27
+ BIND_ADDRESS = str(env.get("BIND_ADDRESS", "0.0.0.0"))
28
+ PING_INTERVAL = int(env.get("PING_INTERVAL", "1200")) # 20 minutes
29
+ HAS_SSL = str(env.get("HAS_SSL", "0").lower()) in ("1", "true", "t", "yes", "y")
30
+ NO_PORT = str(env.get("NO_PORT", "1").lower()) in ("1", "true", "t", "yes", "y")
31
+ FQDN = str('')
32
+ URL = "http{}://{}{}/".format(
33
+ "s" if HAS_SSL else "", FQDN, "" if NO_PORT else ":" + str(PORT)
34
+ )
35
+ KEEP_ALIVE = str(env.get("KEEP_ALIVE", "0").lower()) in ("1", "true", "t", "yes", "y")
36
+
37
+
FileStream/server/__init__.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
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
FileStream/server/exceptions.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ class InvalidHash(Exception):
2
+ message = "Invalid hash"
3
+
4
+ class FIleNotFound(Exception):
5
+ message = "File not found"
FileStream/server/stream_routes.py ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ @routes.get("/", allow_head=True)
16
+ async def root_route_handler(_):
17
+ return web.json_response(
18
+ {
19
+ "message": "HELLO WORLD!",
20
+ "uptime": utils.get_readable_time(time.time() - StartTime),
21
+ "telegram_bot": "@" + FileStream.username,
22
+ "connected_bots": len(multi_clients),
23
+ "version": __version__,
24
+ }
25
+ )
26
+
27
+ @routes.get("/status", allow_head=True)
28
+ async def root_route_handler(_):
29
+ return web.json_response(
30
+ {
31
+ "server_status": "running",
32
+ "uptime": utils.get_readable_time(time.time() - StartTime),
33
+ "telegram_bot": "@" + FileStream.username,
34
+ "connected_bots": len(multi_clients),
35
+ "loads": dict(
36
+ ("bot" + str(c + 1), l)
37
+ for c, (_, l) in enumerate(
38
+ sorted(work_loads.items(), key=lambda x: x[1], reverse=True)
39
+ )
40
+ ),
41
+ "version": __version__,
42
+ }
43
+ )
44
+
45
+ @routes.get("/watch/{path}", allow_head=True)
46
+ async def stream_handler(request: web.Request):
47
+ try:
48
+ path = request.match_info["path"]
49
+ return web.Response(text=await render_page(path), content_type='text/html')
50
+ except InvalidHash as e:
51
+ raise web.HTTPForbidden(text=e.message)
52
+ except FIleNotFound as e:
53
+ raise web.HTTPNotFound(text=e.message)
54
+ except (AttributeError, BadStatusLine, ConnectionResetError):
55
+ pass
56
+
57
+
58
+ @routes.get("/dl/{path}", allow_head=True)
59
+ async def stream_handler(request: web.Request):
60
+ try:
61
+ path = request.match_info["path"]
62
+ return await media_streamer(request, path)
63
+ except InvalidHash as e:
64
+ raise web.HTTPForbidden(text=e.message)
65
+ except FIleNotFound as e:
66
+ raise web.HTTPNotFound(text=e.message)
67
+ except (AttributeError, BadStatusLine, ConnectionResetError):
68
+ pass
69
+ except Exception as e:
70
+ traceback.print_exc()
71
+ logging.critical(e.with_traceback(None))
72
+ logging.debug(traceback.format_exc())
73
+ raise web.HTTPInternalServerError(text=str(e))
74
+
75
+ class_cache = {}
76
+
77
+ async def media_streamer(request: web.Request, db_id: str):
78
+ range_header = request.headers.get("Range", 0)
79
+
80
+ index = min(work_loads, key=work_loads.get)
81
+ faster_client = multi_clients[index]
82
+
83
+ if Telegram.MULTI_CLIENT:
84
+ logging.info(f"Client {index} is now serving {request.headers.get('X-FORWARDED-FOR',request.remote)}")
85
+
86
+ if faster_client in class_cache:
87
+ tg_connect = class_cache[faster_client]
88
+ logging.debug(f"Using cached ByteStreamer object for client {index}")
89
+ else:
90
+ logging.debug(f"Creating new ByteStreamer object for client {index}")
91
+ tg_connect = utils.ByteStreamer(faster_client)
92
+ class_cache[faster_client] = tg_connect
93
+ logging.debug("before calling get_file_properties")
94
+ file_id = await tg_connect.get_file_properties(db_id, multi_clients)
95
+ logging.debug("after calling get_file_properties")
96
+
97
+ file_size = file_id.file_size
98
+
99
+ if range_header:
100
+ from_bytes, until_bytes = range_header.replace("bytes=", "").split("-")
101
+ from_bytes = int(from_bytes)
102
+ until_bytes = int(until_bytes) if until_bytes else file_size - 1
103
+ else:
104
+ from_bytes = request.http_range.start or 0
105
+ until_bytes = (request.http_range.stop or file_size) - 1
106
+
107
+ if (until_bytes > file_size) or (from_bytes < 0) or (until_bytes < from_bytes):
108
+ return web.Response(
109
+ status=416,
110
+ body="416: Range not satisfiable",
111
+ headers={"Content-Range": f"bytes */{file_size}"},
112
+ )
113
+
114
+ chunk_size = 1024 * 1024
115
+ until_bytes = min(until_bytes, file_size - 1)
116
+
117
+ offset = from_bytes - (from_bytes % chunk_size)
118
+ first_part_cut = from_bytes - offset
119
+ last_part_cut = until_bytes % chunk_size + 1
120
+
121
+ req_length = until_bytes - from_bytes + 1
122
+ part_count = math.ceil(until_bytes / chunk_size) - math.floor(offset / chunk_size)
123
+ body = tg_connect.yield_file(
124
+ file_id, index, offset, first_part_cut, last_part_cut, part_count, chunk_size
125
+ )
126
+
127
+ mime_type = file_id.mime_type
128
+ file_name = utils.get_name(file_id)
129
+ disposition = "attachment"
130
+
131
+ if not mime_type:
132
+ mime_type = mimetypes.guess_type(file_name)[0] or "application/octet-stream"
133
+
134
+ # if "video/" in mime_type or "audio/" in mime_type:
135
+ # disposition = "inline"
136
+
137
+ return web.Response(
138
+ status=206 if range_header else 200,
139
+ body=body,
140
+ headers={
141
+ "Content-Type": f"{mime_type}",
142
+ "Content-Range": f"bytes {from_bytes}-{until_bytes}/{file_size}",
143
+ "Content-Length": str(req_length),
144
+ "Content-Disposition": f'{disposition}; filename="{file_name}"',
145
+ "Accept-Ranges": "bytes",
146
+ },
147
+ )
FileStream/template/dl.html ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta property="og:image" content="https://www.flaticon.com/premium-icon/icons/svg/2626/2626281.svg" itemprop="thumbnailUrl">
6
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
8
+ <title>%s</title>
9
+ <link rel="stylesheet" type='text/css' href="https://drive.google.com/uc?export=view&id=1pVLG4gZy7jdow3sO-wFS06aP_A9QX0O6">
10
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Raleway">
11
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Delius">
12
+ <!-- <link rel="stylesheet" href="./style.css"> -->
13
+
14
+ </head>
15
+
16
+ <body class='cyber'>
17
+ <header>
18
+ <div class="toogle"></div>
19
+ <div id="file-name" class="cyber">
20
+ %s
21
+ </div>
22
+ </header>
23
+
24
+ <div class="container">
25
+ <a href=%s>
26
+ <button class="cybr-btn">
27
+ Download
28
+ <span aria-hidden>_</span>
29
+ <span aria-hidden class="cybr-btn__glitch">_404 Error</span>
30
+ <span aria-hidden class="cybr-btn__tag">_%s</span>
31
+ </button>
32
+ </a>
33
+ </div>
34
+
35
+ <footer>
36
+ <span id="fork-text">Fork me on</span>
37
+ <span>
38
+ <a href="https://github.com/DeekshithSH/FileStreamBot" id='github-logo'>
39
+ <svg id='octo' style="width: 1.2rem; padding-left: 5px; fill: var(--footer-icon-color)" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
40
+ </a>
41
+ </span>
42
+ </footer>
43
+
44
+ <script>
45
+ const body = document.querySelector('body');
46
+ const title = document.querySelector('#file-name');
47
+ const footer = document.querySelector('footer');
48
+ const toogle = document.querySelector('.toogle');
49
+ toogle.onclick = () => {
50
+ body.classList.toggle('dark')
51
+ footer.classList.toggle('dark')
52
+ title.classList.toggle('dark')
53
+ }
54
+ </script>
55
+ </body>
56
+ </html>
FileStream/template/stream.html ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>streamHeading</title>
5
+
6
+ <meta charset="UTF-8">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
8
+ <meta http-equiv="X-Frame-Options" content="deny">
9
+
10
+ <link rel="stylesheet" href="https://cdn.plyr.io/3.7.8/plyr.css" />
11
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
12
+
13
+ <script src="https://cdn.plyr.io/3.7.8/plyr.polyfilled.js"></script>
14
+
15
+ <style>
16
+ html, body {
17
+ margin: 0;
18
+ height: 100%;
19
+ }
20
+
21
+ #stream-media {
22
+ height: 100%;
23
+ width: 100%;
24
+ }
25
+
26
+ #error-message {
27
+ color: red;
28
+ font-size: 24px;
29
+ text-align: center;
30
+ margin-top: 20px;
31
+ }
32
+
33
+ .plyr__video-wrapper .plyr-download-button{
34
+ position: absolute;
35
+ top: 10px;
36
+ left: 10px;
37
+ width: 30px;
38
+ height: 30px;
39
+ background-color: rgba(0, 0, 0, 0.7);
40
+ border-radius: 50%;
41
+ text-align: center;
42
+ line-height: 30px;
43
+ color: white;
44
+ z-index: 10;
45
+ }
46
+
47
+ .plyr__volume {
48
+ max-width: initial;
49
+ min-width: initial;
50
+ width: auto;
51
+ position: relative;
52
+ }
53
+
54
+
55
+ .plyr__video-wrapper .plyr-share-button{
56
+ position: absolute;
57
+ top: 50px;
58
+ left: 10px;
59
+ width: 30px;
60
+ height: 30px;
61
+ background-color: rgba(0, 0, 0, 0.7);
62
+ border-radius: 50%;
63
+ text-align: center;
64
+ line-height: 30px;
65
+ color: white;
66
+ z-index: 10;
67
+ }
68
+
69
+ .plyr__video-wrapper .plyr-download-button:hover,
70
+ .plyr__video-wrapper .plyr-share-button:hover{
71
+ background-color: rgba(255, 255, 255, 0.7);
72
+ color: black;
73
+ }
74
+
75
+ .plyr__video-wrapper .plyr-download-button:before {
76
+ font-family: "Font Awesome 5 Free";
77
+ content: "\f019";
78
+ font-weight: bold;
79
+ }
80
+
81
+ .plyr__video-wrapper .plyr-share-button:before {
82
+ font-family: "Font Awesome 5 Free";
83
+ content: "\f064";
84
+ font-weight: bold;
85
+ }
86
+
87
+ .plyr, .plyr__video-wrapper, .plyr__video-embed iframe {
88
+ height: 100%;
89
+ }
90
+
91
+ </style>
92
+ </head>
93
+
94
+ <body>
95
+ <video id="stream-media" controls preload="auto">
96
+ <source src="" type="">
97
+ <p class="vjs-no-js">
98
+ To view this video please enable JavaScript, and consider upgrading to a web browser that supports HTML5 video
99
+ </p>
100
+ </video>
101
+
102
+ <div id="error-message"></div>
103
+
104
+ <script>
105
+ var player = new Plyr('#stream-media', {
106
+ controls:['play-large', 'rewind', 'play', 'fast-forward', 'progress', 'current-time', 'mute', 'settings', 'pip', 'fullscreen'],
107
+ settings:['speed','loop'],
108
+ speed:{selected:1,options:[0.25,0.5,0.75,1,1.25,1.5,1.75,2]},
109
+ seek: 10,
110
+ keyboard: { focused: true, global: true },
111
+ });
112
+
113
+ var mediaLink = "streamMediaLink";
114
+
115
+ if (mediaLink) {
116
+ document.querySelector('#stream-media source').setAttribute('src', mediaLink);
117
+ player.restart();
118
+
119
+ var downloadButton = document.createElement('div');
120
+ downloadButton.className = 'plyr-download-button';
121
+
122
+ downloadButton.onclick = function() {
123
+ event.stopPropagation();
124
+ var link = document.createElement('a');
125
+ link.href = mediaLink;
126
+ document.body.appendChild(link);
127
+ link.click();
128
+ document.body.removeChild(link);
129
+ };
130
+
131
+ player.elements.container.querySelector('.plyr__video-wrapper').appendChild(downloadButton);
132
+
133
+ var shareButton = document.createElement('div');
134
+ shareButton.className = 'plyr-share-button';
135
+
136
+ shareButton.onclick = function() {
137
+ event.stopPropagation();
138
+ if (navigator.share) {
139
+ navigator.share({
140
+ title: "Play",
141
+ url: window.location.href
142
+ });
143
+ }
144
+ };
145
+
146
+ player.elements.container.querySelector('.plyr__video-wrapper').appendChild(shareButton);
147
+
148
+ } else {
149
+ document.getElementById('error-message').textContent = 'Error: Media URL not provided';
150
+ }
151
+ </script>
152
+
153
+ </body>
154
+ </html>
FileStream/utils/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from .keepalive import ping_server
2
+ from .time_format import get_readable_time
3
+ from .file_properties import get_name, get_file_ids
4
+ from .custom_dl import ByteStreamer
FileStream/utils/bot_utils.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pyrogram.errors import UserNotParticipant
2
+ from pyrogram.enums.parse_mode import ParseMode
3
+ from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Message
4
+ from FileStream.utils.translation import LANG
5
+ from FileStream.utils.database import Database
6
+ from FileStream.utils.human_readable import humanbytes
7
+ from FileStream.config import Telegram, Server
8
+ from FileStream.bot import FileStream
9
+
10
+ db = Database(Telegram.DATABASE_URL, Telegram.SESSION_NAME)
11
+
12
+ async def is_user_joined(bot, message: Message):
13
+ try:
14
+ user = await bot.get_chat_member(Telegram.UPDATES_CHANNEL, message.chat.id)
15
+ if user.status == "BANNED":
16
+ await message.reply_text(
17
+ text=LANG.BAN_TEXT.format(Telegram.OWNER_ID),
18
+ parse_mode=ParseMode.MARKDOWN,
19
+ disable_web_page_preview=True
20
+ )
21
+ return False
22
+ except UserNotParticipant:
23
+ await message.reply_text(
24
+ text = "<i>Jᴏɪɴ ᴍʏ ᴜᴘᴅᴀᴛᴇ ᴄʜᴀɴɴᴇʟ ᴛᴏ ᴜsᴇ ᴍᴇ 🔐</i>",
25
+ reply_markup=InlineKeyboardMarkup(
26
+ [[
27
+ InlineKeyboardButton("Jᴏɪɴ ɴᴏᴡ 🔓", url=f"https://t.me/{Telegram.UPDATES_CHANNEL}")
28
+ ]]
29
+ ),
30
+ parse_mode=ParseMode.HTML
31
+ )
32
+ return False
33
+ except Exception:
34
+ await message.reply_text(
35
+ text = f"<i>Sᴏᴍᴇᴛʜɪɴɢ ᴡʀᴏɴɢ ᴄᴏɴᴛᴀᴄᴛ ᴍʏ ᴅᴇᴠᴇʟᴏᴘᴇʀ</i> <b><a href='https://t.me/{Telegram.UPDATES_CHANNEL}'>[ ᴄʟɪᴄᴋ ʜᴇʀᴇ ]</a></b>",
36
+ parse_mode=ParseMode.HTML,
37
+ disable_web_page_preview=True)
38
+ return False
39
+ return True
40
+
41
+ #---------------------[ PRIVATE GEN LINK + CALLBACK ]---------------------#
42
+
43
+ async def gen_link(_id):
44
+ file_info = await db.get_file(_id)
45
+ file_name = file_info['file_name']
46
+ file_size = humanbytes(file_info['file_size'])
47
+ mime_type = file_info['mime_type']
48
+
49
+ page_link = f"{Server.URL}watch/{_id}"
50
+ stream_link = f"{Server.URL}dl/{_id}"
51
+ file_link = f"https://t.me/{FileStream.username}?start=file_{_id}"
52
+
53
+ if "video" in mime_type:
54
+ stream_text = LANG.STREAM_TEXT.format(file_name, file_size, stream_link, page_link, file_link)
55
+ reply_markup = InlineKeyboardMarkup(
56
+ [
57
+ [InlineKeyboardButton("sᴛʀᴇᴀᴍ", url=page_link), InlineKeyboardButton("ᴅᴏᴡɴʟᴏᴀᴅ", url=stream_link)],
58
+ [InlineKeyboardButton("ɢᴇᴛ ғɪʟᴇ", url=file_link), InlineKeyboardButton("ʀᴇᴠᴏᴋᴇ ғɪʟᴇ", callback_data=f"msgdelpvt_{_id}")],
59
+ [InlineKeyboardButton("ᴄʟᴏsᴇ", callback_data="close")]
60
+ ]
61
+ )
62
+ else:
63
+ stream_text = LANG.STREAM_TEXT_X.format(file_name, file_size, stream_link, file_link)
64
+ reply_markup = InlineKeyboardMarkup(
65
+ [
66
+ [InlineKeyboardButton("ᴅᴏᴡɴʟᴏᴀᴅ", url=stream_link)],
67
+ [InlineKeyboardButton("ɢᴇᴛ ғɪʟᴇ", url=file_link), InlineKeyboardButton("ʀᴇᴠᴏᴋᴇ ғɪʟᴇ", callback_data=f"msgdelpvt_{_id}")],
68
+ [InlineKeyboardButton("ᴄʟᴏsᴇ", callback_data="close")]
69
+ ]
70
+ )
71
+ return reply_markup, stream_text
72
+
73
+ #---------------------[ GEN STREAM LINKS FOR CHANNEL ]---------------------#
74
+
75
+ async def gen_linkx(m:Message , _id, name: list):
76
+ file_info = await db.get_file(_id)
77
+ file_name = file_info['file_name']
78
+ mime_type = file_info['mime_type']
79
+ file_size = humanbytes(file_info['file_size'])
80
+
81
+ page_link = f"{Server.URL}watch/{_id}"
82
+ stream_link = f"{Server.URL}dl/{_id}"
83
+ file_link = f"https://t.me/{FileStream.username}?start=file_{_id}"
84
+
85
+ if "video" in mime_type:
86
+ stream_text= LANG.STREAM_TEXT_X.format(file_name, file_size, stream_link, page_link)
87
+ reply_markup = InlineKeyboardMarkup(
88
+ [
89
+ [InlineKeyboardButton("sᴛʀᴇᴀᴍ", url=page_link), InlineKeyboardButton("ᴅᴏᴡɴʟᴏᴀᴅ", url=stream_link)]
90
+ ]
91
+ )
92
+ else:
93
+ stream_text= LANG.STREAM_TEXT_X.format(file_name, file_size, stream_link, file_link)
94
+ reply_markup = InlineKeyboardMarkup(
95
+ [
96
+ [InlineKeyboardButton("ᴅᴏᴡɴʟᴏᴀᴅ", url=stream_link)]
97
+ ]
98
+ )
99
+ return reply_markup, stream_text
100
+
101
+ #---------------------[ USER BANNED ]---------------------#
102
+
103
+ async def is_user_banned(message):
104
+ if await db.is_user_banned(message.from_user.id):
105
+ await message.reply_text(
106
+ text=LANG.BAN_TEXT.format(Telegram.OWNER_ID),
107
+ parse_mode=ParseMode.MARKDOWN,
108
+ disable_web_page_preview=True
109
+ )
110
+ return True
111
+ return False
112
+
113
+ #---------------------[ CHANNEL BANNED ]---------------------#
114
+
115
+ async def is_channel_banned(bot, message):
116
+ if await db.is_user_banned(message.chat.id):
117
+ await bot.edit_message_reply_markup(
118
+ chat_id=message.chat.id,
119
+ message_id=message.id,
120
+ reply_markup=InlineKeyboardMarkup([[
121
+ InlineKeyboardButton(f"ᴄʜᴀɴɴᴇʟ ɪs ʙᴀɴɴᴇᴅ", callback_data="N/A")]])
122
+ )
123
+ return True
124
+ return False
125
+
126
+ #---------------------[ USER AUTH ]---------------------#
127
+
128
+ async def is_user_authorized(message):
129
+ if hasattr(Telegram, 'AUTH_USERS') and Telegram.AUTH_USERS:
130
+ user_id = message.from_user.id
131
+
132
+ if user_id == Telegram.OWNER_ID:
133
+ return True
134
+
135
+ if not (user_id in Telegram.AUTH_USERS):
136
+ await message.reply_text(
137
+ text="You are not authorized to use this bot.",
138
+ parse_mode=ParseMode.MARKDOWN,
139
+ disable_web_page_preview=True
140
+ )
141
+ return False
142
+
143
+ return True
144
+
145
+ #---------------------[ USER EXIST ]---------------------#
146
+
147
+ async def is_user_exist(bot, message):
148
+ if not bool(await db.get_user(message.from_user.id)):
149
+ await db.add_user(message.from_user.id)
150
+ await bot.send_message(
151
+ Telegram.LOG_CHANNEL,
152
+ f"**#NᴇᴡUsᴇʀ**\n**⬩ ᴜsᴇʀ ɴᴀᴍᴇ :** [{message.from_user.first_name}](tg://user?id={message.from_user.id})\n**⬩ ᴜsᴇʀ ɪᴅ :** `{message.from_user.id}`"
153
+ )
154
+
155
+ async def is_channel_exist(bot, message):
156
+ if not bool(await db.get_user(message.chat.id)):
157
+ await db.add_user(message.chat.id)
158
+ members = await bot.get_chat_members_count(message.chat.id)
159
+ await bot.send_message(
160
+ Telegram.LOG_CHANNEL,
161
+ f"**#NᴇᴡCʜᴀɴɴᴇʟ** \n**⬩ ᴄʜᴀᴛ ɴᴀᴍᴇ :** `{message.chat.title}`\n**⬩ ᴄʜᴀᴛ ɪᴅ :** `{message.chat.id}`\n**⬩ ᴛᴏᴛᴀʟ ᴍᴇᴍʙᴇʀs :** `{members}`"
162
+ )
163
+
164
+ async def verify_user(bot, message):
165
+ if not await is_user_authorized(message):
166
+ return False
167
+
168
+ if await is_user_banned(message):
169
+ return False
170
+
171
+ await is_user_exist(bot, message)
172
+
173
+ if Telegram.FORCE_UPDATES_CHANNEL:
174
+ if not await is_user_joined(bot, message):
175
+ return False
176
+
177
+ return True
FileStream/utils/broadcast_helper.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import traceback
3
+ from pyrogram.errors import FloodWait, InputUserDeactivated, UserIsBlocked, PeerIdInvalid
4
+
5
+ async def send_msg(user_id, message):
6
+ try:
7
+ await message.copy(chat_id=user_id)
8
+ return 200, None
9
+ except FloodWait as e:
10
+ await asyncio.sleep(e.value)
11
+ return send_msg(user_id, message)
12
+ except InputUserDeactivated:
13
+ return 400, f"{user_id} : deactivated\n"
14
+ except UserIsBlocked:
15
+ return 400, f"{user_id} : blocked the bot\n"
16
+ except PeerIdInvalid:
17
+ return 400, f"{user_id} : user id invalid\n"
18
+ except Exception as e:
19
+ return 500, f"{user_id} : {traceback.format_exc()}\n"
FileStream/utils/custom_dl.py ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import logging
3
+ from typing import Dict, Union
4
+ from FileStream.bot import work_loads
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
+ from pyrogram.types import Message
11
+
12
+ class ByteStreamer:
13
+ def __init__(self, client: Client):
14
+ self.clean_timer = 30 * 60
15
+ self.client: Client = client
16
+ self.cached_file_ids: Dict[str, FileId] = {}
17
+ asyncio.create_task(self.clean_cache())
18
+
19
+ async def get_file_properties(self, db_id: str, multi_clients) -> FileId:
20
+ """
21
+ Returns the properties of a media of a specific message in a FIleId class.
22
+ if the properties are cached, then it'll return the cached results.
23
+ or it'll generate the properties from the Message ID and cache them.
24
+ """
25
+ if not db_id in self.cached_file_ids:
26
+ logging.debug("Before Calling generate_file_properties")
27
+ await self.generate_file_properties(db_id, multi_clients)
28
+ logging.debug(f"Cached file properties for file with ID {db_id}")
29
+ return self.cached_file_ids[db_id]
30
+
31
+ async def generate_file_properties(self, db_id: str, multi_clients) -> FileId:
32
+ """
33
+ Generates the properties of a media file on a specific message.
34
+ returns ths properties in a FIleId class.
35
+ """
36
+ logging.debug("Before calling get_file_ids")
37
+ file_id = await get_file_ids(self.client, db_id, multi_clients, Message)
38
+ logging.debug(f"Generated file ID and Unique ID for file with ID {db_id}")
39
+ self.cached_file_ids[db_id] = file_id
40
+ logging.debug(f"Cached media file with ID {db_id}")
41
+ return self.cached_file_ids[db_id]
42
+
43
+ async def generate_media_session(self, client: Client, file_id: FileId) -> Session:
44
+ """
45
+ Generates the media session for the DC that contains the media file.
46
+ This is required for getting the bytes from Telegram servers.
47
+ """
48
+
49
+ media_session = client.media_sessions.get(file_id.dc_id, None)
50
+
51
+ if media_session is None:
52
+ if file_id.dc_id != await client.storage.dc_id():
53
+ media_session = Session(
54
+ client,
55
+ file_id.dc_id,
56
+ await Auth(
57
+ client, file_id.dc_id, await client.storage.test_mode()
58
+ ).create(),
59
+ await client.storage.test_mode(),
60
+ is_media=True,
61
+ )
62
+ await media_session.start()
63
+
64
+ for _ in range(6):
65
+ exported_auth = await client.invoke(
66
+ raw.functions.auth.ExportAuthorization(dc_id=file_id.dc_id)
67
+ )
68
+
69
+ try:
70
+ await media_session.invoke(
71
+ raw.functions.auth.ImportAuthorization(
72
+ id=exported_auth.id, bytes=exported_auth.bytes
73
+ )
74
+ )
75
+ break
76
+ except AuthBytesInvalid:
77
+ logging.debug(
78
+ f"Invalid authorization bytes for DC {file_id.dc_id}"
79
+ )
80
+ continue
81
+ else:
82
+ await media_session.stop()
83
+ raise AuthBytesInvalid
84
+ else:
85
+ media_session = Session(
86
+ client,
87
+ file_id.dc_id,
88
+ await client.storage.auth_key(),
89
+ await client.storage.test_mode(),
90
+ is_media=True,
91
+ )
92
+ await media_session.start()
93
+ logging.debug(f"Created media session for DC {file_id.dc_id}")
94
+ client.media_sessions[file_id.dc_id] = media_session
95
+ else:
96
+ logging.debug(f"Using cached media session for DC {file_id.dc_id}")
97
+ return media_session
98
+
99
+
100
+ @staticmethod
101
+ async def get_location(file_id: FileId) -> Union[raw.types.InputPhotoFileLocation,
102
+ raw.types.InputDocumentFileLocation,
103
+ raw.types.InputPeerPhotoFileLocation,]:
104
+ """
105
+ Returns the file location for the media file.
106
+ """
107
+ file_type = file_id.file_type
108
+
109
+ if file_type == FileType.CHAT_PHOTO:
110
+ if file_id.chat_id > 0:
111
+ peer = raw.types.InputPeerUser(
112
+ user_id=file_id.chat_id, access_hash=file_id.chat_access_hash
113
+ )
114
+ else:
115
+ if file_id.chat_access_hash == 0:
116
+ peer = raw.types.InputPeerChat(chat_id=-file_id.chat_id)
117
+ else:
118
+ peer = raw.types.InputPeerChannel(
119
+ channel_id=utils.get_channel_id(file_id.chat_id),
120
+ access_hash=file_id.chat_access_hash,
121
+ )
122
+
123
+ location = raw.types.InputPeerPhotoFileLocation(
124
+ peer=peer,
125
+ volume_id=file_id.volume_id,
126
+ local_id=file_id.local_id,
127
+ big=file_id.thumbnail_source == ThumbnailSource.CHAT_PHOTO_BIG,
128
+ )
129
+ elif file_type == FileType.PHOTO:
130
+ location = raw.types.InputPhotoFileLocation(
131
+ id=file_id.media_id,
132
+ access_hash=file_id.access_hash,
133
+ file_reference=file_id.file_reference,
134
+ thumb_size=file_id.thumbnail_size,
135
+ )
136
+ else:
137
+ location = raw.types.InputDocumentFileLocation(
138
+ id=file_id.media_id,
139
+ access_hash=file_id.access_hash,
140
+ file_reference=file_id.file_reference,
141
+ thumb_size=file_id.thumbnail_size,
142
+ )
143
+ return location
144
+
145
+ async def yield_file(
146
+ self,
147
+ file_id: FileId,
148
+ index: int,
149
+ offset: int,
150
+ first_part_cut: int,
151
+ last_part_cut: int,
152
+ part_count: int,
153
+ chunk_size: int,
154
+ ) -> Union[str, None]:
155
+ """
156
+ Custom generator that yields the bytes of the media file.
157
+ Modded from <https://github.com/eyaadh/megadlbot_oss/blob/master/mega/telegram/utils/custom_download.py#L20>
158
+ Thanks to Eyaadh <https://github.com/eyaadh>
159
+ """
160
+ client = self.client
161
+ work_loads[index] += 1
162
+ logging.debug(f"Starting to yielding file with client {index}.")
163
+ media_session = await self.generate_media_session(client, file_id)
164
+
165
+ current_part = 1
166
+
167
+ location = await self.get_location(file_id)
168
+
169
+ try:
170
+ r = await media_session.invoke(
171
+ raw.functions.upload.GetFile(
172
+ location=location, offset=offset, limit=chunk_size
173
+ ),
174
+ )
175
+ if isinstance(r, raw.types.upload.File):
176
+ while True:
177
+ chunk = r.bytes
178
+ if not chunk:
179
+ break
180
+ elif part_count == 1:
181
+ yield chunk[first_part_cut:last_part_cut]
182
+ elif current_part == 1:
183
+ yield chunk[first_part_cut:]
184
+ elif current_part == part_count:
185
+ yield chunk[:last_part_cut]
186
+ else:
187
+ yield chunk
188
+
189
+ current_part += 1
190
+ offset += chunk_size
191
+
192
+ if current_part > part_count:
193
+ break
194
+
195
+ r = await media_session.invoke(
196
+ raw.functions.upload.GetFile(
197
+ location=location, offset=offset, limit=chunk_size
198
+ ),
199
+ )
200
+ except (TimeoutError, AttributeError):
201
+ pass
202
+ finally:
203
+ logging.debug(f"Finished yielding file with {current_part} parts.")
204
+ work_loads[index] -= 1
205
+
206
+
207
+ async def clean_cache(self) -> None:
208
+ """
209
+ function to clean the cache to reduce memory usage
210
+ """
211
+ while True:
212
+ await asyncio.sleep(self.clean_timer)
213
+ self.cached_file_ids.clear()
214
+ logging.debug("Cleaned the cache")
FileStream/utils/database.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pymongo
2
+ import time
3
+ import motor.motor_asyncio
4
+ from bson.objectid import ObjectId
5
+ from bson.errors import InvalidId
6
+ from FileStream.server.exceptions import FIleNotFound
7
+
8
+ class Database:
9
+ def __init__(self, uri, database_name):
10
+ self._client = motor.motor_asyncio.AsyncIOMotorClient(uri)
11
+ self.db = self._client[database_name]
12
+ self.col = self.db.users
13
+ self.black = self.db.blacklist
14
+ self.file = self.db.file
15
+
16
+ #---------------------[ NEW USER ]---------------------#
17
+ def new_user(self, id):
18
+ return dict(
19
+ id=id,
20
+ join_date=time.time(),
21
+ agreed_to_tos=False,
22
+ Links=0,
23
+ Plan="Free"
24
+ )
25
+
26
+ # ---------------------[ ADD USER ]---------------------#
27
+ async def add_user(self, id):
28
+ user = self.new_user(id)
29
+ await self.col.insert_one(user)
30
+
31
+ # ---------------------[ GET USER ]---------------------#
32
+ async def get_user(self, id):
33
+ user = await self.col.find_one({'id': int(id)})
34
+ return user
35
+
36
+ # ---------------------[ CHECK USER ]---------------------#
37
+ async def total_users_count(self):
38
+ count = await self.col.count_documents({})
39
+ return count
40
+
41
+ async def get_all_users(self):
42
+ all_users = self.col.find({})
43
+ return all_users
44
+
45
+ # ---------------------[ REMOVE USER ]---------------------#
46
+ async def delete_user(self, user_id):
47
+ await self.col.delete_many({'id': int(user_id)})
48
+
49
+ # ---------------------[ BAN, UNBAN USER ]---------------------#
50
+ def black_user(self, id):
51
+ return dict(
52
+ id=id,
53
+ ban_date=time.time()
54
+ )
55
+
56
+ async def ban_user(self, id):
57
+ user = self.black_user(id)
58
+ await self.black.insert_one(user)
59
+
60
+ async def unban_user(self, id):
61
+ await self.black.delete_one({'id': int(id)})
62
+
63
+ async def is_user_banned(self, id):
64
+ user = await self.black.find_one({'id': int(id)})
65
+ return True if user else False
66
+
67
+ async def total_banned_users_count(self):
68
+ count = await self.black.count_documents({})
69
+ return count
70
+
71
+ # ---------------------[ ADD FILE TO DB ]---------------------#
72
+ async def add_file(self, file_info):
73
+ file_info["time"] = time.time()
74
+ fetch_old = await self.get_file_by_fileuniqueid(file_info["user_id"], file_info["file_unique_id"])
75
+ if fetch_old:
76
+ return fetch_old["_id"]
77
+ await self.count_links(file_info["user_id"], "+")
78
+ return (await self.file.insert_one(file_info)).inserted_id
79
+
80
+ # ---------------------[ FIND FILE IN DB ]---------------------#
81
+ async def find_files(self, user_id, range):
82
+ user_files=self.file.find({"user_id": user_id})
83
+ user_files.skip(range[0] - 1)
84
+ user_files.limit(range[1] - range[0] + 1)
85
+ user_files.sort('_id', pymongo.DESCENDING)
86
+ total_files = await self.file.count_documents({"user_id": user_id})
87
+ return user_files, total_files
88
+
89
+ async def get_file(self, _id):
90
+ try:
91
+ file_info=await self.file.find_one({"_id": ObjectId(_id)})
92
+ if not file_info:
93
+ raise FIleNotFound
94
+ return file_info
95
+ except InvalidId:
96
+ raise FIleNotFound
97
+
98
+ async def get_file_by_fileuniqueid(self, id, file_unique_id, many=False):
99
+ if many:
100
+ return self.file.find({"file_unique_id": file_unique_id})
101
+ else:
102
+ file_info=await self.file.find_one({"user_id": id, "file_unique_id": file_unique_id})
103
+ if file_info:
104
+ return file_info
105
+ return False
106
+
107
+ # ---------------------[ TOTAL FILES ]---------------------#
108
+ async def total_files(self, id=None):
109
+ if id:
110
+ return await self.file.count_documents({"user_id": id})
111
+ return await self.file.count_documents({})
112
+
113
+ # ---------------------[ DELETE FILES ]---------------------#
114
+ async def delete_one_file(self, _id):
115
+ await self.file.delete_one({'_id': ObjectId(_id)})
116
+
117
+ # ---------------------[ UPDATE FILES ]---------------------#
118
+ async def update_file_ids(self, _id, file_ids: dict):
119
+ await self.file.update_one({"_id": ObjectId(_id)}, {"$set": {"file_ids": file_ids}})
120
+
121
+ # ---------------------[ PAID SYS ]---------------------#
122
+ async def link_available(self, id):
123
+ user = await self.col.find_one({"id": id})
124
+ if user.get("Plan") == "Plus":
125
+ return "Plus"
126
+ elif user.get("Plan") == "Free":
127
+ files = await self.file.count_documents({"user_id": id})
128
+ if files < 11:
129
+ return True
130
+ return False
131
+
132
+ async def count_links(self, id, operation: str):
133
+ if operation == "-":
134
+ await self.col.update_one({"id": id}, {"$inc": {"Links": -1}})
135
+ elif operation == "+":
136
+ await self.col.update_one({"id": id}, {"$inc": {"Links": 1}})
FileStream/utils/file_properties.py ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import logging
3
+ from datetime import datetime
4
+ from pyrogram import Client
5
+ from typing import Any, Optional
6
+
7
+ from pyrogram.enums import ParseMode, ChatType
8
+ from pyrogram.types import Message
9
+ from pyrogram.file_id import FileId
10
+ from FileStream.bot import FileStream
11
+ from FileStream.utils.database import Database
12
+ from FileStream.config import Telegram, Server
13
+
14
+ db = Database(Telegram.DATABASE_URL, Telegram.SESSION_NAME)
15
+
16
+
17
+ async def get_file_ids(client: Client | bool, db_id: str, multi_clients, message) -> Optional[FileId]:
18
+ logging.debug("Starting of get_file_ids")
19
+ file_info = await db.get_file(db_id)
20
+ if (not "file_ids" in file_info) or not client:
21
+ logging.debug("Storing file_id of all clients in DB")
22
+ log_msg = await send_file(FileStream, db_id, file_info['file_id'], message)
23
+ await db.update_file_ids(db_id, await update_file_id(log_msg.id, multi_clients))
24
+ logging.debug("Stored file_id of all clients in DB")
25
+ if not client:
26
+ return
27
+ file_info = await db.get_file(db_id)
28
+
29
+ file_id_info = file_info.setdefault("file_ids", {})
30
+ if not str(client.id) in file_id_info:
31
+ logging.debug("Storing file_id in DB")
32
+ log_msg = await send_file(FileStream, db_id, file_info['file_id'], message)
33
+ msg = await client.get_messages(Telegram.LOG_CHANNEL, log_msg.id)
34
+ media = get_media_from_message(msg)
35
+ file_id_info[str(client.id)] = getattr(media, "file_id", "")
36
+ await db.update_file_ids(db_id, file_id_info)
37
+ logging.debug("Stored file_id in DB")
38
+
39
+ logging.debug("Middle of get_file_ids")
40
+ file_id = FileId.decode(file_id_info[str(client.id)])
41
+ setattr(file_id, "file_size", file_info['file_size'])
42
+ setattr(file_id, "mime_type", file_info['mime_type'])
43
+ setattr(file_id, "file_name", file_info['file_name'])
44
+ setattr(file_id, "unique_id", file_info['file_unique_id'])
45
+ logging.debug("Ending of get_file_ids")
46
+ return file_id
47
+
48
+
49
+ def get_media_from_message(message: "Message") -> Any:
50
+ media_types = (
51
+ "audio",
52
+ "document",
53
+ "photo",
54
+ "sticker",
55
+ "animation",
56
+ "video",
57
+ "voice",
58
+ "video_note",
59
+ )
60
+ for attr in media_types:
61
+ media = getattr(message, attr, None)
62
+ if media:
63
+ return media
64
+
65
+
66
+ def get_media_file_size(m):
67
+ media = get_media_from_message(m)
68
+ return getattr(media, "file_size", "None")
69
+
70
+
71
+ def get_name(media_msg: Message | FileId) -> str:
72
+ if isinstance(media_msg, Message):
73
+ media = get_media_from_message(media_msg)
74
+ file_name = getattr(media, "file_name", "")
75
+
76
+ elif isinstance(media_msg, FileId):
77
+ file_name = getattr(media_msg, "file_name", "")
78
+
79
+ if not file_name:
80
+ if isinstance(media_msg, Message) and media_msg.media:
81
+ media_type = media_msg.media.value
82
+ elif media_msg.file_type:
83
+ media_type = media_msg.file_type.name.lower()
84
+ else:
85
+ media_type = "file"
86
+
87
+ formats = {
88
+ "photo": "jpg", "audio": "mp3", "voice": "ogg",
89
+ "video": "mp4", "animation": "mp4", "video_note": "mp4",
90
+ "sticker": "webp"
91
+ }
92
+
93
+ ext = formats.get(media_type)
94
+ ext = "." + ext if ext else ""
95
+
96
+ date = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
97
+ file_name = f"{media_type}-{date}{ext}"
98
+
99
+ return file_name
100
+
101
+
102
+ def get_file_info(message):
103
+ media = get_media_from_message(message)
104
+ if message.chat.type == ChatType.PRIVATE:
105
+ user_idx = message.from_user.id
106
+ else:
107
+ user_idx = message.chat.id
108
+ return {
109
+ "user_id": user_idx,
110
+ "file_id": getattr(media, "file_id", ""),
111
+ "file_unique_id": getattr(media, "file_unique_id", ""),
112
+ "file_name": get_name(message),
113
+ "file_size": getattr(media, "file_size", 0),
114
+ "mime_type": getattr(media, "mime_type", "None/unknown")
115
+ }
116
+
117
+
118
+ async def update_file_id(msg_id, multi_clients):
119
+ file_ids = {}
120
+ for client_id, client in multi_clients.items():
121
+ log_msg = await client.get_messages(Telegram.LOG_CHANNEL, msg_id)
122
+ media = get_media_from_message(log_msg)
123
+ file_ids[str(client.id)] = getattr(media, "file_id", "")
124
+
125
+ return file_ids
126
+
127
+
128
+ async def send_file(client: Client, db_id, file_id: str, message):
129
+ file_caption = message.caption
130
+ if file_caption is None:
131
+ file_caption = 'okay'
132
+ log_msg = await client.send_cached_media(chat_id=Telegram.LOG_CHANNEL, file_id=file_id,
133
+ caption=f'**{file_caption}**')
134
+
135
+ if message.chat.type == ChatType.PRIVATE:
136
+ await log_msg.reply_text(
137
+ text=f"reguested by : [{message.from_user.first_name}](tg://user?id={message.from_user.id})\nuser id : `{message.from_user.id}`\n file id : `{db_id}`\nStream Link : `https://telegbotnavpan2.onrender.com/watch/{db_id}`\nDownload Link : `https://telegbotnavpan2.onrender.com/dl/{db_id}`",
138
+ disable_web_page_preview=True, parse_mode=ParseMode.MARKDOWN, quote=True)
139
+ else:
140
+ await log_msg.reply_text(
141
+ text=f"file id : `{db_id}`\nStream Link : `https://telegbotnavpan2.onrender.com/watch/{db_id}`\nDownload Link : `https://telegbotnavpan2.onrender.com/dl/{db_id}`",
142
+ disable_web_page_preview=True, parse_mode=ParseMode.MARKDOWN, quote=True)
143
+
144
+ return log_msg
145
+ # return await client.send_cached_media(Telegram.BIN_CHANNEL, file_id)
146
+
FileStream/utils/human_readable.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ def humanbytes(size):
2
+ if not size:
3
+ return ""
4
+ power = 2**10
5
+ n = 0
6
+ Dic_powerN = {0: ' ', 1: 'Ki', 2: 'Mi', 3: 'Gi', 4: 'Ti'}
7
+ while size > power:
8
+ size /= power
9
+ n += 1
10
+ return str(round(size, 2)) + " " + Dic_powerN[n] + 'B'
FileStream/utils/keepalive.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import logging
3
+ import aiohttp
4
+ import traceback
5
+ from FileStream.config import Server
6
+
7
+ async def ping_server():
8
+ sleep_time = Server.PING_INTERVAL
9
+ while True:
10
+ await asyncio.sleep(sleep_time)
11
+ try:
12
+ async with aiohttp.ClientSession(
13
+ timeout=aiohttp.ClientTimeout(total=10)
14
+ ) as session:
15
+ async with session.get(Server.URL) as resp:
16
+ logging.info("Pinged server with response: {}".format(resp.status))
17
+ except TimeoutError:
18
+ logging.warning("Couldn't connect to the site URL..!")
19
+ except Exception:
20
+ traceback.print_exc()
FileStream/utils/render_template.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import aiohttp
2
+ import aiofiles
3
+ import urllib.parse
4
+ from FileStream.config import Telegram, Server
5
+ from FileStream.utils.database import Database
6
+ from FileStream.utils.human_readable import humanbytes
7
+ db = Database(Telegram.DATABASE_URL, Telegram.SESSION_NAME)
8
+
9
+ async def render_page(db_id):
10
+ file_data=await db.get_file(db_id)
11
+ src = urllib.parse.urljoin(Server.URL, f'dl/{file_data["_id"]}')
12
+
13
+ if str((file_data['mime_type']).split('/')[0].strip()) == 'video':
14
+ async with aiofiles.open('FileStream/template/stream.html') as r:
15
+ heading = 'Watch {}'.format(file_data['file_name'])
16
+ html_template = await r.read()
17
+ html = html_template.replace('streamMediaLink', src).replace('streamHeading', heading)
18
+ else:
19
+ async with aiofiles.open('FileStream/template/dl.html') as r:
20
+ async with aiohttp.ClientSession() as s:
21
+ async with s.get(src) as u:
22
+ heading = 'Download {}'.format(file_data['file_name'])
23
+ file_size = humanbytes(int(u.headers.get('Content-Length')))
24
+ html = (await r.read()) % (heading, file_data['file_name'], src, file_size)
25
+ return html
FileStream/utils/time_format.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
FileStream/utils/translation.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
2
+ from FileStream.config import Telegram
3
+
4
+ class LANG(object):
5
+
6
+ START_TEXT = """
7
+ <b>👋 Hᴇʏ, </b>{}\n
8
+ <b>I'ᴍ ᴛᴇʟᴇɢʀᴀᴍ ғɪʟᴇs sᴛʀᴇᴀᴍɪɴɢ ʙᴏᴛ ᴀs ᴡᴇʟʟ ᴅɪʀᴇᴄᴛ ʟɪɴᴋs ɢᴇɴᴇʀᴀᴛᴏʀ</b>\n
9
+ <b>ᴡᴏʀᴋɪɴɢ ᴏɴ ᴄʜᴀɴɴᴇʟs ᴀɴᴅ ᴘʀɪᴠᴀᴛᴇ ᴄʜᴀᴛ</b>\n
10
+ <b>💕 @{}</b>\n"""
11
+
12
+ HELP_TEXT = """
13
+ <b>- ᴀᴅᴅ ᴍᴇ ᴀs ᴀɴ ᴀᴅᴍɪɴ ᴏɴ ᴛʜᴇ ᴄʜᴀɴɴᴇʟ</b>
14
+ <b>- sᴇɴᴅ ᴍᴇ ᴀɴʏ ᴅᴏᴄᴜᴍᴇɴᴛ ᴏʀ ᴍᴇᴅɪᴀ</b>
15
+ <b>- ɪ'ʟʟ ᴘʀᴏᴠɪᴅᴇ sᴛʀᴇᴀᴍᴀʙʟᴇ ʟɪɴᴋ</b>\n
16
+ <b>🔞 ᴀᴅᴜʟᴛ ᴄᴏɴᴛᴇɴᴛ sᴛʀɪᴄᴛʟʏ ᴘʀᴏʜɪʙɪᴛᴇᴅ.</b>\n
17
+ <i><b> ʀᴇᴘᴏʀᴛ ʙᴜɢs ᴛᴏ <a href='https://telegram.me/AvishkarPatil'>ᴅᴇᴠᴇʟᴏᴘᴇʀ</a></b></i>"""
18
+
19
+ ABOUT_TEXT = """
20
+ <b>⚜ ᴍʏ ɴᴀᴍᴇ : {}</b>\n
21
+ <b>✦ ᴠᴇʀsɪᴏɴ : {}</b>
22
+ <b>✦ ᴜᴘᴅᴀᴛᴇᴅ ᴏɴ : 19-November-2023</b>
23
+ <b>✦ ᴅᴇᴠᴇʟᴏᴘᴇʀ : <a href='https://telegram.me/AvishkarPatil'>Avishkar Patil</a></b>\n
24
+ """
25
+
26
+ STREAM_TEXT = """
27
+ <i><u>𝗬𝗼𝘂𝗿 𝗟𝗶𝗻𝗸 𝗚𝗲𝗻𝗲𝗿𝗮𝘁𝗲𝗱 !</u></i>\n
28
+ <b>📂 Fɪʟᴇ ɴᴀᴍᴇ :</b> <b>{}</b>\n
29
+ <b>📦 Fɪʟᴇ ꜱɪᴢᴇ :</b> <code>{}</code>\n
30
+ <b>📥 Dᴏᴡɴʟᴏᴀᴅ :</b> <code>{}</code>\n
31
+ <b>🖥 Wᴀᴛᴄʜ :</b> <code>{}</code>\n
32
+ <b>🔗 Sʜᴀʀᴇ :</b> <code>{}</code>\n"""
33
+
34
+ STREAM_TEXT_X = """
35
+ <i><u>𝗬𝗼𝘂𝗿 𝗟𝗶𝗻𝗸 𝗚𝗲𝗻𝗲𝗿𝗮𝘁𝗲𝗱 !</u></i>\n
36
+ <b>📂 Fɪʟᴇ ɴᴀᴍᴇ :</b> <b>{}</b>\n
37
+ <b>📦 Fɪʟᴇ ꜱɪᴢᴇ :</b> <code>{}</code>\n
38
+ <b>📥 Dᴏᴡɴʟᴏᴀᴅ :</b> <code>{}</code>\n
39
+ <b>🔗 Sʜᴀʀᴇ :</b> <code>{}</code>\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
+ )
Procfile ADDED
@@ -0,0 +1 @@
 
 
1
+ web: python -m FileStream
app.json ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "FileStreamBot",
3
+ "description": "A Telegram Bot to Convert Telegram Files To Direct & Streamable Links.",
4
+ "keywords": [
5
+ "telegram",
6
+ "files",
7
+ "stream"
8
+ ],
9
+ "repository": "https://github.com/avipatilpro/FileStreamBot/",
10
+ "success_url": "/",
11
+ "logo": "https://i.ibb.co/ZJzJ9Hq/link-3x.png",
12
+ "env": {
13
+ "ENV": {
14
+ "description": "Set this to True if you don't want to crash the bot",
15
+ "value": "True"
16
+ },
17
+ "API_ID": {
18
+ "description": "Get this value from https://my.telegram.org"
19
+ },
20
+ "API_HASH": {
21
+ "description": "Get this value from https://my.telegram.org"
22
+ },
23
+ "BOT_TOKEN": {
24
+ "description": "Get this value from @BotFather"
25
+ },
26
+ "LOG_CHANNEL": {
27
+ "description": "The BIN Channel ID. Read the readme for more info about this var"
28
+ },
29
+ "OWNER_ID": {
30
+ "description": "Your Telegram User ID as Owner"
31
+ },
32
+ "DATABASE_URL": {
33
+ "description": "MongoDB URI for saving User Data and Files List created by user."
34
+ },
35
+ "AUTH_USERS": {
36
+ "description": "Put IDs of Banned Channels where bot will not work. You can add multiple IDs & separate with Space.",
37
+ "required": false
38
+ },
39
+ "UPDATES_CHANNEL": {
40
+ "description": "Channel Username without `@` to set channel as Update Channel",
41
+ "required": false
42
+ },
43
+ "FORCE_UPDATES_CHANNEL": {
44
+ "description": "Set to True, so every user have to Join update channel to use the bot.",
45
+ "required": false
46
+ },
47
+ "SLEEP_THRESHOLD": {
48
+ "description": "Set global flood wait threshold, auto-retry requests under 60s. ",
49
+ "required": false
50
+ },
51
+ "PING_INTERVAL": {
52
+ "description": " Heroku ping interval (ms), defaults to 1200 (20 minutes).",
53
+ "required": false
54
+ },
55
+ "WORKERS": {
56
+ "description": "No. of workers that is to be assigned.",
57
+ "required": false
58
+ },
59
+ "PORT": {
60
+ "description": "The port that you want your webapp to be listened to",
61
+ "required": false
62
+ },
63
+ "NO_PORT": {
64
+ "description": "If you don't want your port to be displayed. Set True or False",
65
+ "value": "True",
66
+ "required": false
67
+ },
68
+ "HAS_SSL": {
69
+ "description": "(can be either True or False) If you want the generated links in https format.",
70
+ "value": "True",
71
+ "required": false
72
+ },
73
+ "KEEP_ALIVE": {
74
+ "description": "Self-ping server every PING_INTERVAL sec to avoid sleeping",
75
+ "value": "True",
76
+ "required": false
77
+ },
78
+ "BIND_ADRESS": {
79
+ "description": "Your server bind adress. Defauls to 0.0.0.0",
80
+ "required": false
81
+ },
82
+ "FQDN": {
83
+ "description": "Heroku app Link without http/s, you can set later. its required",
84
+ "required": false
85
+ },
86
+ "SESSION_NAME": {
87
+ "description": "Name for the Database created on your MongoDB. Defaults to FileStream",
88
+ "required": false
89
+ },
90
+ "IMAGE_FILEID": {
91
+ "description": "To set Image at /files command. Defaults to pre-set image.",
92
+ "required": false
93
+ },
94
+ "MODE": {
95
+ "description": "Should be set to `secondary` if you only want to use the server for serving files.",
96
+ "required": false
97
+ }
98
+ },
99
+ "buildpacks": [
100
+ {
101
+ "url": "heroku/python"
102
+ }
103
+ ],
104
+ "stack": "heroku-22",
105
+ "formation": {
106
+ "web": {
107
+ "quantity": 1,
108
+ "size": "eco"
109
+ }
110
+ }
111
+ }
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ aiohttp
2
+ pyrogram
3
+ python-dotenv
4
+ tgcrypto
5
+ motor
6
+ aiofiles
7
+ dnspython
8
+ requests