Updated ?

Nothing new but, looks like something changed ;) !
This commit is contained in:
Avi Patil
2023-12-10 03:22:07 +05:30
parent 1c2e6f31f5
commit 61b7dac2b7
44 changed files with 2197 additions and 1125 deletions

2
.gitattributes vendored
View File

@@ -1,2 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

11
.gitignore vendored
View File

@@ -115,3 +115,14 @@ dmypy.json
#session files
*.session
*.session-journal
.env
test.py
/test
streambot.log.*
.idea/workspace.xml
*.xml
*.xml
.idea/workspace.xml

View File

@@ -1,2 +0,0 @@
language = "bash"
run = "pip3 install -r requirements.txt; python3 -m WebStreamer"

11
Dockerfile Normal file
View File

@@ -0,0 +1,11 @@
FROM python:3.11
WORKDIR /app
COPY . /app
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "-m", "FileStream"]

5
FileStream/__init__.py Normal file
View File

@@ -0,0 +1,5 @@
import time
__version__ = "1.0.1"
StartTime = time.time()

82
FileStream/__main__.py Normal file
View File

@@ -0,0 +1,82 @@
import sys
import asyncio
import logging
import traceback
import logging.handlers as handlers
from .config import Telegram, Server
from aiohttp import web
from pyrogram import idle
from FileStream.bot import FileStream
from FileStream.server import web_server
from FileStream.utils import ping_server
from FileStream.bot.clients import initialize_clients
logging.basicConfig(
level=logging.INFO,
datefmt="%d/%m/%Y %H:%M:%S",
format='[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s',
handlers=[logging.StreamHandler(stream=sys.stdout),
handlers.RotatingFileHandler("streambot.log", mode="a", maxBytes=104857600, backupCount=2, encoding="utf-8")],)
logging.getLogger("aiohttp").setLevel(logging.ERROR)
logging.getLogger("pyrogram").setLevel(logging.ERROR)
logging.getLogger("aiohttp.web").setLevel(logging.ERROR)
server = web.AppRunner(web_server())
loop = asyncio.get_event_loop()
async def start_services():
print()
if Telegram.SECONDARY:
print("------------------ Starting as Secondary Server ------------------")
else:
print("------------------- Starting as Primary Server -------------------")
print()
print("-------------------- Initializing Telegram Bot --------------------")
await FileStream.start()
bot_info = await FileStream.get_me()
FileStream.id = bot_info.id
FileStream.username = bot_info.username
FileStream.fname=bot_info.first_name
print("------------------------------ DONE ------------------------------")
print()
print("---------------------- Initializing Clients ----------------------")
await initialize_clients()
print("------------------------------ DONE ------------------------------")
if Server.KEEP_ALIVE:
print("------------------ Starting Keep Alive Service ------------------")
print()
asyncio.create_task(ping_server())
print()
print("--------------------- Initializing Web Server ---------------------")
await server.setup()
await web.TCPSite(server, Server.BIND_ADDRESS, Server.PORT).start()
print("------------------------------ DONE ------------------------------")
print()
print("------------------------- Service Started -------------------------")
print(" bot =>> {}".format(bot_info.first_name))
if bot_info.dc_id:
print(" DC ID =>> {}".format(str(bot_info.dc_id)))
print(" URL =>> {}".format(Server.URL))
print("------------------------------------------------------------------")
await idle()
async def cleanup():
await server.cleanup()
await FileStream.stop()
if __name__ == "__main__":
try:
loop.run_until_complete(start_services())
except KeyboardInterrupt:
pass
except Exception as err:
logging.error(traceback.format_exc())
finally:
loop.run_until_complete(cleanup())
loop.stop()
print("------------------------ Stopped Services ------------------------")

View File

@@ -0,0 +1,25 @@
from ..config import Telegram
from pyrogram import Client
if Telegram.SECONDARY:
plugins=None
no_updates=True
else:
plugins={"root": "FileStream/bot/plugins"}
no_updates=None
FileStream = Client(
name="FileStream",
api_id=Telegram.API_ID,
api_hash=Telegram.API_HASH,
workdir="FileStream",
plugins=plugins,
bot_token=Telegram.BOT_TOKEN,
sleep_threshold=Telegram.SLEEP_THRESHOLD,
workers=Telegram.WORKERS,
no_updates=no_updates
)
multi_clients = {}
work_loads = {}

59
FileStream/bot/clients.py Normal file
View File

@@ -0,0 +1,59 @@
import asyncio
import logging
from os import environ
from ..config import Telegram
from pyrogram import Client
from . import multi_clients, work_loads, FileStream
async def initialize_clients():
all_tokens = dict(
(c + 1, t)
for c, (_, t) in enumerate(
filter(
lambda n: n[0].startswith("MULTI_TOKEN"), sorted(environ.items())
)
)
)
if not all_tokens:
multi_clients[0] = FileStream
work_loads[0] = 0
print("No additional clients found, using default client")
return
async def start_client(client_id, token):
try:
if len(token) >= 100:
session_string=token
bot_token=None
print(f'Starting Client - {client_id} Using Session String')
else:
session_string=None
bot_token=token
print(f'Starting Client - {client_id} Using Bot Token')
if client_id == len(all_tokens):
await asyncio.sleep(2)
print("This will take some time, please wait...")
client = await Client(
name=str(client_id),
api_id=Telegram.API_ID,
api_hash=Telegram.API_HASH,
bot_token=bot_token,
sleep_threshold=Telegram.SLEEP_THRESHOLD,
no_updates=True,
session_string=session_string,
in_memory=True,
).start()
client.id = (await client.get_me()).id
work_loads[client_id] = 0
return client_id, client
except Exception:
logging.error(f"Failed starting Client - {client_id} Error:", exc_info=True)
clients = await asyncio.gather(*[start_client(i, token) for i, token in all_tokens.items()])
multi_clients.update(dict(clients))
if len(multi_clients) != 1:
Telegram.MULTI_CLIENT = True
print("Multi-Client Mode Enabled")
else:
print("No additional clients were initialized, using default client")

View File

@@ -0,0 +1,160 @@
import os
import time
import string
import random
import asyncio
import aiofiles
import datetime
from FileStream.utils.broadcast_helper import send_msg
from FileStream.utils.database import Database
from FileStream.bot import FileStream
from FileStream.server.exceptions import FIleNotFound
from FileStream.config import Telegram, Server
from pyrogram import filters, Client
from pyrogram.types import Message
from pyrogram.enums.parse_mode import ParseMode
db = Database(Telegram.DATABASE_URL, Telegram.SESSION_NAME)
broadcast_ids = {}
@FileStream.on_message(filters.command("status") & filters.private & filters.user(Telegram.OWNER_ID))
async def sts(c: Client, m: Message):
await m.reply_text(text=f"""**Total Users in DB:** `{await db.total_users_count()}`
**Banned Users in DB:** `{await db.total_banned_users_count()}`
**Total Links Generated: ** `{await db.total_files()}`"""
, parse_mode=ParseMode.MARKDOWN, quote=True)
@FileStream.on_message(filters.command("ban") & filters.private & filters.user(Telegram.OWNER_ID))
async def sts(b, m: Message):
id = m.text.split("/ban ")[-1]
if not await db.is_user_banned(int(id)):
try:
await db.ban_user(int(id))
await db.delete_user(int(id))
await m.reply_text(text=f"`{id}`** is Banned** ", parse_mode=ParseMode.MARKDOWN, quote=True)
if not str(id).startswith('-100'):
await b.send_message(
chat_id=id,
text="**Your Banned to Use The Bot**",
parse_mode=ParseMode.MARKDOWN,
disable_web_page_preview=True
)
except Exception as e:
await m.reply_text(text=f"**something went wrong: {e}** ", parse_mode=ParseMode.MARKDOWN, quote=True)
else:
await m.reply_text(text=f"`{id}`** is Already Banned** ", parse_mode=ParseMode.MARKDOWN, quote=True)
@FileStream.on_message(filters.command("unban") & filters.private & filters.user(Telegram.OWNER_ID))
async def sts(b, m: Message):
id = m.text.split("/unban ")[-1]
if await db.is_user_banned(int(id)):
try:
await db.unban_user(int(id))
await m.reply_text(text=f"`{id}`** is Unbanned** ", parse_mode=ParseMode.MARKDOWN, quote=True)
if not str(id).startswith('-100'):
await b.send_message(
chat_id=id,
text="**Your Unbanned now Use can use The Bot**",
parse_mode=ParseMode.MARKDOWN,
disable_web_page_preview=True
)
except Exception as e:
await m.reply_text(text=f"** something went wrong: {e}**", parse_mode=ParseMode.MARKDOWN, quote=True)
else:
await m.reply_text(text=f"`{id}`** is not Banned** ", parse_mode=ParseMode.MARKDOWN, quote=True)
@FileStream.on_message(filters.command("broadcast") & filters.private & filters.user(Telegram.OWNER_ID) & filters.reply)
async def broadcast_(c, m):
all_users = await db.get_all_users()
broadcast_msg = m.reply_to_message
while True:
broadcast_id = ''.join([random.choice(string.ascii_letters) for i in range(3)])
if not broadcast_ids.get(broadcast_id):
break
out = await m.reply_text(
text=f"Broadcast initiated! You will be notified with log file when all the users are notified."
)
start_time = time.time()
total_users = await db.total_users_count()
done = 0
failed = 0
success = 0
broadcast_ids[broadcast_id] = dict(
total=total_users,
current=done,
failed=failed,
success=success
)
async with aiofiles.open('broadcast.txt', 'w') as broadcast_log_file:
async for user in all_users:
sts, msg = await send_msg(
user_id=int(user['id']),
message=broadcast_msg
)
if msg is not None:
await broadcast_log_file.write(msg)
if sts == 200:
success += 1
else:
failed += 1
if sts == 400:
await db.delete_user(user['id'])
done += 1
if broadcast_ids.get(broadcast_id) is None:
break
else:
broadcast_ids[broadcast_id].update(
dict(
current=done,
failed=failed,
success=success
)
)
try:
await out.edit_text(f"Broadcast Status\n\ncurrent: {done}\nfailed:{failed}\nsuccess: {success}")
except:
pass
if broadcast_ids.get(broadcast_id):
broadcast_ids.pop(broadcast_id)
completed_in = datetime.timedelta(seconds=int(time.time() - start_time))
await asyncio.sleep(3)
await out.delete()
if failed == 0:
await m.reply_text(
text=f"broadcast completed in `{completed_in}`\n\nTotal users {total_users}.\nTotal done {done}, {success} success and {failed} failed.",
quote=True
)
else:
await m.reply_document(
document='broadcast.txt',
caption=f"broadcast completed in `{completed_in}`\n\nTotal users {total_users}.\nTotal done {done}, {success} success and {failed} failed.",
quote=True
)
os.remove('broadcast.txt')
@FileStream.on_message(filters.command("del") & filters.private & filters.user(Telegram.OWNER_ID))
async def sts(c: Client, m: Message):
file_id = m.text.split(" ")[-1]
try:
file_info = await db.get_file(file_id)
except FIleNotFound:
await m.reply_text(
text=f"**File Already Deleted**",
quote=True
)
return
await db.delete_one_file(file_info['_id'])
await db.count_links(file_info['user_id'], "-")
await m.reply_text(
text=f"**Fɪʟᴇ Dᴇʟᴇᴛᴇᴅ Sᴇssғʟʟʏ !** ",
quote=True
)

View File

@@ -0,0 +1,198 @@
import datetime
import math
from FileStream import __version__
from FileStream.bot import FileStream
from FileStream.config import Telegram, Server
from FileStream.utils.translation import LANG, BUTTON
from FileStream.utils.bot_utils import gen_link
from FileStream.utils.database import Database
from FileStream.utils.human_readable import humanbytes
from FileStream.server.exceptions import FIleNotFound
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
from pyrogram.file_id import FileId, FileType, PHOTO_TYPES
from pyrogram.enums.parse_mode import ParseMode
db = Database(Telegram.DATABASE_URL, Telegram.SESSION_NAME)
#---------------------[ START CMD ]---------------------#
@FileStream.on_callback_query()
async def cb_data(bot, update: CallbackQuery):
usr_cmd = update.data.split("_")
if usr_cmd[0] == "home":
await update.message.edit_text(
text=LANG.START_TEXT.format(update.from_user.mention, FileStream.username),
disable_web_page_preview=True,
reply_markup=BUTTON.START_BUTTONS
)
elif usr_cmd[0] == "help":
await update.message.edit_text(
text=LANG.HELP_TEXT.format(Telegram.OWNER_ID),
disable_web_page_preview=True,
reply_markup=BUTTON.HELP_BUTTONS
)
elif usr_cmd[0] == "about":
await update.message.edit_text(
text=LANG.ABOUT_TEXT.format(FileStream.fname, __version__),
disable_web_page_preview=True,
reply_markup=BUTTON.ABOUT_BUTTONS
)
#---------------------[ MY FILES CMD ]---------------------#
elif usr_cmd[0] == "N/A":
await update.answer("N/A", True)
elif usr_cmd[0] == "close":
await update.message.delete()
elif usr_cmd[0] == "msgdelete":
await update.message.edit_caption(
caption= "**Cɴғɪʀᴍ ʏ ᴡᴀɴᴛ ᴛᴏ ᴅᴇʟᴇᴛᴇ ᴛʜᴇ Fɪʟᴇ**\n\n",
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]}")]])
)
elif usr_cmd[0] == "msgdelyes":
await delete_user_file(usr_cmd[1], int(usr_cmd[2]), update)
return
elif usr_cmd[0] == "msgdelpvt":
await update.message.edit_caption(
caption= "**Cɴғɪʀᴍ ʏ ᴡᴀɴᴛ ᴛᴏ ᴅᴇʟᴇᴛᴇ ᴛʜᴇ Fɪʟᴇ**\n\n",
reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("ʏᴇs", callback_data=f"msgdelpvtyes_{usr_cmd[1]}"), InlineKeyboardButton("ɴᴏ", callback_data=f"mainstream_{usr_cmd[1]}")]])
)
elif usr_cmd[0] == "msgdelpvtyes":
await delete_user_filex(usr_cmd[1], update)
return
elif usr_cmd[0] == "mainstream":
_id = usr_cmd[1]
reply_markup, stream_text = await gen_link(_id=_id)
await update.message.edit_text(
text=stream_text,
parse_mode=ParseMode.HTML,
disable_web_page_preview=True,
reply_markup=reply_markup,
)
elif usr_cmd[0] == "userfiles":
file_list, total_files = await gen_file_list_button(int(usr_cmd[1]), update.from_user.id)
await update.message.edit_caption(
caption="Total files: {}".format(total_files),
reply_markup=InlineKeyboardMarkup(file_list)
)
elif usr_cmd[0] == "myfile":
await gen_file_menu(usr_cmd[1], usr_cmd[2], update)
return
elif usr_cmd[0] == "sendfile":
myfile = await db.get_file(usr_cmd[1])
file_name = myfile['file_name']
await update.answer(f"Sending File {file_name}")
await update.message.reply_cached_media(myfile['file_id'], caption=f'**{file_name}**')
else:
await update.message.delete()
#---------------------[ MY FILES FUNC ]---------------------#
async def gen_file_list_button(file_list_no: int, user_id: int):
file_range=[file_list_no*10-10+1, file_list_no*10]
user_files, total_files=await db.find_files(user_id, file_range)
file_list=[]
async for x in user_files:
file_list.append([InlineKeyboardButton(x["file_name"], callback_data=f"myfile_{x['_id']}_{file_list_no}")])
if total_files > 10:
file_list.append(
[InlineKeyboardButton("", callback_data="{}".format("userfiles_"+str(file_list_no-1) if file_list_no > 1 else 'N/A')),
InlineKeyboardButton(f"{file_list_no}/{math.ceil(total_files/10)}", callback_data="N/A"),
InlineKeyboardButton("", callback_data="{}".format("userfiles_"+str(file_list_no+1) if total_files > file_list_no*10 else 'N/A'))]
)
if not file_list:
file_list.append(
[InlineKeyboardButton("ᴇᴍᴘᴛʏ", callback_data="N/A")])
file_list.append([InlineKeyboardButton("ʟsᴇ", callback_data="close")])
return file_list, total_files
async def gen_file_menu(_id, file_list_no, update: CallbackQuery):
try:
myfile_info=await db.get_file(_id)
except FIleNotFound:
await update.answer("File Not Found")
return
file_id=FileId.decode(myfile_info['file_id'])
if file_id.file_type in PHOTO_TYPES:
file_type = "Image"
elif file_id.file_type == FileType.VOICE:
file_type = "Voice"
elif file_id.file_type in (FileType.VIDEO, FileType.ANIMATION, FileType.VIDEO_NOTE):
file_type = "Video"
elif file_id.file_type == FileType.DOCUMENT:
file_type = "Document"
elif file_id.file_type == FileType.STICKER:
file_type = "Sticker"
elif file_id.file_type == FileType.AUDIO:
file_type = "Audio"
else:
file_type = "Unknown"
page_link = f"{Server.URL}watch/{myfile_info['_id']}"
stream_link = f"{Server.URL}dl/{myfile_info['_id']}"
if "video" in file_type.lower():
MYFILES_BUTTONS = InlineKeyboardMarkup(
[
[InlineKeyboardButton("sᴛʀᴇᴀᴍ", url=page_link), InlineKeyboardButton("ᴅᴏᴡɴʟᴏᴀᴅ", url=stream_link)],
[InlineKeyboardButton("ɢᴇᴛ ғɪʟᴇ", callback_data=f"sendfile_{myfile_info['_id']}"),
InlineKeyboardButton("ʀᴇᴠᴏᴋᴇ ғɪʟᴇ", callback_data=f"msgdelete_{myfile_info['_id']}_{file_list_no}")],
[InlineKeyboardButton("ʙᴀᴄᴋ", callback_data="userfiles_{}".format(file_list_no))]
]
)
else:
MYFILES_BUTTONS = InlineKeyboardMarkup(
[
[InlineKeyboardButton("ᴅᴏᴡɴʟᴏᴀᴅ", url=stream_link)],
[InlineKeyboardButton("ɢᴇᴛ ғɪʟᴇ", callback_data=f"sendfile_{myfile_info['_id']}"),
InlineKeyboardButton("ʀᴇᴠᴏᴋᴇ ғɪʟᴇ", callback_data=f"msgdelete_{myfile_info['_id']}_{file_list_no}")],
[InlineKeyboardButton("ʙᴀᴄᴋ", callback_data="userfiles_{}".format(file_list_no))]
]
)
TiMe = myfile_info['time']
if type(TiMe) == float:
date = datetime.datetime.fromtimestamp(TiMe)
await update.edit_message_caption(
caption="**File Name :** `{}`\n**File Size :** `{}`\n**File Type :** `{}`\n**Created On :** `{}`".format(myfile_info['file_name'],
humanbytes(int(myfile_info['file_size'])),
file_type,
TiMe if isinstance(TiMe,str) else date.date()),
reply_markup=MYFILES_BUTTONS )
async def delete_user_file(_id, file_list_no: int, update:CallbackQuery):
try:
myfile_info=await db.get_file(_id)
except FIleNotFound:
await update.answer("File Already Deleted")
return
await db.delete_one_file(myfile_info['_id'])
await db.count_links(update.from_user.id, "-")
await update.message.edit_caption(
caption= "**Fɪʟᴇ Dᴇʟᴇᴛᴇᴅ Sᴇssғʟʟʏ !**" + update.message.caption.replace("Cɴғɪʀᴍ ʏ ᴡᴀɴᴛ ᴛᴏ ᴅᴇʟᴇᴛᴇ ᴛʜᴇ Fɪʟᴇ", ""),
reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("ʙᴀᴄᴋ", callback_data=f"userfiles_1")]])
)
async def delete_user_filex(_id, update:CallbackQuery):
try:
myfile_info=await db.get_file(_id)
except FIleNotFound:
await update.answer("File Already Deleted")
return
await db.delete_one_file(myfile_info['_id'])
await db.count_links(update.from_user.id, "-")
await update.message.edit_caption(
caption= "**Fɪʟᴇ Dᴇʟᴇᴛᴇᴅ Sᴇssғʟʟʏ !**\n\n",
reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("ʟsᴇ", callback_data=f"close")]])
)

View File

@@ -0,0 +1,119 @@
import logging
import math
from FileStream import __version__
from FileStream.bot import FileStream
from FileStream.server.exceptions import FIleNotFound
from FileStream.utils.bot_utils import gen_linkx, verify_user
from FileStream.config import Telegram
from FileStream.utils.database import Database
from FileStream.utils.translation import LANG, BUTTON
from pyrogram import filters, Client
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Message
from pyrogram.enums.parse_mode import ParseMode
db = Database(Telegram.DATABASE_URL, Telegram.SESSION_NAME)
@FileStream.on_message(filters.command('start') & filters.private)
async def start(bot: Client, message: Message):
if not await verify_user(bot, message):
return
usr_cmd = message.text.split("_")[-1]
if usr_cmd == "/start":
await message.reply_text(
text=LANG.START_TEXT.format(message.from_user.mention, FileStream.username),
parse_mode=ParseMode.HTML,
disable_web_page_preview=True,
reply_markup=BUTTON.START_BUTTONS
)
else:
if "stream_" in message.text:
try:
file_check = await db.get_file(usr_cmd)
file_id = str(file_check['_id'])
if file_id == usr_cmd:
reply_markup, stream_text = await gen_linkx(m=message, _id=file_id,
name=[FileStream.username, FileStream.fname])
await message.reply_text(
text=stream_text,
parse_mode=ParseMode.HTML,
disable_web_page_preview=True,
reply_markup=reply_markup,
quote=True
)
except FIleNotFound as e:
await message.reply_text("File Not Found")
except Exception as e:
await message.reply_text("Something Went Wrong")
logging.error(e)
elif "file_" in message.text:
try:
file_check = await db.get_file(usr_cmd)
db_id = str(file_check['_id'])
file_id = file_check['file_id']
file_name = file_check['file_name']
if db_id == usr_cmd:
await message.reply_cached_media(file_id=file_id, caption=f'**{file_name}**')
except FIleNotFound as e:
await message.reply_text("**File Not Found**")
except Exception as e:
await message.reply_text("Something Went Wrong")
logging.error(e)
else:
await message.reply_text(f"**Invalid Command**")
@FileStream.on_message(filters.private & filters.command(["about"]))
async def start(bot, message):
if not await verify_user(bot, message):
return
await message.reply_text(
text=LANG.ABOUT_TEXT.format(FileStream.fname, __version__),
disable_web_page_preview=True,
reply_markup=BUTTON.ABOUT_BUTTONS
)
@FileStream.on_message((filters.command('help')) & filters.private)
async def help_handler(bot, message):
if not await verify_user(bot, message):
return
await message.reply_text(
text=LANG.HELP_TEXT.format(Telegram.OWNER_ID),
parse_mode=ParseMode.HTML,
disable_web_page_preview=True,
reply_markup=BUTTON.HELP_BUTTONS
)
# ---------------------------------------------------------------------------------------------------
@FileStream.on_message(filters.command('files') & filters.private)
async def my_files(bot: Client, message: Message):
if not await verify_user(bot, message):
return
user_files, total_files = await db.find_files(message.from_user.id, [1, 10])
file_list = []
async for x in user_files:
file_list.append([InlineKeyboardButton(x["file_name"], callback_data=f"myfile_{x['_id']}_{1}")])
if total_files > 10:
file_list.append(
[
InlineKeyboardButton("", callback_data="N/A"),
InlineKeyboardButton(f"1/{math.ceil(total_files / 10)}", callback_data="N/A"),
InlineKeyboardButton("", callback_data="userfiles_2")
],
)
if not file_list:
file_list.append(
[InlineKeyboardButton("ᴇᴍᴘᴛʏ", callback_data="N/A")],
)
file_list.append([InlineKeyboardButton("ʟsᴇ", callback_data="close")])
await message.reply_photo(photo=Telegram.IMAGE_FILEID,
caption="Total files: {}".format(total_files),
reply_markup=InlineKeyboardMarkup(file_list))

View File

@@ -0,0 +1,95 @@
import asyncio
from FileStream.bot import FileStream, multi_clients
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
from FileStream.utils.database import Database
from FileStream.utils.file_properties import get_file_ids, get_file_info
from FileStream.config import Telegram
from pyrogram import filters, Client
from pyrogram.errors import FloodWait
from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
from pyrogram.enums.parse_mode import ParseMode
db = Database(Telegram.DATABASE_URL, Telegram.SESSION_NAME)
@FileStream.on_message(
filters.private
& (
filters.document
| filters.video
| filters.video_note
| filters.audio
| filters.voice
| filters.animation
| filters.photo
),
group=4,
)
async def private_receive_handler(bot: Client, message: Message):
if not await is_user_authorized(message):
return
if await is_user_banned(message):
return
await is_user_exist(bot, message)
if Telegram.FORCE_UPDATES_CHANNEL:
if not await is_user_joined(bot, message):
return
try:
inserted_id = await db.add_file(get_file_info(message))
await get_file_ids(False, inserted_id, multi_clients, message)
reply_markup, stream_text = await gen_link(_id=inserted_id)
await message.reply_text(
text=stream_text,
parse_mode=ParseMode.HTML,
disable_web_page_preview=True,
reply_markup=reply_markup,
quote=True
)
except FloodWait as e:
print(f"Sleeping for {str(e.value)}s")
await asyncio.sleep(e.value)
await bot.send_message(chat_id=Telegram.LOG_CHANNEL,
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)}`",
disable_web_page_preview=True, parse_mode=ParseMode.MARKDOWN)
@FileStream.on_message(
filters.channel
& ~filters.forwarded
& ~filters.media_group
& (
filters.document
| filters.video
| filters.video_note
| filters.audio
| filters.voice
| filters.photo
)
)
async def channel_receive_handler(bot: Client, message: Message):
if await is_channel_banned(bot, message):
return
await is_channel_exist(bot, message)
try:
inserted_id = await db.add_file(get_file_info(message))
await get_file_ids(False, inserted_id, multi_clients, message)
reply_markup, stream_link = await gen_link(_id=inserted_id)
await bot.edit_message_reply_markup(
chat_id=message.chat.id,
message_id=message.id,
reply_markup=InlineKeyboardMarkup(
[[InlineKeyboardButton("Dɴʟᴀᴅ ʟɪɴᴋ 📥",
url=f"https://t.me/{FileStream.username}?start=stream_{str(inserted_id)}")]])
)
except FloodWait as w:
print(f"Sleeping for {str(w.x)}s")
await asyncio.sleep(w.x)
await bot.send_message(chat_id=Telegram.LOG_CHANNEL,
text=f"ɢᴏᴛ ғʟᴏᴏᴅᴡᴀɪᴛ ᴏғ {str(w.x)}s FROM {message.chat.title}\n\n**CHANNEL ID:** `{str(message.chat.id)}`",
disable_web_page_preview=True)
except Exception as e:
await bot.send_message(chat_id=Telegram.LOG_CHANNEL, text=f"**#EʀʀʀTʀᴀᴋᴇʙᴀᴋ:** `{e}`",
disable_web_page_preview=True)
print(f"Cᴀɴ'ᴛ Eᴅɪᴛ Bʀᴀᴅᴀsᴛ Mᴇssᴀɢᴇ!\nEʀʀʀ: **Gɪᴇ ᴍᴇ ᴇᴅɪᴛ ᴘᴇʀᴍɪssɪɴ ɪɴ ᴘᴅᴀᴛᴇs ᴀɴᴅ ʙɪɴ Cʜᴀɴɴᴇʟ!{e}**")

39
FileStream/config.py Normal file
View File

@@ -0,0 +1,39 @@
from os import environ as env
from dotenv import load_dotenv
load_dotenv()
class Telegram:
API_ID = int(env.get("API_ID"))
API_HASH = str(env.get("API_HASH"))
BOT_TOKEN = str(env.get("BOT_TOKEN"))
OWNER_ID = int(env.get('OWNER_ID', '7978482443'))
WORKERS = int(env.get("WORKERS", "6")) # 6 workers = 6 commands at once
DATABASE_URL = str(env.get('DATABASE_URL'))
UPDATES_CHANNEL = str(env.get('UPDATES_CHANNEL', "Telegram"))
SESSION_NAME = str(env.get('SESSION_NAME', 'FileStream'))
FORCE_UPDATES_CHANNEL = env.get('FORCE_UPDATES_CHANNEL', False)
FORCE_UPDATES_CHANNEL = True if str(FORCE_UPDATES_CHANNEL).lower() == "true" else False
SLEEP_THRESHOLD = int(env.get("SLEEP_THRESHOLD", "60"))
IMAGE_FILEID = env.get('IMAGE_FILEID', "https://telegra.ph/file/5bb9935be0229adf98b73.jpg")
MULTI_CLIENT = False
LOG_CHANNEL = int(
env.get("BIN_CHANNEL", None)
) # you NEED to use a CHANNEL when you're using MULTI_CLIENT
MODE = env.get("MODE", "primary")
SECONDARY = True if MODE.lower() == "secondary" else False
AUTH_USERS = list(set(int(x) for x in str(env.get("AUTH_USERS", "")).split()))
class Server:
PORT = int(env.get("PORT", 8080))
BIND_ADDRESS = str(env.get("BIND_ADDRESS", "0.0.0.0"))
PING_INTERVAL = int(env.get("PING_INTERVAL", "1200")) # 20 minutes
HAS_SSL = str(env.get("HAS_SSL", "0").lower()) in ("1", "true", "t", "yes", "y")
NO_PORT = str(env.get("NO_PORT", "0").lower()) in ("1", "true", "t", "yes", "y")
FQDN = str(env.get("FQDN", BIND_ADDRESS))
URL = "http{}://{}{}/".format(
"s" if HAS_SSL else "", FQDN, "" if NO_PORT else ":" + str(PORT)
)
KEEP_ALIVE = str(env.get("KEEP_ALIVE", "0").lower()) in ("1", "true", "t", "yes", "y")

View File

@@ -1,9 +1,7 @@
from aiohttp import web
from .stream_routes import routes
async def web_server():
def web_server():
web_app = web.Application(client_max_size=30000000)
web_app.add_routes(routes)
return web_app

View File

@@ -0,0 +1,5 @@
class InvalidHash(Exception):
message = "Invalid hash"
class FIleNotFound(Exception):
message = "File not found"

View File

@@ -0,0 +1,136 @@
import time
import math
import logging
import mimetypes
import traceback
from aiohttp import web
from aiohttp.http_exceptions import BadStatusLine
from FileStream.bot import multi_clients, work_loads, FileStream
from FileStream.config import Telegram, Server
from FileStream.server.exceptions import FIleNotFound, InvalidHash
from FileStream import utils, StartTime, __version__
from FileStream.utils.render_template import render_page
routes = web.RouteTableDef()
@routes.get("/status", allow_head=True)
async def root_route_handler(_):
return web.json_response(
{
"server_status": "running",
"uptime": utils.get_readable_time(time.time() - StartTime),
"telegram_bot": "@" + FileStream.username,
"connected_bots": len(multi_clients),
"loads": dict(
("bot" + str(c + 1), l)
for c, (_, l) in enumerate(
sorted(work_loads.items(), key=lambda x: x[1], reverse=True)
)
),
"version": __version__,
}
)
@routes.get("/watch/{path}", allow_head=True)
async def stream_handler(request: web.Request):
try:
path = request.match_info["path"]
return web.Response(text=await render_page(path), content_type='text/html')
except InvalidHash as e:
raise web.HTTPForbidden(text=e.message)
except FIleNotFound as e:
raise web.HTTPNotFound(text=e.message)
except (AttributeError, BadStatusLine, ConnectionResetError):
pass
@routes.get("/dl/{path}", allow_head=True)
async def stream_handler(request: web.Request):
try:
path = request.match_info["path"]
return await media_streamer(request, path)
except InvalidHash as e:
raise web.HTTPForbidden(text=e.message)
except FIleNotFound as e:
raise web.HTTPNotFound(text=e.message)
except (AttributeError, BadStatusLine, ConnectionResetError):
pass
except Exception as e:
traceback.print_exc()
logging.critical(e.with_traceback(None))
logging.debug(traceback.format_exc())
raise web.HTTPInternalServerError(text=str(e))
class_cache = {}
async def media_streamer(request: web.Request, db_id: str):
range_header = request.headers.get("Range", 0)
index = min(work_loads, key=work_loads.get)
faster_client = multi_clients[index]
if Telegram.MULTI_CLIENT:
logging.info(f"Client {index} is now serving {request.headers.get('X-FORWARDED-FOR',request.remote)}")
if faster_client in class_cache:
tg_connect = class_cache[faster_client]
logging.debug(f"Using cached ByteStreamer object for client {index}")
else:
logging.debug(f"Creating new ByteStreamer object for client {index}")
tg_connect = utils.ByteStreamer(faster_client)
class_cache[faster_client] = tg_connect
logging.debug("before calling get_file_properties")
file_id = await tg_connect.get_file_properties(db_id, multi_clients)
logging.debug("after calling get_file_properties")
file_size = file_id.file_size
if range_header:
from_bytes, until_bytes = range_header.replace("bytes=", "").split("-")
from_bytes = int(from_bytes)
until_bytes = int(until_bytes) if until_bytes else file_size - 1
else:
from_bytes = request.http_range.start or 0
until_bytes = (request.http_range.stop or file_size) - 1
if (until_bytes > file_size) or (from_bytes < 0) or (until_bytes < from_bytes):
return web.Response(
status=416,
body="416: Range not satisfiable",
headers={"Content-Range": f"bytes */{file_size}"},
)
chunk_size = 1024 * 1024
until_bytes = min(until_bytes, file_size - 1)
offset = from_bytes - (from_bytes % chunk_size)
first_part_cut = from_bytes - offset
last_part_cut = until_bytes % chunk_size + 1
req_length = until_bytes - from_bytes + 1
part_count = math.ceil(until_bytes / chunk_size) - math.floor(offset / chunk_size)
body = tg_connect.yield_file(
file_id, index, offset, first_part_cut, last_part_cut, part_count, chunk_size
)
mime_type = file_id.mime_type
file_name = utils.get_name(file_id)
disposition = "attachment"
if not mime_type:
mime_type = mimetypes.guess_type(file_name)[0] or "application/octet-stream"
# if "video/" in mime_type or "audio/" in mime_type:
# disposition = "inline"
return web.Response(
status=206 if range_header else 200,
body=body,
headers={
"Content-Type": f"{mime_type}",
"Content-Range": f"bytes {from_bytes}-{until_bytes}/{file_size}",
"Content-Length": str(req_length),
"Content-Disposition": f'{disposition}; filename="{file_name}"',
"Accept-Ranges": "bytes",
},
)

View File

@@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta property="og:image" content="https://www.flaticon.com/premium-icon/icons/svg/2626/2626281.svg" itemprop="thumbnailUrl">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>%s</title>
<link rel="stylesheet" type='text/css' href="https://drive.google.com/uc?export=view&id=1pVLG4gZy7jdow3sO-wFS06aP_A9QX0O6">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Raleway">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Delius">
<!-- <link rel="stylesheet" href="./style.css"> -->
</head>
<body class='cyber'>
<header>
<div class="toogle"></div>
<div id="file-name" class="cyber">
%s
</div>
</header>
<div class="container">
<a href=%s>
<button class="cybr-btn">
Download
<span aria-hidden>_</span>
<span aria-hidden class="cybr-btn__glitch">_404 Error</span>
<span aria-hidden class="cybr-btn__tag">_%s</span>
</button>
</a>
</div>
<footer>
<span id="fork-text">Fork me on</span>
<span>
<a href="https://github.com/DeekshithSH/FileStreamBot" id='github-logo'>
<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>
</a>
</span>
</footer>
<script>
const body = document.querySelector('body');
const title = document.querySelector('#file-name');
const footer = document.querySelector('footer');
const toogle = document.querySelector('.toogle');
toogle.onclick = () => {
body.classList.toggle('dark')
footer.classList.toggle('dark')
title.classList.toggle('dark')
}
</script>
</body>
</html>

View File

@@ -0,0 +1,154 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>streamHeading</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-Frame-Options" content="deny">
<link rel="stylesheet" href="https://cdn.plyr.io/3.7.8/plyr.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<script src="https://cdn.plyr.io/3.7.8/plyr.polyfilled.js"></script>
<style>
html, body {
margin: 0;
height: 100%;
}
#stream-media {
height: 100%;
width: 100%;
}
#error-message {
color: red;
font-size: 24px;
text-align: center;
margin-top: 20px;
}
.plyr__video-wrapper .plyr-download-button{
position: absolute;
top: 10px;
left: 10px;
width: 30px;
height: 30px;
background-color: rgba(0, 0, 0, 0.7);
border-radius: 50%;
text-align: center;
line-height: 30px;
color: white;
z-index: 10;
}
.plyr__volume {
max-width: initial;
min-width: initial;
width: auto;
position: relative;
}
.plyr__video-wrapper .plyr-share-button{
position: absolute;
top: 50px;
left: 10px;
width: 30px;
height: 30px;
background-color: rgba(0, 0, 0, 0.7);
border-radius: 50%;
text-align: center;
line-height: 30px;
color: white;
z-index: 10;
}
.plyr__video-wrapper .plyr-download-button:hover,
.plyr__video-wrapper .plyr-share-button:hover{
background-color: rgba(255, 255, 255, 0.7);
color: black;
}
.plyr__video-wrapper .plyr-download-button:before {
font-family: "Font Awesome 5 Free";
content: "\f019";
font-weight: bold;
}
.plyr__video-wrapper .plyr-share-button:before {
font-family: "Font Awesome 5 Free";
content: "\f064";
font-weight: bold;
}
.plyr, .plyr__video-wrapper, .plyr__video-embed iframe {
height: 100%;
}
</style>
</head>
<body>
<video id="stream-media" controls preload="auto">
<source src="" type="">
<p class="vjs-no-js">
To view this video please enable JavaScript, and consider upgrading to a web browser that supports HTML5 video
</p>
</video>
<div id="error-message"></div>
<script>
var player = new Plyr('#stream-media', {
controls:['play-large', 'rewind', 'play', 'fast-forward', 'progress', 'current-time', 'mute', 'settings', 'pip', 'fullscreen'],
settings:['speed','loop'],
speed:{selected:1,options:[0.25,0.5,0.75,1,1.25,1.5,1.75,2]},
seek: 10,
keyboard: { focused: true, global: true },
});
var mediaLink = "streamMediaLink";
if (mediaLink) {
document.querySelector('#stream-media source').setAttribute('src', mediaLink);
player.restart();
var downloadButton = document.createElement('div');
downloadButton.className = 'plyr-download-button';
downloadButton.onclick = function() {
event.stopPropagation();
var link = document.createElement('a');
link.href = mediaLink;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
player.elements.container.querySelector('.plyr__video-wrapper').appendChild(downloadButton);
var shareButton = document.createElement('div');
shareButton.className = 'plyr-share-button';
shareButton.onclick = function() {
event.stopPropagation();
if (navigator.share) {
navigator.share({
title: "Play",
url: window.location.href
});
}
};
player.elements.container.querySelector('.plyr__video-wrapper').appendChild(shareButton);
} else {
document.getElementById('error-message').textContent = 'Error: Media URL not provided';
}
</script>
</body>
</html>

View File

@@ -0,0 +1,4 @@
from .keepalive import ping_server
from .time_format import get_readable_time
from .file_properties import get_name, get_file_ids
from .custom_dl import ByteStreamer

View File

@@ -0,0 +1,177 @@
from pyrogram.errors import UserNotParticipant
from pyrogram.enums.parse_mode import ParseMode
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Message
from FileStream.utils.translation import LANG
from FileStream.utils.database import Database
from FileStream.utils.human_readable import humanbytes
from FileStream.config import Telegram, Server
from FileStream.bot import FileStream
db = Database(Telegram.DATABASE_URL, Telegram.SESSION_NAME)
async def is_user_joined(bot, message: Message):
try:
user = await bot.get_chat_member(Telegram.UPDATES_CHANNEL, message.chat.id)
if user.status == "BANNED":
await message.reply_text(
text=LANG.BAN_TEXT.format(Telegram.OWNER_ID),
parse_mode=ParseMode.MARKDOWN,
disable_web_page_preview=True
)
return False
except UserNotParticipant:
await message.reply_text(
text = "<i>Jɪɴ ᴍʏ ᴜᴘᴅᴀᴛᴇ ᴄʜᴀɴɴᴇʟ ᴛᴏ sᴇ ᴍᴇ 🔐</i>",
reply_markup=InlineKeyboardMarkup(
[[
InlineKeyboardButton("Jɪɴ ɴᴏᴡ 🔓", url=f"https://t.me/{Telegram.UPDATES_CHANNEL}")
]]
),
parse_mode=ParseMode.HTML
)
return False
except Exception:
await message.reply_text(
text = f"<i>Sᴍᴇᴛʜɪɴɢ ᴡʀᴏɴɢ ᴄᴏɴᴛᴀᴄᴛ ᴍʏ ᴅᴇᴠᴇʟᴏᴘᴇʀ</i> <b><a href='https://t.me/{Telegram.UPDATES_CHANNEL}'>[ ᴄʟɪᴄᴋ ʜᴇʀᴇ ]</a></b>",
parse_mode=ParseMode.HTML,
disable_web_page_preview=True)
return False
return True
#---------------------[ PRIVATE GEN LINK + CALLBACK ]---------------------#
async def gen_link(_id):
file_info = await db.get_file(_id)
file_name = file_info['file_name']
file_size = humanbytes(file_info['file_size'])
mime_type = file_info['mime_type']
page_link = f"{Server.URL}watch/{_id}"
stream_link = f"{Server.URL}dl/{_id}"
file_link = f"https://t.me/{FileStream.username}?start=file_{_id}"
if "video" in mime_type:
stream_text = LANG.STREAM_TEXT.format(file_name, file_size, stream_link, page_link, file_link)
reply_markup = InlineKeyboardMarkup(
[
[InlineKeyboardButton("sᴛʀᴇᴀᴍ", url=page_link), InlineKeyboardButton("ᴅᴏᴡɴʟᴏᴀᴅ", url=stream_link)],
[InlineKeyboardButton("ɢᴇᴛ ғɪʟᴇ", url=file_link), InlineKeyboardButton("ʀᴇᴠᴏᴋᴇ ғɪʟᴇ", callback_data=f"msgdelpvt_{_id}")],
[InlineKeyboardButton("ʟsᴇ", callback_data="close")]
]
)
else:
stream_text = LANG.STREAM_TEXT_X.format(file_name, file_size, stream_link, file_link)
reply_markup = InlineKeyboardMarkup(
[
[InlineKeyboardButton("ᴅᴏᴡɴʟᴏᴀᴅ", url=stream_link)],
[InlineKeyboardButton("ɢᴇᴛ ғɪʟᴇ", url=file_link), InlineKeyboardButton("ʀᴇᴠᴏᴋᴇ ғɪʟᴇ", callback_data=f"msgdelpvt_{_id}")],
[InlineKeyboardButton("ʟsᴇ", callback_data="close")]
]
)
return reply_markup, stream_text
#---------------------[ GEN STREAM LINKS FOR CHANNEL ]---------------------#
async def gen_linkx(m:Message , _id, name: list):
file_info = await db.get_file(_id)
file_name = file_info['file_name']
mime_type = file_info['mime_type']
file_size = humanbytes(file_info['file_size'])
page_link = f"{Server.URL}watch/{_id}"
stream_link = f"{Server.URL}dl/{_id}"
file_link = f"https://t.me/{FileStream.username}?start=file_{_id}"
if "video" in mime_type:
stream_text= LANG.STREAM_TEXT_X.format(file_name, file_size, stream_link, page_link)
reply_markup = InlineKeyboardMarkup(
[
[InlineKeyboardButton("sᴛʀᴇᴀᴍ", url=page_link), InlineKeyboardButton("ᴅᴏᴡɴʟᴏᴀᴅ", url=stream_link)]
]
)
else:
stream_text= LANG.STREAM_TEXT_X.format(file_name, file_size, stream_link, file_link)
reply_markup = InlineKeyboardMarkup(
[
[InlineKeyboardButton("ᴅᴏᴡɴʟᴏᴀᴅ", url=stream_link)]
]
)
return reply_markup, stream_text
#---------------------[ USER BANNED ]---------------------#
async def is_user_banned(message):
if await db.is_user_banned(message.from_user.id):
await message.reply_text(
text=LANG.BAN_TEXT.format(Telegram.OWNER_ID),
parse_mode=ParseMode.MARKDOWN,
disable_web_page_preview=True
)
return True
return False
#---------------------[ CHANNEL BANNED ]---------------------#
async def is_channel_banned(bot, message):
if await db.is_user_banned(message.chat.id):
await bot.edit_message_reply_markup(
chat_id=message.chat.id,
message_id=message.id,
reply_markup=InlineKeyboardMarkup([[
InlineKeyboardButton(f"ᴄʜᴀɴɴᴇʟ ɪs ʙᴀɴɴᴇᴅ", callback_data="N/A")]])
)
return True
return False
#---------------------[ USER AUTH ]---------------------#
async def is_user_authorized(message):
if hasattr(Telegram, 'AUTH_USERS') and Telegram.AUTH_USERS:
user_id = message.from_user.id
if user_id == Telegram.OWNER_ID:
return True
if not (user_id in Telegram.AUTH_USERS):
await message.reply_text(
text="You are not authorized to use this bot.",
parse_mode=ParseMode.MARKDOWN,
disable_web_page_preview=True
)
return False
return True
#---------------------[ USER EXIST ]---------------------#
async def is_user_exist(bot, message):
if not bool(await db.get_user(message.from_user.id)):
await db.add_user(message.from_user.id)
await bot.send_message(
Telegram.LOG_CHANNEL,
f"**#NᴇUsᴇʀ**\n**⬩ sᴇʀ ɴᴀᴍᴇ :** [{message.from_user.first_name}](tg://user?id={message.from_user.id})\n**⬩ sᴇʀ ɪᴅ :** `{message.from_user.id}`"
)
async def is_channel_exist(bot, message):
if not bool(await db.get_user(message.chat.id)):
await db.add_user(message.chat.id)
members = await bot.get_chat_members_count(message.chat.id)
await bot.send_message(
Telegram.LOG_CHANNEL,
f"**#NᴇCʜᴀɴɴᴇʟ** \n**⬩ ᴄʜᴀᴛ ɴᴀᴍᴇ :** `{message.chat.title}`\n**⬩ ᴄʜᴀᴛ ɪᴅ :** `{message.chat.id}`\n**⬩ ᴛᴏᴛᴀʟ ᴍᴇᴍʙᴇʀs :** `{members}`"
)
async def verify_user(bot, message):
if not await is_user_authorized(message):
return False
if await is_user_banned(message):
return False
await is_user_exist(bot, message)
if Telegram.FORCE_UPDATES_CHANNEL:
if not await is_user_joined(bot, message):
return False
return True

View File

@@ -1,15 +1,13 @@
import asyncio
import traceback
from pyrogram.errors import FloodWait, InputUserDeactivated, UserIsBlocked, PeerIdInvalid
async def send_msg(user_id, message):
try:
await message.forward(chat_id=user_id)
await message.copy(chat_id=user_id)
return 200, None
except FloodWait as e:
await asyncio.sleep(e.x)
await asyncio.sleep(e.value)
return send_msg(user_id, message)
except InputUserDeactivated:
return 400, f"{user_id} : deactivated\n"

View File

@@ -0,0 +1,214 @@
import asyncio
import logging
from typing import Dict, Union
from FileStream.bot import work_loads
from pyrogram import Client, utils, raw
from .file_properties import get_file_ids
from pyrogram.session import Session, Auth
from pyrogram.errors import AuthBytesInvalid
from pyrogram.file_id import FileId, FileType, ThumbnailSource
from pyrogram.types import Message
class ByteStreamer:
def __init__(self, client: Client):
self.clean_timer = 30 * 60
self.client: Client = client
self.cached_file_ids: Dict[str, FileId] = {}
asyncio.create_task(self.clean_cache())
async def get_file_properties(self, db_id: str, multi_clients) -> FileId:
"""
Returns the properties of a media of a specific message in a FIleId class.
if the properties are cached, then it'll return the cached results.
or it'll generate the properties from the Message ID and cache them.
"""
if not db_id in self.cached_file_ids:
logging.debug("Before Calling generate_file_properties")
await self.generate_file_properties(db_id, multi_clients)
logging.debug(f"Cached file properties for file with ID {db_id}")
return self.cached_file_ids[db_id]
async def generate_file_properties(self, db_id: str, multi_clients) -> FileId:
"""
Generates the properties of a media file on a specific message.
returns ths properties in a FIleId class.
"""
logging.debug("Before calling get_file_ids")
file_id = await get_file_ids(self.client, db_id, multi_clients, Message)
logging.debug(f"Generated file ID and Unique ID for file with ID {db_id}")
self.cached_file_ids[db_id] = file_id
logging.debug(f"Cached media file with ID {db_id}")
return self.cached_file_ids[db_id]
async def generate_media_session(self, client: Client, file_id: FileId) -> Session:
"""
Generates the media session for the DC that contains the media file.
This is required for getting the bytes from Telegram servers.
"""
media_session = client.media_sessions.get(file_id.dc_id, None)
if media_session is None:
if file_id.dc_id != await client.storage.dc_id():
media_session = Session(
client,
file_id.dc_id,
await Auth(
client, file_id.dc_id, await client.storage.test_mode()
).create(),
await client.storage.test_mode(),
is_media=True,
)
await media_session.start()
for _ in range(6):
exported_auth = await client.invoke(
raw.functions.auth.ExportAuthorization(dc_id=file_id.dc_id)
)
try:
await media_session.invoke(
raw.functions.auth.ImportAuthorization(
id=exported_auth.id, bytes=exported_auth.bytes
)
)
break
except AuthBytesInvalid:
logging.debug(
f"Invalid authorization bytes for DC {file_id.dc_id}"
)
continue
else:
await media_session.stop()
raise AuthBytesInvalid
else:
media_session = Session(
client,
file_id.dc_id,
await client.storage.auth_key(),
await client.storage.test_mode(),
is_media=True,
)
await media_session.start()
logging.debug(f"Created media session for DC {file_id.dc_id}")
client.media_sessions[file_id.dc_id] = media_session
else:
logging.debug(f"Using cached media session for DC {file_id.dc_id}")
return media_session
@staticmethod
async def get_location(file_id: FileId) -> Union[raw.types.InputPhotoFileLocation,
raw.types.InputDocumentFileLocation,
raw.types.InputPeerPhotoFileLocation,]:
"""
Returns the file location for the media file.
"""
file_type = file_id.file_type
if file_type == FileType.CHAT_PHOTO:
if file_id.chat_id > 0:
peer = raw.types.InputPeerUser(
user_id=file_id.chat_id, access_hash=file_id.chat_access_hash
)
else:
if file_id.chat_access_hash == 0:
peer = raw.types.InputPeerChat(chat_id=-file_id.chat_id)
else:
peer = raw.types.InputPeerChannel(
channel_id=utils.get_channel_id(file_id.chat_id),
access_hash=file_id.chat_access_hash,
)
location = raw.types.InputPeerPhotoFileLocation(
peer=peer,
volume_id=file_id.volume_id,
local_id=file_id.local_id,
big=file_id.thumbnail_source == ThumbnailSource.CHAT_PHOTO_BIG,
)
elif file_type == FileType.PHOTO:
location = raw.types.InputPhotoFileLocation(
id=file_id.media_id,
access_hash=file_id.access_hash,
file_reference=file_id.file_reference,
thumb_size=file_id.thumbnail_size,
)
else:
location = raw.types.InputDocumentFileLocation(
id=file_id.media_id,
access_hash=file_id.access_hash,
file_reference=file_id.file_reference,
thumb_size=file_id.thumbnail_size,
)
return location
async def yield_file(
self,
file_id: FileId,
index: int,
offset: int,
first_part_cut: int,
last_part_cut: int,
part_count: int,
chunk_size: int,
) -> Union[str, None]:
"""
Custom generator that yields the bytes of the media file.
Modded from <https://github.com/eyaadh/megadlbot_oss/blob/master/mega/telegram/utils/custom_download.py#L20>
Thanks to Eyaadh <https://github.com/eyaadh>
"""
client = self.client
work_loads[index] += 1
logging.debug(f"Starting to yielding file with client {index}.")
media_session = await self.generate_media_session(client, file_id)
current_part = 1
location = await self.get_location(file_id)
try:
r = await media_session.invoke(
raw.functions.upload.GetFile(
location=location, offset=offset, limit=chunk_size
),
)
if isinstance(r, raw.types.upload.File):
while True:
chunk = r.bytes
if not chunk:
break
elif part_count == 1:
yield chunk[first_part_cut:last_part_cut]
elif current_part == 1:
yield chunk[first_part_cut:]
elif current_part == part_count:
yield chunk[:last_part_cut]
else:
yield chunk
current_part += 1
offset += chunk_size
if current_part > part_count:
break
r = await media_session.invoke(
raw.functions.upload.GetFile(
location=location, offset=offset, limit=chunk_size
),
)
except (TimeoutError, AttributeError):
pass
finally:
logging.debug(f"Finished yielding file with {current_part} parts.")
work_loads[index] -= 1
async def clean_cache(self) -> None:
"""
function to clean the cache to reduce memory usage
"""
while True:
await asyncio.sleep(self.clean_timer)
self.cached_file_ids.clear()
logging.debug("Cleaned the cache")

View File

@@ -0,0 +1,136 @@
import pymongo
import time
import motor.motor_asyncio
from bson.objectid import ObjectId
from bson.errors import InvalidId
from FileStream.server.exceptions import FIleNotFound
class Database:
def __init__(self, uri, database_name):
self._client = motor.motor_asyncio.AsyncIOMotorClient(uri)
self.db = self._client[database_name]
self.col = self.db.users
self.black = self.db.blacklist
self.file = self.db.file
#---------------------[ NEW USER ]---------------------#
def new_user(self, id):
return dict(
id=id,
join_date=time.time(),
agreed_to_tos=False,
Links=0,
Plan="Free"
)
# ---------------------[ ADD USER ]---------------------#
async def add_user(self, id):
user = self.new_user(id)
await self.col.insert_one(user)
# ---------------------[ GET USER ]---------------------#
async def get_user(self, id):
user = await self.col.find_one({'id': int(id)})
return user
# ---------------------[ CHECK USER ]---------------------#
async def total_users_count(self):
count = await self.col.count_documents({})
return count
async def get_all_users(self):
all_users = self.col.find({})
return all_users
# ---------------------[ REMOVE USER ]---------------------#
async def delete_user(self, user_id):
await self.col.delete_many({'id': int(user_id)})
# ---------------------[ BAN, UNBAN USER ]---------------------#
def black_user(self, id):
return dict(
id=id,
ban_date=time.time()
)
async def ban_user(self, id):
user = self.black_user(id)
await self.black.insert_one(user)
async def unban_user(self, id):
await self.black.delete_one({'id': int(id)})
async def is_user_banned(self, id):
user = await self.black.find_one({'id': int(id)})
return True if user else False
async def total_banned_users_count(self):
count = await self.black.count_documents({})
return count
# ---------------------[ ADD FILE TO DB ]---------------------#
async def add_file(self, file_info):
file_info["time"] = time.time()
fetch_old = await self.get_file_by_fileuniqueid(file_info["user_id"], file_info["file_unique_id"])
if fetch_old:
return fetch_old["_id"]
await self.count_links(file_info["user_id"], "+")
return (await self.file.insert_one(file_info)).inserted_id
# ---------------------[ FIND FILE IN DB ]---------------------#
async def find_files(self, user_id, range):
user_files=self.file.find({"user_id": user_id})
user_files.skip(range[0] - 1)
user_files.limit(range[1] - range[0] + 1)
user_files.sort('_id', pymongo.DESCENDING)
total_files = await self.file.count_documents({"user_id": user_id})
return user_files, total_files
async def get_file(self, _id):
try:
file_info=await self.file.find_one({"_id": ObjectId(_id)})
if not file_info:
raise FIleNotFound
return file_info
except InvalidId:
raise FIleNotFound
async def get_file_by_fileuniqueid(self, id, file_unique_id, many=False):
if many:
return self.file.find({"file_unique_id": file_unique_id})
else:
file_info=await self.file.find_one({"user_id": id, "file_unique_id": file_unique_id})
if file_info:
return file_info
return False
# ---------------------[ TOTAL FILES ]---------------------#
async def total_files(self, id=None):
if id:
return await self.file.count_documents({"user_id": id})
return await self.file.count_documents({})
# ---------------------[ DELETE FILES ]---------------------#
async def delete_one_file(self, _id):
await self.file.delete_one({'_id': ObjectId(_id)})
# ---------------------[ UPDATE FILES ]---------------------#
async def update_file_ids(self, _id, file_ids: dict):
await self.file.update_one({"_id": ObjectId(_id)}, {"$set": {"file_ids": file_ids}})
# ---------------------[ PAID SYS ]---------------------#
async def link_available(self, id):
user = await self.col.find_one({"id": id})
if user.get("Plan") == "Plus":
return "Plus"
elif user.get("Plan") == "Free":
files = await self.file.count_documents({"user_id": id})
if files < 11:
return True
return False
async def count_links(self, id, operation: str):
if operation == "-":
await self.col.update_one({"id": id}, {"$inc": {"Links": -1}})
elif operation == "+":
await self.col.update_one({"id": id}, {"$inc": {"Links": 1}})

View File

@@ -0,0 +1,146 @@
from __future__ import annotations
import logging
from datetime import datetime
from pyrogram import Client
from typing import Any, Optional
from pyrogram.enums import ParseMode, ChatType
from pyrogram.types import Message
from pyrogram.file_id import FileId
from FileStream.bot import FileStream
from FileStream.utils.database import Database
from FileStream.config import Telegram, Server
db = Database(Telegram.DATABASE_URL, Telegram.SESSION_NAME)
async def get_file_ids(client: Client | bool, db_id: str, multi_clients, message) -> Optional[FileId]:
logging.debug("Starting of get_file_ids")
file_info = await db.get_file(db_id)
if (not "file_ids" in file_info) or not client:
logging.debug("Storing file_id of all clients in DB")
log_msg = await send_file(FileStream, db_id, file_info['file_id'], message)
await db.update_file_ids(db_id, await update_file_id(log_msg.id, multi_clients))
logging.debug("Stored file_id of all clients in DB")
if not client:
return
file_info = await db.get_file(db_id)
file_id_info = file_info.setdefault("file_ids", {})
if not str(client.id) in file_id_info:
logging.debug("Storing file_id in DB")
log_msg = await send_file(FileStream, db_id, file_info['file_id'], message)
msg = await client.get_messages(Telegram.LOG_CHANNEL, log_msg.id)
media = get_media_from_message(msg)
file_id_info[str(client.id)] = getattr(media, "file_id", "")
await db.update_file_ids(db_id, file_id_info)
logging.debug("Stored file_id in DB")
logging.debug("Middle of get_file_ids")
file_id = FileId.decode(file_id_info[str(client.id)])
setattr(file_id, "file_size", file_info['file_size'])
setattr(file_id, "mime_type", file_info['mime_type'])
setattr(file_id, "file_name", file_info['file_name'])
setattr(file_id, "unique_id", file_info['file_unique_id'])
logging.debug("Ending of get_file_ids")
return file_id
def get_media_from_message(message: "Message") -> Any:
media_types = (
"audio",
"document",
"photo",
"sticker",
"animation",
"video",
"voice",
"video_note",
)
for attr in media_types:
media = getattr(message, attr, None)
if media:
return media
def get_media_file_size(m):
media = get_media_from_message(m)
return getattr(media, "file_size", "None")
def get_name(media_msg: Message | FileId) -> str:
if isinstance(media_msg, Message):
media = get_media_from_message(media_msg)
file_name = getattr(media, "file_name", "")
elif isinstance(media_msg, FileId):
file_name = getattr(media_msg, "file_name", "")
if not file_name:
if isinstance(media_msg, Message) and media_msg.media:
media_type = media_msg.media.value
elif media_msg.file_type:
media_type = media_msg.file_type.name.lower()
else:
media_type = "file"
formats = {
"photo": "jpg", "audio": "mp3", "voice": "ogg",
"video": "mp4", "animation": "mp4", "video_note": "mp4",
"sticker": "webp"
}
ext = formats.get(media_type)
ext = "." + ext if ext else ""
date = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
file_name = f"{media_type}-{date}{ext}"
return file_name
def get_file_info(message):
media = get_media_from_message(message)
if message.chat.type == ChatType.PRIVATE:
user_idx = message.from_user.id
else:
user_idx = message.chat.id
return {
"user_id": user_idx,
"file_id": getattr(media, "file_id", ""),
"file_unique_id": getattr(media, "file_unique_id", ""),
"file_name": get_name(message),
"file_size": getattr(media, "file_size", 0),
"mime_type": getattr(media, "mime_type", "None/unknown")
}
async def update_file_id(msg_id, multi_clients):
file_ids = {}
for client_id, client in multi_clients.items():
log_msg = await client.get_messages(Telegram.LOG_CHANNEL, msg_id)
media = get_media_from_message(log_msg)
file_ids[str(client.id)] = getattr(media, "file_id", "")
return file_ids
async def send_file(client: Client, db_id, file_id: str, message):
file_caption = message.caption
if file_caption is None:
file_caption = message.file_name
log_msg = await client.send_cached_media(chat_id=Telegram.LOG_CHANNEL, file_id=file_id,
caption=f'**{file_caption}**')
if message.chat.type == ChatType.PRIVATE:
await log_msg.reply_text(
text=f"**RᴇQᴛᴇᴅ ʙʏ :** [{message.from_user.first_name}](tg://user?id={message.from_user.id})\n**Uᴇʀ ɪᴅ :** `{message.from_user.id}`\n**Fɪʟᴇ ɪᴅ :** `{db_id}`",
disable_web_page_preview=True, parse_mode=ParseMode.MARKDOWN, quote=True)
else:
await log_msg.reply_text(
text=f"**RᴇQᴛᴇᴅ ʙʏ :** {message.chat.title} \n**Cʜᴀɴɴᴇʟ ɪᴅ :** `{message.chat.id}`\n**Fɪʟᴇ ɪᴅ :** `{db_id}`",
disable_web_page_preview=True, parse_mode=ParseMode.MARKDOWN, quote=True)
return log_msg
# return await client.send_cached_media(Telegram.BIN_CHANNEL, file_id)

View File

@@ -1,7 +1,4 @@
def humanbytes(size):
# https://stackoverflow.com/a/49361727/4723940
# 2**10 = 1024
if not size:
return ""
power = 2**10

View File

@@ -2,15 +2,17 @@ import asyncio
import logging
import aiohttp
import traceback
from WebStreamer.vars import Var
from FileStream.config import Server
async def ping_server():
sleep_time = Var.PING_INTERVAL
sleep_time = Server.PING_INTERVAL
while True:
await asyncio.sleep(sleep_time)
try:
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session:
async with session.get(Var.URL) as resp:
async with aiohttp.ClientSession(
timeout=aiohttp.ClientTimeout(total=10)
) as session:
async with session.get(Server.URL) as resp:
logging.info("Pinged server with response: {}".format(resp.status))
except TimeoutError:
logging.warning("Couldn't connect to the site URL..!")

View File

@@ -0,0 +1,25 @@
import aiohttp
import aiofiles
import urllib.parse
from FileStream.config import Telegram, Server
from FileStream.utils.database import Database
from FileStream.utils.human_readable import humanbytes
db = Database(Telegram.DATABASE_URL, Telegram.SESSION_NAME)
async def render_page(db_id):
file_data=await db.get_file(db_id)
src = urllib.parse.urljoin(Server.URL, f'dl/{file_data["_id"]}')
if str((file_data['mime_type']).split('/')[0].strip()) == 'video':
async with aiofiles.open('FileStream/template/stream.html') as r:
heading = 'Watch {}'.format(file_data['file_name'])
html_template = await r.read()
html = html_template.replace('streamMediaLink', src).replace('streamHeading', heading)
else:
async with aiofiles.open('FileStream/template/dl.html') as r:
async with aiohttp.ClientSession() as s:
async with s.get(src) as u:
heading = 'Download {}'.format(file_data['file_name'])
file_size = humanbytes(int(u.headers.get('Content-Length')))
html = (await r.read()) % (heading, file_data['file_name'], src, file_size)
return html

View File

@@ -0,0 +1,22 @@
def get_readable_time(seconds: int) -> str:
count = 0
readable_time = ""
time_list = []
time_suffix_list = ["s", "m", "h", " days"]
while count < 4:
count += 1
if count < 3:
remainder, result = divmod(seconds, 60)
else:
remainder, result = divmod(seconds, 24)
if seconds == 0 and remainder == 0:
break
time_list.append(int(result))
seconds = int(remainder)
for x in range(len(time_list)):
time_list[x] = str(time_list[x]) + time_suffix_list[x]
if len(time_list) == 4:
readable_time += time_list.pop() + ", "
time_list.reverse()
readable_time += ": ".join(time_list)
return readable_time

View File

@@ -0,0 +1,72 @@
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
from FileStream.config import Telegram
class LANG(object):
START_TEXT = """
<b>👋 Hᴇʏ, </b>{}\n
<b>I'ᴍ ᴛᴇʟᴇɢʀᴀᴍ ғɪʟᴇs sᴛʀᴇᴀᴍɪɴɢ ʙᴏᴛ ᴀs ᴡᴇʟʟ ᴅɪʀᴇᴄᴛ ʟɪɴᴋs ɢᴇɴᴇʀᴀᴛᴏʀ</b>\n
<b>ᴡᴏʀᴋɪɴɢ ᴏɴ ʜᴀɴɴᴇʟs ᴀɴᴅ ᴘʀɪᴠᴀᴛᴇ ᴄʜᴀᴛ</b>\n
<b>💕 @{}</b>\n"""
HELP_TEXT = """
<b>- ᴀᴅᴅ ᴍᴇ ᴀs ᴀɴ ᴀᴅᴍɪɴ ᴏɴ ᴛʜᴇ ᴄʜᴀɴɴᴇʟ</b>
<b>- sᴇɴᴅ ᴍᴇ ᴀɴʏ ᴅᴏᴄᴜᴍᴇɴᴛ ᴏʀ ᴍᴇᴅɪᴀ</b>
<b>- ɪ'ʟʟ ᴘʀᴏᴠɪᴅᴇ sᴛʀᴇᴀᴍᴀʙʟᴇ ʟɪɴᴋ</b>\n
<b>🔞 ᴀᴅᴜʟᴛ ᴄᴏɴᴛᴇɴᴛ sᴛʀɪᴛʟʏ ᴘʀᴏʜɪʙɪᴛᴇᴅ.</b>\n
<i><b> ʀᴇᴘᴏʀᴛ ʙɢs ᴛᴏ <a href='https://telegram.me/AvishkarPatil'>ᴅᴇᴠᴇʟᴏᴘᴇʀ</a></b></i>"""
ABOUT_TEXT = """
<b>⚜ ᴍʏ ɴᴀᴍᴇ : {}</b>\n
<b>✦ ᴇʀsɪɴ : {}</b>
<b>✦ ᴜᴘᴅᴀᴛᴇᴅ ᴏɴ : 19-November-2023</b>
<b>✦ ᴅᴇᴠᴇʟᴏᴘᴇʀ : <a href='https://telegram.me/AvishkarPatil'>Avishkar Patil</a></b>\n
"""
STREAM_TEXT = """
<i><u>𝗬𝗼𝘂𝗿 𝗟𝗶𝗻𝗸 𝗚𝗲𝗻𝗲𝗿𝗮𝘁𝗲𝗱 !</u></i>\n
<b>📂 Fɪʟᴇ ɴᴀᴍᴇ :</b> <b>{}</b>\n
<b>📦 Fɪʟᴇ ꜱɪᴢᴇ :</b> <code>{}</code>\n
<b>📥 Dɴʟᴀᴅ :</b> <code>{}</code>\n
<b>🖥 Wᴀᴛʜ :</b> <code>{}</code>\n
<b>🔗 Sʜᴀʀᴇ :</b> <code>{}</code>\n"""
STREAM_TEXT_X = """
<i><u>𝗬𝗼𝘂𝗿 𝗟𝗶𝗻𝗸 𝗚𝗲𝗻𝗲𝗿𝗮𝘁𝗲𝗱 !</u></i>\n
<b>📂 Fɪʟᴇ ɴᴀᴍᴇ :</b> <b>{}</b>\n
<b>📦 Fɪʟᴇ ꜱɪᴢᴇ :</b> <code>{}</code>\n
<b>📥 Dɴʟᴀᴅ :</b> <code>{}</code>\n
<b>🔗 Sʜᴀʀᴇ :</b> <code>{}</code>\n"""
BAN_TEXT = "__Sʀʀʏ Sɪʀ, Y ᴀʀᴇ Bᴀɴɴᴇᴅ ᴛᴏ sᴇ ᴍᴇ.__\n\n**[Cɴᴛᴀᴛ Dᴇᴇʟᴘᴇʀ](tg://user?id={}) Tʜᴇʏ Wɪʟʟ Hᴇʟᴘ Y**"
class BUTTON(object):
START_BUTTONS = InlineKeyboardMarkup(
[[
InlineKeyboardButton('ʜᴇʟᴘ', callback_data='help'),
InlineKeyboardButton('ᴀʙᴏᴜᴛ', callback_data='about'),
InlineKeyboardButton('ʟsᴇ', callback_data='close')
],
[InlineKeyboardButton("📢 ᴜᴘᴅᴀᴛᴇ ᴄʜᴀɴɴᴇʟ", url=f'https://t.me/{Telegram.UPDATES_CHANNEL}')]
]
)
HELP_BUTTONS = InlineKeyboardMarkup(
[[
InlineKeyboardButton('ʜᴏᴍᴇ', callback_data='home'),
InlineKeyboardButton('ᴀʙᴏᴜᴛ', callback_data='about'),
InlineKeyboardButton('ʟsᴇ', callback_data='close'),
],
[InlineKeyboardButton("📢 ᴜᴘᴅᴀᴛᴇ ᴄʜᴀɴɴᴇʟ", url=f'https://t.me/{Telegram.UPDATES_CHANNEL}')]
]
)
ABOUT_BUTTONS = InlineKeyboardMarkup(
[[
InlineKeyboardButton('ʜᴏᴍᴇ', callback_data='home'),
InlineKeyboardButton('ʜᴇʟᴘ', callback_data='help'),
InlineKeyboardButton('ʟsᴇ', callback_data='close'),
],
[InlineKeyboardButton("📢 ᴜᴘᴅᴀᴛᴇ ᴄʜᴀɴɴᴇʟ", url=f'https://t.me/{Telegram.UPDATES_CHANNEL}')]
]
)

View File

@@ -1 +1 @@
web: python -m WebStreamer
web: python -m FileStream

196
README.md
View File

@@ -1,24 +1,20 @@
<h1 align="center">FileStreamBot</h1>
<p align="center">
<a href="https://github.com/Avipatilpro/FileStreamBot">
<img src="https://socialify.git.ci/avipatilpro/FileStreamBot/image?description=1&descriptionEditable=Telegram%20File%20to%20Link%20Fastest%20Bot%20%2C%20also%20used%20for%20Movies%20streaming%20Generate%20Direct%20Links&font=KoHo&forks=1&logo=https%3A%2F%2Fi.ibb.co%2FZJzJ9Hq%2Flink-3x.png&pattern=Brick%20Wall&stargazers=1&theme=Dark" alt="Cover Image" width="550">
<img src="https://graph.org/file/80d1f94e81bbc1acadb36.jpg" alt="Cover Image" width="550">
</a>
</p>
<p align="center">
A Telegram bot to all media and documents files to web link .
<br />
</strong></a>
<br />
<br><b>
<a href="https://github.com/Avipatilpro/FileStreamBot/issues">Report a Bug</a>
|
<a href="https://github.com/Avipatilpro/FileStreamBot/issues">Request Feature</a>
</p>
<a href="https://github.com/Avipatilpro/FileStreamBot/issues">Request Feature</a></b>
</p>
## 🚸 Updating .............
## 🍁 About This Bot :
### 🍁 About :
<p align="center">
<a href="https://github.com/Avipatilpro/FileStreamBot">
@@ -26,115 +22,175 @@
</a>
</p>
<p align='center'>
This bot will give you stream links for Telegram files without the need of waiting till the download completes
This bot provides stream links for Telegram files without the necessity of waiting for the download to complete, offering the ability to store files.
</p>
## ♢ How to make your own :
### ♢ How to Deploy :
<i>Either you could locally host or deploy on [Heroku](https://heroku.com)</i>
<i>Either you could locally host, VPS, or deploy on [Heroku](https://heroku.com)</i>
#### ♢ Click on This Drop-down and get more details
<br>
<details>
<summary><b>Deploy on Heroku :</b></summary>
<summary><b>Deploy on Heroku (Paid) :</b></summary>
- Fork This Repo
- Click on Deploy Easily
- Press the below button to Fast deploy on Heroku
1. Fork This Repo
2. Click on Deploy Easily
<h4> So Follow Above Steps 👆 and then also deply other wise not work</h4>
Press the below button to Fast deploy on Heroku
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)
then goto the <a href="#mandatory-vars">variables tab</a> for more info on setting up environmental variables. </details>
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/avipatilpro/FileStreamBot)
- Go to <a href="#mandatory-vars">variables tab</a> for more info on setting up environmental variables. </details>
<details>
<summary><b>Host it on VPS Locally :</b></summary>
<summary><b>Deploy Locally :</b></summary>
<br>
```py
```sh
git clone https://github.com/avipatilpro/FileStreamBot
cd FileStreamBot
virtualenv -p /usr/bin/python3 venv
python3 -m venv ./venv
. ./venv/bin/activate
pip install -r requirements.txt
python3 -m WebStreamer
python3 -m FileStream
```
and to stop the whole bot,
- To stop the whole bot,
do <kbd>CTRL</kbd>+<kbd>C</kbd>
Setting up things
- If you want to run this bot 24/7 on the VPS, follow these steps.
```sh
sudo apt install tmux -y
tmux
python3 -m FileStream
```
- now you can close the VPS and the bot will run on it.
</details>
<details>
<summary><b>Deploy using Docker :</b></summary>
<br>
* Clone the repository:
```sh
git clone https://github.com/avipatilpro/FileStreamBot
cd FileStreamBot
```
* Build own Docker image:
```sh
docker build -t file-stream .
```
* Create ENV and Start Container:
```sh
docker run -d --restart unless-stopped --name fsb \
-v /PATH/TO/.env:/app/.env \
-p 8000:8000 \
file-stream
```
- if you need to change the variables in .env file after your bot was already started, all you need to do is restart the container for the bot settings to get updated:
```sh
docker restart fsb
```
</details>
<details>
<summary><b>Setting up things :</b></summary>
If you're on Heroku, just add these in the Environmental Variables
or if you're Locally hosting, create a file named `.env` in the root directory and add all the variables there.
An example of `.env` file:
```py
API_ID=12345
API_HASH=esx576f8738x883f3sfzx83
BOT_TOKEN=55838383:yourtbottokenhere
BIN_CHANNEL=-100
```sh
API_ID = 789456
API_HASH = ysx275f9638x896g43sfzx65
BOT_TOKEN = 12345678:your_bot_token
BIN_CHANNEL = -100xxxxxxxx
DATABASE_URL = mongodb://admin:pass@192.168.27.1
FQDN = 192.168.27.1
HAS_SSL = False
MULTI_TOKEN1 = 12345678:bot_token_multi_client_1
MULTI_TOKEN2 = 12345678:bot_token_multi_client_2
OWNER_ID = 987456321
PORT = 8080
FQDN=your_server_ip
OWNER_ID=your_user_id
DATABASE_URL=mongodb_uri
```
</details>
<details>
<summary><b>Vars and Details :</b></summary>
`API_ID` : Goto [my.telegram.org](https://my.telegram.org) to obtain this.
#### 📝 Mandatory Vars :
`API_HASH` : Goto [my.telegram.org](https://my.telegram.org) to obtain this.
* `API_ID`: API ID of your Telegram account, can be obtained from [My Telegram](https://my.telegram.org). `int`
* `API_HASH`: API hash of your Telegram account, can be obtained from [My Telegram](https://my.telegram.org). `str`
* `OWNER_ID`: Your Telegram User ID, Send `/id` to [@missrose_bot](https://telegram.dog/MissRose_bot) to get Your Telegram User ID `int`
* `BOT_TOKEN`: Telegram API token of your bot, can be obtained from [@BotFather](https://t.me/BotFather). `str`
* `LOG_CHANNEL`: ID of the channel where bot will forward all files received from users. `int`.
* `BOT_WORKERS`: Number of updates bot should process from Telegram at once, by default to 10 updates. `int`
* `DATABASE_URL`: MongoDB URI for saving User Data and Files List created by user. `str`
* `FQDN`: A Fully Qualified Domain Name if present without http/s. Defaults to `BIND_ADDRESS`. `str`
`BOT_TOKEN` : Get the bot token from [@BotFather](https://telegram.dog/BotFather)
#### 🗼 MultiClient Vars :
* `MULTI_TOKEN1`: Add your first bot token or session strings here. `str`
* `MULTI_TOKEN2`: Add your second bot token or session strings here. `str`
`BIN_CHANNEL` : Create a new channel (private/public), add [@missrose_bot](https://telegram.dog/MissRose_bot) as admin to the channel and type /id. Now copy paste the ID into this field.
#### 🪐 Optional Vars :
`OWNER_ID` : Your Telegram User ID
* `UPDATES_CHANNEL`: Channel Username without `@` to set channel as Update Channel `str`
* `FORCE_UPDATES_CHANNEL`: Set to True, so every user have to Join update channel to use the bot. `bool`
* `AUTH_USERS`: Put authorized user IDs to use bot, separated by <kbd>Space</kbd>. `int`
* `SLEEP_THRESHOLD`: Set global flood wait threshold, auto-retry requests under 60s. `int`
* `SESSION_NAME`: Name for the Database created on your MongoDB. Defaults to `FileStream`. `str`
* `IMAGE_FILEID`: To set Image at `/files` command. Defaults to pre-set image. `str`
* `WORKERS`: Number of maximum concurrent workers for handling incoming updates. Defaults to `6`. `int`
* `PORT`: The port that you want your webapp to be listened to. Defaults to `8080`. `int`
* `BIND_ADDRESS`: Your server bind adress. Defauls to `0.0.0.0`. `int`
* `MODE`: Should be set to `secondary` if you only want to use the server for serving files. `str`
* `PING_INTERVAL`: Heroku ping interval (ms), defaults to 1200 (20 minutes). `int`
* `KEEP_ALIVE`: Self-ping server every PING_INTERVAL sec to avoid sleeping defaults to `False`.
* `NO_PORT`: (True/False) Set PORT to 80 or 443 hide port display; ignore if on Heroku. Defaults to `False`.
* `HAS_SSL`: (can be either `True` or `False`) If you want the generated links in https format. Defaults to `False`.
`DATABASE_URL` : MongoDB URI for saving User IDs when they first Start the Bot. We will use that for Broadcasting to them. I will try to add more features related with Database. If you need help to get the URI you can ask in [Me Telegram](https://t.me/Avishkarpatil).
Option Vars
`UPDATES_CHANNEL` : Put a Public Channel Username, so every user have to Join that channel to use the bot. Must add bot to channel as Admin to work properly.
`BANNED_CHANNELS` : Put IDs of Banned Channels where bot will not work. You can add multiple IDs & separate with <kbd>Space</kbd>.
`SLEEP_THRESHOLD` : Set a sleep threshold for flood wait exceptions happening globally in this telegram bot instance, below which any request that raises a flood wait will be automatically invoked again after sleeping for the required amount of time. Flood wait exceptions requiring higher waiting times will be raised. Defaults to 60 seconds.
`WORKERS` : Number of maximum concurrent workers for handling incoming updates. Defaults to `3`
`PORT` : The port that you want your webapp to be listened to. Defaults to `8080`
`WEB_SERVER_BIND_ADDRESS` : Your server bind adress. Defauls to `0.0.0.0`
`NO_PORT` : If you don't want your port to be displayed. You should point your `PORT` to `80` (http) or `443` (https) for the links to work. Ignore this if you're on Heroku.
`FQDN` : A Fully Qualified Domain Name if present. Defaults to `WEB_SERVER_BIND_ADDRESS` </details>
</details>
<details>
<summary><b>How to Use :</b></summary>
:warning: **Before using the bot, don't forget to add the bot to the `BIN_CHANNEL` as an Admin**
:warning: **Before using the bot, don't forget to add the bot to the `LOG_CHANNEL` as an Admin**
`/start` : To check if the bot is alive or not.
#### ‍☠️ Bot Commands :
To get an instant stream link, just forward any media to the bot and boom, its fast af.
```sh
/start : To check if the bot is alive or not.
/help : To Get Help Message.
/about : To check About the Bot.
/files : To Get All Files List of User.
/del : To Delete Files from DB with FileID.
/ban : To Ban Any Channel or User to use bot.
/unban : To Unban Any Channel or User to use bot.
/status : To Get Bot Status and Total Users.
/broadcast : To Broadcast any message to all users of bot.
```
### Channel Support
Bot also Supported with Channels. Just add bot Channel as Admin. If any new file comes in Channel it will edit it with **Get Download Link** Button. </details>
#### 🍟 Channel Support :
*Bot also Supported with Channels. Just add bot Channel as Admin. If any new file comes in Channel it will edit it with **Get Download Link** Button.*
</details>
### ❤️ Thanks To :
- [**Me**](https://github.com/AvishkarPatil) : Owner of This FileStreamBot
- [**Deekshith SH**](https://github.com/DeekshithSH) : for some modules.
- [**EverythingSuckz**](https://github.com/EverythingSuckz) : for his [FileStreamBot](https://github.com/EverythingSuckz/FileStreamBot)
---
<h4 align='center'>© 2022 Aνιѕнкαя Pαтι</h4>
<h4 align='center'>© 2024 Aνιѕнкαя Pαтι</h4>
<!-- DO NOT REMOVE THIS CREDIT 🤬 🤬 -->

View File

@@ -1 +0,0 @@
# This file is a part of avipatilpro/FileStreamBot

View File

@@ -1,64 +0,0 @@
import os
import sys
import glob
import asyncio
import logging
import importlib
from pathlib import Path
from pyrogram import idle
from .bot import StreamBot
from .vars import Var
from aiohttp import web
from .server import web_server
from .utils.keepalive import ping_server
ppath = "WebStreamer/bot/plugins/*.py"
files = glob.glob(ppath)
loop = asyncio.get_event_loop()
async def start_services():
print('\n')
print('------------------- Initalizing Telegram Bot -------------------')
await StreamBot.start()
print('\n')
print('---------------------- DONE ----------------------')
print('\n')
print('------------------- Importing -------------------')
for name in files:
with open(name) as a:
patt = Path(a.name)
plugin_name = patt.stem.replace(".py", "")
plugins_dir = Path(f"WebStreamer/bot/plugins/{plugin_name}.py")
import_path = ".plugins.{}".format(plugin_name)
spec = importlib.util.spec_from_file_location(import_path, plugins_dir)
load = importlib.util.module_from_spec(spec)
spec.loader.exec_module(load)
sys.modules["WebStreamer.bot.plugins." + plugin_name] = load
print("Imported => " + plugin_name)
print('\n')
print('------------------- Initalizing Web Server -------------------')
app = web.AppRunner(await web_server())
await app.setup()
bind_address = "0.0.0.0" if Var.ON_HEROKU else Var.FQDN
await web.TCPSite(app, bind_address, Var.PORT).start()
print('\n')
print('----------------------- Service Started -----------------------')
print(' bot =>> {}'.format((await StreamBot.get_me()).first_name))
print(' server ip =>> {}:{}'.format(bind_address, Var.PORT))
if Var.ON_HEROKU:
print(' app runnng on =>> {}'.format(Var.FQDN))
if Var.ON_HEROKU:
print('------------------ Starting Keep Alive Service ------------------')
print('\n')
await asyncio.create_task(ping_server())
print('---------------------------------------------------------------')
await idle()
if __name__ == '__main__':
try:
loop.run_until_complete(start_services())
except KeyboardInterrupt:
print('----------------------- Service Stopped -----------------------')

View File

@@ -1,12 +0,0 @@
from pyrogram import Client
from ..vars import Var
StreamBot = Client(
name='Web Streamer',
api_id=Var.API_ID,
api_hash=Var.API_HASH,
bot_token=Var.BOT_TOKEN,
sleep_threshold=Var.SLEEP_THRESHOLD,
workers=Var.WORKERS
)

View File

@@ -1,90 +0,0 @@
# (c) @Avishkarpatil
import os
import time
import string
import random
import asyncio
import aiofiles
import datetime
from WebStreamer.utils.broadcast_helper import send_msg
from WebStreamer.utils.database import Database
from WebStreamer.bot import StreamBot
from WebStreamer.vars import Var
from pyrogram import filters, Client
from pyrogram.types import Message
from pyrogram.enums.parse_mode import ParseMode
db = Database(Var.DATABASE_URL, Var.SESSION_NAME)
broadcast_ids = {}
@StreamBot.on_message(filters.command("status") & filters.private & filters.user(Var.OWNER_ID))
async def sts(c: Client, m: Message):
total_users = await db.total_users_count()
await m.reply_text(text=f"**Total Users in DB:** `{total_users}`", parse_mode=ParseMode.MARKDOWN, quote=True)
@StreamBot.on_message(filters.command("broadcast") & filters.private & filters.user(Var.OWNER_ID) & filters.reply)
async def broadcast_(c, m):
all_users = await db.get_all_users()
broadcast_msg = m.reply_to_message
while True:
broadcast_id = ''.join([random.choice(string.ascii_letters) for i in range(3)])
if not broadcast_ids.get(broadcast_id):
break
out = await m.reply_text(
text=f"Broadcast initiated! You will be notified with log file when all the users are notified."
)
start_time = time.time()
total_users = await db.total_users_count()
done = 0
failed = 0
success = 0
broadcast_ids[broadcast_id] = dict(
total=total_users,
current=done,
failed=failed,
success=success
)
async with aiofiles.open('broadcast.txt', 'w') as broadcast_log_file:
async for user in all_users:
sts, msg = await send_msg(
user_id=int(user['id']),
message=broadcast_msg
)
if msg is not None:
await broadcast_log_file.write(msg)
if sts == 200:
success += 1
else:
failed += 1
if sts == 400:
await db.delete_user(user['id'])
done += 1
if broadcast_ids.get(broadcast_id) is None:
break
else:
broadcast_ids[broadcast_id].update(
dict(
current=done,
failed=failed,
success=success
)
)
if broadcast_ids.get(broadcast_id):
broadcast_ids.pop(broadcast_id)
completed_in = datetime.timedelta(seconds=int(time.time() - start_time))
await asyncio.sleep(3)
await out.delete()
if failed == 0:
await m.reply_text(
text=f"broadcast completed in `{completed_in}`\n\nTotal users {total_users}.\nTotal done {done}, {success} success and {failed} failed.",
quote=True
)
else:
await m.reply_document(
document='broadcast.txt',
caption=f"broadcast completed in `{completed_in}`\n\nTotal users {total_users}.\nTotal done {done}, {success} success and {failed} failed.",
quote=True
)
os.remove('broadcast.txt')

View File

@@ -1,261 +0,0 @@
import urllib.parse
from WebStreamer.bot import StreamBot
from WebStreamer.vars import Var
from WebStreamer.utils.human_readable import humanbytes
from WebStreamer.utils.database import Database
from pyrogram import filters
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
from pyrogram.errors import UserNotParticipant
from pyrogram.enums.parse_mode import ParseMode
db = Database(Var.DATABASE_URL, Var.SESSION_NAME)
START_TEXT = """
<i>👋 Hᴇʏ,</i>{}\n
<i>I'ᴍ Tᴇʟᴇɢʀᴀᴍ Fɪʟᴇs Sᴛʀᴇᴀᴍɪɴɢ Bᴛ ᴀs ᴡᴇʟʟ Dɪʀᴇᴛ Lɪɴᴋs Gᴇɴᴇʀᴀᴛᴇ</i>\n
<i>Cʟɪᴋ ᴏɴ Hᴇʟᴘ ᴛᴏ ɢᴇᴛ ᴍᴏʀᴇ ɪɴғᴏʀᴍᴀᴛɪᴏɴ</i>\n
<i><u>𝗪𝗔𝗥𝗡𝗜𝗡𝗚 🚸</u></i>
<b>🔞 Pʀɴ ᴄᴏɴᴛᴇɴᴛꜱ ʟᴇᴀᴅꜱ ᴛᴏ ᴘᴇʀᴍᴀɴᴇɴᴛ ʙᴀɴ ʏ.</b>\n\n
<i><b>🍃 Bᴛ Mᴀɪɴᴛᴀɪɴᴇᴅ Bʏ :</b>@AvishkarPatil</i>"""
HELP_TEXT = """
<i>- Sᴇɴᴅ ᴍᴇ ᴀɴʏ ꜰɪʟᴇ (ᴏʀ) ᴍᴇᴅɪᴀ ꜰʀᴏᴍ ᴛᴇʟᴇɢʀᴀᴍ.</i>
<i>- I ᴡɪʟʟ ᴘʀᴏᴠɪᴅᴇ ᴇxᴛᴇʀɴᴀʟ ᴅɪʀᴇᴄᴛ ᴅᴏᴡɴʟᴏᴀᴅ ʟɪɴᴋ !.</i>
<i>- Aᴅᴅ Mᴇ ɪɴ ʏᴏᴜʀ Cʜᴀɴɴᴇʟ Fʀ Dɪʀᴇᴛ Dɴʟᴀᴅ Lɪɴᴋs Bᴛᴛɴ</i>
<i>- Tʜɪs Pᴇʀᴍᴇᴀɴᴛ Lɪɴᴋ Wɪᴛʜ Fᴀsᴛᴇsᴛ Sᴘᴇᴇᴅ</i>\n
<u>🔸 𝗪𝗔𝗥𝗡𝗜𝗡𝗚 🚸</u>\n
<b>🔞 Pʀɴ ᴄᴏɴᴛᴇɴᴛꜱ ʟᴇᴀᴅꜱ ᴛᴏ ᴘᴇʀᴍᴀɴᴇɴᴛ ʙᴀɴ ʏ.</b>\n
<i>Cɴᴛᴀᴛ ᴅᴇᴠᴇʟᴏᴘᴇʀ (ᴏʀ) ʀᴇᴘᴏʀᴛ ʙᴜɢꜱ</i> <b>: <a href='https://t.me/Avishkarpatil'>[ ᴄʟɪᴄᴋ ʜᴇʀᴇ ]</a></b>"""
ABOUT_TEXT = """
<b>⚜ Mʏ ɴᴀᴍᴇ : FileStreamX</b>\n
<b>🔸Vᴇʀɪɴ : <a href='https://telegram.me/AvishkarPatil'>3.0.1</a></b>\n
<b>🔹Sʀᴇ : <a href='https://github.com/avipatilpro/FileStreamBot'>Cʟɪᴋ Hᴇʀᴇ</a></b>\n
<b>🔸GitHub : <a href='https://GitHub.com/avipatilpro'>Fʟʟ</a></b>\n
<b>🔹Dᴇᴇʟᴘᴇʀ : <a href='https://telegram.me/Avishkarpatil'>Aɪsʜᴋᴀʀ Pᴀᴛɪʟ</a></b>\n
<b>🔸Lᴀᴛ ᴜᴘᴅᴀᴛᴇᴅ : <a href='https://telegram.me/AvishkarPatil'>[ 26 - ᴊᴜɴᴇ - 2022 ] 03:35 ᴀᴍ</a></b>"""
START_BUTTONS = InlineKeyboardMarkup(
[[
InlineKeyboardButton('Hᴇʟᴘ', callback_data='help'),
InlineKeyboardButton('', callback_data='about'),
InlineKeyboardButton('sᴇ', callback_data='close')
]]
)
HELP_BUTTONS = InlineKeyboardMarkup(
[[
InlineKeyboardButton('Hᴍᴇ', callback_data='home'),
InlineKeyboardButton('', callback_data='about'),
InlineKeyboardButton('sᴇ', callback_data='close')
]]
)
ABOUT_BUTTONS = InlineKeyboardMarkup(
[[
InlineKeyboardButton('Hᴍᴇ', callback_data='home'),
InlineKeyboardButton('Hᴇʟᴘ', callback_data='help'),
InlineKeyboardButton('sᴇ', callback_data='close')
]]
)
@StreamBot.on_callback_query()
async def cb_data(bot, update):
if update.data == "home":
await update.message.edit_text(
text=START_TEXT.format(update.from_user.mention),
disable_web_page_preview=True,
reply_markup=START_BUTTONS
)
elif update.data == "help":
await update.message.edit_text(
text=HELP_TEXT,
disable_web_page_preview=True,
reply_markup=HELP_BUTTONS
)
elif update.data == "about":
await update.message.edit_text(
text=ABOUT_TEXT,
disable_web_page_preview=True,
reply_markup=ABOUT_BUTTONS
)
else:
await update.message.delete()
def get_media_file_size(m):
media = m.video or m.audio or m.document
if media and media.file_size:
return media.file_size
else:
return None
def get_media_file_name(m):
media = m.video or m.document or m.audio
if media and media.file_name:
return urllib.parse.quote_plus(media.file_name)
else:
return None
@StreamBot.on_message(filters.command('start') & filters.private)
async def start(b, m):
if not await db.is_user_exist(m.from_user.id):
await db.add_user(m.from_user.id)
await b.send_message(
Var.BIN_CHANNEL,
f"**Nᴇ Usᴇʀ Jɪɴᴇᴅ:** \n\n__Mʏ Nᴇɪᴇɴᴅ__ [{m.from_user.first_name}](tg://user?id={m.from_user.id}) __Sᴛᴀʀᴛᴇᴅ Yʀ Bᴛ !!__"
)
usr_cmd = m.text.split("_")[-1]
if usr_cmd == "/start":
if Var.UPDATES_CHANNEL != "None":
try:
user = await b.get_chat_member(Var.UPDATES_CHANNEL, m.chat.id)
if user.status == "kicked":
await b.send_message(
chat_id=m.chat.id,
text="__Sʀʀʏ Sɪʀ, Y ᴀʀᴇ Bᴀɴɴᴇᴅ ᴛᴏ sᴇ ᴍᴇ. Cɴᴛᴀᴛ ᴛʜᴇ Dᴇᴇʟᴘᴇʀ__\n\n @AvishkarPatil **Tʜᴇʏ Wɪʟʟ Hᴇʟᴘ Y**",
parse_mode=ParseMode.MARKDOWN,
disable_web_page_preview=True
)
return
except UserNotParticipant:
await b.send_message(
chat_id=m.chat.id,
text="<i>Jɪɴ ᴍʏ ᴜᴘᴅᴀᴛᴇ ᴄʜᴀɴɴᴇʟ ᴛᴏ sᴇ ᴍᴇ 🔐</i>",
reply_markup=InlineKeyboardMarkup(
[[
InlineKeyboardButton("Jɪɴ ɴᴏᴡ 🔓", url=f"https://t.me/{Var.UPDATES_CHANNEL}")
]]
),
parse_mode=ParseMode.HTML
)
return
except Exception:
await b.send_message(
chat_id=m.chat.id,
text="<i>Sᴍᴇᴛʜɪɴɢ ᴡʀᴏɴɢ ᴄᴏɴᴛᴀᴄᴛ ᴍʏ ᴅᴇᴠᴇʟᴏᴘᴇʀ</i> <b><a href='http://t.me/Avishkarpatil'>[ ᴄʟɪᴄᴋ ʜᴇʀᴇ ]</a></b>",
parse_mode=ParseMode.HTML,
disable_web_page_preview=True)
return
await m.reply_text(
text=START_TEXT.format(m.from_user.mention),
parse_mode=ParseMode.HTML,
disable_web_page_preview=True,
reply_markup=START_BUTTONS
)
else:
if Var.UPDATES_CHANNEL != "None":
try:
user = await b.get_chat_member(Var.UPDATES_CHANNEL, m.chat.id)
if user.status == "kicked":
await b.send_message(
chat_id=m.chat.id,
text="**Sʀʀʏ Sɪʀ, Y ᴀʀᴇ Bᴀɴɴᴇᴅ ᴛᴏ sᴇ ᴍᴇ. Qɪᴋʟʏ ᴄᴏɴᴛᴀᴄᴛ** @Avishkarpatil",
parse_mode=ParseMode.MARKDOWN,
disable_web_page_preview=True
)
return
except UserNotParticipant:
await b.send_message(
chat_id=m.chat.id,
text="**Pʟᴇᴀsᴇ Jɪɴ Mʏ Uᴘᴅᴀᴛᴇs Cʜᴀɴɴᴇʟ ᴛᴏ sᴇ ᴛʜɪs Bᴛ**!\n\n**Dᴇ ᴛᴏ Oᴇʀʟᴀᴅ, Oɴʟʏ Cʜᴀɴɴᴇʟ Sʙsʀɪʙᴇʀs ᴄᴀɴ sᴇ ᴛʜᴇ Bᴛ**!",
reply_markup=InlineKeyboardMarkup(
[[
InlineKeyboardButton("🤖 Jɪɴ Uᴘᴅᴀᴛᴇs Cʜᴀɴɴᴇʟ", url=f"https://t.me/{Var.UPDATES_CHANNEL}")],
[InlineKeyboardButton("🔄 Refresh / Try Again", url=f"https://t.me/{(await b.get_me()).username}?start=AvishkarPatil_{usr_cmd}")
]]
),
parse_mode=ParseMode.MARKDOWN
)
return
except Exception:
await b.send_message(
chat_id=m.chat.id,
text="**Sᴍᴇᴛʜɪɴɢ ᴡᴇɴᴛ Wʀɴɢ. Cɴᴛᴀᴛ ᴍᴇ** [Aɪsʜᴋᴀʀ Pᴀᴛɪʟ](https://t.me/Avishkarpatil).",
parse_mode=ParseMode.MARKDOWN,
disable_web_page_preview=True)
return
get_msg = await b.get_messages(chat_id=Var.BIN_CHANNEL, message_ids=int(usr_cmd))
file_name = get_media_file_name(get_msg)
file_size = humanbytes(get_media_file_size(get_msg))
stream_link = "https://{}/{}/{}".format(Var.FQDN, get_msg.id, file_name) if Var.ON_HEROKU or Var.NO_PORT else \
"http://{}:{}/{}/{}".format(Var.FQDN,
Var.PORT,
get_msg.id,
file_name)
msg_text ="""
<i><u>𝗬𝗼𝘂𝗿 𝗟𝗶𝗻𝗸 𝗚𝗲𝗻𝗲𝗿𝗮𝘁𝗲𝗱 !</u></i>\n
<b>📂 Fɪʟᴇ ɴᴀᴍᴇ :</b> <i>{}</i>\n
<b>📦 Fɪʟᴇ ꜱɪᴢᴇ :</b> <i>{}</i>\n
<b>📥 Dɴʟᴀᴅ :</b> <i>{}</i>\n
<b>🚸 Nᴛᴇ : Lɪɴᴋ ᴇxᴘɪʀᴇᴅ ɪɴ 24 ʜᴏᴜʀꜱ</b>\n
<i>🍃 Bᴛ Mᴀɪɴᴛᴀɪɴᴇᴅ Bʏ :</i> <b>@AvishkarPatil</b>
"""
await m.reply_text(
text=msg_text.format(file_name, file_size, stream_link),
parse_mode=ParseMode.HTML,
reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Dɴʟᴀᴅ ɴᴏᴡ 📥", url=stream_link)]])
)
@StreamBot.on_message(filters.private & filters.command(["about"]))
async def start(bot, update):
await update.reply_text(
text=ABOUT_TEXT.format(update.from_user.mention),
disable_web_page_preview=True,
reply_markup=ABOUT_BUTTONS
)
@StreamBot.on_message(filters.command('help') & filters.private)
async def help_handler(bot, message):
if not await db.is_user_exist(message.from_user.id):
await db.add_user(message.from_user.id)
await bot.send_message(
Var.BIN_CHANNEL,
f"**Nᴇ Usᴇʀ Jɪɴᴇᴅ **\n\n__Mʏ Nᴇɪᴇɴᴅ__ [{message.from_user.first_name}](tg://user?id={message.from_user.id}) __Started Your Bot !!__"
)
if Var.UPDATES_CHANNEL is not None:
try:
user = await bot.get_chat_member(Var.UPDATES_CHANNEL, message.chat.id)
if user.status == "kicked":
await bot.send_message(
chat_id=message.chat.id,
text="<i>Sʀʀʏ Sɪʀ, Y ᴀʀᴇ Bᴀɴɴᴇᴅ ᴛᴏ sᴇ ᴍᴇ. Cɴᴛᴀᴛ ᴛʜᴇ Dᴇᴇʟᴘᴇʀ</i>",
parse_mode=ParseMode.HTML,
disable_web_page_preview=True
)
return
except UserNotParticipant:
await bot.send_message(
chat_id=message.chat.id,
text="**Pʟᴇᴀsᴇ Jɪɴ Mʏ Uᴘᴅᴀᴛᴇs Cʜᴀɴɴᴇʟ ᴛᴏ sᴇ ᴛʜɪs Bᴛ!**\n\n__Dᴇ ᴛᴏ Oᴇʀʟᴀᴅ, Oɴʟʏ Cʜᴀɴɴᴇʟ Sʙsʀɪʙᴇʀs ᴄᴀɴ sᴇ ᴛʜᴇ Bᴛ!__",
reply_markup=InlineKeyboardMarkup(
[[
InlineKeyboardButton("🤖 Jɪɴ Uᴘᴅᴀᴛᴇs Cʜᴀɴɴᴇʟ", url=f"https://t.me/{Var.UPDATES_CHANNEL}")
]]
),
parse_mode=ParseMode.MARKDOWN
)
return
except Exception:
await bot.send_message(
chat_id=message.chat.id,
text="__Sᴍᴇᴛʜɪɴɢ ᴡᴇɴᴛ Wʀɴɢ. Cɴᴛᴀᴛ ᴍᴇ__ [Aɪsʜᴋᴀʀ Pᴀᴛɪʟ](https://t.me/Avishkarpatil).",
parse_mode=ParseMode.MARKDOWN,
disable_web_page_preview=True)
return
await message.reply_text(
text=HELP_TEXT,
parse_mode=ParseMode.HTML,
disable_web_page_preview=True,
reply_markup=HELP_BUTTONS
)

View File

@@ -1,134 +0,0 @@
# (c) @Avishkarpatil
import asyncio
import urllib.parse
from WebStreamer.bot import StreamBot
from WebStreamer.utils.database import Database
from WebStreamer.utils.human_readable import humanbytes
from WebStreamer.vars import Var
from pyrogram import filters, Client
from pyrogram.errors import FloodWait, UserNotParticipant
from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
from pyrogram.enums.parse_mode import ParseMode
db = Database(Var.DATABASE_URL, Var.SESSION_NAME)
def get_media_file_size(m):
media = m.video or m.audio or m.document
if media and media.file_size:
return media.file_size
else:
return None
def get_media_file_name(m):
media = m.video or m.document or m.audio
if media and media.file_name:
return urllib.parse.quote_plus(media.file_name)
else:
return None
@StreamBot.on_message(filters.private & (filters.document | filters.video | filters.audio), group=4)
async def private_receive_handler(c: Client, m: Message):
if not await db.is_user_exist(m.from_user.id):
await db.add_user(m.from_user.id)
await c.send_message(
Var.BIN_CHANNEL,
f"Nᴇ Usᴇʀ Jɪɴᴇᴅ : \n\nNᴀᴍᴇ : [{m.from_user.first_name}](tg://user?id={m.from_user.id}) Sᴛᴀʀᴛᴇᴅ Yʀ Bᴛ !!"
)
if Var.UPDATES_CHANNEL != "None":
try:
user = await c.get_chat_member(Var.UPDATES_CHANNEL, m.chat.id)
if user.status == "kicked":
await c.send_message(
chat_id=m.chat.id,
text="__Sʀʀʏ Sɪʀ, Y ᴀʀᴇ Bᴀɴɴᴇᴅ ᴛᴏ sᴇ ᴍᴇ.__\n\n **Cɴᴛᴀᴛ Dᴇᴇʟᴘᴇʀ @Avishkarpatil Tʜᴇʏ Wɪʟʟ Hᴇʟᴘ Y**",
parse_mode=ParseMode.MARKDOWN,
disable_web_page_preview=True
)
return
except UserNotParticipant:
await c.send_message(
chat_id=m.chat.id,
text="""<i>Jɪɴ ᴍʏ ᴜᴘᴅᴀᴛᴇ ᴄʜᴀɴɴᴇʟ ᴛᴏ ᴜꜱᴇ ᴍᴇ 🔐</i>""",
reply_markup=InlineKeyboardMarkup(
[[ InlineKeyboardButton("Jɪɴ ɴᴏᴡ 🔓", url=f"https://t.me/{Var.UPDATES_CHANNEL}") ]]
),
parse_mode=ParseMode.HTML
)
return
except Exception:
await c.send_message(
chat_id=m.chat.id,
text="**Sᴍᴇᴛʜɪɴɢ ᴡᴇɴᴛ Wʀɴɢ. Cɴᴛᴀᴛ ᴍʏ ʙss** @Avishkarpatil",
parse_mode=ParseMode.MARKDOWN,
disable_web_page_preview=True)
return
try:
log_msg = await m.forward(chat_id=Var.BIN_CHANNEL)
file_name = get_media_file_name(m)
file_size = humanbytes(get_media_file_size(m))
stream_link = "https://{}/{}/{}".format(Var.FQDN, log_msg.id, file_name) if Var.ON_HEROKU or Var.NO_PORT else \
"http://{}:{}/{}/{}".format(Var.FQDN,
Var.PORT,
log_msg.id,
file_name)
msg_text ="""
<i><u>𝗬𝗼𝘂𝗿 𝗟𝗶𝗻𝗸 𝗚𝗲𝗻𝗲𝗿𝗮𝘁𝗲𝗱 !</u></i>\n
<b>📂 Fɪʟᴇ ɴᴀᴍᴇ :</b> <i>{}</i>\n
<b>📦 Fɪʟᴇ ꜱɪᴢᴇ :</b> <i>{}</i>\n
<b>📥 Dɴʟᴀᴅ :</b> <i>{}</i>\n
<b>🚸 Nᴛᴇ : Tʜɪs ᴘᴇʀᴍᴀɴᴇɴᴛ Lɪɴᴋ, Nᴛ Exᴘɪʀᴇᴅ</b>\n
<i>© @AvishkarPatil </i>"""
await log_msg.reply_text(text=f"**RᴇQᴛᴇᴅ ʙʏ :** [{m.from_user.first_name}](tg://user?id={m.from_user.id})\n**Uᴇʀ ɪᴅ :** `{m.from_user.id}`\n**Dɴʟᴀᴅ ʟɪɴᴋ :** {stream_link}", disable_web_page_preview=True, parse_mode=ParseMode.MARKDOWN, quote=True)
await m.reply_text(
text=msg_text.format(file_name, file_size, stream_link),
parse_mode=ParseMode.HTML,
disable_web_page_preview=True,
reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Dɴʟᴀᴅ ɴᴏᴡ 📥", url=stream_link)]]),
quote=True
)
except FloodWait as e:
print(f"Sleeping for {str(e.value)}s")
await asyncio.sleep(e.value)
await c.send_message(chat_id=Var.BIN_CHANNEL, text=f"Gᴛ FʟᴅWᴀɪᴛ ᴏғ {str(e.value)}s from [{m.from_user.first_name}](tg://user?id={m.from_user.id})\n\n**𝚄𝚜𝚎𝚛 𝙸𝙳 :** `{str(m.from_user.id)}`", disable_web_page_preview=True, parse_mode=ParseMode.MARKDOWN)
@StreamBot.on_message(filters.channel & (filters.document | filters.video), group=-1)
async def channel_receive_handler(bot, broadcast):
if int(broadcast.chat.id) in Var.BANNED_CHANNELS:
await bot.leave_chat(broadcast.chat.id)
return
try:
log_msg = await broadcast.forward(chat_id=Var.BIN_CHANNEL)
stream_link = "https://{}/{}".format(Var.FQDN, log_msg.id) if Var.ON_HEROKU or Var.NO_PORT else \
"http://{}:{}/{}".format(Var.FQDN,
Var.PORT,
log_msg.id)
await log_msg.reply_text(
text=f"**Cʜᴀɴɴᴇʟ Nᴀᴍᴇ:** `{broadcast.chat.title}`\n**Cʜᴀɴɴᴇʟ ID:** `{broadcast.chat.id}`\n**Rᴇǫᴇsᴛ ᴜʀʟ:** https://t.me/{(await bot.get_me()).username}?start=AvishkarPatil_{str(log_msg.id)}",
# text=f"**Cʜᴀɴɴᴇʟ Nᴀᴍᴇ:** `{broadcast.chat.title}`\n**Cʜᴀɴɴᴇʟ ID:** `{broadcast.chat.id}`\n**Rᴇǫᴇsᴛ ᴜʀʟ:** https://t.me/FxStreamBot?start=AvishkarPatil_{str(log_msg.id)}",
quote=True,
parse_mode=ParseMode.MARKDOWN
)
await bot.edit_message_reply_markup(
chat_id=broadcast.chat.id,
message_id=broadcast.id,
reply_markup=InlineKeyboardMarkup(
[[InlineKeyboardButton("Dɴʟᴀᴅ ʟɪɴᴋ 📥", url=f"https://t.me/{(await bot.get_me()).username}?start=AvishkarPatil_{str(log_msg.id)}")]])
# [[InlineKeyboardButton("Dɴʟᴀᴅ ʟɪɴᴋ 📥", url=f"https://t.me/FxStreamBot?start=AvishkarPatil_{str(log_msg.id)}")]])
)
except FloodWait as w:
print(f"Sleeping for {str(w.value)}s")
await asyncio.sleep(w.value)
await bot.send_message(chat_id=Var.BIN_CHANNEL,
text=f"Gᴛ FʟᴅWᴀɪᴛ ᴏғ {str(w.value)}s from {broadcast.chat.title}\n\n**Cʜᴀɴɴᴇʟ ID:** `{str(broadcast.chat.id)}`",
disable_web_page_preview=True, parse_mode=ParseMode.MARKDOWN)
except Exception as e:
await bot.send_message(chat_id=Var.BIN_CHANNEL, text=f"**#ᴇʀʀʀ_ᴛʀᴀᴇʙᴀᴋ:** `{e}`", disable_web_page_preview=True, parse_mode=ParseMode.MARKDOWN)
print(f"Cᴀɴ'ᴛ Eᴅɪᴛ Bʀᴀᴅᴀsᴛ Mᴇssᴀɢᴇ!\nEʀʀʀ: {e}")

View File

@@ -1,79 +0,0 @@
# Avishkar Patil | AbirHasan2005
import math
import logging
import secrets
import mimetypes
from ..vars import Var
from aiohttp import web
from ..bot import StreamBot
from ..utils.custom_dl import TGCustomYield, chunk_size, offset_fix
routes = web.RouteTableDef()
@routes.get("/", allow_head=True)
async def root_route_handler(request):
bot_details = await StreamBot.get_me()
return web.json_response({"status": "running",
"maintained_by": "Avishkar_Patil",
"server_permission": "Open",
"Telegram_Bot": '@'+bot_details.username})
@routes.get("/{message_id}")
@routes.get("/{message_id}/")
@routes.get(r"/{message_id:\d+}/{name}")
async def stream_handler(request):
try:
message_id = int(request.match_info['message_id'])
return await media_streamer(request, message_id)
except ValueError as e:
logging.error(e)
raise web.HTTPNotFound
async def media_streamer(request, message_id: int):
range_header = request.headers.get('Range', 0)
media_msg = await StreamBot.get_messages(Var.BIN_CHANNEL, message_id)
file_properties = await TGCustomYield().generate_file_properties(media_msg)
file_size = file_properties.file_size
if range_header:
from_bytes, until_bytes = range_header.replace('bytes=', '').split('-')
from_bytes = int(from_bytes)
until_bytes = int(until_bytes) if until_bytes else file_size - 1
else:
from_bytes = request.http_range.start or 0
until_bytes = request.http_range.stop or file_size - 1
req_length = until_bytes - from_bytes
new_chunk_size = await chunk_size(req_length)
offset = await offset_fix(from_bytes, new_chunk_size)
first_part_cut = from_bytes - offset
last_part_cut = (until_bytes % new_chunk_size) + 1
part_count = math.ceil(req_length / new_chunk_size)
body = TGCustomYield().yield_file(media_msg, offset, first_part_cut, last_part_cut, part_count,
new_chunk_size)
file_name = file_properties.file_name if file_properties.file_name \
else f"{secrets.token_hex(2)}.jpeg"
mime_type = file_properties.mime_type if file_properties.mime_type \
else f"{mimetypes.guess_type(file_name)}"
return_resp = web.Response(
status=206 if range_header else 200,
body=body,
headers={
"Content-Type": mime_type,
"Content-Range": f"bytes {from_bytes}-{until_bytes}/{file_size}",
"Content-Disposition": f'attachment; filename="{file_name}"',
"Accept-Ranges": "bytes",
}
)
if return_resp.status == 200:
return_resp.headers.add("Content-Length", str(file_size))
return return_resp

View File

@@ -1 +0,0 @@
# This file is a part of avipatilpro/FileStreamBot

View File

@@ -1,231 +0,0 @@
import math
from typing import Union
from pyrogram.types import Message
from ..bot import StreamBot
from pyrogram import Client, utils, raw
from pyrogram.session import Session, Auth
from pyrogram.errors import AuthBytesInvalid
from pyrogram.file_id import FileId, FileType, ThumbnailSource
async def chunk_size(length):
return 2 ** max(min(math.ceil(math.log2(length / 1024)), 10), 2) * 1024
async def offset_fix(offset, chunksize):
offset -= offset % chunksize
return offset
class TGCustomYield:
def __init__(self):
""" A custom method to stream files from telegram.
functions:
generate_file_properties: returns the properties for a media on a specific message contained in FileId class.
generate_media_session: returns the media session for the DC that contains the media file on the message.
yield_file: yield a file from telegram servers for streaming.
"""
self.main_bot = StreamBot
@staticmethod
async def generate_file_properties(msg: Message):
error_message = "This message doesn't contain any downloadable media"
available_media = ("audio", "document", "photo", "sticker", "animation", "video", "voice", "video_note")
if isinstance(msg, Message):
for kind in available_media:
media = getattr(msg, kind, None)
if media is not None:
break
else:
raise ValueError(error_message)
else:
media = msg
if isinstance(media, str):
file_id_str = media
else:
file_id_str = media.file_id
file_id_obj = FileId.decode(file_id_str)
# The below lines are added to avoid a break in routes.py
setattr(file_id_obj, "file_size", getattr(media, "file_size", 0))
setattr(file_id_obj, "mime_type", getattr(media, "mime_type", ""))
setattr(file_id_obj, "file_name", getattr(media, "file_name", ""))
return file_id_obj
async def generate_media_session(self, client: Client, msg: Message):
data = await self.generate_file_properties(msg)
media_session = client.media_sessions.get(data.dc_id, None)
if media_session is None:
if data.dc_id != await client.storage.dc_id():
media_session = Session(
client, data.dc_id, await Auth(client, data.dc_id, await client.storage.test_mode()).create(),
await client.storage.test_mode(), is_media=True
)
await media_session.start()
for _ in range(3):
exported_auth = await client.invoke(
raw.functions.auth.ExportAuthorization(
dc_id=data.dc_id
)
)
try:
await media_session.invoke(
raw.functions.auth.ImportAuthorization(
id=exported_auth.id,
bytes=exported_auth.bytes
)
)
except AuthBytesInvalid:
continue
else:
break
else:
await media_session.stop()
raise AuthBytesInvalid
else:
media_session = Session(
client, data.dc_id, await client.storage.auth_key(),
await client.storage.test_mode(), is_media=True
)
await media_session.start()
client.media_sessions[data.dc_id] = media_session
return media_session
@staticmethod
async def get_location(file_id: FileId):
file_type = file_id.file_type
if file_type == FileType.CHAT_PHOTO:
if file_id.chat_id > 0:
peer = raw.types.InputPeerUser(
user_id=file_id.chat_id,
access_hash=file_id.chat_access_hash
)
else:
if file_id.chat_access_hash == 0:
peer = raw.types.InputPeerChat(
chat_id=-file_id.chat_id
)
else:
peer = raw.types.InputPeerChannel(
channel_id=utils.get_channel_id(file_id.chat_id),
access_hash=file_id.chat_access_hash
)
location = raw.types.InputPeerPhotoFileLocation(
peer=peer,
volume_id=file_id.volume_id,
local_id=file_id.local_id,
big=file_id.thumbnail_source == ThumbnailSource.CHAT_PHOTO_BIG
)
elif file_type == FileType.PHOTO:
location = raw.types.InputPhotoFileLocation(
id=file_id.media_id,
access_hash=file_id.access_hash,
file_reference=file_id.file_reference,
thumb_size=file_id.thumbnail_size
)
else:
location = raw.types.InputDocumentFileLocation(
id=file_id.media_id,
access_hash=file_id.access_hash,
file_reference=file_id.file_reference,
thumb_size=file_id.thumbnail_size
)
return location
async def yield_file(self, media_msg: Message, offset: int, first_part_cut: int,
last_part_cut: int, part_count: int, chunk_size: int) -> Union[str, None]: #pylint: disable=unsubscriptable-object
client = self.main_bot
data = await self.generate_file_properties(media_msg)
media_session = await self.generate_media_session(client, media_msg)
current_part = 1
location = await self.get_location(data)
r = await media_session.invoke(
raw.functions.upload.GetFile(
location=location,
offset=offset,
limit=chunk_size
),
)
if isinstance(r, raw.types.upload.File):
while current_part <= part_count:
chunk = r.bytes
if not chunk:
break
offset += chunk_size
if part_count == 1:
yield chunk[first_part_cut:last_part_cut]
break
if current_part == 1:
yield chunk[first_part_cut:]
if 1 < current_part <= part_count:
yield chunk
r = await media_session.invoke(
raw.functions.upload.GetFile(
location=location,
offset=offset,
limit=chunk_size
),
)
current_part += 1
async def download_as_bytesio(self, media_msg: Message):
client = self.main_bot
data = await self.generate_file_properties(media_msg)
media_session = await self.generate_media_session(client, media_msg)
location = await self.get_location(data)
limit = 1024 * 1024
offset = 0
r = await media_session.invoke(
raw.functions.upload.GetFile(
location=location,
offset=offset,
limit=limit
)
)
if isinstance(r, raw.types.upload.File):
m_file = []
# m_file.name = file_name
while True:
chunk = r.bytes
if not chunk:
break
m_file.append(chunk)
offset += limit
r = await media_session.invoke(
raw.functions.upload.GetFile(
location=location,
offset=offset,
limit=limit
)
)
return m_file

View File

@@ -1,36 +0,0 @@
import datetime
import motor.motor_asyncio
class Database:
def __init__(self, uri, database_name):
self._client = motor.motor_asyncio.AsyncIOMotorClient(uri)
self.db = self._client[database_name]
self.col = self.db.users
def new_user(self, id):
return dict(
id=id,
join_date=datetime.date.today().isoformat()
)
async def add_user(self, id):
user = self.new_user(id)
await self.col.insert_one(user)
async def is_user_exist(self, id):
user = await self.col.find_one({'id': int(id)})
return True if user else False
async def total_users_count(self):
count = await self.col.count_documents({})
return count
async def get_all_users(self):
all_users = self.col.find({})
return all_users
async def delete_user(self, user_id):
await self.col.delete_many({'id': int(user_id)})

View File

@@ -1,33 +0,0 @@
# (c) @AvishkarPatil | @EverythingSuckz
from os import getenv, environ
from dotenv import load_dotenv
load_dotenv()
class Var(object):
API_ID = int(getenv('API_ID'))
API_HASH = str(getenv('API_HASH'))
BOT_TOKEN = str(getenv('BOT_TOKEN'))
SESSION_NAME = str(getenv('SESSION_NAME', 'AviStreamBot'))
SLEEP_THRESHOLD = int(getenv('SLEEP_THRESHOLD', '60'))
WORKERS = int(getenv('WORKERS', '4'))
BIN_CHANNEL = int(getenv('BIN_CHANNEL'))
PORT = int(getenv('PORT', 8080))
BIND_ADRESS = str(getenv('WEB_SERVER_BIND_ADDRESS', '0.0.0.0'))
OWNER_ID = int(getenv('OWNER_ID', '797848243'))
NO_PORT = bool(getenv('NO_PORT', False))
APP_NAME = None
if 'DYNO' in environ:
ON_HEROKU = True
APP_NAME = str(getenv('APP_NAME'))
else:
ON_HEROKU = False
FQDN = str(getenv('FQDN', BIND_ADRESS)) if not ON_HEROKU or getenv('FQDN') else APP_NAME+'.herokuapp.com'
URL = "https://{}/".format(FQDN) if ON_HEROKU or NO_PORT else \
"http://{}:{}/".format(FQDN, PORT)
DATABASE_URL = str(getenv('DATABASE_URL'))
PING_INTERVAL = int(getenv('PING_INTERVAL', '500'))
UPDATES_CHANNEL = str(getenv('UPDATES_CHANNEL', None))
BANNED_CHANNELS = list(set(int(x) for x in str(getenv("BANNED_CHANNELS", "-1001296894100")).split()))

View File

@@ -1,29 +1,19 @@
{
"name": "Avi-FileStreamBot",
"description": "A Pyrogram Telegram bot to Stream Telegram files to web. @Avishkarpatil",
"name": "FileStreamBot",
"description": "A Telegram Bot to Convert Telegram Files To Direct & Streamable Links.",
"keywords": [
"telegram",
"stream",
"web",
"pyrogram",
"aiohttp",
"python",
"plugin",
"modular",
"media"
"files",
"stream"
],
"repository": "https://github.com/avipatilpro/FileStreamBot/",
"success_url": "/",
"logo": "https://i.ibb.co/ZJzJ9Hq/link-3x.png",
"website": "avipatilweb.ml",
"env": {
"ENV": {
"description": "Set this to True if you don't want to crash the bot",
"value": "True"
},
"APP_NAME": {
"description": "Copy-Paste the app name that you just typed above."
},
"API_ID": {
"description": "Get this value from https://my.telegram.org"
},
@@ -33,66 +23,89 @@
"BOT_TOKEN": {
"description": "Get this value from @BotFather"
},
"BIN_CHANNEL": {
"LOG_CHANNEL": {
"description": "The BIN Channel ID. Read the readme for more info about this var"
},
"PING_INTERVAL": {
"description": "Add Ping Interval Default Is 500",
"required": false
},
"DATABASE_URL": {
"description": "MongoDB URI for saving User IDs when they first Start the Bot. We will use that for Broadcasting to them. I will try to add more features related with Database. If you need help to get the URI you can ask in Support Group: https://t.me/linux_repo"
},
"OWNER_ID": {
"description": "Your Telegram User ID"
"description": "Your Telegram User ID as Owner"
},
"BANNED_CHANNELS": {
"DATABASE_URL": {
"description": "MongoDB URI for saving User Data and Files List created by user."
},
"AUTH_USERS": {
"description": "Put IDs of Banned Channels where bot will not work. You can add multiple IDs & separate with Space.",
"required": false
},
"UPDATES_CHANNEL": {
"description": "Put a Public Channel Username, so every user have to Join that channel to use the bot. Must add bot to channel as Admin to work properly.",
"description": "Channel Username without `@` to set channel as Update Channel",
"required": false
},
"FORCE_UPDATES_CHANNEL": {
"description": "Set to True, so every user have to Join update channel to use the bot.",
"required": false
},
"SLEEP_THRESHOLD": {
"description": "Floodwait Sleep timer. Read the readme for more info about this var",
"description": "Set global flood wait threshold, auto-retry requests under 60s. ",
"required": false
},
"PING_INTERVAL": {
"description": " Heroku ping interval (ms), defaults to 1200 (20 minutes).",
"required": false
},
"WORKERS": {
"description": "No. of workers that is to be assigned. Read the readme for more info about this var",
"description": "No. of workers that is to be assigned.",
"required": false
},
"PORT": {
"description": "Port that you want your webapp to be listened to. Read the readme for more info about this var",
"description": "The port that you want your webapp to be listened to",
"required": false
},
"NO_PORT": {
"description": "If you don't want your port to be displayed. Read the readme for more info about this var",
"value": "False",
"description": "If you don't want your port to be displayed. Set True or False",
"value": "True",
"required": false
},
"HAS_SSL": {
"description": "(can be either True or False) If you want the generated links in https format.",
"value": "True",
"required": false
},
"KEEP_ALIVE": {
"description": "Self-ping server every PING_INTERVAL sec to avoid sleeping",
"value": "True",
"required": false
},
"BIND_ADRESS": {
"description": "Read the readme for more info about this var",
"description": "Your server bind adress. Defauls to 0.0.0.0",
"required": false
},
"FQDN": {
"description": "Read the readme for more info about this var",
"description": "Heroku app Link without http/s, you can set later. its required",
"required": false
},
"SESSION_NAME": {
"description": " Session Name for Bot [ Add AvishkarPatil ]",
"description": "Name for the Database created on your MongoDB. Defaults to FileStream",
"required": false
},
"IMAGE_FILEID": {
"description": "To set Image at /files command. Defaults to pre-set image.",
"required": false
},
"MODE": {
"description": "Should be set to `secondary` if you only want to use the server for serving files.",
"required": false
}
},
"buildpacks": [{
"buildpacks": [
{
"url": "heroku/python"
}],
}
],
"stack": "heroku-22",
"formation": {
"web": {
"quantity": 1,
"size": "free"
"size": "eco"
}
}
}

View File

@@ -1,7 +1,8 @@
pyrogram>=2.0.0,<=2.0.30
tgcrypto<=1.2.3
aiohttp<=3.8.1
python-dotenv<=0.20.0
aiohttp
pyrogram
python-dotenv
tgcrypto
motor
aiofiles
dnspython
requests