Add:
 • Multiple Commands Support.
 • Cancel Confirmation.
Fixes / Misc Changes:
 • Catch Message Delete Error on Bot mode.
 • Improve Cancelling Logic.
 • Switch to A Generator for AsyncShell.
 • Diffrenciate Traceback, Text logging.
 • Clean up .shell code.
This commit is contained in:
anonymousx97 2023-07-31 15:53:06 +05:30
parent 216e0dcfe1
commit 1f11bc8405
6 changed files with 103 additions and 83 deletions

View File

@ -35,7 +35,11 @@ class BOT(Client):
def the_decorator(func): def the_decorator(func):
@wraps(func) @wraps(func)
def wrapper(): def wrapper():
Config.CMD_DICT[cmd] = func if isinstance(cmd, list):
for _cmd in cmd:
Config.CMD_DICT[_cmd] = func
else:
Config.CMD_DICT[cmd] = func
wrapper() wrapper()
return func return func
@ -48,7 +52,7 @@ class BOT(Client):
await self.set_filter_list() await self.set_filter_list()
await aiohttp_tools.session_switch() await aiohttp_tools.session_switch()
await self.edit_restart_msg() await self.edit_restart_msg()
await self.log(text="#Social-dl\n__Started__") await self.log(text="#SocialDL\n<i>Started</i>")
print("started") print("started")
await idle() await idle()
await aiohttp_tools.session_switch() await aiohttp_tools.session_switch()
@ -73,15 +77,23 @@ class BOT(Client):
importlib.import_module(py_name) importlib.import_module(py_name)
async def log( async def log(
self, text, chat=None, func=None, name="log.txt", disable_web_page_preview=True self,
text="",
traceback="",
chat=None,
func=None,
name="log.txt",
disable_web_page_preview=True,
parse_mode=ParseMode.HTML,
): ):
if chat or func: if traceback:
text = f"<b>Function:</b> {func}\n<b>Chat:</b> {chat}\n<b>Traceback:</b>\n{text}" text = f"#Traceback\n<b>Function:</b> {func}\n<b>Chat:</b> {chat}\n<b>Traceback:</b>\n<code>{traceback}</code>"
return await self.send_message( return await self.send_message(
chat_id=Config.LOG_CHAT, chat_id=Config.LOG_CHAT,
text=text, text=text,
name=name, name=name,
disable_web_page_preview=disable_web_page_preview, disable_web_page_preview=disable_web_page_preview,
parse_mode=parse_mode,
) )
async def restart(self): async def restart(self):

View File

@ -1,6 +1,7 @@
import asyncio import asyncio
from functools import cached_property from functools import cached_property
from pyrogram.errors import MessageDeleteForbidden
from pyrogram.types import Message as MSG from pyrogram.types import Message as MSG
from app import Config from app import Config
@ -83,9 +84,12 @@ class Message(MSG):
return reply return reply
async def delete(self, reply=False): async def delete(self, reply=False):
await super().delete() try:
if reply and self.replied: await super().delete()
await self.replied.delete() if reply and self.replied:
await self.replied.delete()
except MessageDeleteForbidden:
pass
async def async_deleter(self, del_in, task, block): async def async_deleter(self, del_in, task, block):
if block: if block:

View File

@ -27,34 +27,32 @@ async def run_shell_cmd(cmd):
class AsyncShell: class AsyncShell:
full_std = ""
is_not_completed = True
def __init__(self, process): def __init__(self, process):
self.process = process self.process = process
self.full_std = ""
self.is_done = False
async def get_output(self): async def read_output(self):
while True: while True:
# Check output and stop loop if it's emtpy
line = (await self.process.stdout.readline()).decode("utf-8") line = (await self.process.stdout.readline()).decode("utf-8")
if not line: if not line:
break break
self.full_std += line self.full_std += line
# Let the Subprocess complete and let it shut down self.is_done = True
await self.process.wait() await self.process.wait()
self.is_not_completed = False
async def get_output(self):
while not self.is_done:
yield self.full_std
@classmethod @classmethod
async def run_cmd(cls, cmd): async def run_cmd(cls, cmd, name="shell"):
# Create Subprocess and initialise self using cls
sub_process = cls( sub_process = cls(
process=await asyncio.create_subprocess_shell( process=await asyncio.create_subprocess_shell(
cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT
) )
) )
# Start Checking output but don't block code by awaiting it. asyncio.create_task(sub_process.read_output())
asyncio.create_task(sub_process.get_output())
# Sleep for a short time to let previous task start
await asyncio.sleep(0.5) await asyncio.sleep(0.5)
# Return Self object
return sub_process return sub_process

View File

@ -15,46 +15,52 @@ from app.core import aiohttp_tools as aio # isort:skip
# Run shell commands # Run shell commands
async def run_cmd(bot, message): async def run_cmd(bot, message):
cmd = message.input.strip() cmd = message.input.strip()
status_ = await message.reply("executing...") reply = await message.reply("executing...")
proc_stdout = await shell.run_shell_cmd(cmd) try:
proc_stdout = await asyncio.Task(
shell.run_shell_cmd(cmd), name=f"{message.chat.id}-{reply.id}"
)
except asyncio.exceptions.CancelledError:
return await reply.edit("`Cancelled...`")
output = f"`${cmd}`\n\n`{proc_stdout}`" output = f"`${cmd}`\n\n`{proc_stdout}`"
return await status_.edit(output, name="sh.txt", disable_web_page_preview=True) return await reply.edit(output, name="sh.txt", disable_web_page_preview=True)
# Shell but Live Output # Shell but Live Output
async def live_shell(bot, message): async def live_shell(bot, message):
cmd = message.input.strip() try:
sub_process = await shell.AsyncShell.run_cmd(cmd) cmd = message.input.strip()
reply = await message.reply("`getting live output....`") reply = await message.reply("`getting live output....`")
output = "" sub_process = await shell.AsyncShell.run_cmd(cmd)
sleep_for = 1 sleep_for = 1
while sub_process.is_not_completed: output = ""
# Edit message only when there's new output. async for stdout in sub_process.get_output():
if output != sub_process.full_std: if output != stdout:
output = sub_process.full_std if len(stdout) <= 4096:
if len(output) <= 4096: await reply.edit(
await reply.edit( f"`{stdout}`",
f"`{output}`", disable_web_page_preview=True,
disable_web_page_preview=True, parse_mode=ParseMode.MARKDOWN,
parse_mode=ParseMode.MARKDOWN, )
) output = stdout
# Reset sleep duration if sleep_for >= 5:
if sleep_for >= 5: sleep_for = 1
sleep_for = 1 await asyncio.Task(
# Sleep to Unblock running loop and let output reader read new asyncio.sleep(sleep_for), name=f"{message.chat.id}-{reply.id}"
# output. )
await asyncio.sleep(sleep_for) sleep_for += 1
sleep_for += 1 return await reply.edit(
# If the subprocess is finished edit the message with cmd and full f"`$ {cmd}\n\n``{sub_process.full_std}`",
# output name="shell.txt",
return await reply.edit( disable_web_page_preview=True,
f"`$ {cmd}\n\n``{sub_process.full_std}`", )
name="shell.txt", except asyncio.exceptions.CancelledError:
disable_web_page_preview=True, return await reply.edit("`Cancelled...`")
)
# Run Python code # Run Python code
async def executor(bot, message): async def executor(bot, message):
code = message.flt_input.strip() code = message.flt_input.strip()
if not code: if not code:
@ -67,7 +73,11 @@ async def executor(bot, message):
try: try:
# Create and initialise the function # Create and initialise the function
exec(f"async def _exec(bot, message):\n {formatted_code}") exec(f"async def _exec(bot, message):\n {formatted_code}")
func_out = await locals()["_exec"](bot, message) func_out = await asyncio.Task(
locals()["_exec"](bot, message), name=f"{message.chat.id}-{reply.id}"
)
except asyncio.exceptions.CancelledError:
return await reply.edit("`Cancelled....`")
except BaseException: except BaseException:
func_out = str(traceback.format_exc()) func_out = str(traceback.format_exc())
sys.stdout = sys.__stdout__ sys.stdout = sys.__stdout__

View File

@ -1,19 +1,21 @@
import asyncio
from app import bot from app import bot
from app.social_dl import current_tasks
@bot.add_cmd(cmd="cancel") @bot.add_cmd(cmd=["cancel", "c"])
async def cancel_task(bot, message): async def cancel_task(bot, message):
task_id = message.reply_id task_id = message.reply_id
if not task_id: if not task_id:
return await message.reply("Reply To a Command or Bot's Response Message.") return await message.reply(
task = current_tasks.get(task_id) "Reply To a Command or Bot's Response Message.", del_in=8
if not task: )
return await message.reply("Task not in Currently Running Tasks.") all_tasks = asyncio.all_tasks()
reply = await message.reply("Cancelling....") tasks = [x for x in all_tasks if x.get_name() == f"{message.chat.id}-{task_id}"]
cancelled = task.cancel() if not tasks:
if cancelled: return await message.reply("Task not in Currently Running Tasks.", del_in=8)
response = "Task Cancelled Successfully." response = ""
else: for task in tasks:
response = "Task not Running.\nIt either is Finished or has Errored." status = task.cancel()
await reply.edit(response) response += f"Task: __{task.get_name()}__\nCancelled: __{status}__\n"
await message.reply(response, del_in=5)

View File

@ -6,31 +6,27 @@ from app.core import filters
from app.core.MediaHandler import ExtractAndSendMedia from app.core.MediaHandler import ExtractAndSendMedia
from app.core.message import Message from app.core.message import Message
current_tasks = {}
@bot.add_cmd(cmd="dl") @bot.add_cmd(cmd="dl")
async def dl(bot, message): async def dl(bot, message):
reply = await bot.send_message( reply = await bot.send_message(
chat_id=message.chat.id, text="`trying to download...`" chat_id=message.chat.id, text="`trying to download...`"
) )
task = asyncio.Task(ExtractAndSendMedia.process(message)) coro = ExtractAndSendMedia.process(message)
current_tasks[reply.id] = task task = asyncio.Task(coro, name=f"{message.chat.id}-{message.id}")
media = await task media = await task
if media.exceptions: if media.exceptions:
exceptions = "\n".join(media.exceptions) exceptions = "\n".join(media.exceptions)
await bot.log( await bot.log(
text=exceptions, traceback=exceptions,
func="DL", func="DL",
chat=message.chat.title or message.chat.first_name, chat=message.chat.title or message.chat.first_name,
name="traceback.txt", name="traceback.txt",
) )
current_tasks.pop(reply.id)
return await reply.edit(f"Media Download Failed.") return await reply.edit(f"Media Download Failed.")
if media.media_objects: if media.media_objects:
await message.delete() await message.delete()
await reply.delete() await reply.delete()
current_tasks.pop(reply.id)
@bot.on_message(filters.user_filter) @bot.on_message(filters.user_filter)
@ -38,36 +34,34 @@ async def dl(bot, message):
async def cmd_dispatcher(bot, message): async def cmd_dispatcher(bot, message):
parsed_message = Message.parse_message(message) parsed_message = Message.parse_message(message)
func = Config.CMD_DICT[parsed_message.cmd] func = Config.CMD_DICT[parsed_message.cmd]
coro = func(bot, parsed_message)
try: try:
task = asyncio.Task(func(bot, parsed_message)) task = asyncio.Task(coro, name=f"{message.chat.id}-{message.id}")
current_tasks[message.id] = task
await task await task
except asyncio.exceptions.CancelledError: except asyncio.exceptions.CancelledError:
pass await bot.log(text=f"<b>#Cancelled</b>:\n<code>{message.text}</code>")
except BaseException: except BaseException:
await bot.log( await bot.log(
text=str(traceback.format_exc()), traceback=str(traceback.format_exc()),
chat=message.chat.title or message.chat.first_name, chat=message.chat.title or message.chat.first_name,
func=func.__name__, func=func.__name__,
name="traceback.txt", name="traceback.txt",
) )
current_tasks.pop(message.id)
@bot.on_message(filters.chat_filter) @bot.on_message(filters.chat_filter)
async def dl_dispatcher(bot, message): async def dl_dispatcher(bot, message):
parsed_message = Message.parse_message(message) parsed_message = Message.parse_message(message)
coro = dl(bot, parsed_message)
try: try:
task = asyncio.Task(dl(bot, parsed_message)) task = asyncio.Task(coro, name=f"{message.chat.id}-{message.id}")
current_tasks[message.id] = task
await task await task
except asyncio.exceptions.CancelledError: except asyncio.exceptions.CancelledError:
pass await bot.log(text=f"<b>#Cancelled</b>:\n<code>{message.text}</code>")
except BaseException: except BaseException:
await bot.log( await bot.log(
text=str(traceback.format_exc()), traceback=str(traceback.format_exc()),
chat=message.chat.title or message.chat.first_name, chat=message.chat.title or message.chat.first_name,
func=dl.__name__, func=dl.__name__,
name="traceback.txt", name="traceback.txt",
) )
current_tasks.pop(message.id)