From 3bbc4b00715c9530e812e505605e877c4eeedc4c Mon Sep 17 00:00:00 2001
From: thedragonsinn <98635854+thedragonsinn@users.noreply.github.com>
Date: Mon, 29 Jan 2024 20:17:20 +0530
Subject: [PATCH] a shit ton of changes not gonna write changelog.
---
.gitignore | 1 +
app/__init__.py | 16 +--
app/config.py | 17 +--
app/core/__init__.py | 6 +-
app/core/client.py | 76 ++++++++++
app/core/client/client.py | 134 ------------------
app/core/{client => }/conversation.py | 56 ++++----
app/core/db.py | 6 +-
app/core/decorators/add_cmd.py | 8 +-
app/core/{client => handlers}/filters.py | 15 +-
app/core/{client => handlers}/handler.py | 36 ++---
app/core/logger.py | 30 ++--
app/core/methods/__init__.py | 2 +
app/core/methods/channel_loggers.py | 36 +++++
app/core/methods/send_message.py | 31 ++++
app/core/types/message.py | 55 +++----
app/plugins/admin/ban.py | 4 +-
app/plugins/admin/fbans.py | 50 +++----
app/plugins/admin/promote.py | 2 -
app/plugins/dev/exec.py | 1 -
app/plugins/dev/loader.py | 27 ++--
app/plugins/dev/shell.py | 29 ++--
app/plugins/sudo/commands.py | 2 +-
app/plugins/sudo/users.py | 55 +++----
app/plugins/{utils => sys_utils}/cmdinfo.py | 0
app/plugins/{utils => sys_utils}/help.py | 0
app/plugins/{utils => sys_utils}/logs.py | 0
app/plugins/{utils => sys_utils}/ping.py | 0
app/plugins/{utils => sys_utils}/repo.py | 0
app/plugins/{utils => sys_utils}/restart.py | 0
app/plugins/{utils => sys_utils}/update.py | 2 +-
app/plugins/{tools => tg_tools}/cancel.py | 0
app/plugins/{tools => tg_tools}/chat.py | 0
app/plugins/{tools => tg_tools}/click.py | 0
app/plugins/{tools => tg_tools}/delete.py | 2 +-
.../{tools => tg_tools}/get_message.py | 0
app/plugins/{tools => tg_tools}/kang.py | 16 +--
.../{tools => tg_tools}/pm_n_tag_logger.py | 69 +++------
app/plugins/{tools => tg_tools}/pm_permit.py | 27 ++--
app/plugins/{tools => tg_tools}/reply.py | 2 +-
app/plugins/{tools => tg_tools}/respond.py | 0
app/utils/__init__.py | 6 +
app/utils/downloader.py | 13 +-
app/utils/shell.py | 21 +--
44 files changed, 423 insertions(+), 430 deletions(-)
create mode 100644 app/core/client.py
delete mode 100644 app/core/client/client.py
rename app/core/{client => }/conversation.py (79%)
rename app/core/{client => handlers}/filters.py (85%)
rename app/core/{client => handlers}/handler.py (59%)
create mode 100644 app/core/methods/__init__.py
create mode 100644 app/core/methods/channel_loggers.py
create mode 100644 app/core/methods/send_message.py
rename app/plugins/{utils => sys_utils}/cmdinfo.py (100%)
rename app/plugins/{utils => sys_utils}/help.py (100%)
rename app/plugins/{utils => sys_utils}/logs.py (100%)
rename app/plugins/{utils => sys_utils}/ping.py (100%)
rename app/plugins/{utils => sys_utils}/repo.py (100%)
rename app/plugins/{utils => sys_utils}/restart.py (100%)
rename app/plugins/{utils => sys_utils}/update.py (97%)
rename app/plugins/{tools => tg_tools}/cancel.py (100%)
rename app/plugins/{tools => tg_tools}/chat.py (100%)
rename app/plugins/{tools => tg_tools}/click.py (100%)
rename app/plugins/{tools => tg_tools}/delete.py (94%)
rename app/plugins/{tools => tg_tools}/get_message.py (100%)
rename app/plugins/{tools => tg_tools}/kang.py (95%)
rename app/plugins/{tools => tg_tools}/pm_n_tag_logger.py (73%)
rename app/plugins/{tools => tg_tools}/pm_permit.py (87%)
rename app/plugins/{tools => tg_tools}/reply.py (94%)
rename app/plugins/{tools => tg_tools}/respond.py (100%)
create mode 100644 app/utils/__init__.py
diff --git a/.gitignore b/.gitignore
index 8788524..adebd1e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ __pycache__
.idea/
conf_backup/
logs/
+.mypy_cache
diff --git a/app/__init__.py b/app/__init__.py
index 3076adf..f10dcbd 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -7,20 +7,14 @@ tracemalloc.start()
load_dotenv("config.env")
-from app.config import Config # NOQA
-from app.core.db import DB, DB_CLIENT, CustomDB # NOQA
-from app.core import Message # NOQA
-
-from app.core.logger import getLogger # NOQA
-
-LOGGER = getLogger("PLAIN-UB")
-
-from app.core.client.client import BOT # NOQA
-
if "com.termux" not in os.environ.get("PATH", ""):
import uvloop
uvloop.install()
-bot: BOT = BOT()
+
+from app.config import Config # NOQA
+from app.core import DB, DB_CLIENT, CustomDB, Message, Convo # NOQA
+from app.core.client import BOT, bot # NOQA
+from app.core.logger import LOGGER # NOQA
diff --git a/app/config.py b/app/config.py
index 5f73b60..d544647 100644
--- a/app/config.py
+++ b/app/config.py
@@ -1,22 +1,20 @@
-import json
import os
from git import Repo
+from app.utils import Str
-class _Config:
- class CMD:
- def __init__(self, cmd: str, func, path: str, doc: str, sudo: bool):
+
+class _Config(Str):
+ class CMD(Str):
+ def __init__(self, cmd: str, func, path: str, sudo: bool):
self.cmd = cmd
self.func = func
self.path: str = path
self.dirname: str = os.path.basename(os.path.dirname(path))
- self.doc: str = doc or "Not Documented."
+ self.doc: str = func.__doc__ or "Not Documented."
self.sudo: bool = sudo
- def __str__(self):
- return json.dumps(self.__dict__, indent=4, ensure_ascii=False, default=str)
-
def __init__(self):
self.CMD_DICT: dict[str, _Config.CMD] = {}
@@ -64,8 +62,5 @@ class _Config:
"UPSTREAM_REPO", "https://github.com/thedragonsinn/plain-ub"
)
- def __str__(self):
- return json.dumps(self.__dict__, indent=4, ensure_ascii=False, default=str)
-
Config = _Config()
diff --git a/app/core/__init__.py b/app/core/__init__.py
index badae98..0309fce 100644
--- a/app/core/__init__.py
+++ b/app/core/__init__.py
@@ -1,3 +1,3 @@
-from app.core.client import filters
-
-from app.core.types.message import Message # NOQA
+from app.core.conversation import Conversation as Convo
+from app.core.db import DB, DB_CLIENT, CustomDB
+from app.core.types.message import Message
diff --git a/app/core/client.py b/app/core/client.py
new file mode 100644
index 0000000..942f421
--- /dev/null
+++ b/app/core/client.py
@@ -0,0 +1,76 @@
+import asyncio
+import glob
+import importlib
+import logging
+import os
+import sys
+
+from pyrogram import Client, idle
+from pyrogram.enums import ParseMode
+
+from app import DB_CLIENT, Config
+from app.core.conversation import Conversation
+from app.core.decorators.add_cmd import AddCmd
+from app.core.methods import ChannelLogger, SendMessage
+from app.utils.aiohttp_tools import aio
+
+LOGGER = logging.getLogger("PLAIN-UB")
+
+
+def import_modules():
+ for py_module in glob.glob(pathname="app/**/[!^_]*.py", recursive=True):
+ name = os.path.splitext(py_module)[0]
+ py_name = name.replace("/", ".")
+ try:
+ mod = importlib.import_module(py_name)
+ if hasattr(mod, "init_task"):
+ Config.INIT_TASKS.append(mod.init_task())
+ except Exception as ie:
+ LOGGER.error(ie, exc_info=True)
+
+
+class BOT(AddCmd, SendMessage, ChannelLogger, Client):
+ def __init__(self):
+ super().__init__(
+ name="bot",
+ api_id=int(os.environ.get("API_ID")),
+ api_hash=os.environ.get("API_HASH"),
+ session_string=os.environ.get("SESSION_STRING").strip(),
+ parse_mode=ParseMode.DEFAULT,
+ sleep_threshold=30,
+ max_concurrent_transmissions=2,
+ )
+ self.log = LOGGER
+ self.Convo = Conversation
+
+ async def boot(self) -> None:
+ await super().start()
+ LOGGER.info("Connected to TG.")
+ import_modules()
+ LOGGER.info("Plugins Imported.")
+ await asyncio.gather(*Config.INIT_TASKS)
+ Config.INIT_TASKS.clear()
+ LOGGER.info("Init Tasks Completed.")
+ await self.log_text(text="Started")
+ LOGGER.info("Idling...")
+ await idle()
+ await self.shut_down()
+
+ @staticmethod
+ async def shut_down():
+ await aio.close()
+ if Config.MESSAGE_LOGGER_TASK:
+ Config.MESSAGE_LOGGER_TASK.cancel()
+ LOGGER.info("DB Closed.")
+ DB_CLIENT.close()
+
+ async def restart(self, hard=False) -> None:
+ await self.shut_down()
+ await super().stop(block=False)
+ if hard:
+ os.execl("/bin/bash", "/bin/bash", "run")
+ LOGGER.info("Restarting...")
+ os.execl(sys.executable, sys.executable, "-m", "app")
+
+
+bot: BOT = BOT()
diff --git a/app/core/client/client.py b/app/core/client/client.py
deleted file mode 100644
index 2f9ae7d..0000000
--- a/app/core/client/client.py
+++ /dev/null
@@ -1,134 +0,0 @@
-import asyncio
-import glob
-import importlib
-import os
-import sys
-import traceback
-from io import BytesIO
-
-from pyrogram import Client, filters, idle
-from pyrogram.enums import ParseMode
-from pyrogram.types import Message as Msg
-
-from app import DB_CLIENT, LOGGER, Config, Message
-from app.core.decorators.add_cmd import AddCmd
-from app.utils.aiohttp_tools import aio
-
-
-def import_modules():
- for py_module in glob.glob(pathname="app/**/[!^_]*.py", recursive=True):
- name = os.path.splitext(py_module)[0]
- py_name = name.replace("/", ".")
- try:
- mod = importlib.import_module(py_name)
- if hasattr(mod, "init_task"):
- Config.INIT_TASKS.append(mod.init_task())
- except BaseException:
- LOGGER.error(traceback.format_exc())
-
-
-class BOT(Client, AddCmd):
- def __init__(self):
- super().__init__(
- name="bot",
- api_id=int(os.environ.get("API_ID")),
- api_hash=os.environ.get("API_HASH"),
- session_string=os.environ.get("SESSION_STRING"),
- in_memory=True,
- parse_mode=ParseMode.DEFAULT,
- sleep_threshold=30,
- max_concurrent_transmissions=2,
- )
- from app.core.client.conversation import Conversation
-
- self.Convo = Conversation
- self.log = LOGGER
-
- async def get_response(
- self, chat_id: int, filters: filters.Filter = None, timeout: int = 8
- ) -> Message | None:
- try:
- async with self.Convo(
- chat_id=chat_id, filters=filters, timeout=timeout
- ) as convo:
- response: Message | None = await convo.get_response()
- return response
- except TimeoutError:
- return
-
- async def boot(self) -> None:
- await super().start()
- LOGGER.info("Connected to TG.")
- import_modules()
- LOGGER.info("Plugins Imported.")
- await asyncio.gather(*Config.INIT_TASKS)
- Config.INIT_TASKS.clear()
- LOGGER.info("Init Tasks Completed.")
- await self.log_text(text="Started")
- LOGGER.info("Idling...")
- await idle()
- await self.shut_down()
-
- @staticmethod
- async def shut_down():
- await aio.close()
- if Config.MESSAGE_LOGGER_TASK:
- Config.MESSAGE_LOGGER_TASK.cancel()
- LOGGER.info("DB Closed.")
- DB_CLIENT.close()
-
- async def log_text(
- self,
- text,
- name="log.txt",
- disable_web_page_preview=True,
- parse_mode=ParseMode.HTML,
- type: str = "",
- ) -> Message | Msg:
- if type:
- if hasattr(LOGGER, type):
- getattr(LOGGER, type)(text)
- text = f"#{type.upper()}\n{text}"
-
- return (await self.send_message(
- chat_id=Config.LOG_CHAT,
- text=text,
- name=name,
- disable_web_page_preview=disable_web_page_preview,
- parse_mode=parse_mode,
- )) # fmt:skip
-
- @staticmethod
- async def log_message(message: Message | Msg):
- return (await message.copy(chat_id=Config.LOG_CHAT)) # fmt: skip
-
- async def restart(self, hard=False) -> None:
- await self.shut_down()
- await super().stop(block=False)
- if hard:
- os.execl("/bin/bash", "/bin/bash", "run")
- LOGGER.info("Restarting...")
- os.execl(sys.executable, sys.executable, "-m", "app")
-
- async def send_message(
- self,
- chat_id: int | str,
- text,
- name: str = "output.txt",
- disable_web_page_preview: bool = False,
- **kwargs,
- ) -> Message | Msg:
- text = str(text)
- if len(text) < 4096:
- message = await super().send_message(
- chat_id=chat_id,
- text=text,
- disable_web_page_preview=disable_web_page_preview,
- **kwargs,
- )
- return Message.parse(message=message)
- doc = BytesIO(bytes(text, encoding="utf-8"))
- doc.name = name
- return (await super().send_document(
- chat_id=chat_id, document=doc, **kwargs
- )) # fmt: skip
diff --git a/app/core/client/conversation.py b/app/core/conversation.py
similarity index 79%
rename from app/core/client/conversation.py
rename to app/core/conversation.py
index d7c9aa0..befd1b8 100644
--- a/app/core/client/conversation.py
+++ b/app/core/conversation.py
@@ -1,11 +1,12 @@
import asyncio
-import json
from pyrogram.filters import Filter
from pyrogram.types import Message
+from app.utils import Str
-class Conversation:
+
+class Conversation(Str):
CONVO_DICT: dict[int, "Conversation"] = {}
class DuplicateConvo(Exception):
@@ -13,20 +14,41 @@ class Conversation:
super().__init__(f"Conversation already started with {chat} ")
def __init__(
- self, chat_id: int | str, filters: Filter | None = None, timeout: int = 10
+ self,
+ client,
+ chat_id: int | str,
+ filters: Filter | None = None,
+ timeout: int = 10,
):
self.chat_id = chat_id
+ self._client = client
self.filters = filters
- self.timeout = timeout
self.response = None
self.responses: list = []
+ self.timeout = timeout
self.set_future()
- from app import bot
- self._client = bot
+ async def __aenter__(self) -> "Conversation":
+ if isinstance(self.chat_id, str):
+ self.chat_id = (await self._client.get_chat(self.chat_id)).id
+ if self.chat_id in Conversation.CONVO_DICT.keys():
+ raise self.DuplicateConvo(self.chat_id)
+ Conversation.CONVO_DICT[self.chat_id] = self
+ return self
- def __str__(self):
- return json.dumps(self.__dict__, indent=4, ensure_ascii=False, default=str)
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
+ Conversation.CONVO_DICT.pop(self.chat_id, None)
+ if not self.response.done():
+ self.response.cancel()
+
+ @classmethod
+ async def get_resp(cls, client, *args, **kwargs) -> Message | None:
+ try:
+ async with cls(*args, client=client, **kwargs) as convo:
+ response: Message | None = await convo.get_response()
+ return response
+ except TimeoutError:
+ return
def set_future(self, *args, **kwargs):
future = asyncio.Future()
@@ -36,7 +58,7 @@ class Conversation:
async def get_response(self, timeout: int | None = None) -> Message | None:
try:
resp_future: asyncio.Future.result = await asyncio.wait_for(
- self.response, timeout=timeout or self.timeout
+ fut=self.response, timeout=timeout or self.timeout
)
return resp_future
except asyncio.TimeoutError:
@@ -76,19 +98,3 @@ class Conversation:
response = await self.get_response(timeout=timeout or self.timeout)
return message, response
return message
-
- async def __aenter__(self) -> "Conversation":
- if isinstance(self.chat_id, str):
- self.chat_id = (await self._client.get_chat(self.chat_id)).id
- if (
- self.chat_id in Conversation.CONVO_DICT.keys()
- and Conversation.CONVO_DICT[self.chat_id].filters == self.filters
- ):
- raise self.DuplicateConvo(self.chat_id)
- Conversation.CONVO_DICT[self.chat_id] = self
- return self
-
- async def __aexit__(self, exc_type, exc_val, exc_tb):
- Conversation.CONVO_DICT.pop(self.chat_id, None)
- if not self.response.done():
- self.response.cancel()
diff --git a/app/core/db.py b/app/core/db.py
index 018d99f..3539cb8 100644
--- a/app/core/db.py
+++ b/app/core/db.py
@@ -4,15 +4,17 @@ import dns.resolver
from motor.core import AgnosticClient, AgnosticDatabase
from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorCollection
+from app.utils import Str
+
dns.resolver.default_resolver = dns.resolver.Resolver(configure=False)
dns.resolver.default_resolver.nameservers = ["8.8.8.8"]
-DB_CLIENT: AgnosticClient = AsyncIOMotorClient(os.environ.get("DB_URL"))
+DB_CLIENT: AgnosticClient = AsyncIOMotorClient(os.environ.get("DB_URL").strip())
DB: AgnosticDatabase = DB_CLIENT["plain_ub"]
-class CustomDB(AsyncIOMotorCollection):
+class CustomDB(AsyncIOMotorCollection, Str):
def __init__(self, collection_name: str):
super().__init__(database=DB, name=collection_name)
diff --git a/app/core/decorators/add_cmd.py b/app/core/decorators/add_cmd.py
index 361ec0a..d9ed5e9 100644
--- a/app/core/decorators/add_cmd.py
+++ b/app/core/decorators/add_cmd.py
@@ -15,15 +15,11 @@ class AddCmd:
if isinstance(cmd, list):
for _cmd in cmd:
Config.CMD_DICT[_cmd] = Config.CMD(
- cmd=_cmd,
- func=func,
- path=path,
- doc=func.__doc__,
- sudo=allow_sudo,
+ cmd=_cmd, func=func, path=path, sudo=allow_sudo
)
else:
Config.CMD_DICT[cmd] = Config.CMD(
- cmd=cmd, func=func, path=path, doc=func.__doc__, sudo=allow_sudo
+ cmd=cmd, func=func, path=path, sudo=allow_sudo
)
wrapper()
diff --git a/app/core/client/filters.py b/app/core/handlers/filters.py
similarity index 85%
rename from app/core/client/filters.py
rename to app/core/handlers/filters.py
index 25c9ca2..953a3a1 100644
--- a/app/core/client/filters.py
+++ b/app/core/handlers/filters.py
@@ -2,7 +2,7 @@ from pyrogram import filters as _filters
from pyrogram.types import Message
from app import Config
-from app.core.client.conversation import Conversation
+from app.core.conversation import Conversation
convo_filter = _filters.create(
lambda _, __, message: (message.chat.id in Conversation.CONVO_DICT.keys())
@@ -13,11 +13,14 @@ convo_filter = _filters.create(
def cmd_check(message: Message, trigger: str, sudo: bool = False) -> bool:
start_str = message.text.split(maxsplit=1)[0]
cmd = start_str.replace(trigger, "", 1)
- return (
- bool(cmd in Config.SUDO_CMD_LIST)
- if sudo
- else bool(cmd in Config.CMD_DICT.keys())
- )
+ cmd_obj = Config.CMD_DICT.get(cmd)
+ if not cmd_obj:
+ return False
+ if sudo:
+ in_sudo = cmd in Config.SUDO_CMD_LIST
+ has_access = Config.CMD_DICT[cmd].sudo
+ return in_sudo and has_access
+ return True
def basic_check(message: Message):
diff --git a/app/core/client/handler.py b/app/core/handlers/handler.py
similarity index 59%
rename from app/core/client/handler.py
rename to app/core/handlers/handler.py
index 88ad2bd..ce5babf 100644
--- a/app/core/client/handler.py
+++ b/app/core/handlers/handler.py
@@ -1,10 +1,9 @@
import asyncio
-import traceback
from pyrogram.types import Message as Msg
-from app import BOT, Config, bot
-from app.core import Message, filters
+from app import BOT, Config, Message, bot
+from app.core.handlers import filters
@bot.on_message(
@@ -16,10 +15,15 @@ from app.core import Message, filters
async def cmd_dispatcher(bot: BOT, message: Message) -> None:
message = Message.parse(message)
func = Config.CMD_DICT[message.cmd].func
- coro = func(bot, message)
- x = await run_coro(coro, message)
- if not x and message.is_from_owner:
- await message.delete()
+ task = asyncio.Task(func(bot, message), name=message.task_id)
+ try:
+ await task
+ if message.is_from_owner:
+ await message.delete()
+ except asyncio.exceptions.CancelledError:
+ await bot.log_text(text=f"#Cancelled:\n{message.text}")
+ except Exception as e:
+ bot.log.error(e, exc_info=True, extra={"tg_message": message})
message.stop_propagation()
@@ -32,21 +36,3 @@ async def convo_handler(bot: BOT, message: Msg):
conv_obj.responses.append(message)
conv_obj.response.set_result(message)
message.continue_propagation()
-
-
-async def run_coro(coro, message: Message) -> None | int:
- try:
- task = asyncio.Task(coro, name=message.task_id)
- await task
- except asyncio.exceptions.CancelledError:
- await bot.log_text(text=f"#Cancelled:\n{message.text}")
- except BaseException:
- text = (
- "#Traceback"
- f"\nFunction: {coro.__name__}"
- f"\nChat: {message.chat.title or message.from_user.first_name}"
- f"\nTraceback:"
- f"\n
{traceback.format_exc()}"
- )
- await bot.log_text(text=text, type="error")
- return 1
diff --git a/app/core/logger.py b/app/core/logger.py
index ae69f29..64b53dd 100644
--- a/app/core/logger.py
+++ b/app/core/logger.py
@@ -1,5 +1,5 @@
-import os
import asyncio
+import os
from logging import (
ERROR,
INFO,
@@ -11,29 +11,41 @@ from logging import (
handlers,
)
-os.makedirs("logs", exist_ok=True)
+from app import bot
+
+os.makedirs(name="logs", exist_ok=True)
+
+LOGGER = getLogger("PLAIN-UB")
class TgErrorHandler(Handler):
def emit(self, log_record):
- if log_record.levelno < ERROR:
- return
- from app import bot
if not bot.is_connected:
- return
+ return
+ self.format(log_record)
+ chat = ""
+ if hasattr(log_record, "tg_message"):
+ chat = (
+ log_record.tg_message.chat.title
+ or log_record.tg_message.chat.first_name
+ )
text = (
f"#{log_record.levelname} #TRACEBACK"
+ f"\nChat: {chat}"
f"\nLine No: {log_record.lineno}"
f"\nFunc: {log_record.funcName}"
f"\nModule: {log_record.module}"
f"\nTime: {log_record.asctime}"
- f"\nError Message:\n{log_record.message}"
+ f"\nError Message:\n{log_record.exc_text or log_record.message}"
)
asyncio.run_coroutine_threadsafe(
coro=bot.log_text(text=text, name="traceback.txt"), loop=bot.loop
)
+custom_handler = TgErrorHandler()
+custom_handler.setLevel(ERROR)
+
basicConfig(
level=INFO,
format="[%(levelname)s] [%(asctime)s] [%(name)s] [%(module)s]: %(message)s",
@@ -45,10 +57,10 @@ basicConfig(
maxBytes=5 * 1024 * 1024,
backupCount=2,
encoding=None,
- delay=0,
+ delay=False,
),
StreamHandler(),
- TgErrorHandler(),
+ custom_handler,
},
)
diff --git a/app/core/methods/__init__.py b/app/core/methods/__init__.py
new file mode 100644
index 0000000..2bb068b
--- /dev/null
+++ b/app/core/methods/__init__.py
@@ -0,0 +1,2 @@
+from app.core.methods.channel_loggers import ChannelLogger
+from app.core.methods.send_message import SendMessage
diff --git a/app/core/methods/channel_loggers.py b/app/core/methods/channel_loggers.py
new file mode 100644
index 0000000..50cd55b
--- /dev/null
+++ b/app/core/methods/channel_loggers.py
@@ -0,0 +1,36 @@
+import logging
+
+from pyrogram import Client
+from pyrogram.enums import ParseMode
+
+from app import Config
+from app.core.types.message import Message
+
+LOGGER = logging.getLogger("PLAIN-UB")
+
+
+class ChannelLogger(Client):
+ async def log_text(
+ self,
+ text,
+ name="log.txt",
+ disable_web_page_preview=True,
+ parse_mode=ParseMode.HTML,
+ type: str = "",
+ ) -> Message:
+ if type:
+ if hasattr(LOGGER, type):
+ getattr(LOGGER, type)(text)
+ text = f"#{type.upper()}\n{text}"
+
+ return (await self.send_message(
+ chat_id=Config.LOG_CHAT,
+ text=text,
+ name=name,
+ disable_web_page_preview=disable_web_page_preview,
+ parse_mode=parse_mode,
+ )) # fmt:skip
+
+ @staticmethod
+ async def log_message(message: Message):
+ return (await message.copy(chat_id=Config.LOG_CHAT)) # fmt: skip
diff --git a/app/core/methods/send_message.py b/app/core/methods/send_message.py
new file mode 100644
index 0000000..c002b9f
--- /dev/null
+++ b/app/core/methods/send_message.py
@@ -0,0 +1,31 @@
+from io import BytesIO
+
+from pyrogram import Client
+
+from app.core.types.message import Message
+
+
+class SendMessage(Client):
+ async def send_message(
+ self,
+ chat_id: int | str,
+ text,
+ name: str = "output.txt",
+ disable_web_page_preview: bool = False,
+ **kwargs,
+ ) -> Message:
+ if not isinstance(text, str):
+ text = str(text)
+ if len(text) < 4096:
+ message = await super().send_message(
+ chat_id=chat_id,
+ text=text,
+ disable_web_page_preview=disable_web_page_preview,
+ **kwargs,
+ )
+ return Message.parse(message=message)
+ doc = BytesIO(bytes(text, encoding="utf-8"))
+ doc.name = name
+ return (await super().send_document(
+ chat_id=chat_id, document=doc, **kwargs
+ )) # fmt: skip
diff --git a/app/core/types/message.py b/app/core/types/message.py
index f479823..84f5059 100644
--- a/app/core/types/message.py
+++ b/app/core/types/message.py
@@ -1,12 +1,14 @@
import asyncio
from functools import cached_property
+from pyrogram.enums import MessageEntityType
from pyrogram.errors import MessageDeleteForbidden
from pyrogram.filters import Filter
from pyrogram.types import Message as Msg
from pyrogram.types import User
from app import Config
+from app.core.conversation import Conversation
class Message(Msg):
@@ -19,7 +21,7 @@ class Message(Msg):
def cmd(self) -> str | None:
raw_cmd = self.text_list[0]
cmd = raw_cmd.replace(self.trigger, "", 1)
- return cmd if cmd in Config.CMD_DICT else None
+ return cmd if cmd in Config.CMD_DICT.keys() else None
@cached_property
def flags(self) -> list:
@@ -27,7 +29,7 @@ class Message(Msg):
@cached_property
def flt_input(self) -> str:
- split_lines = self.input.split("\n", maxsplit=1)
+ split_lines = self.input.split(sep="\n", maxsplit=1)
split_lines[0] = " ".join(
[word for word in split_lines[0].split(" ") if word not in self.flags]
)
@@ -91,9 +93,10 @@ class Message(Msg):
except MessageDeleteForbidden:
pass
- async def edit(self, text, del_in: int = 0, block=True, **kwargs) -> "Message":
+ async def edit(
+ self, text, del_in: int = 0, block=True, name: str = "output.txt", **kwargs
+ ) -> "Message":
if len(str(text)) < 4096:
- kwargs.pop("name", "")
task = super().edit_text(text=text, **kwargs)
if del_in:
reply = await self.async_deleter(task=task, del_in=del_in, block=block)
@@ -102,41 +105,41 @@ class Message(Msg):
self.text = reply.text
else:
_, reply = await asyncio.gather(
- super().delete(), self.reply(text, **kwargs)
+ super().delete(), self.reply(text, name=name, **kwargs)
)
return reply
- async def extract_user_n_reason(self) -> list[User | str | Exception, str | None]:
+ async def extract_user_n_reason(self) -> tuple[User | str | Exception, str | None]:
if self.replied:
- return [self.replied.from_user, self.flt_input]
- inp_list = self.flt_input.split(maxsplit=1)
- if not inp_list:
- return [
+ return self.replied.from_user, self.flt_input
+ input_text_list = self.flt_input.split(maxsplit=1)
+ if not input_text_list:
+ return (
"Unable to Extract User info.\nReply to a user or input @ | id.",
- "",
- ]
- user = inp_list[0]
+ None,
+ )
+ user = input_text_list[0]
reason = None
- if len(inp_list) >= 2:
- reason = inp_list[1]
+ if len(input_text_list) >= 2:
+ reason = input_text_list[1]
+ if self.entities:
+ for entity in self.entities:
+ if entity == MessageEntityType.MENTION:
+ return entity.user, reason
if user.isdigit():
user = int(user)
elif user.startswith("@"):
user = user.strip("@")
try:
- return [await self._client.get_users(user_ids=user), reason]
- except Exception as e:
- return [e, reason]
+ return (await self._client.get_users(user_ids=user)), reason
+ except Exception:
+ return user, reason
async def get_response(self, filters: Filter = None, timeout: int = 8):
- try:
- async with self._client.Convo(
- chat_id=self.chat.id, filters=filters, timeout=timeout
- ) as convo:
- response: Message | None = await convo.get_response()
- return response
- except TimeoutError:
- return
+ response: Message | None = await Conversation.get_resp(
+ client=self._client, chat_id=self.chat.id, filters=filters, timeout=timeout
+ )
+ return response
async def log(self):
return (await self.copy(Config.LOG_CHAT)) # fmt:skip
diff --git a/app/plugins/admin/ban.py b/app/plugins/admin/ban.py
index 9bc3ebe..e71327d 100644
--- a/app/plugins/admin/ban.py
+++ b/app/plugins/admin/ban.py
@@ -23,7 +23,7 @@ async def ban_or_unban(bot: BOT, message: Message) -> None:
try:
await action
await message.reply(
- text=f"{message.cmd.capitalize()}ned: {user.mention}\nReason: {reason}."
+ text=f"{message.cmd.capitalize()}ned: {user.mention}\nReason: {reason}"
)
except Exception as e:
await message.reply(text=e, del_in=10)
@@ -40,7 +40,7 @@ async def kick_user(bot: BOT, message: Message):
await asyncio.sleep(1)
await bot.unban_chat_member(chat_id=message.chat.id, user_id=user.id)
await message.reply(
- text=f"{message.cmd.capitalize()}ed: {user.mention}\nReason: {reason}."
+ text=f"{message.cmd.capitalize()}ed: {user.mention}\nReason: {reason}"
)
except Exception as e:
await message.reply(text=e, del_in=10)
diff --git a/app/plugins/admin/fbans.py b/app/plugins/admin/fbans.py
index 8829d71..0365cfd 100644
--- a/app/plugins/admin/fbans.py
+++ b/app/plugins/admin/fbans.py
@@ -1,8 +1,7 @@
import asyncio
-from functools import cached_property
from pyrogram import filters
-from pyrogram.enums import ChatMemberStatus
+from pyrogram.enums import ChatMemberStatus, ChatType
from pyrogram.types import Chat, User
from app import BOT, Config, CustomDB, Message, bot
@@ -10,7 +9,7 @@ from app.utils.helpers import get_name
DB = CustomDB("FED_LIST")
-BASIC_FILTER = filters.user([609517172, 2059887769]) & ~filters.service # NOQA
+BASIC_FILTER = filters.user([609517172, 2059887769]) & ~filters.service
FBAN_REGEX = filters.regex(
r"(New FedBan|"
@@ -26,16 +25,6 @@ FBAN_REGEX = filters.regex(
UNFBAN_REGEX = filters.regex(r"(New un-FedBan|I'll give|Un-FedBan)")
-class _User(User):
- def __init__(self, id):
- super().__init__(id=id)
- self.first_name = id
-
- @cached_property
- def mention(self) -> str:
- return f"{self.id}"
-
-
@bot.add_cmd(cmd="addf")
async def add_fed(bot: BOT, message: Message):
"""
@@ -96,8 +85,12 @@ async def fed_ban(bot: BOT, message: Message):
await progress.edit(user)
return
if not isinstance(user, User):
- user = _User(id=message.text_list[1])
- if user.id in [Config.OWNER_ID, *Config.SUDO_USERS]:
+ user_id = user
+ user_mention = f"{user_id}"
+ else:
+ user_id = user.id
+ user_mention = user.mention
+ if user_id in [Config.OWNER_ID, *Config.SUDO_USERS, *Config.SUDO_USERS]:
await progress.edit("Cannot Fban Owner/Sudo users.")
return
proof_str: str = ""
@@ -110,17 +103,20 @@ async def fed_ban(bot: BOT, message: Message):
reason = f"{reason}{proof_str}"
- if message.replied:
- me = await bot.get_chat_member(message.chat.id, "me")
+ if message.replied and not message.chat.type == ChatType.PRIVATE:
+ me = await bot.get_chat_member(chat_id=message.chat.id, user_id="me")
if me.status in {ChatMemberStatus.OWNER, ChatMemberStatus.ADMINISTRATOR}:
await message.replied.reply(
- f"!dban {reason}", disable_web_page_preview=True, del_in=3, block=False
+ text=f"!dban {reason}",
+ disable_web_page_preview=True,
+ del_in=3,
+ block=False,
)
await progress.edit("❯❯")
total: int = 0
failed: list[str] = []
- fban_cmd: str = f"/fban {user.id} {reason}"
+ fban_cmd: str = f"/fban {user_id} {reason}"
async for fed in DB.find():
chat_id = int(fed["_id"])
total += 1
@@ -139,8 +135,8 @@ async def fed_ban(bot: BOT, message: Message):
await progress.edit("You Don't have any feds connected!")
return
resp_str = (
- f"❯❯❯ FBanned {user.mention}"
- f"\nID: {user.id}"
+ f"❯❯❯ FBanned {user_mention}"
+ f"\nID: {user_id}"
f"\nReason: {reason}"
f"\nInitiated in: {message.chat.title or 'PM'}"
)
@@ -168,12 +164,16 @@ async def un_fban(bot: BOT, message: Message):
await progress.edit(user)
return
if not isinstance(user, User):
- user = _User(id=message.text_list[1])
+ user_id = user
+ user_mention = f"{user_id}"
+ else:
+ user_id = user.id
+ user_mention = user.mention
await progress.edit("❯❯")
total: int = 0
failed: list[str] = []
- unfban_cmd: str = f"/unfban {user.id} {reason}"
+ unfban_cmd: str = f"/unfban {user_id} {reason}"
async for fed in DB.find():
chat_id = int(fed["_id"])
total += 1
@@ -189,7 +189,9 @@ async def un_fban(bot: BOT, message: Message):
if not total:
await progress.edit("You Don't have any feds connected!")
return
- resp_str = f"❯❯❯ Un-FBanned {user.mention}\nID: {user.id}\nReason: {reason}\n"
+ resp_str = (
+ f"❯❯❯ Un-FBanned {user_mention}" f"\nID: {user_id}" f"\nReason: {reason}\n"
+ )
if failed:
resp_str += f"Failed in: {len(failed)}/{total}\n• " + "\n• ".join(failed)
else:
diff --git a/app/plugins/admin/promote.py b/app/plugins/admin/promote.py
index 126d5ea..b83fed9 100644
--- a/app/plugins/admin/promote.py
+++ b/app/plugins/admin/promote.py
@@ -67,8 +67,6 @@ async def promote_or_demote(bot: BOT, message: Message) -> None:
chat_id=message.chat.id, user_id=user.id, privileges=privileges
)
if promote:
- # Let server promote admin before setting title
- # Bot is too fast moment 😂😂😂
await asyncio.sleep(1)
await bot.set_administrator_title(
chat_id=message.chat.id, user_id=user.id, title=title or "Admin"
diff --git a/app/plugins/dev/exec.py b/app/plugins/dev/exec.py
index 7006949..a084ad0 100644
--- a/app/plugins/dev/exec.py
+++ b/app/plugins/dev/exec.py
@@ -62,6 +62,5 @@ if Config.DEV_MODE:
cmd="py",
func=executor,
path=inspect.stack()[0][1],
- doc=executor.__doc__,
sudo=False,
)
diff --git a/app/plugins/dev/loader.py b/app/plugins/dev/loader.py
index bc96350..c06e3e6 100644
--- a/app/plugins/dev/loader.py
+++ b/app/plugins/dev/loader.py
@@ -12,19 +12,29 @@ async def loader(bot: BOT, message: Message) -> Message | None:
not message.replied
or not message.replied.document
or not message.replied.document.file_name.endswith(".py")
- ):
- return await message.reply("Reply to a Plugin.")
+ ) and "-r" not in message.flags:
+ await message.reply("Reply to a Plugin.")
+ return
+ if "-r" in message.flags:
+ plugin = message.flt_input
+ cmd_module = Config.CMD_DICT.get(plugin)
+ if not cmd_module:
+ await message.reply(text="Invalid cmd.")
+ return
+ module = str(cmd_module.func.__module__)
+ else:
+ file_name: str = os.path.splitext(message.replied.document.file_name)[0]
+ module = f"app.temp.{file_name}"
+ await message.replied.download("app/temp/")
reply: Message = await message.reply("Loading....")
- file_name: str = os.path.splitext(message.replied.document.file_name)[0]
- reload = sys.modules.pop(f"app.temp.{file_name}", None)
+ reload = sys.modules.pop(module, None)
status: str = "Reloaded" if reload else "Loaded"
- await message.replied.download("app/temp/")
try:
- importlib.import_module(f"app.temp.{file_name}")
- except BaseException:
+ importlib.import_module(module)
+ except Exception as e:
await reply.edit(str(traceback.format_exc()))
return
- await reply.edit(f"{status} {file_name}.py.")
+ await reply.edit(f"{status} {module}")
if Config.DEV_MODE:
@@ -32,6 +42,5 @@ if Config.DEV_MODE:
cmd="load",
func=loader,
path=inspect.stack()[0][1],
- doc=loader.__doc__,
sudo=False,
)
diff --git a/app/plugins/dev/shell.py b/app/plugins/dev/shell.py
index 0dfc32f..b3deae8 100644
--- a/app/plugins/dev/shell.py
+++ b/app/plugins/dev/shell.py
@@ -11,7 +11,7 @@ async def run_cmd(bot: BOT, message: Message) -> Message | None:
cmd: str = message.input.strip()
reply: Message = await message.reply("executing...")
try:
- proc_stdout: str = await asyncio.Task(
+ proc_stdout: str = await asyncio.create_task(
shell.run_shell_cmd(cmd), name=reply.task_id
)
except asyncio.exceptions.CancelledError:
@@ -21,7 +21,7 @@ async def run_cmd(bot: BOT, message: Message) -> Message | None:
# Shell with Live Output
-async def live_shell(bot: BOT, message: Message) -> Message | None:
+async def live_shell(bot: BOT, message: Message):
cmd: str = message.input.strip()
reply: Message = await message.reply("`getting live output....`")
sub_process: shell.AsyncShell = await shell.AsyncShell.run_cmd(cmd)
@@ -30,25 +30,24 @@ async def live_shell(bot: BOT, message: Message) -> Message | None:
try:
async for stdout in sub_process.get_output():
if output != stdout:
- if len(stdout) <= 4096:
- await reply.edit(
- f"```shell\n{stdout}```",
- disable_web_page_preview=True,
- parse_mode=ParseMode.MARKDOWN,
- )
+ await reply.edit(
+ text=f"```shell\n{stdout}```",
+ disable_web_page_preview=True,
+ parse_mode=ParseMode.MARKDOWN,
+ )
output = stdout
if sleep_for >= 6:
- sleep_for = 1
- await asyncio.Task(asyncio.sleep(sleep_for), name=reply.task_id)
- sleep_for += 1
- return await reply.edit(
- f"~${cmd}\n\n{sub_process.full_std}",
+ sleep_for = 2
+ await asyncio.create_task(asyncio.sleep(sleep_for), name=reply.task_id)
+ sleep_for += 2
+ await reply.edit(
+ text=f"~${cmd}\n\n{sub_process.full_std}",
name="shell.txt",
disable_web_page_preview=True,
)
except asyncio.exceptions.CancelledError:
sub_process.cancel()
- return await reply.edit(f"`Cancelled....`")
+ await reply.edit(f"`Cancelled....`")
if Config.DEV_MODE:
@@ -56,13 +55,11 @@ if Config.DEV_MODE:
cmd="shell",
func=live_shell,
path=inspect.stack()[0][1],
- doc=live_shell.__doc__,
sudo=False,
)
Config.CMD_DICT["sh"] = Config.CMD(
cmd="sh",
func=run_cmd,
path=inspect.stack()[0][1],
- doc=run_cmd.__doc__,
sudo=False,
)
diff --git a/app/plugins/sudo/commands.py b/app/plugins/sudo/commands.py
index d75143f..349fe63 100644
--- a/app/plugins/sudo/commands.py
+++ b/app/plugins/sudo/commands.py
@@ -28,7 +28,7 @@ async def add_scmd(bot: BOT, message: Message):
return
cmd = message.flt_input
response = await message.reply(f"Adding {cmd} to sudo....")
- func = Config.CMD_DICT.get(cmd, None)
+ func = Config.CMD_DICT.get(cmd)
if not func:
await response.edit(text=f"{cmd} not a valid command.", del_in=10)
return
diff --git a/app/plugins/sudo/users.py b/app/plugins/sudo/users.py
index 0a774ca..7684095 100644
--- a/app/plugins/sudo/users.py
+++ b/app/plugins/sudo/users.py
@@ -3,7 +3,6 @@ import asyncio
from pyrogram.types import User
from app import BOT, Config, CustomDB, Message, bot
-from app.plugins.admin.fbans import _User
from app.utils.helpers import extract_user_data, get_name
SUDO = CustomDB("COMMON_SETTINGS")
@@ -53,23 +52,22 @@ async def add_sudo(bot: BOT, message: Message) -> Message | None:
"""
response = await message.reply("Extracting User info...")
user, _ = await message.extract_user_n_reason()
- if isinstance(user, str):
- await response.edit(user)
- return
if not isinstance(user, User):
- user: _User = _User(id=message.text_list[1])
- config, text = (
- (Config.SUPERUSERS, "Super Users")
- if "-su" in message.flags
- else (Config.SUDO_USERS, "Sudo Users.")
- )
- if user.id in config:
+ await response.edit("unable to extract user info.")
+ return
+ if "-su" in message.flags:
+ add_list, remove_list = Config.SUPERUSERS, Config.SUDO_USERS
+ text = "Super Users"
+ else:
+ add_list, remove_list = Config.SUDO_USERS, Config.SUPERUSERS
+ text = "Sudo Users"
+ if user.id in add_list:
await response.edit(
text=f"{get_name(user)} already in Sudo with same privileges!", del_in=5
)
return
response_str = f"#SUDO\n{user.mention} added to {text} List."
- config.append(user.id)
+ add_and_remove(user.id, add_list, remove_list)
if "-temp" not in message.flags:
await SUDO_USERS.add_data(
{
@@ -102,21 +100,21 @@ async def remove_sudo(bot: BOT, message: Message) -> Message | None:
await response.edit(user)
return
if not isinstance(user, User):
- user: _User = _User(id=message.text_list[1])
- config, text = (
- (Config.SUPERUSERS, "Super Users")
- if "-su" in message.flags
- else (Config.SUDO_USERS, "Sudo Users.")
- )
- if user.id not in config:
- await response.edit(text=f"{get_name(user)} not in {text}!", del_in=5)
+ await response.edit("unable to extract user info.")
return
- config.remove(user.id)
- response_str = f"{user.mention} removed from {text} List"
+ if user.id not in {*Config.SUDO_USERS, *Config.SUPERUSERS}:
+ await response.edit(text=f"{get_name(user)} not in Sudo!", del_in=5)
+ return
+ if "-su" in message.flags:
+ response_str = f"{user.mention}'s Super User access is revoked to Sudo only."
+ add_and_remove(user.id, Config.SUDO_USERS, Config.SUPERUSERS)
+ else:
+ add_and_remove(user.id, remove_list=Config.SUPERUSERS)
+ add_and_remove(user.id, remove_list=Config.SUDO_USERS)
+ response_str = f"{user.mention}'s access to bot has been removed."
if "-temp" not in message.flags:
if "-su" in message.flags:
- response_str += " and added to Sudo."
- await SUDO_USERS.add_data({"_id": user.id, "super": "-su" in message.flags})
+ await SUDO_USERS.add_data({"_id": user.id, "super": False})
else:
await SUDO_USERS.delete_data(id=user.id)
else:
@@ -125,6 +123,15 @@ async def remove_sudo(bot: BOT, message: Message) -> Message | None:
await response.log()
+def add_and_remove(
+ u_id: int, add_list: list | None = None, remove_list: list | None = None
+):
+ if add_list is not None and u_id not in add_list:
+ add_list.append(u_id)
+ if remove_list is not None and u_id in remove_list:
+ remove_list.remove(u_id)
+
+
@bot.add_cmd(cmd="vsudo")
async def sudo_list(bot: BOT, message: Message):
"""
diff --git a/app/plugins/utils/cmdinfo.py b/app/plugins/sys_utils/cmdinfo.py
similarity index 100%
rename from app/plugins/utils/cmdinfo.py
rename to app/plugins/sys_utils/cmdinfo.py
diff --git a/app/plugins/utils/help.py b/app/plugins/sys_utils/help.py
similarity index 100%
rename from app/plugins/utils/help.py
rename to app/plugins/sys_utils/help.py
diff --git a/app/plugins/utils/logs.py b/app/plugins/sys_utils/logs.py
similarity index 100%
rename from app/plugins/utils/logs.py
rename to app/plugins/sys_utils/logs.py
diff --git a/app/plugins/utils/ping.py b/app/plugins/sys_utils/ping.py
similarity index 100%
rename from app/plugins/utils/ping.py
rename to app/plugins/sys_utils/ping.py
diff --git a/app/plugins/utils/repo.py b/app/plugins/sys_utils/repo.py
similarity index 100%
rename from app/plugins/utils/repo.py
rename to app/plugins/sys_utils/repo.py
diff --git a/app/plugins/utils/restart.py b/app/plugins/sys_utils/restart.py
similarity index 100%
rename from app/plugins/utils/restart.py
rename to app/plugins/sys_utils/restart.py
diff --git a/app/plugins/utils/update.py b/app/plugins/sys_utils/update.py
similarity index 97%
rename from app/plugins/utils/update.py
rename to app/plugins/sys_utils/update.py
index 47a401d..838b2a9 100644
--- a/app/plugins/utils/update.py
+++ b/app/plugins/sys_utils/update.py
@@ -3,7 +3,7 @@ import asyncio
from git import Repo
from app import BOT, Config, Message, bot
-from app.plugins.utils.restart import restart
+from app.plugins.sys_utils.restart import restart
async def get_commits(repo: Repo) -> str | None:
diff --git a/app/plugins/tools/cancel.py b/app/plugins/tg_tools/cancel.py
similarity index 100%
rename from app/plugins/tools/cancel.py
rename to app/plugins/tg_tools/cancel.py
diff --git a/app/plugins/tools/chat.py b/app/plugins/tg_tools/chat.py
similarity index 100%
rename from app/plugins/tools/chat.py
rename to app/plugins/tg_tools/chat.py
diff --git a/app/plugins/tools/click.py b/app/plugins/tg_tools/click.py
similarity index 100%
rename from app/plugins/tools/click.py
rename to app/plugins/tg_tools/click.py
diff --git a/app/plugins/tools/delete.py b/app/plugins/tg_tools/delete.py
similarity index 94%
rename from app/plugins/tools/delete.py
rename to app/plugins/tg_tools/delete.py
index 736d442..03f1275 100644
--- a/app/plugins/tools/delete.py
+++ b/app/plugins/tg_tools/delete.py
@@ -1,6 +1,6 @@
from app import BOT, bot
from app.core import Message
-from app.plugins.tools.get_message import parse_link
+from app.plugins.tg_tools.get_message import parse_link
@bot.add_cmd(cmd="del")
diff --git a/app/plugins/tools/get_message.py b/app/plugins/tg_tools/get_message.py
similarity index 100%
rename from app/plugins/tools/get_message.py
rename to app/plugins/tg_tools/get_message.py
diff --git a/app/plugins/tools/kang.py b/app/plugins/tg_tools/kang.py
similarity index 95%
rename from app/plugins/tools/kang.py
rename to app/plugins/tg_tools/kang.py
index a3cd2ca..dae9698 100644
--- a/app/plugins/tools/kang.py
+++ b/app/plugins/tg_tools/kang.py
@@ -42,7 +42,7 @@ async def kang_sticker(bot: BOT, message: Message):
text=f"Kanged: here"
)
return
- async with bot.Convo(chat_id="stickers", timeout=60) as convo:
+ async with bot.Convo(client=bot, chat_id="stickers", timeout=60) as convo:
await convo.send_message(text="/addsticker", get_response=True, timeout=5)
await convo.send_message(text=pack_name, get_response=True, timeout=5)
if kwargs.get("sticker"):
@@ -66,7 +66,7 @@ async def kang_sticker(bot: BOT, message: Message):
async def create_n_kang(
kwargs: dict, pack_title: str, pack_name: str, message: Message
):
- async with bot.Convo(chat_id="stickers", timeout=60) as convo:
+ async with bot.Convo(client=bot, chat_id="stickers", timeout=60) as convo:
await convo.send_message(text=kwargs["cmd"], get_response=True, timeout=5)
await convo.send_message(text=pack_title, get_response=True, timeout=5)
if kwargs.get("sticker"):
@@ -177,11 +177,7 @@ async def video_kang(message: Message, ff=False) -> dict:
limit = 50
is_video = True
return dict(
- cmd=cmd,
- limit=limit,
- is_video=is_video,
- file=output_file,
- path=down_dir,
+ cmd=cmd, limit=limit, is_video=is_video, file=output_file, path=down_dir
)
@@ -223,9 +219,5 @@ async def sticker_kang(message: Message) -> dict:
input_file: BytesIO = await message.download(in_memory=True)
input_file.seek(0)
return dict(
- emoji=emoji,
- file=input_file,
- cmd="/newvideo",
- is_video=True,
- limit=50,
+ emoji=emoji, file=input_file, cmd="/newvideo", is_video=True, limit=50
)
diff --git a/app/plugins/tools/pm_n_tag_logger.py b/app/plugins/tg_tools/pm_n_tag_logger.py
similarity index 73%
rename from app/plugins/tools/pm_n_tag_logger.py
rename to app/plugins/tg_tools/pm_n_tag_logger.py
index b048e90..538e9ff 100644
--- a/app/plugins/tools/pm_n_tag_logger.py
+++ b/app/plugins/tg_tools/pm_n_tag_logger.py
@@ -2,7 +2,7 @@ import asyncio
from collections import defaultdict
from pyrogram import filters
-from pyrogram.enums import ChatType, MessageEntityType, ParseMode
+from pyrogram.enums import ChatType, MessageEntityType
from pyrogram.errors import MessageIdInvalid
from app import BOT, Config, CustomDB, Message, bot
@@ -158,7 +158,15 @@ async def log_pm(message: Message, log_info: bool):
try:
await message.forward(Config.MESSAGE_LOGGER_CHAT)
except MessageIdInvalid:
- await log_deleted_message(message)
+ notice = (
+ f"{message.from_user.mention} [{message.from_user.id}] deleted this message."
+ f"\n\n---\n\n"
+ f"Message: \n{message.chat.title or message.chat.first_name} ({message.chat.id})"
+ f"\n\n---\n\n"
+ f"{notice}Caption:\n{message.caption or 'No Caption in media.'}"
+ )
+
+ await message.copy(Config.MESSAGE_LOGGER_CHAT, caption=notice)
async def log_chat(message: Message):
@@ -166,58 +174,25 @@ async def log_chat(message: Message):
mention, u_id = message.sender_chat.title, message.sender_chat.id
else:
mention, u_id = message.from_user.mention, message.from_user.id
+ notice = (
+ f"{mention} [{u_id}] deleted this message."
+ f"\n\n---\n\n"
+ f"Message: \n{message.chat.title or message.chat.first_name} ({message.chat.id})"
+ f"\n\n---\n\n"
+ f"{notice}Caption:\n{message.caption or 'No Caption in media.'}"
+ )
+
if message.reply_to_message:
try:
await message.reply_to_message.forward(Config.MESSAGE_LOGGER_CHAT)
except MessageIdInvalid:
- await log_deleted_message(message.reply_to_message, data=(mention, u_id))
+ await message.reply_to_message.copy(
+ Config.MESSAGE_LOGGER_CHAT, caption=notice
+ )
try:
logged = await message.forward(Config.MESSAGE_LOGGER_CHAT)
await logged.reply(
text=f"#TAG\n{mention} [{u_id}]\nMessage: \n{message.chat.title} ({message.chat.id})",
)
except MessageIdInvalid:
- await log_deleted_message(message, data=(mention, u_id))
-
-
-async def log_deleted_message(message: Message, data: tuple | None = None):
- if data:
- mention, u_id = data
- else:
- mention, u_id = message.from_user.mention, message.from_user.id
- notice = f"{mention} [{u_id}] deleted this message.\n\n---\n\nMessage: \n{message.chat.title or message.chat.first_name} ({message.chat.id})\n\n---\n\n"
- if not message.media:
- await bot.send_message(
- chat_id=Config.MESSAGE_LOGGER_CHAT,
- text=f"{notice}Text:\n{message.text}",
- disable_web_page_preview=True,
- parse_mode=ParseMode.HTML,
- )
- return
- kwargs = dict(
- chat_id=Config.MESSAGE_LOGGER_CHAT,
- caption=f"{notice}Caption:\n{message.caption or 'No Caption in media.'}",
- parse_mode=ParseMode.HTML,
- )
- if message.photo:
- await bot.send_photo(**kwargs, photo=message.photo.file_id)
- elif message.audio:
- await bot.send_audio(**kwargs, audio=message.audio.file_id)
- elif message.animation:
- await bot.send_animation(
- **kwargs, animation=message.animation.file_id, unsave=True
- )
- elif message.document:
- await bot.send_document(
- **kwargs, document=message.document.file_id, force_document=True
- )
- elif message.video:
- await bot.send_video(**kwargs, video=message.video.file_id)
- elif message.voice:
- await bot.send_voice(**kwargs, voice=message.voice.file_id)
- elif message.sticker:
- await bot.send_sticker(
- chat_id=Config.MESSAGE_LOGGER_CHAT, sticker=message.sticker.file_id
- )
- else:
- await bot.send_message(chat_id=Config.MESSAGE_LOGGER_CHAT, text=str(message))
+ await message.copy(Config.MESSAGE_LOGGER_CHAT, caption=notice)
diff --git a/app/plugins/tools/pm_permit.py b/app/plugins/tg_tools/pm_permit.py
similarity index 87%
rename from app/plugins/tools/pm_permit.py
rename to app/plugins/tg_tools/pm_permit.py
index 12899b6..f06c3dc 100644
--- a/app/plugins/tools/pm_permit.py
+++ b/app/plugins/tg_tools/pm_permit.py
@@ -1,4 +1,5 @@
import asyncio
+from collections import defaultdict
from pyrogram import filters
from pyrogram.enums import ChatType
@@ -16,15 +17,12 @@ allowed_filter = filters.create(lambda _, __, m: m.chat.id in ALLOWED_USERS)
guard_check = filters.create(lambda _, __, ___: Config.PM_GUARD)
-RECENT_USERS: dict = {}
+RECENT_USERS: dict = defaultdict(int)
async def init_task():
guard = await PM_GUARD.find_one({"_id": "guard_switch"})
- if not guard:
- return
- global ALLOWED_USERS
- ALLOWED_USERS = [user_id["_id"] async for user_id in PM_USERS.find()]
+ [ALLOWED_USERS.append(user_id["_id"]) async for user_id in PM_USERS.find()]
Config.PM_GUARD = guard["value"]
@@ -36,7 +34,6 @@ async def init_task():
)
async def handle_new_pm(bot: BOT, message: Message):
user_id = message.from_user.id
- RECENT_USERS[user_id] = RECENT_USERS.get(user_id, 0)
if RECENT_USERS[user_id] == 0:
await bot.log_text(
text=f"#PMGUARD\n{message.from_user.mention} [{user_id}] has messaged you.",
@@ -66,9 +63,11 @@ async def handle_new_pm(bot: BOT, message: Message):
)
async def auto_approve(bot: BOT, message: Message):
message = Message.parse(message=message)
- await message.reply("Auto-Approved to PM.", del_in=5)
ALLOWED_USERS.append(message.chat.id)
- await PM_USERS.insert_one({"_id": message.chat.id})
+ await asyncio.gather(
+ PM_USERS.insert_one({"_id": message.chat.id}),
+ message.reply("Auto-Approved to PM.", del_in=5),
+ )
@bot.add_cmd(cmd="pmguard")
@@ -91,12 +90,16 @@ async def pmguard(bot: BOT, message: Message):
PM_GUARD.add_data({"_id": "guard_switch", "value": value}),
message.reply(text=f"PM Guard is enabled: {value}!", del_in=8),
)
- await init_task()
@bot.add_cmd(cmd=["a", "allow"])
async def allow_pm(bot: BOT, message: Message):
- user_id, name = get_user_name(message)
+ """
+ CMD: A | ALLOW
+ INFO: Approve a User to PM.
+ USAGE: .a|.allow [reply to a user or in pm]
+ """
+ user_id, name = get_userID_name(message)
if not user_id:
await message.reply(
"Unable to extract User to allow.\nGive user id | Reply to a user | use in PM."
@@ -115,7 +118,7 @@ async def allow_pm(bot: BOT, message: Message):
@bot.add_cmd(cmd="nopm")
async def no_pm(bot: BOT, message: Message):
- user_id, name = get_user_name(message)
+ user_id, name = get_userID_name(message)
if not user_id:
await message.reply(
"Unable to extract User to Dis-allow.\nGive user id | Reply to a user | use in PM."
@@ -131,7 +134,7 @@ async def no_pm(bot: BOT, message: Message):
)
-def get_user_name(message: Message) -> tuple:
+def get_userID_name(message: Message) -> tuple:
if message.flt_input and message.flt_input.isdigit():
user_id = int(message.flt_input)
return user_id, user_id
diff --git a/app/plugins/tools/reply.py b/app/plugins/tg_tools/reply.py
similarity index 94%
rename from app/plugins/tools/reply.py
rename to app/plugins/tg_tools/reply.py
index cbfa327..7862fad 100644
--- a/app/plugins/tools/reply.py
+++ b/app/plugins/tg_tools/reply.py
@@ -1,5 +1,5 @@
from app import BOT, Message, bot
-from app.plugins.tools.get_message import parse_link
+from app.plugins.tg_tools.get_message import parse_link
@bot.add_cmd(cmd="reply")
diff --git a/app/plugins/tools/respond.py b/app/plugins/tg_tools/respond.py
similarity index 100%
rename from app/plugins/tools/respond.py
rename to app/plugins/tg_tools/respond.py
diff --git a/app/utils/__init__.py b/app/utils/__init__.py
new file mode 100644
index 0000000..13e1996
--- /dev/null
+++ b/app/utils/__init__.py
@@ -0,0 +1,6 @@
+import json
+
+
+class Str:
+ def __str__(self):
+ return json.dumps(self.__dict__, indent=4, ensure_ascii=False, default=str)
diff --git a/app/utils/downloader.py b/app/utils/downloader.py
index c20d6f8..45ae1d1 100644
--- a/app/utils/downloader.py
+++ b/app/utils/downloader.py
@@ -1,4 +1,3 @@
-import json
import os
import re
import shutil
@@ -9,11 +8,12 @@ import aiohttp
from pyrogram.types import Message as Msg
from app.core.types.message import Message
+from app.utils import Str
from app.utils.helpers import progress
from app.utils.media_helper import bytes_to_mb, get_filename, get_type
-class DownloadedFile:
+class DownloadedFile(Str):
def __init__(
self,
name: str,
@@ -26,15 +26,8 @@ class DownloadedFile:
self.size = size
self.type = get_type(path=name)
- def __str__(self):
- return json.dumps(
- self.__dict__,
- indent=4,
- ensure_ascii=False,
- default=str)
-
-class Download:
+class Download(Str):
"""Download a file in async using aiohttp.
Attributes:
diff --git a/app/utils/shell.py b/app/utils/shell.py
index 0543709..eeba110 100644
--- a/app/utils/shell.py
+++ b/app/utils/shell.py
@@ -1,6 +1,5 @@
import asyncio
import os
-from typing import AsyncIterable
async def run_shell_cmd(cmd: str) -> str:
@@ -35,24 +34,28 @@ async def get_duration(file) -> int:
class AsyncShell:
- def __init__(self, process: asyncio.create_subprocess_shell):
+ def __init__(
+ self,
+ process: asyncio.create_subprocess_shell,
+ ):
self.process: asyncio.create_subprocess_shell = process
self.full_std: str = ""
+ self.last_line: str = ""
self.is_done: bool = False
self._task: asyncio.Task | None = None
async def read_output(self) -> None:
- while True:
- line: str = (await self.process.stdout.readline()).decode("utf-8")
- if not line:
- break
- self.full_std += line
+ async for line in self.process.stdout:
+ decoded_line = line.decode("utf-8")
+ self.full_std += decoded_line
+ self.last_line = decoded_line
self.is_done = True
await self.process.wait()
- async def get_output(self) -> AsyncIterable:
+ async def get_output(self):
while not self.is_done:
- yield self.full_std
+ yield self.full_std if len(self.full_std) < 4000 else self.last_line
+ await asyncio.sleep(0)
def cancel(self) -> None:
if not self.is_done: