a shit ton of changes not gonna write changelog.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,3 +6,4 @@ __pycache__
|
||||
.idea/
|
||||
conf_backup/
|
||||
logs/
|
||||
.mypy_cache
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
76
app/core/client.py
Normal file
76
app/core/client.py
Normal file
@@ -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="<i>Started</i>")
|
||||
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()
|
||||
@@ -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="<i>Started</i>")
|
||||
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
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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):
|
||||
@@ -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"<b>#Cancelled</b>:\n<code>{message.text}</code>")
|
||||
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"<b>#Cancelled</b>:\n<code>{message.text}</code>")
|
||||
except BaseException:
|
||||
text = (
|
||||
"#Traceback"
|
||||
f"\n<b>Function:</b> {coro.__name__}"
|
||||
f"\n<b>Chat:</b> {message.chat.title or message.from_user.first_name}"
|
||||
f"\n<b>Traceback:</b>"
|
||||
f"\n<pre language=python>{traceback.format_exc()}</pre>"
|
||||
)
|
||||
await bot.log_text(text=text, type="error")
|
||||
return 1
|
||||
@@ -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"<b>\nChat</b>: {chat}"
|
||||
f"\n<b>Line No</b>: <code>{log_record.lineno}</code>"
|
||||
f"\n<b>Func</b>: <code>{log_record.funcName}</code>"
|
||||
f"\n<b>Module</b>: <code>{log_record.module}</code>"
|
||||
f"\n<b>Time</b>: <code>{log_record.asctime}</code>"
|
||||
f"\n<b>Error Message</b>:\n<pre language=python>{log_record.message}</pre>"
|
||||
f"\n<b>Error Message</b>:\n<pre language=python>{log_record.exc_text or log_record.message}</pre>"
|
||||
)
|
||||
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,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
2
app/core/methods/__init__.py
Normal file
2
app/core/methods/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from app.core.methods.channel_loggers import ChannelLogger
|
||||
from app.core.methods.send_message import SendMessage
|
||||
36
app/core/methods/channel_loggers.py
Normal file
36
app/core/methods/channel_loggers.py
Normal file
@@ -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
|
||||
31
app/core/methods/send_message.py
Normal file
31
app/core/methods/send_message.py
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"<a href='tg://user?id={self.id}'>{self.id}</a>"
|
||||
|
||||
|
||||
@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"<a href='tg://user?id={user_id}'>{user_id}</a>"
|
||||
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 <a href='tg://user?id={user.id}'>{user.id}</a> {reason}"
|
||||
fban_cmd: str = f"/fban <a href='tg://user?id={user_id}'>{user_id}</a> {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"❯❯❯ <b>FBanned</b> {user.mention}"
|
||||
f"\n<b>ID</b>: {user.id}"
|
||||
f"❯❯❯ <b>FBanned</b> {user_mention}"
|
||||
f"\n<b>ID</b>: {user_id}"
|
||||
f"\n<b>Reason</b>: {reason}"
|
||||
f"\n<b>Initiated in</b>: {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"<a href='tg://user?id={user_id}'>{user_id}</a>"
|
||||
else:
|
||||
user_id = user.id
|
||||
user_mention = user.mention
|
||||
|
||||
await progress.edit("❯❯")
|
||||
total: int = 0
|
||||
failed: list[str] = []
|
||||
unfban_cmd: str = f"/unfban <a href='tg://user?id={user.id}'>{user.id}</a> {reason}"
|
||||
unfban_cmd: str = f"/unfban <a href='tg://user?id={user_id}'>{user_id}</a> {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"❯❯❯ <b>Un-FBanned {user.mention}\nID: {user.id}\nReason: {reason}\n"
|
||||
resp_str = (
|
||||
f"❯❯❯ <b>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:
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -62,6 +62,5 @@ if Config.DEV_MODE:
|
||||
cmd="py",
|
||||
func=executor,
|
||||
path=inspect.stack()[0][1],
|
||||
doc=executor.__doc__,
|
||||
sudo=False,
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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"<pre language=shell>~${cmd}\n\n{sub_process.full_std}</pre>",
|
||||
sleep_for = 2
|
||||
await asyncio.create_task(asyncio.sleep(sleep_for), name=reply.task_id)
|
||||
sleep_for += 2
|
||||
await reply.edit(
|
||||
text=f"<pre language=shell>~${cmd}\n\n{sub_process.full_std}</pre>",
|
||||
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,
|
||||
)
|
||||
|
||||
@@ -28,7 +28,7 @@ async def add_scmd(bot: BOT, message: Message):
|
||||
return
|
||||
cmd = message.flt_input
|
||||
response = await message.reply(f"Adding <b>{cmd}</b> to sudo....")
|
||||
func = Config.CMD_DICT.get(cmd, None)
|
||||
func = Config.CMD_DICT.get(cmd)
|
||||
if not func:
|
||||
await response.edit(text=f"<b>{cmd}</b> not a valid command.", del_in=10)
|
||||
return
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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:
|
||||
@@ -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")
|
||||
@@ -42,7 +42,7 @@ async def kang_sticker(bot: BOT, message: Message):
|
||||
text=f"Kanged: <a href='t.me/addstickers/{pack_name}'>here</a>"
|
||||
)
|
||||
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
|
||||
)
|
||||
@@ -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<a href='{message.link}'>{message.chat.title or message.chat.first_name}</a> ({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<a href='{message.link}'>{message.chat.title or message.chat.first_name}</a> ({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<a href='{message.link}'>{message.chat.title}</a> ({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<a href='{message.link}'>{message.chat.title or message.chat.first_name}</a> ({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)
|
||||
@@ -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: <b>{value}</b>!", 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.\n<code>Give user id | Reply to a user | use in PM.</code>"
|
||||
@@ -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.\n<code>Give user id | Reply to a user | use in PM.</code>"
|
||||
@@ -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
|
||||
@@ -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")
|
||||
6
app/utils/__init__.py
Normal file
6
app/utils/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
import json
|
||||
|
||||
|
||||
class Str:
|
||||
def __str__(self):
|
||||
return json.dumps(self.__dict__, indent=4, ensure_ascii=False, default=str)
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user