mirror of
https://github.com/avipatilpro/FileStreamBot.git
synced 2026-01-15 14:22:53 -03:00
Updated ?
Nothing new but, looks like something changed ;) !
This commit is contained in:
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,2 +1,2 @@
|
|||||||
# Auto detect text files and perform LF normalization
|
|
||||||
* text=auto
|
* text=auto
|
||||||
|
|||||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -114,4 +114,15 @@ dmypy.json
|
|||||||
.pyre/
|
.pyre/
|
||||||
|
|
||||||
#session files
|
#session files
|
||||||
*.session
|
*.session
|
||||||
|
*.session-journal
|
||||||
|
|
||||||
|
.env
|
||||||
|
test.py
|
||||||
|
/test
|
||||||
|
|
||||||
|
streambot.log.*
|
||||||
|
.idea/workspace.xml
|
||||||
|
*.xml
|
||||||
|
*.xml
|
||||||
|
.idea/workspace.xml
|
||||||
|
|||||||
2
.replit
2
.replit
@@ -1,2 +0,0 @@
|
|||||||
language = "bash"
|
|
||||||
run = "pip3 install -r requirements.txt; python3 -m WebStreamer"
|
|
||||||
11
Dockerfile
Normal file
11
Dockerfile
Normal 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
5
FileStream/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import time
|
||||||
|
|
||||||
|
__version__ = "1.0.1"
|
||||||
|
StartTime = time.time()
|
||||||
|
|
||||||
82
FileStream/__main__.py
Normal file
82
FileStream/__main__.py
Normal 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 ------------------------")
|
||||||
25
FileStream/bot/__init__.py
Normal file
25
FileStream/bot/__init__.py
Normal 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
59
FileStream/bot/clients.py
Normal 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")
|
||||||
160
FileStream/bot/plugins/admin.py
Normal file
160
FileStream/bot/plugins/admin.py
Normal 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
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
198
FileStream/bot/plugins/callback.py
Normal file
198
FileStream/bot/plugins/callback.py
Normal 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")]])
|
||||||
|
)
|
||||||
|
|
||||||
119
FileStream/bot/plugins/start.py
Normal file
119
FileStream/bot/plugins/start.py
Normal 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))
|
||||||
|
|
||||||
|
|
||||||
95
FileStream/bot/plugins/stream.py
Normal file
95
FileStream/bot/plugins/stream.py
Normal 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
39
FileStream/config.py
Normal 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")
|
||||||
|
|
||||||
|
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from .stream_routes import routes
|
from .stream_routes import routes
|
||||||
|
|
||||||
|
def web_server():
|
||||||
async def web_server():
|
|
||||||
web_app = web.Application(client_max_size=30000000)
|
web_app = web.Application(client_max_size=30000000)
|
||||||
web_app.add_routes(routes)
|
web_app.add_routes(routes)
|
||||||
return web_app
|
return web_app
|
||||||
5
FileStream/server/exceptions.py
Normal file
5
FileStream/server/exceptions.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
class InvalidHash(Exception):
|
||||||
|
message = "Invalid hash"
|
||||||
|
|
||||||
|
class FIleNotFound(Exception):
|
||||||
|
message = "File not found"
|
||||||
136
FileStream/server/stream_routes.py
Normal file
136
FileStream/server/stream_routes.py
Normal 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",
|
||||||
|
},
|
||||||
|
)
|
||||||
56
FileStream/template/dl.html
Normal file
56
FileStream/template/dl.html
Normal 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>
|
||||||
154
FileStream/template/stream.html
Normal file
154
FileStream/template/stream.html
Normal 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>
|
||||||
4
FileStream/utils/__init__.py
Normal file
4
FileStream/utils/__init__.py
Normal 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
|
||||||
177
FileStream/utils/bot_utils.py
Normal file
177
FileStream/utils/bot_utils.py
Normal 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
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import traceback
|
import traceback
|
||||||
from pyrogram.errors import FloodWait, InputUserDeactivated, UserIsBlocked, PeerIdInvalid
|
from pyrogram.errors import FloodWait, InputUserDeactivated, UserIsBlocked, PeerIdInvalid
|
||||||
|
|
||||||
|
|
||||||
async def send_msg(user_id, message):
|
async def send_msg(user_id, message):
|
||||||
try:
|
try:
|
||||||
await message.forward(chat_id=user_id)
|
await message.copy(chat_id=user_id)
|
||||||
return 200, None
|
return 200, None
|
||||||
except FloodWait as e:
|
except FloodWait as e:
|
||||||
await asyncio.sleep(e.x)
|
await asyncio.sleep(e.value)
|
||||||
return send_msg(user_id, message)
|
return send_msg(user_id, message)
|
||||||
except InputUserDeactivated:
|
except InputUserDeactivated:
|
||||||
return 400, f"{user_id} : deactivated\n"
|
return 400, f"{user_id} : deactivated\n"
|
||||||
214
FileStream/utils/custom_dl.py
Normal file
214
FileStream/utils/custom_dl.py
Normal 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")
|
||||||
136
FileStream/utils/database.py
Normal file
136
FileStream/utils/database.py
Normal 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}})
|
||||||
146
FileStream/utils/file_properties.py
Normal file
146
FileStream/utils/file_properties.py
Normal 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)
|
||||||
|
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
|
|
||||||
def humanbytes(size):
|
def humanbytes(size):
|
||||||
# https://stackoverflow.com/a/49361727/4723940
|
|
||||||
# 2**10 = 1024
|
|
||||||
if not size:
|
if not size:
|
||||||
return ""
|
return ""
|
||||||
power = 2**10
|
power = 2**10
|
||||||
@@ -2,15 +2,17 @@ import asyncio
|
|||||||
import logging
|
import logging
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import traceback
|
import traceback
|
||||||
from WebStreamer.vars import Var
|
from FileStream.config import Server
|
||||||
|
|
||||||
async def ping_server():
|
async def ping_server():
|
||||||
sleep_time = Var.PING_INTERVAL
|
sleep_time = Server.PING_INTERVAL
|
||||||
while True:
|
while True:
|
||||||
await asyncio.sleep(sleep_time)
|
await asyncio.sleep(sleep_time)
|
||||||
try:
|
try:
|
||||||
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session:
|
async with aiohttp.ClientSession(
|
||||||
async with session.get(Var.URL) as resp:
|
timeout=aiohttp.ClientTimeout(total=10)
|
||||||
|
) as session:
|
||||||
|
async with session.get(Server.URL) as resp:
|
||||||
logging.info("Pinged server with response: {}".format(resp.status))
|
logging.info("Pinged server with response: {}".format(resp.status))
|
||||||
except TimeoutError:
|
except TimeoutError:
|
||||||
logging.warning("Couldn't connect to the site URL..!")
|
logging.warning("Couldn't connect to the site URL..!")
|
||||||
25
FileStream/utils/render_template.py
Normal file
25
FileStream/utils/render_template.py
Normal 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
|
||||||
22
FileStream/utils/time_format.py
Normal file
22
FileStream/utils/time_format.py
Normal 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
|
||||||
72
FileStream/utils/translation.py
Normal file
72
FileStream/utils/translation.py
Normal 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}')]
|
||||||
|
]
|
||||||
|
)
|
||||||
202
README.md
202
README.md
@@ -1,24 +1,20 @@
|
|||||||
<h1 align="center">FileStreamBot</h1>
|
<h1 align="center">FileStreamBot</h1>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/Avipatilpro/FileStreamBot">
|
<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>
|
</a>
|
||||||
|
</p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
A Telegram bot to all media and documents files to web link .
|
|
||||||
<br />
|
|
||||||
</strong></a>
|
</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">Report a Bug</a>
|
||||||
|
|
|
|
||||||
<a href="https://github.com/Avipatilpro/FileStreamBot/issues">Request Feature</a>
|
<a href="https://github.com/Avipatilpro/FileStreamBot/issues">Request Feature</a></b>
|
||||||
</p>
|
</p>
|
||||||
</p>
|
|
||||||
|
|
||||||
## 🚸 Updating .............
|
|
||||||
|
|
||||||
|
|
||||||
## 🍁 About This Bot :
|
|
||||||
|
### 🍁 About :
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/Avipatilpro/FileStreamBot">
|
<a href="https://github.com/Avipatilpro/FileStreamBot">
|
||||||
@@ -26,115 +22,175 @@
|
|||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p align='center'>
|
<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>
|
</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
|
#### ♢ Click on This Drop-down and get more details
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<details>
|
<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
|
[](https://heroku.com/deploy?template=https://github.com/avipatilpro/FileStreamBot)
|
||||||
2. Click on Deploy Easily
|
- Go to <a href="#mandatory-vars">variables tab</a> for more info on setting up environmental variables. </details>
|
||||||
|
|
||||||
<h4> So Follow Above Steps 👆 and then also deply other wise not work</h4>
|
|
||||||
|
|
||||||
Press the below button to Fast deploy on Heroku
|
|
||||||
|
|
||||||
[](https://heroku.com/deploy)
|
|
||||||
|
|
||||||
then goto the <a href="#mandatory-vars">variables tab</a> for more info on setting up environmental variables. </details>
|
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>Host it on VPS Locally :</b></summary>
|
<summary><b>Deploy Locally :</b></summary>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
```sh
|
||||||
```py
|
|
||||||
git clone https://github.com/avipatilpro/FileStreamBot
|
git clone https://github.com/avipatilpro/FileStreamBot
|
||||||
cd FileStreamBot
|
cd FileStreamBot
|
||||||
virtualenv -p /usr/bin/python3 venv
|
python3 -m venv ./venv
|
||||||
. ./venv/bin/activate
|
. ./venv/bin/activate
|
||||||
pip install -r requirements.txt
|
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>
|
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
|
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.
|
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:
|
An example of `.env` file:
|
||||||
|
|
||||||
```py
|
```sh
|
||||||
API_ID=12345
|
API_ID = 789456
|
||||||
API_HASH=esx576f8738x883f3sfzx83
|
API_HASH = ysx275f9638x896g43sfzx65
|
||||||
BOT_TOKEN=55838383:yourtbottokenhere
|
BOT_TOKEN = 12345678:your_bot_token
|
||||||
BIN_CHANNEL=-100
|
BIN_CHANNEL = -100xxxxxxxx
|
||||||
PORT=8080
|
DATABASE_URL = mongodb://admin:pass@192.168.27.1
|
||||||
FQDN=your_server_ip
|
FQDN = 192.168.27.1
|
||||||
OWNER_ID=your_user_id
|
HAS_SSL = False
|
||||||
DATABASE_URL=mongodb_uri
|
MULTI_TOKEN1 = 12345678:bot_token_multi_client_1
|
||||||
|
MULTI_TOKEN2 = 12345678:bot_token_multi_client_2
|
||||||
|
OWNER_ID = 987456321
|
||||||
|
PORT = 8080
|
||||||
```
|
```
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>Vars and Details :</b></summary>
|
<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).
|
</details>
|
||||||
|
|
||||||
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>
|
<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
|
#### 🍟 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>
|
|
||||||
|
|
||||||
|
*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 🤬 🤬 -->
|
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
# This file is a part of avipatilpro/FileStreamBot
|
|
||||||
@@ -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 -----------------------')
|
|
||||||
@@ -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
|
|
||||||
)
|
|
||||||
@@ -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')
|
|
||||||
@@ -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('Aʙᴏᴜᴛ', callback_data='about'),
|
|
||||||
InlineKeyboardButton('Cʟᴏsᴇ', callback_data='close')
|
|
||||||
]]
|
|
||||||
)
|
|
||||||
HELP_BUTTONS = InlineKeyboardMarkup(
|
|
||||||
[[
|
|
||||||
InlineKeyboardButton('Hᴏᴍᴇ', callback_data='home'),
|
|
||||||
InlineKeyboardButton('Aʙᴏᴜᴛ', callback_data='about'),
|
|
||||||
InlineKeyboardButton('Cʟᴏsᴇ', callback_data='close')
|
|
||||||
]]
|
|
||||||
)
|
|
||||||
ABOUT_BUTTONS = InlineKeyboardMarkup(
|
|
||||||
[[
|
|
||||||
InlineKeyboardButton('Hᴏᴍᴇ', callback_data='home'),
|
|
||||||
InlineKeyboardButton('Hᴇʟᴘ', callback_data='help'),
|
|
||||||
InlineKeyboardButton('Cʟᴏ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ᴇᴡ Fʀɪᴇɴᴅ__ [{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ᴇᴡ Fʀɪᴇɴᴅ__ [{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
|
|
||||||
)
|
|
||||||
|
|
||||||
@@ -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}")
|
|
||||||
@@ -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
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
# This file is a part of avipatilpro/FileStreamBot
|
|
||||||
@@ -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
|
|
||||||
@@ -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)})
|
|
||||||
@@ -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()))
|
|
||||||
185
app.json
185
app.json
@@ -1,98 +1,111 @@
|
|||||||
{
|
{
|
||||||
"name": "Avi-FileStreamBot",
|
"name": "FileStreamBot",
|
||||||
"description": "A Pyrogram Telegram bot to Stream Telegram files to web. @Avishkarpatil",
|
"description": "A Telegram Bot to Convert Telegram Files To Direct & Streamable Links.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"telegram",
|
"telegram",
|
||||||
"stream",
|
"files",
|
||||||
"web",
|
"stream"
|
||||||
"pyrogram",
|
|
||||||
"aiohttp",
|
|
||||||
"python",
|
|
||||||
"plugin",
|
|
||||||
"modular",
|
|
||||||
"media"
|
|
||||||
],
|
],
|
||||||
"repository": "https://github.com/avipatilpro/FileStreamBot/",
|
"repository": "https://github.com/avipatilpro/FileStreamBot/",
|
||||||
"success_url": "/",
|
"success_url": "/",
|
||||||
"logo": "https://i.ibb.co/ZJzJ9Hq/link-3x.png",
|
"logo": "https://i.ibb.co/ZJzJ9Hq/link-3x.png",
|
||||||
"website": "avipatilweb.ml",
|
|
||||||
"env": {
|
"env": {
|
||||||
"ENV": {
|
"ENV": {
|
||||||
"description": "Set this to True if you don't want to crash the bot",
|
"description": "Set this to True if you don't want to crash the bot",
|
||||||
"value": "True"
|
"value": "True"
|
||||||
},
|
},
|
||||||
"APP_NAME": {
|
"API_ID": {
|
||||||
"description": "Copy-Paste the app name that you just typed above."
|
"description": "Get this value from https://my.telegram.org"
|
||||||
},
|
},
|
||||||
"API_ID": {
|
"API_HASH": {
|
||||||
"description": "Get this value from https://my.telegram.org"
|
"description": "Get this value from https://my.telegram.org"
|
||||||
},
|
},
|
||||||
"API_HASH": {
|
"BOT_TOKEN": {
|
||||||
"description": "Get this value from https://my.telegram.org"
|
"description": "Get this value from @BotFather"
|
||||||
},
|
},
|
||||||
"BOT_TOKEN": {
|
"LOG_CHANNEL": {
|
||||||
"description": "Get this value from @BotFather"
|
"description": "The BIN Channel ID. Read the readme for more info about this var"
|
||||||
},
|
},
|
||||||
"BIN_CHANNEL": {
|
"OWNER_ID": {
|
||||||
"description": "The BIN Channel ID. Read the readme for more info about this var"
|
"description": "Your Telegram User ID as Owner"
|
||||||
|
},
|
||||||
},
|
"DATABASE_URL": {
|
||||||
"PING_INTERVAL": {
|
"description": "MongoDB URI for saving User Data and Files List created by user."
|
||||||
"description": "Add Ping Interval Default Is 500",
|
},
|
||||||
"required": false
|
"AUTH_USERS": {
|
||||||
},
|
"description": "Put IDs of Banned Channels where bot will not work. You can add multiple IDs & separate with Space.",
|
||||||
"DATABASE_URL": {
|
"required": false
|
||||||
"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"
|
},
|
||||||
},
|
"UPDATES_CHANNEL": {
|
||||||
"OWNER_ID": {
|
"description": "Channel Username without `@` to set channel as Update Channel",
|
||||||
"description": "Your Telegram User ID"
|
"required": false
|
||||||
},
|
},
|
||||||
"BANNED_CHANNELS": {
|
"FORCE_UPDATES_CHANNEL": {
|
||||||
"description": "Put IDs of Banned Channels where bot will not work. You can add multiple IDs & separate with Space.",
|
"description": "Set to True, so every user have to Join update channel to use the bot.",
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
"UPDATES_CHANNEL": {
|
"SLEEP_THRESHOLD": {
|
||||||
"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": "Set global flood wait threshold, auto-retry requests under 60s. ",
|
||||||
"required": false
|
"required": false
|
||||||
|
},
|
||||||
},
|
"PING_INTERVAL": {
|
||||||
"SLEEP_THRESHOLD": {
|
"description": " Heroku ping interval (ms), defaults to 1200 (20 minutes).",
|
||||||
"description": "Floodwait Sleep timer. Read the readme for more info about this var",
|
"required": false
|
||||||
"required": false
|
},
|
||||||
},
|
"WORKERS": {
|
||||||
"WORKERS": {
|
"description": "No. of workers that is to be assigned.",
|
||||||
"description": "No. of workers that is to be assigned. Read the readme for more info about this var",
|
"required": false
|
||||||
"required": false
|
},
|
||||||
},
|
"PORT": {
|
||||||
"PORT": {
|
"description": "The port that you want your webapp to be listened to",
|
||||||
"description": "Port that you want your webapp to be listened to. Read the readme for more info about this var",
|
"required": false
|
||||||
"required": false
|
},
|
||||||
},
|
"NO_PORT": {
|
||||||
"NO_PORT": {
|
"description": "If you don't want your port to be displayed. Set True or False",
|
||||||
"description": "If you don't want your port to be displayed. Read the readme for more info about this var",
|
"value": "True",
|
||||||
"value": "False",
|
"required": false
|
||||||
"required": false
|
},
|
||||||
},
|
"HAS_SSL": {
|
||||||
"BIND_ADRESS": {
|
"description": "(can be either True or False) If you want the generated links in https format.",
|
||||||
"description": "Read the readme for more info about this var",
|
"value": "True",
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
"FQDN": {
|
"KEEP_ALIVE": {
|
||||||
"description": "Read the readme for more info about this var",
|
"description": "Self-ping server every PING_INTERVAL sec to avoid sleeping",
|
||||||
"required": false
|
"value": "True",
|
||||||
},
|
"required": false
|
||||||
"SESSION_NAME": {
|
},
|
||||||
"description": " Session Name for Bot [ Add AvishkarPatil ]",
|
"BIND_ADRESS": {
|
||||||
"required": false
|
"description": "Your server bind adress. Defauls to 0.0.0.0",
|
||||||
}
|
"required": false
|
||||||
|
},
|
||||||
|
"FQDN": {
|
||||||
|
"description": "Heroku app Link without http/s, you can set later. its required",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"SESSION_NAME": {
|
||||||
|
"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"
|
"url": "heroku/python"
|
||||||
}],
|
}
|
||||||
|
],
|
||||||
|
"stack": "heroku-22",
|
||||||
"formation": {
|
"formation": {
|
||||||
"web": {
|
"web": {
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"size": "free"
|
"size": "eco"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
pyrogram>=2.0.0,<=2.0.30
|
aiohttp
|
||||||
tgcrypto<=1.2.3
|
pyrogram
|
||||||
aiohttp<=3.8.1
|
python-dotenv
|
||||||
python-dotenv<=0.20.0
|
tgcrypto
|
||||||
motor
|
motor
|
||||||
aiofiles
|
aiofiles
|
||||||
dnspython
|
dnspython
|
||||||
|
requests
|
||||||
Reference in New Issue
Block a user