mirror of
https://github.com/avipatilpro/FileStreamBot.git
synced 2026-01-16 06:42:54 -03:00
Updated ?
Nothing new but, looks like something changed ;) !
This commit is contained in:
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
|
||||
19
FileStream/utils/broadcast_helper.py
Normal file
19
FileStream/utils/broadcast_helper.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import asyncio
|
||||
import traceback
|
||||
from pyrogram.errors import FloodWait, InputUserDeactivated, UserIsBlocked, PeerIdInvalid
|
||||
|
||||
async def send_msg(user_id, message):
|
||||
try:
|
||||
await message.copy(chat_id=user_id)
|
||||
return 200, None
|
||||
except FloodWait as e:
|
||||
await asyncio.sleep(e.value)
|
||||
return send_msg(user_id, message)
|
||||
except InputUserDeactivated:
|
||||
return 400, f"{user_id} : deactivated\n"
|
||||
except UserIsBlocked:
|
||||
return 400, f"{user_id} : blocked the bot\n"
|
||||
except PeerIdInvalid:
|
||||
return 400, f"{user_id} : user id invalid\n"
|
||||
except Exception as e:
|
||||
return 500, f"{user_id} : {traceback.format_exc()}\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)
|
||||
|
||||
10
FileStream/utils/human_readable.py
Normal file
10
FileStream/utils/human_readable.py
Normal file
@@ -0,0 +1,10 @@
|
||||
def humanbytes(size):
|
||||
if not size:
|
||||
return ""
|
||||
power = 2**10
|
||||
n = 0
|
||||
Dic_powerN = {0: ' ', 1: 'Ki', 2: 'Mi', 3: 'Gi', 4: 'Ti'}
|
||||
while size > power:
|
||||
size /= power
|
||||
n += 1
|
||||
return str(round(size, 2)) + " " + Dic_powerN[n] + 'B'
|
||||
20
FileStream/utils/keepalive.py
Normal file
20
FileStream/utils/keepalive.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import asyncio
|
||||
import logging
|
||||
import aiohttp
|
||||
import traceback
|
||||
from FileStream.config import Server
|
||||
|
||||
async def ping_server():
|
||||
sleep_time = Server.PING_INTERVAL
|
||||
while True:
|
||||
await asyncio.sleep(sleep_time)
|
||||
try:
|
||||
async with aiohttp.ClientSession(
|
||||
timeout=aiohttp.ClientTimeout(total=10)
|
||||
) as session:
|
||||
async with session.get(Server.URL) as resp:
|
||||
logging.info("Pinged server with response: {}".format(resp.status))
|
||||
except TimeoutError:
|
||||
logging.warning("Couldn't connect to the site URL..!")
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
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}')]
|
||||
]
|
||||
)
|
||||
Reference in New Issue
Block a user