🔖 Update to v1.3.1

This commit is contained in:
xtaodada 2023-03-12 11:56:01 +08:00
parent 00a45fefc6
commit 2b37f95377
Signed by: xtaodada
GPG Key ID: 4CBB3F4FA8C85659
59 changed files with 1607 additions and 1174 deletions

View File

@ -4,7 +4,16 @@ from typing import Callable, Awaitable, Set, Dict
from coloredlogs import ColoredFormatter from coloredlogs import ColoredFormatter
from datetime import datetime, timezone from datetime import datetime, timezone
from logging import getLogger, StreamHandler, CRITICAL, INFO, basicConfig, DEBUG, Formatter, FileHandler from logging import (
getLogger,
StreamHandler,
CRITICAL,
INFO,
basicConfig,
DEBUG,
Formatter,
FileHandler,
)
from os import getcwd from os import getcwd
from pagermaid.config import Config from pagermaid.config import Config
@ -12,8 +21,8 @@ from pagermaid.scheduler import scheduler
import pyromod.listen import pyromod.listen
from pyrogram import Client from pyrogram import Client
pgm_version = "1.3.0" pgm_version = "1.3.1"
pgm_version_code = 1300 pgm_version_code = 1301
CMD_LIST = {} CMD_LIST = {}
module_dir = __path__[0] module_dir = __path__[0]
working_dir = getcwd() working_dir = getcwd()
@ -21,7 +30,11 @@ working_dir = getcwd()
read_context = {} read_context = {}
help_messages = {} help_messages = {}
hook_functions: Dict[str, Set[Callable[[], Awaitable[None]]]] = { hook_functions: Dict[str, Set[Callable[[], Awaitable[None]]]] = {
"startup": set(), "shutdown": set(), "command_pre": set(), "command_post": set(), "process_error": set(), "startup": set(),
"shutdown": set(),
"command_pre": set(),
"command_post": set(),
"process_error": set(),
"load_plugins_finished": set(), "load_plugins_finished": set(),
} }
all_permissions = [] all_permissions = []
@ -51,6 +64,7 @@ start_time = datetime.now(timezone.utc)
with contextlib.suppress(ImportError): with contextlib.suppress(ImportError):
import uvloop # noqa import uvloop # noqa
uvloop.install() uvloop.install()
if not scheduler.running: if not scheduler.running:
@ -68,16 +82,11 @@ bot.job = scheduler
async def log(message): async def log(message):
logs.info( logs.info(message.replace("`", '"'))
message.replace('`', '\"')
)
if not Config.LOG: if not Config.LOG:
return return
try: try:
await bot.send_message( await bot.send_message(Config.LOG_ID, message)
Config.LOG_ID,
message
)
except Exception: except Exception:
Config.LOG = False Config.LOG = False
Config.LOG_ID = "me" Config.LOG_ID = "me"

View File

@ -17,7 +17,7 @@ path.insert(1, f"{working_dir}{sep}plugins")
async def main(): async def main():
logs.info(lang('platform') + platform + lang('platform_load')) logs.info(lang("platform") + platform + lang("platform_load"))
try: try:
await start_client(bot) await start_client(bot)
@ -35,7 +35,9 @@ async def main():
try: try:
import_module(f"pagermaid.modules.{module_name}") import_module(f"pagermaid.modules.{module_name}")
except BaseException as exception: except BaseException as exception:
logs.info(f"{lang('module')} {module_name} {lang('error')}: {type(exception)}: {exception}") logs.info(
f"{lang('module')} {module_name} {lang('error')}: {type(exception)}: {exception}"
)
for plugin_name in plugin_list.copy(): for plugin_name in plugin_list.copy():
try: try:
import_module(f"plugins.{plugin_name}") import_module(f"plugins.{plugin_name}")
@ -45,11 +47,12 @@ async def main():
plugin_manager.load_local_plugins() plugin_manager.load_local_plugins()
await process_exit(start=True, _client=bot) await process_exit(start=True, _client=bot)
logs.info(lang('start')) logs.info(lang("start"))
await Hook.load_success_exec() await Hook.load_success_exec()
await Hook.startup() await Hook.startup()
await idle() await idle()
await bot.stop() await bot.stop()
bot.run(main()) bot.run(main())

View File

@ -28,15 +28,12 @@ class AliasManager:
def get_all_alias_text(self) -> str: def get_all_alias_text(self) -> str:
texts = [] texts = []
texts.extend( texts.extend(f"`{i.command}` > `{i.alias}`" for i in self.alias_list)
f'`{i.command}` > `{i.alias}`' return "\n".join(texts)
for i in self.alias_list
)
return '\n'.join(texts)
@staticmethod @staticmethod
def save(): def save():
with open(f"data{sep}alias.json", 'w', encoding="utf-8") as f: with open(f"data{sep}alias.json", "w", encoding="utf-8") as f:
json_dump(Config.alias_dict, f) json_dump(Config.alias_dict, f)
@staticmethod @staticmethod

View File

@ -20,7 +20,7 @@ def cache(ttl=datetime.timedelta(minutes=15)):
nonlocal cache_data nonlocal cache_data
bound = inspect.signature(func).bind(*args, **kw) bound = inspect.signature(func).bind(*args, **kw)
bound.apply_defaults() bound.apply_defaults()
ins_key = '|'.join([f'{k}_{v}' for k, v in bound.arguments.items()]) ins_key = "|".join([f"{k}_{v}" for k, v in bound.arguments.items()])
data: Cache = cache_data.get(ins_key, Cache(value=None, time=None)) data: Cache = cache_data.get(ins_key, Cache(value=None, time=None))
now = datetime.datetime.now() now = datetime.datetime.now()
if (not data.time) or ((now - data.time) > ttl): if (not data.time) or ((now - data.time) > ttl):
@ -31,5 +31,7 @@ def cache(ttl=datetime.timedelta(minutes=15)):
except Exception as e: except Exception as e:
raise e raise e
return data.value return data.value
return wrapped return wrapped
return wrap return wrap

View File

@ -10,7 +10,7 @@ from pagermaid import Config
from pagermaid.common.cache import cache from pagermaid.common.cache import cache
from pagermaid.utils import client from pagermaid.utils import client
plugins_path = Path('plugins') plugins_path = Path("plugins")
class LocalPlugin(BaseModel): class LocalPlugin(BaseModel):
@ -57,11 +57,11 @@ class RemotePlugin(LocalPlugin):
... ...
async def install(self) -> bool: async def install(self) -> bool:
html = await client.get(f'{Config.GIT_SOURCE}{self.name}/main.py') html = await client.get(f"{Config.GIT_SOURCE}{self.name}/main.py")
if html.status_code == 200: if html.status_code == 200:
self.remove() self.remove()
with open(plugins_path / f"{self.name}.py", mode="wb") as f: with open(plugins_path / f"{self.name}.py", mode="wb") as f:
f.write(html.text.encode('utf-8')) f.write(html.text.encode("utf-8"))
return True return True
return False return False
@ -76,11 +76,11 @@ class PluginManager:
def load_local_version_map(self): def load_local_version_map(self):
if not os.path.exists(plugins_path / "version.json"): if not os.path.exists(plugins_path / "version.json"):
return return
with open(plugins_path / "version.json", 'r', encoding="utf-8") as f: with open(plugins_path / "version.json", "r", encoding="utf-8") as f:
self.version_map = json.load(f) self.version_map = json.load(f)
def save_local_version_map(self): def save_local_version_map(self):
with open(plugins_path / "version.json", 'w', encoding="utf-8") as f: with open(plugins_path / "version.json", "w", encoding="utf-8") as f:
json.dump(self.version_map, f, indent=4) json.dump(self.version_map, f, indent=4)
def get_local_version(self, name: str) -> Optional[float]: def get_local_version(self, name: str) -> Optional[float]:
@ -116,15 +116,17 @@ class PluginManager:
def load_local_plugins(self) -> List[LocalPlugin]: def load_local_plugins(self) -> List[LocalPlugin]:
self.load_local_version_map() self.load_local_version_map()
self.plugins = [] self.plugins = []
for plugin in os.listdir('plugins'): for plugin in os.listdir("plugins"):
if plugin.endswith('.py') or plugin.endswith('.py.disabled'): if plugin.endswith(".py") or plugin.endswith(".py.disabled"):
plugin = plugin[:-12] if plugin.endswith('.py.disabled') else plugin[:-3] plugin = (
plugin[:-12] if plugin.endswith(".py.disabled") else plugin[:-3]
)
self.plugins.append( self.plugins.append(
LocalPlugin( LocalPlugin(
name=plugin, name=plugin,
installed=self.get_plugin_install_status(plugin), installed=self.get_plugin_install_status(plugin),
status=self.get_plugin_load_status(plugin), status=self.get_plugin_load_status(plugin),
version=self.get_local_version(plugin) version=self.get_local_version(plugin),
) )
) )
return self.plugins return self.plugins
@ -140,7 +142,8 @@ class PluginManager:
RemotePlugin( RemotePlugin(
**plugin, **plugin,
status=False, status=False,
) for plugin in plugin_list )
for plugin in plugin_list
] ]
self.remote_plugins = plugins self.remote_plugins = plugins
self.remote_version_map = {} self.remote_version_map = {}

View File

@ -4,7 +4,14 @@ import os
import pagermaid.config import pagermaid.config
import pagermaid.modules import pagermaid.modules
from pagermaid import read_context, bot, help_messages, all_permissions, hook_functions, logs from pagermaid import (
read_context,
bot,
help_messages,
all_permissions,
hook_functions,
logs,
)
from pagermaid.common.plugin import plugin_manager from pagermaid.common.plugin import plugin_manager
from pagermaid.hook import Hook from pagermaid.hook import Hook
from pagermaid.utils import lang from pagermaid.utils import lang
@ -32,7 +39,9 @@ async def reload_all():
if module_name in loaded_plugins: if module_name in loaded_plugins:
importlib.reload(module) importlib.reload(module)
except BaseException as exception: except BaseException as exception:
logs.info(f"{lang('module')} {module_name} {lang('error')}: {type(exception)}: {exception}") logs.info(
f"{lang('module')} {module_name} {lang('error')}: {type(exception)}: {exception}"
)
for plugin_name in pagermaid.modules.plugin_list.copy(): for plugin_name in pagermaid.modules.plugin_list.copy():
try: try:
plugin = importlib.import_module(f"plugins.{plugin_name}") plugin = importlib.import_module(f"plugins.{plugin_name}")

View File

@ -17,11 +17,11 @@ class Status(BaseModel):
async def human_time_duration(seconds) -> str: async def human_time_duration(seconds) -> str:
parts = {} parts = {}
time_units = ( time_units = (
('%m', 60 * 60 * 24 * 30), ("%m", 60 * 60 * 24 * 30),
('%d', 60 * 60 * 24), ("%d", 60 * 60 * 24),
('%H', 60 * 60), ("%H", 60 * 60),
('%M', 60), ("%M", 60),
('%S', 1) ("%S", 1),
) )
for unit, div in time_units: for unit, div in time_units:
amount, seconds = divmod(int(seconds), div) amount, seconds = divmod(int(seconds), div)
@ -48,7 +48,7 @@ async def get_status() -> Status:
return Status( return Status(
version=pgm_version, version=pgm_version,
run_time=uptime, run_time=uptime,
cpu_percent=f'{cpu_percent}%', cpu_percent=f"{cpu_percent}%",
ram_percent=f'{ram_stat.percent}%', ram_percent=f"{ram_stat.percent}%",
swap_percent=f'{swap_stat.percent}%', swap_percent=f"{swap_stat.percent}%",
) )

View File

@ -4,9 +4,9 @@ from pagermaid.utils import execute
async def update(force: bool = False): async def update(force: bool = False):
await execute('git fetch --all') await execute("git fetch --all")
if force: if force:
await execute('git reset --hard origin/master') await execute("git reset --hard origin/master")
await execute('git pull --all') await execute("git pull --all")
await execute(f"{executable} -m pip install --upgrade -r requirements.txt") await execute(f"{executable} -m pip install --upgrade -r requirements.txt")
await execute(f"{executable} -m pip install -r requirements.txt") await execute(f"{executable} -m pip install -r requirements.txt")

View File

@ -15,9 +15,9 @@ def strtobool(val, default=False):
'val' is anything else. 'val' is anything else.
""" """
val = val.lower() val = val.lower()
if val in ('y', 'yes', 't', 'true', 'on', '1'): if val in ("y", "yes", "t", "true", "on", "1"):
return 1 return 1
elif val in ('n', 'no', 'f', 'false', 'off', '0'): elif val in ("n", "no", "f", "false", "off", "0"):
return 0 return 0
else: else:
print("[Degrade] invalid truth value %r" % (val,)) print("[Degrade] invalid truth value %r" % (val,))
@ -27,7 +27,9 @@ def strtobool(val, default=False):
try: try:
config: Dict = load(open(r"config.yml", encoding="utf-8"), Loader=FullLoader) config: Dict = load(open(r"config.yml", encoding="utf-8"), Loader=FullLoader)
except FileNotFoundError: except FileNotFoundError:
print("The configuration file does not exist, and a new configuration file is being generated.") print(
"The configuration file does not exist, and a new configuration file is being generated."
)
copyfile(f"{os.getcwd()}{os.sep}config.gen.yml", "config.yml") copyfile(f"{os.getcwd()}{os.sep}config.gen.yml", "config.yml")
sys.exit(1) sys.exit(1)
@ -43,19 +45,29 @@ class Config:
# TGX # TGX
API_ID = DEFAULT_API_ID API_ID = DEFAULT_API_ID
API_HASH = DEFAULT_API_HASH API_HASH = DEFAULT_API_HASH
QRCODE_LOGIN = strtobool(os.environ.get("QRCODE_LOGIN", config.get("qrcode_login", "false"))) QRCODE_LOGIN = strtobool(
os.environ.get("QRCODE_LOGIN", config.get("qrcode_login", "false"))
)
STRING_SESSION = os.environ.get("STRING_SESSION") STRING_SESSION = os.environ.get("STRING_SESSION")
DEBUG = strtobool(os.environ.get("PGM_DEBUG", config["debug"])) DEBUG = strtobool(os.environ.get("PGM_DEBUG", config["debug"]))
ERROR_REPORT = strtobool(os.environ.get("PGM_ERROR_REPORT", config["error_report"]), True) ERROR_REPORT = strtobool(
os.environ.get("PGM_ERROR_REPORT", config["error_report"]), True
)
LANGUAGE = os.environ.get("PGM_LANGUAGE", config["application_language"]) LANGUAGE = os.environ.get("PGM_LANGUAGE", config["application_language"])
REGION = os.environ.get("PGM_REGION", config["application_region"]) REGION = os.environ.get("PGM_REGION", config["application_region"])
TIME_ZONE = os.environ.get("PGM_TIME_ZONE", config.get("timezone", "Asia/Shanghai")) TIME_ZONE = os.environ.get(
"PGM_TIME_ZONE", config.get("timezone", "Asia/Shanghai")
)
TTS = os.environ.get("PGM_TTS", config["application_tts"]) TTS = os.environ.get("PGM_TTS", config["application_tts"])
LOG = strtobool(os.environ.get("PGM_LOG", config["log"])) LOG = strtobool(os.environ.get("PGM_LOG", config["log"]))
LOG_ID = int(os.environ.get("PGM_LOG_ID", config["log_chatid"])) LOG_ID = int(os.environ.get("PGM_LOG_ID", config["log_chatid"]))
IPV6 = strtobool(os.environ.get("PGM_IPV6", config["ipv6"])) IPV6 = strtobool(os.environ.get("PGM_IPV6", config["ipv6"]))
ALLOW_ANALYTIC = strtobool(os.environ.get("PGM_ALLOW_ANALYTIC", config["allow_analytic"]), True) ALLOW_ANALYTIC = strtobool(
SENTRY_API = "https://2e13a517aeb542e7a307cba8996b6d1a@o1342815.ingest.sentry.io/6617119" os.environ.get("PGM_ALLOW_ANALYTIC", config["allow_analytic"]), True
)
SENTRY_API = (
"https://2e13a517aeb542e7a307cba8996b6d1a@o1342815.ingest.sentry.io/6617119"
)
MIXPANEL_API = "c79162511383b0fa1e9c062a2a86c855" MIXPANEL_API = "c79162511383b0fa1e9c062a2a86c855"
TIME_FORM = os.environ.get("PGM_TIME_FORM", config["time_form"]) TIME_FORM = os.environ.get("PGM_TIME_FORM", config["time_form"])
DATE_FORM = os.environ.get("PGM_DATE_FORM", config["date_form"]) DATE_FORM = os.environ.get("PGM_DATE_FORM", config["date_form"])
@ -63,31 +75,39 @@ class Config:
SILENT = strtobool(os.environ.get("PGM_PGM_SILENT", config["silent"]), True) SILENT = strtobool(os.environ.get("PGM_PGM_SILENT", config["silent"]), True)
PROXY_ADDRESS = os.environ.get("PGM_PROXY_ADDRESS", config["proxy_addr"]) PROXY_ADDRESS = os.environ.get("PGM_PROXY_ADDRESS", config["proxy_addr"])
PROXY_PORT = os.environ.get("PGM_PROXY_PORT", config["proxy_port"]) PROXY_PORT = os.environ.get("PGM_PROXY_PORT", config["proxy_port"])
PROXY_HTTP_ADDRESS = os.environ.get("PGM_PROXY_HTTP_ADDRESS", config["http_addr"]) PROXY_HTTP_ADDRESS = os.environ.get(
"PGM_PROXY_HTTP_ADDRESS", config["http_addr"]
)
PROXY_HTTP_PORT = os.environ.get("PGM_PROXY_HTTP_PORT", config["http_port"]) PROXY_HTTP_PORT = os.environ.get("PGM_PROXY_HTTP_PORT", config["http_port"])
PROXY = None PROXY = None
if PROXY_ADDRESS and PROXY_PORT: if PROXY_ADDRESS and PROXY_PORT:
PROXY = dict( PROXY = dict(scheme="socks5", hostname=PROXY_ADDRESS, port=int(PROXY_PORT))
scheme="socks5",
hostname=PROXY_ADDRESS,
port=int(PROXY_PORT)
)
elif PROXY_HTTP_ADDRESS and PROXY_HTTP_PORT: elif PROXY_HTTP_ADDRESS and PROXY_HTTP_PORT:
PROXY = dict( PROXY = dict(
scheme="http", scheme="http", hostname=PROXY_HTTP_ADDRESS, port=int(PROXY_HTTP_PORT)
hostname=PROXY_HTTP_ADDRESS,
port=int(PROXY_HTTP_PORT)
) )
GIT_SOURCE = os.environ.get("PGM_GIT_SOURCE", config["git_source"]) GIT_SOURCE = os.environ.get("PGM_GIT_SOURCE", config["git_source"])
GIT_SOURCE = GIT_SOURCE.replace("TeamPGM/PagerMaid_Plugins/", "TeamPGM/PagerMaid_Plugins_Pyro/") GIT_SOURCE = GIT_SOURCE.replace(
"TeamPGM/PagerMaid_Plugins/", "TeamPGM/PagerMaid_Plugins_Pyro/"
)
try: try:
with open(f"languages{os.sep}built-in{os.sep}{LANGUAGE}.yml", "r", encoding="utf-8") as f: with open(
f"languages{os.sep}built-in{os.sep}{LANGUAGE}.yml",
"r",
encoding="utf-8",
) as f:
lang_dict = safe_load(f) lang_dict = safe_load(f)
except Exception as e: except Exception as e:
print("[Degrade] Reading language YAML file failed, try to use the english language file.") print(
"[Degrade] Reading language YAML file failed, try to use the english language file."
)
print(e) print(e)
try: try:
with open(f"languages{os.sep}built-in{os.sep}{LANGUAGE}.yml", "r", encoding="utf-8") as f: with open(
f"languages{os.sep}built-in{os.sep}{LANGUAGE}.yml",
"r",
encoding="utf-8",
) as f:
lang_dict = safe_load(f) lang_dict = safe_load(f)
except Exception as e: except Exception as e:
print("[Error] Reading English language YAML file failed.") print("[Error] Reading English language YAML file failed.")
@ -99,8 +119,12 @@ class Config:
except Exception as e: except Exception as e:
alias_dict = {} alias_dict = {}
web_interface = config.get("web_interface", {}) web_interface = config.get("web_interface", {})
WEB_ENABLE = strtobool(os.environ.get("WEB_ENABLE", web_interface.get("enable", "False"))) WEB_ENABLE = strtobool(
WEB_SECRET_KEY = os.environ.get("WEB_SECRET_KEY", web_interface.get("secret_key", "secret_key")) os.environ.get("WEB_ENABLE", web_interface.get("enable", "False"))
)
WEB_SECRET_KEY = os.environ.get(
"WEB_SECRET_KEY", web_interface.get("secret_key", "secret_key")
)
WEB_HOST = os.environ.get("WEB_HOST", web_interface.get("host", "127.0.0.1")) WEB_HOST = os.environ.get("WEB_HOST", web_interface.get("host", "127.0.0.1"))
WEB_PORT = int(os.environ.get("WEB_PORT", web_interface.get("port", 3333))) WEB_PORT = int(os.environ.get("WEB_PORT", web_interface.get("port", 3333)))
WEB_ORIGINS = web_interface.get("origins", ["*"]) WEB_ORIGINS = web_interface.get("origins", ["*"])

View File

@ -9,8 +9,13 @@ from pagermaid import all_permissions, module_dir
# init permissions # init permissions
if not os_path.exists(f"data{os_path.sep}gm_policy.csv"): if not os_path.exists(f"data{os_path.sep}gm_policy.csv"):
copyfile(f"{module_dir}{os_path.sep}assets{os_path.sep}gm_policy.csv", f"data{os_path.sep}gm_policy.csv") copyfile(
permissions = casbin.Enforcer(f"pagermaid{sep}assets{sep}gm_model.conf", f"data{sep}gm_policy.csv") f"{module_dir}{os_path.sep}assets{os_path.sep}gm_policy.csv",
f"data{os_path.sep}gm_policy.csv",
)
permissions = casbin.Enforcer(
f"pagermaid{sep}assets{sep}gm_model.conf", f"data{sep}gm_policy.csv"
)
permissions.logger.setLevel(CRITICAL) permissions.logger.setLevel(CRITICAL)
@ -27,15 +32,14 @@ def enforce_permission(user: int, permission: str):
data = permission.split(".") data = permission.split(".")
if len(data) != 2: if len(data) != 2:
raise ValueError("Invalid permission format") raise ValueError("Invalid permission format")
if permissions.enforce( if permissions.enforce(str(user), data[0], "access") and not permissions.enforce(
str(user), data[0], "access" str(user), permission, "ejection"
) and not permissions.enforce(str(user), permission, "ejection"): ):
return True return True
if permissions.enforce( return bool(
str(user), permission, "access" permissions.enforce(str(user), permission, "access")
) and not permissions.enforce(str(user), permission, "ejection"): and not permissions.enforce(str(user), permission, "ejection")
return True )
return False
def parse_pen(pen: Permission) -> List[Permission]: def parse_pen(pen: Permission) -> List[Permission]:
@ -45,7 +49,11 @@ def parse_pen(pen: Permission) -> List[Permission]:
raise ValueError("Wildcard not allowed in root name") raise ValueError("Wildcard not allowed in root name")
datas = [] datas = []
for i in all_permissions: for i in all_permissions:
if pen.root == i.root and len(findall(pen.sub.replace("*", r"([\s\S]*)"), i.sub)) > 0 and i not in datas: if (
pen.root == i.root
and len(findall(pen.sub.replace("*", r"([\s\S]*)"), i.sub)) > 0
and i not in datas
):
datas.append(i) datas.append(i)
if not datas: if not datas:
raise ValueError("No permission found") raise ValueError("No permission found")

View File

@ -14,9 +14,11 @@ class Hook:
""" """
注册一个启动钩子 注册一个启动钩子
""" """
def decorator(function): def decorator(function):
hook_functions["startup"].add(function) hook_functions["startup"].add(function)
return function return function
return decorator return decorator
@staticmethod @staticmethod
@ -24,9 +26,11 @@ class Hook:
""" """
注册一个关闭钩子 注册一个关闭钩子
""" """
def decorator(function): def decorator(function):
hook_functions["shutdown"].add(function) hook_functions["shutdown"].add(function)
return function return function
return decorator return decorator
@staticmethod @staticmethod
@ -79,7 +83,9 @@ class Hook:
@staticmethod @staticmethod
async def startup(): async def startup():
if cors := [startup(**inject(None, startup)) for startup in hook_functions["startup"]]: # noqa if cors := [
startup(**inject(None, startup)) for startup in hook_functions["startup"]
]: # noqa
try: try:
await asyncio.gather(*cors) await asyncio.gather(*cors)
except Exception as exception: except Exception as exception:
@ -87,7 +93,10 @@ class Hook:
@staticmethod @staticmethod
async def shutdown(): async def shutdown():
if cors := [shutdown(**inject(None, shutdown)) for shutdown in hook_functions["shutdown"]]: # noqa if cors := [
shutdown(**inject(None, shutdown))
for shutdown in hook_functions["shutdown"]
]: # noqa
try: try:
await asyncio.gather(*cors) await asyncio.gather(*cors)
except Exception as exception: except Exception as exception:
@ -136,12 +145,20 @@ class Hook:
logs.info(f"[command_post]: {type(exception)}: {exception}") logs.info(f"[command_post]: {type(exception)}: {exception}")
@staticmethod @staticmethod
async def process_error_exec(message: Message, command, exc_info: BaseException, exc_format: str): async def process_error_exec(
message: Message, command, exc_info: BaseException, exc_format: str
):
cors = [] cors = []
try: try:
for error in hook_functions["process_error"]: for error in hook_functions["process_error"]:
try: try:
data = inject(message, error, command=command, exc_info=exc_info, exc_format=exc_format) data = inject(
message,
error,
command=command,
exc_info=exc_info,
exc_format=exc_format,
)
except Exception as exception: except Exception as exception:
logs.info(f"[process_error]: {type(exception)}: {exception}") logs.info(f"[process_error]: {type(exception)}: {exception}")
continue continue
@ -158,7 +175,10 @@ class Hook:
@staticmethod @staticmethod
async def load_success_exec(): async def load_success_exec():
if cors := [load(**inject(None, load)) for load in hook_functions["load_plugins_finished"]]: # noqa if cors := [
load(**inject(None, load))
for load in hook_functions["load_plugins_finished"]
]: # noqa
try: try:
await asyncio.gather(*cors) await asyncio.gather(*cors)
except Exception as exception: except Exception as exception:

View File

@ -10,7 +10,8 @@ from pyrogram.errors.exceptions.bad_request_400 import (
MessageIdInvalid, MessageIdInvalid,
MessageNotModified, MessageNotModified,
MessageEmpty, MessageEmpty,
UserNotParticipant, PeerIdInvalid UserNotParticipant,
PeerIdInvalid,
) )
from pyrogram.handlers import MessageHandler, EditedMessageHandler from pyrogram.handlers import MessageHandler, EditedMessageHandler
@ -18,8 +19,20 @@ from pagermaid import help_messages, logs, Config, bot, read_context, all_permis
from pagermaid.common.ignore import ignore_groups_manager from pagermaid.common.ignore import ignore_groups_manager
from pagermaid.group_manager import Permission from pagermaid.group_manager import Permission
from pagermaid.inject import inject from pagermaid.inject import inject
from pagermaid.single_utils import Message, AlreadyInConversationError, TimeoutConversationError, ListenerCanceled from pagermaid.single_utils import (
from pagermaid.utils import lang, attach_report, sudo_filter, alias_command, get_permission_name, process_exit Message,
AlreadyInConversationError,
TimeoutConversationError,
ListenerCanceled,
)
from pagermaid.utils import (
lang,
attach_report,
sudo_filter,
alias_command,
get_permission_name,
process_exit,
)
from pagermaid.hook import Hook from pagermaid.hook import Hook
_lock = asyncio.Lock() _lock = asyncio.Lock()
@ -54,11 +67,13 @@ def listener(**args):
if command is not None: if command is not None:
if command in help_messages: if command in help_messages:
if help_messages[alias_command(command)]["priority"] <= priority: if help_messages[alias_command(command)]["priority"] <= priority:
raise ValueError(f"{lang('error_prefix')} {lang('command')} \"{command}\" {lang('has_reg')}") raise ValueError(
f"{lang('error_prefix')} {lang('command')} \"{command}\" {lang('has_reg')}"
)
else: else:
block_process = True block_process = True
pattern = fr"^(,|){alias_command(command, disallow_alias)}(?: |$)([\s\S]*)" pattern = rf"^(,|){alias_command(command, disallow_alias)}(?: |$)([\s\S]*)"
sudo_pattern = fr"^(/){alias_command(command, disallow_alias)}(?: |$)([\s\S]*)" sudo_pattern = rf"^(/){alias_command(command, disallow_alias)}(?: |$)([\s\S]*)"
if pattern is not None and not pattern.startswith("(?i)"): if pattern is not None and not pattern.startswith("(?i)"):
args["pattern"] = f"(?i){pattern}" args["pattern"] = f"(?i){pattern}"
else: else:
@ -72,11 +87,7 @@ def listener(**args):
else: else:
base_filters = filters.all base_filters = filters.all
permission_name = get_permission_name(is_plugin, need_admin, command) permission_name = get_permission_name(is_plugin, need_admin, command)
sudo_filters = ( sudo_filters = sudo_filter(permission_name) & ~filters.via_bot & ~filters.forwarded
sudo_filter(permission_name)
& ~filters.via_bot
& ~filters.forwarded
)
if args["pattern"]: if args["pattern"]:
base_filters &= filters.regex(args["pattern"]) base_filters &= filters.regex(args["pattern"])
sudo_filters &= filters.regex(sudo_pattern) sudo_filters &= filters.regex(sudo_pattern)
@ -112,7 +123,6 @@ def listener(**args):
del args["block_process"] del args["block_process"]
def decorator(function): def decorator(function):
async def handler(client: Client, message: Message): async def handler(client: Client, message: Message):
try: try:
# ignore # ignore
@ -155,30 +165,29 @@ def listener(**args):
raise StopPropagation from e raise StopPropagation from e
except KeyboardInterrupt as e: except KeyboardInterrupt as e:
raise KeyboardInterrupt from e raise KeyboardInterrupt from e
except (UserNotParticipant, MessageNotModified, MessageEmpty, Flood, Forbidden, PeerIdInvalid): except (
UserNotParticipant,
MessageNotModified,
MessageEmpty,
Flood,
Forbidden,
PeerIdInvalid,
):
logs.warning( logs.warning(
"An unknown chat error occurred while processing a command.", "An unknown chat error occurred while processing a command.",
) )
except MessageIdInvalid: except MessageIdInvalid:
logs.warning( logs.warning("Please Don't Delete Commands While it's Processing..")
"Please Don't Delete Commands While it's Processing.."
)
except AlreadyInConversationError: except AlreadyInConversationError:
logs.warning( logs.warning("Please Don't Send Commands In The Same Conversation..")
"Please Don't Send Commands In The Same Conversation.."
)
with contextlib.suppress(BaseException): with contextlib.suppress(BaseException):
await message.edit(lang("conversation_already_in_error")) await message.edit(lang("conversation_already_in_error"))
except TimeoutConversationError: except TimeoutConversationError:
logs.warning( logs.warning("Conversation Timed out while processing commands..")
"Conversation Timed out while processing commands.."
)
with contextlib.suppress(BaseException): with contextlib.suppress(BaseException):
await message.edit(lang("conversation_timed_out_error")) await message.edit(lang("conversation_timed_out_error"))
except ListenerCanceled: except ListenerCanceled:
logs.warning( logs.warning("Listener Canceled While Processing Commands..")
"Listener Canceled While Processing Commands.."
)
with contextlib.suppress(BaseException): with contextlib.suppress(BaseException):
await message.edit(lang("reload_des")) await message.edit(lang("reload_des"))
except ContinuePropagation as e: except ContinuePropagation as e:
@ -199,35 +208,54 @@ def listener(**args):
if Config.ERROR_REPORT: if Config.ERROR_REPORT:
report = f"""# Generated: {strftime('%H:%M %d/%m/%Y', gmtime())}. \n# ChatID: {message.chat.id}. \n# UserID: {message.from_user.id if message.from_user else message.sender_chat.id}. \n# Message: \n-----BEGIN TARGET MESSAGE-----\n{message.text or message.caption}\n-----END TARGET MESSAGE-----\n# Traceback: \n-----BEGIN TRACEBACK-----\n{str(exc_format)}\n-----END TRACEBACK-----\n# Error: "{str(exc_info)}". \n""" report = f"""# Generated: {strftime('%H:%M %d/%m/%Y', gmtime())}. \n# ChatID: {message.chat.id}. \n# UserID: {message.from_user.id if message.from_user else message.sender_chat.id}. \n# Message: \n-----BEGIN TARGET MESSAGE-----\n{message.text or message.caption}\n-----END TARGET MESSAGE-----\n# Traceback: \n-----BEGIN TRACEBACK-----\n{str(exc_format)}\n-----END TRACEBACK-----\n# Error: "{str(exc_info)}". \n"""
await attach_report(report, f"exception.{time()}.pgp.txt", None, await attach_report(
"PGP Error report generated.") report,
await Hook.process_error_exec(message, command, exc_info, exc_format) f"exception.{time()}.pgp.txt",
None,
"PGP Error report generated.",
)
await Hook.process_error_exec(
message, command, exc_info, exc_format
)
if (message.chat.id, message.id) in read_context: if (message.chat.id, message.id) in read_context:
del read_context[(message.chat.id, message.id)] del read_context[(message.chat.id, message.id)]
if block_process: if block_process:
message.stop_propagation() message.stop_propagation()
message.continue_propagation() message.continue_propagation()
bot.add_handler(MessageHandler(handler, filters=base_filters), group=0 + priority) bot.add_handler(
MessageHandler(handler, filters=base_filters), group=0 + priority
)
if command: if command:
bot.add_handler(MessageHandler(handler, filters=sudo_filters), group=50 + priority) bot.add_handler(
MessageHandler(handler, filters=sudo_filters), group=50 + priority
)
if not ignore_edited: if not ignore_edited:
bot.add_handler(EditedMessageHandler(handler, filters=base_filters), group=1 + priority) bot.add_handler(
EditedMessageHandler(handler, filters=base_filters), group=1 + priority
)
if command: if command:
bot.add_handler(EditedMessageHandler(handler, filters=sudo_filters), group=51 + priority) bot.add_handler(
EditedMessageHandler(handler, filters=sudo_filters),
group=51 + priority,
)
return handler return handler
if description is not None and command is not None: if description is not None and command is not None:
if parameters is None: if parameters is None:
parameters = "" parameters = ""
help_messages.update({ help_messages.update(
f"{alias_command(command)}": {"permission": permission_name, {
f"{alias_command(command)}": {
"permission": permission_name,
"use": f"**{lang('use_method')}:** `,{command} {parameters}`\n" "use": f"**{lang('use_method')}:** `,{command} {parameters}`\n"
f"**{lang('need_permission')}:** `{permission_name}`\n" f"**{lang('need_permission')}:** `{permission_name}`\n"
f"{description}", f"{description}",
"priority": priority, } "priority": priority,
}) }
}
)
all_permissions.append(Permission(permission_name)) all_permissions.append(Permission(permission_name))
return decorator return decorator
@ -261,49 +289,53 @@ def raw_listener(filter_s):
except ContinuePropagation as e: except ContinuePropagation as e:
raise ContinuePropagation from e raise ContinuePropagation from e
except MessageIdInvalid: except MessageIdInvalid:
logs.warning( logs.warning("Please Don't Delete Commands While it's Processing..")
"Please Don't Delete Commands While it's Processing.."
)
except AlreadyInConversationError: except AlreadyInConversationError:
logs.warning( logs.warning("Please Don't Send Commands In The Same Conversation..")
"Please Don't Send Commands In The Same Conversation.."
)
with contextlib.suppress(BaseException): with contextlib.suppress(BaseException):
await message.edit(lang("conversation_already_in_error")) await message.edit(lang("conversation_already_in_error"))
except TimeoutConversationError: except TimeoutConversationError:
logs.warning( logs.warning("Conversation Timed out while processing commands..")
"Conversation Timed out while processing commands.."
)
with contextlib.suppress(BaseException): with contextlib.suppress(BaseException):
await message.edit(lang("conversation_timed_out_error")) await message.edit(lang("conversation_timed_out_error"))
except ListenerCanceled: except ListenerCanceled:
logs.warning( logs.warning("Listener Canceled While Processing Commands..")
"Listener Canceled While Processing Commands.."
)
with contextlib.suppress(BaseException): with contextlib.suppress(BaseException):
await message.edit(lang("reload_des")) await message.edit(lang("reload_des"))
except SystemExit: except SystemExit:
await process_exit(start=False, _client=client, message=message) await process_exit(start=False, _client=client, message=message)
await Hook.shutdown() await Hook.shutdown()
sys.exit(0) sys.exit(0)
except (UserNotParticipant, MessageNotModified, MessageEmpty, Flood, Forbidden): except (
UserNotParticipant,
MessageNotModified,
MessageEmpty,
Flood,
Forbidden,
):
pass pass
except BaseException: except BaseException:
exc_info = sys.exc_info()[1] exc_info = sys.exc_info()[1]
exc_format = format_exc() exc_format = format_exc()
with contextlib.suppress(BaseException): with contextlib.suppress(BaseException):
await message.edit(lang('run_error'), no_reply=True) await message.edit(lang("run_error"), no_reply=True)
if Config.ERROR_REPORT: if Config.ERROR_REPORT:
report = f"# Generated: {strftime('%H:%M %d/%m/%Y', gmtime())}. \n" \ report = (
f"# ChatID: {message.chat.id}. \n" \ f"# Generated: {strftime('%H:%M %d/%m/%Y', gmtime())}. \n"
f"# UserID: {message.from_user.id if message.from_user else message.sender_chat.id}. \n" \ f"# ChatID: {message.chat.id}. \n"
f"# Message: \n-----BEGIN TARGET MESSAGE-----\n" \ f"# UserID: {message.from_user.id if message.from_user else message.sender_chat.id}. \n"
f"{message.text}\n-----END TARGET MESSAGE-----\n" \ f"# Message: \n-----BEGIN TARGET MESSAGE-----\n"
f"# Traceback: \n-----BEGIN TRACEBACK-----\n" \ f"{message.text}\n-----END TARGET MESSAGE-----\n"
f"{str(exc_format)}\n-----END TRACEBACK-----\n" \ f"# Traceback: \n-----BEGIN TRACEBACK-----\n"
f"# Error: \"{str(exc_info)}\". \n" f"{str(exc_format)}\n-----END TRACEBACK-----\n"
await attach_report(report, f"exception.{time()}.pagermaid", None, f'# Error: "{str(exc_info)}". \n'
"Error report generated.") )
await attach_report(
report,
f"exception.{time()}.pagermaid",
None,
"Error report generated.",
)
message.continue_propagation() message.continue_propagation()
bot.add_handler(MessageHandler(handler, filters=filter_s), group=2) bot.add_handler(MessageHandler(handler, filters=filter_s), group=2)

View File

@ -12,9 +12,7 @@ def __list_modules():
return [ return [
basename(file)[:-3] basename(file)[:-3]
for file in module_paths for file in module_paths
if isfile(file) if isfile(file) and file.endswith(".py") and not file.endswith("__init__.py")
and file.endswith(".py")
and not file.endswith("__init__.py")
] ]
@ -25,21 +23,15 @@ def __list_plugins():
return [ return [
basename(file)[:-3] basename(file)[:-3]
for file in plugin_paths for file in plugin_paths
if isfile(file) if isfile(file) and file.endswith(".py") and not file.endswith("__init__.py")
and file.endswith(".py")
and not file.endswith("__init__.py")
] ]
module_list_string = "".join( module_list_string = "".join(f"{module}, " for module in sorted(__list_modules()))
f"{module}, " for module in sorted(__list_modules())
)
module_list_string = module_list_string[:-2] module_list_string = module_list_string[:-2]
plugin_list_string = "".join( plugin_list_string = "".join(f"{plugin}, " for plugin in sorted(__list_plugins()))
f"{plugin}, " for plugin in sorted(__list_plugins())
)
plugin_list_string = plugin_list_string[:-2] plugin_list_string = plugin_list_string[:-2]

View File

@ -11,16 +11,19 @@ from pagermaid.utils import lang
import contextlib import contextlib
@listener(is_plugin=False, command="profile", @listener(
description=lang('profile_des'), is_plugin=False,
parameters="<username>") command="profile",
description=lang("profile_des"),
parameters="<username>",
)
async def profile(client: Client, message: Message): async def profile(client: Client, message: Message):
"""Queries profile of a user.""" """Queries profile of a user."""
if len(message.parameter) > 1: if len(message.parameter) > 1:
await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")
return return
if not Config.SILENT: if not Config.SILENT:
message = await message.edit(lang('profile_process')) message = await message.edit(lang("profile_process"))
if message.reply_to_message: if message.reply_to_message:
user = message.reply_to_message.from_user user = message.reply_to_message.from_user
if not user: if not user:
@ -43,39 +46,54 @@ async def profile(client: Client, message: Message):
try: try:
user = await client.get_users(user) user = await client.get_users(user)
except PeerIdInvalid: except PeerIdInvalid:
return await message.edit(f"{lang('error_prefix')}{lang('profile_e_nof')}") return await message.edit(
f"{lang('error_prefix')}{lang('profile_e_nof')}"
)
except UsernameNotOccupied: except UsernameNotOccupied:
return await message.edit(f"{lang('error_prefix')}{lang('profile_e_nou')}") return await message.edit(
f"{lang('error_prefix')}{lang('profile_e_nou')}"
)
except OverflowError: except OverflowError:
return await message.edit(f"{lang('error_prefix')}{lang('profile_e_long')}") return await message.edit(
f"{lang('error_prefix')}{lang('profile_e_long')}"
)
except Exception as exception: except Exception as exception:
raise exception raise exception
user_type = "Bot" if user.is_bot else lang('profile_user') user_type = "Bot" if user.is_bot else lang("profile_user")
username_system = f"@{user.username}" if user.username is not None else lang('profile_noset') username_system = (
f"@{user.username}" if user.username is not None else lang("profile_noset")
)
if not user.first_name: if not user.first_name:
await message.edit(f"{lang('error_prefix')}{lang('profile_e_no')}") await message.edit(f"{lang('error_prefix')}{lang('profile_e_no')}")
return return
first_name = user.first_name.replace("\u2060", "") first_name = user.first_name.replace("\u2060", "")
last_name = user.last_name.replace("\u2060", "") if user.last_name is not None else lang('profile_noset') last_name = (
verified = lang('profile_yes') if user.is_verified else lang('profile_no') user.last_name.replace("\u2060", "")
restricted = lang('profile_yes') if user.is_restricted else lang('profile_no') if user.last_name is not None
caption = f"**{lang('profile_name')}:** \n" \ else lang("profile_noset")
f"{lang('profile_username')}: {username_system} \n" \ )
f"ID: {user.id} \n" \ verified = lang("profile_yes") if user.is_verified else lang("profile_no")
f"{lang('profile_fname')}: {first_name} \n" \ restricted = lang("profile_yes") if user.is_restricted else lang("profile_no")
f"{lang('profile_lname')}: {last_name} \n" \ caption = (
f"{lang('profile_verified')}: {verified} \n" \ f"**{lang('profile_name')}:** \n"
f"{lang('profile_restricted')}: {restricted} \n" \ f"{lang('profile_username')}: {username_system} \n"
f"{lang('profile_type')}: {user_type} \n" \ f"ID: {user.id} \n"
f"{lang('profile_fname')}: {first_name} \n"
f"{lang('profile_lname')}: {last_name} \n"
f"{lang('profile_verified')}: {verified} \n"
f"{lang('profile_restricted')}: {restricted} \n"
f"{lang('profile_type')}: {user_type} \n"
f"[{first_name}](tg://user?id={user.id})" f"[{first_name}](tg://user?id={user.id})"
)
photo = await client.download_media(user.photo.big_file_id) photo = await client.download_media(user.photo.big_file_id)
reply_to = message.reply_to_message.id if message.reply_to_message else message.reply_to_top_message_id reply_to = (
message.reply_to_message.id
if message.reply_to_message
else message.reply_to_top_message_id
)
try: try:
await client.send_photo( await client.send_photo(
message.chat.id, message.chat.id, photo, caption=caption, reply_to_message_id=reply_to
photo,
caption=caption,
reply_to_message_id=reply_to
) )
await message.delete() await message.delete()
return remove(photo) return remove(photo)
@ -83,17 +101,21 @@ async def profile(client: Client, message: Message):
await message.edit(caption) await message.edit(caption)
@listener(is_plugin=False, outgoing=True, command="block", @listener(
is_plugin=False,
outgoing=True,
command="block",
need_admin=True, need_admin=True,
description=lang('block_des'), description=lang("block_des"),
parameters="(username/uid/reply)") parameters="(username/uid/reply)",
)
async def block_user(client: Client, message: Message): async def block_user(client: Client, message: Message):
"""Block a user.""" """Block a user."""
if len(message.parameter) > 1: if len(message.parameter) > 1:
await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")
return return
if not Config.SILENT: if not Config.SILENT:
message = await message.edit(lang('block_process')) message = await message.edit(lang("block_process"))
user = message.obtain_user() user = message.obtain_user()
if not user: if not user:
return await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") return await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")
@ -103,17 +125,21 @@ async def block_user(client: Client, message: Message):
await message.edit(f"`{user}` {lang('block_exist')}") await message.edit(f"`{user}` {lang('block_exist')}")
@listener(is_plugin=False, outgoing=True, command="unblock", @listener(
is_plugin=False,
outgoing=True,
command="unblock",
need_admin=True, need_admin=True,
description=lang('unblock_des'), description=lang("unblock_des"),
parameters="<username/uid/reply>") parameters="<username/uid/reply>",
)
async def unblock_user(client: Client, message: Message): async def unblock_user(client: Client, message: Message):
"""Unblock a user.""" """Unblock a user."""
if len(message.parameter) > 1: if len(message.parameter) > 1:
await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")
return return
if not Config.SILENT: if not Config.SILENT:
message = await message.edit(lang('unblock_process')) message = await message.edit(lang("unblock_process"))
user = message.obtain_user() user = message.obtain_user()
if not user: if not user:
return await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") return await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")

View File

@ -7,9 +7,13 @@ from pagermaid.enums import Client, Message
from pagermaid.listener import listener from pagermaid.listener import listener
@listener(is_plugin=False, outgoing=True, command="ghost", @listener(
description=lang('ghost_des'), is_plugin=False,
parameters="<true|false|status>") outgoing=True,
command="ghost",
description=lang("ghost_des"),
parameters="<true|false|status>",
)
async def ghost(client: Client, message: Message): async def ghost(client: Client, message: Message):
"""Toggles ghosting of a user.""" """Toggles ghosting of a user."""
if len(message.parameter) != 1: if len(message.parameter) != 1:
@ -19,33 +23,41 @@ async def ghost(client: Client, message: Message):
self_user_id = myself.id self_user_id = myself.id
if message.parameter[0] == "true": if message.parameter[0] == "true":
if message.chat.id == self_user_id: if message.chat.id == self_user_id:
return await message.edit(lang('ghost_e_mark')) return await message.edit(lang("ghost_e_mark"))
sqlite[f"ghosted.chat_id.{str(message.chat.id)}"] = True sqlite[f"ghosted.chat_id.{str(message.chat.id)}"] = True
await message.safe_delete() await message.safe_delete()
await log(f"{lang('ghost_set_f')} ChatID {str(message.chat.id)} {lang('ghost_set_l')}") await log(
f"{lang('ghost_set_f')} ChatID {str(message.chat.id)} {lang('ghost_set_l')}"
)
elif message.parameter[0] == "false": elif message.parameter[0] == "false":
if message.chat.id == self_user_id: if message.chat.id == self_user_id:
await message.edit(lang('ghost_e_mark')) await message.edit(lang("ghost_e_mark"))
return return
try: try:
del sqlite[f"ghosted.chat_id.{str(message.chat.id)}"] del sqlite[f"ghosted.chat_id.{str(message.chat.id)}"]
except KeyError: except KeyError:
return await message.edit(lang('ghost_e_noexist')) return await message.edit(lang("ghost_e_noexist"))
await message.safe_delete() await message.safe_delete()
await log(f"{lang('ghost_set_f')} ChatID {str(message.chat.id)} {lang('ghost_cancel')}") await log(
f"{lang('ghost_set_f')} ChatID {str(message.chat.id)} {lang('ghost_cancel')}"
)
elif message.parameter[0] == "status": elif message.parameter[0] == "status":
if sqlite.get(f"ghosted.chat_id.{str(message.chat.id)}", None): if sqlite.get(f"ghosted.chat_id.{str(message.chat.id)}", None):
await message.edit(lang('ghost_e_exist')) await message.edit(lang("ghost_e_exist"))
else: else:
await message.edit(lang('ghost_e_noexist')) await message.edit(lang("ghost_e_noexist"))
else: else:
await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")
@listener(is_plugin=False, outgoing=True, command="deny", @listener(
is_plugin=False,
outgoing=True,
command="deny",
need_admin=True, need_admin=True,
description=lang('deny_des'), description=lang("deny_des"),
parameters="<true|false|status>") parameters="<true|false|status>",
)
async def deny(client: Client, message: Message): async def deny(client: Client, message: Message):
"""Toggles denying of a user.""" """Toggles denying of a user."""
if len(message.parameter) != 1: if len(message.parameter) != 1:
@ -55,25 +67,25 @@ async def deny(client: Client, message: Message):
self_user_id = myself.id self_user_id = myself.id
if message.parameter[0] == "true": if message.parameter[0] == "true":
if message.chat.id == self_user_id: if message.chat.id == self_user_id:
return await message.edit(lang('ghost_e_mark')) return await message.edit(lang("ghost_e_mark"))
sqlite[f"denied.chat_id.{str(message.chat.id)}"] = True sqlite[f"denied.chat_id.{str(message.chat.id)}"] = True
await message.safe_delete() await message.safe_delete()
await log(f"ChatID {str(message.chat.id)} {lang('deny_set')}") await log(f"ChatID {str(message.chat.id)} {lang('deny_set')}")
elif message.parameter[0] == "false": elif message.parameter[0] == "false":
if message.chat.id == self_user_id: if message.chat.id == self_user_id:
await message.edit(lang('ghost_e_mark')) await message.edit(lang("ghost_e_mark"))
return return
try: try:
del sqlite[f"denied.chat_id.{str(message.chat.id)}"] del sqlite[f"denied.chat_id.{str(message.chat.id)}"]
except KeyError: except KeyError:
return await message.edit(lang('deny_e_noexist')) return await message.edit(lang("deny_e_noexist"))
await message.safe_delete() await message.safe_delete()
await log(f"ChatID {str(message.chat.id)} {lang('deny_cancel')}") await log(f"ChatID {str(message.chat.id)} {lang('deny_cancel')}")
elif message.parameter[0] == "status": elif message.parameter[0] == "status":
if sqlite.get(f"denied.chat_id.{str(message.chat.id)}", None): if sqlite.get(f"denied.chat_id.{str(message.chat.id)}", None):
await message.edit(lang('deny_e_exist')) await message.edit(lang("deny_e_exist"))
else: else:
await message.edit(lang('deny_e_noexist')) await message.edit(lang("deny_e_noexist"))
else: else:
await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")

View File

@ -39,10 +39,11 @@ def un_tar_gz(filename, dirs):
return False return False
@listener(is_plugin=False, outgoing=True, command="backup", @listener(
description=lang('backup_des')) is_plugin=False, outgoing=True, command="backup", description=lang("backup_des")
)
async def backup(message: Message): async def backup(message: Message):
await message.edit(lang('backup_process')) await message.edit(lang("backup_process"))
# Remove old backup # Remove old backup
if os.path.exists(pgm_backup_zip_name): if os.path.exists(pgm_backup_zip_name):
@ -50,7 +51,12 @@ async def backup(message: Message):
# remove mp3 , they are so big ! # remove mp3 , they are so big !
for i in os.listdir("data"): for i in os.listdir("data"):
if i.find(".mp3") != -1 or i.find(".jpg") != -1 or i.find(".flac") != -1 or i.find(".ogg") != -1: if (
i.find(".mp3") != -1
or i.find(".jpg") != -1
or i.find(".flac") != -1
or i.find(".ogg") != -1
):
os.remove(f"data{os.sep}{i}") os.remove(f"data{os.sep}{i}")
# run backup function # run backup function
@ -65,37 +71,41 @@ async def backup(message: Message):
await message.edit(lang("backup_success")) await message.edit(lang("backup_success"))
@listener(is_plugin=False, outgoing=True, command="recovery", @listener(
is_plugin=False,
outgoing=True,
command="recovery",
need_admin=True, need_admin=True,
description=lang('recovery_des')) description=lang("recovery_des"),
)
async def recovery(message: Message): async def recovery(message: Message):
reply = message.reply_to_message reply = message.reply_to_message
if not reply: if not reply:
return await message.edit(lang('recovery_file_error')) return await message.edit(lang("recovery_file_error"))
if not reply.document: if not reply.document:
return await message.edit(lang('recovery_file_error')) return await message.edit(lang("recovery_file_error"))
try: try:
if ".tar.gz" not in reply.document.file_name: if ".tar.gz" not in reply.document.file_name:
return await message.edit(lang('recovery_file_error')) return await message.edit(lang("recovery_file_error"))
await message.edit(lang('recovery_down')) await message.edit(lang("recovery_down"))
# Start download process # Start download process
pgm_backup_zip_name = await reply.download() # noqa pgm_backup_zip_name = await reply.download() # noqa
except Exception as e: # noqa except Exception as e: # noqa
print(e, format_exc()) print(e, format_exc())
return await message.edit(lang('recovery_file_error')) return await message.edit(lang("recovery_file_error"))
# Extract backup files # Extract backup files
await message.edit(lang('recovery_process')) await message.edit(lang("recovery_process"))
if not os.path.exists(pgm_backup_zip_name): if not os.path.exists(pgm_backup_zip_name):
return await message.edit(lang('recovery_file_not_found')) return await message.edit(lang("recovery_file_not_found"))
elif not un_tar_gz(pgm_backup_zip_name, ""): elif not un_tar_gz(pgm_backup_zip_name, ""):
os.remove(pgm_backup_zip_name) os.remove(pgm_backup_zip_name)
return await message.edit(lang('recovery_file_error')) return await message.edit(lang("recovery_file_error"))
# Cleanup # Cleanup
if os.path.exists(pgm_backup_zip_name): if os.path.exists(pgm_backup_zip_name):
os.remove(pgm_backup_zip_name) os.remove(pgm_backup_zip_name)
await message.edit(lang('recovery_success') + " " + lang('apt_reboot')) await message.edit(lang("recovery_success") + " " + lang("apt_reboot"))
sys.exit(0) sys.exit(0)

View File

@ -8,9 +8,13 @@ from pagermaid.listener import listener
from pagermaid.utils import lang, Message from pagermaid.utils import lang, Message
@listener(is_plugin=False, outgoing=True, command="time", @listener(
description=lang('time_des'), is_plugin=False,
parameters=lang('time_parameters')) outgoing=True,
command="time",
description=lang("time_des"),
parameters=lang("time_parameters"),
)
async def time(message: Message): async def time(message: Message):
"""For querying time.""" """For querying time."""
if len(message.parameter) == 1: if len(message.parameter) == 1:
@ -37,24 +41,24 @@ async def time(message: Message):
time_zone = await get_timezone(country) time_zone = await get_timezone(country)
if not time_zone: if not time_zone:
if len(message.parameter) < 1: if len(message.parameter) < 1:
await message.edit(lang('time_config')) await message.edit(lang("time_config"))
return return
try: try:
time_num, utc_num = int(message.parameter[0]), int(message.parameter[0]) time_num, utc_num = int(message.parameter[0]), int(message.parameter[0])
if time_num == 0: if time_num == 0:
time_num, utc_num = '', '' time_num, utc_num = "", ""
elif 0 < time_num < 13: elif 0 < time_num < 13:
time_num, utc_num = f'-{time_num}', f'+{time_num}' time_num, utc_num = f"-{time_num}", f"+{time_num}"
elif -13 < time_num < 0: elif -13 < time_num < 0:
time_num, utc_num = f'+{-time_num}', f'{time_num}' time_num, utc_num = f"+{-time_num}", f"{time_num}"
elif time_num < -12: elif time_num < -12:
time_num, utc_num = '+12', '-12' time_num, utc_num = "+12", "-12"
elif time_num > 12: elif time_num > 12:
time_num, utc_num = '-12', '+12' time_num, utc_num = "-12", "+12"
time_zone = timezone(f'Etc/GMT{time_num}') time_zone = timezone(f"Etc/GMT{time_num}")
country_name = f'UTC{utc_num}' country_name = f"UTC{utc_num}"
except ValueError: except ValueError:
await message.edit(lang('arg_error')) await message.edit(lang("arg_error"))
return return
else: else:
try: try:
@ -62,9 +66,11 @@ async def time(message: Message):
except KeyError: except KeyError:
country_name = country country_name = country
await message.edit(f"**{country_name} {lang('time_time')}**\n" await message.edit(
f"**{country_name} {lang('time_time')}**\n"
f"`{datetime.now(time_zone).strftime(date_form)} " f"`{datetime.now(time_zone).strftime(date_form)} "
f"{datetime.now(time_zone).strftime(time_form)}`") f"{datetime.now(time_zone).strftime(time_form)}`"
)
async def get_timezone(target): async def get_timezone(target):

View File

@ -15,76 +15,138 @@ from pagermaid.listener import listener
import pathlib import pathlib
@listener(is_plugin=False, command="help", @listener(
description=lang('help_des'), is_plugin=False,
parameters=f"<{lang('command')}>") command="help",
description=lang("help_des"),
parameters=f"<{lang('command')}>",
)
async def help_command(message: Message): async def help_command(message: Message):
"""The help new command,""" """The help new command,"""
if message.arguments: if message.arguments:
if message.arguments in help_messages: if message.arguments in help_messages:
if from_self(message) or \ if from_self(message) or enforce_permission(
enforce_permission(from_msg_get_sudo_uid(message), help_messages[message.arguments]["permission"]): from_msg_get_sudo_uid(message),
help_messages[message.arguments]["permission"],
):
await message.edit(f"{help_messages[message.arguments]['use']}") await message.edit(f"{help_messages[message.arguments]['use']}")
else: else:
await message.edit(lang('help_no_permission')) await message.edit(lang("help_no_permission"))
else: else:
await message.edit(lang('arg_error')) await message.edit(lang("arg_error"))
else: else:
result = f"**{lang('help_list')}: \n**" result = f"**{lang('help_list')}: \n**"
support_commands = ['username', 'name', 'pfp', 'bio', 'rmpfp', support_commands = [
'profile', 'block', 'unblock', 'ghost', 'deny', 'convert', "username",
'caption', 'ocr', 'highlight', 'time', 'translate', "name",
'tts', 'google', 'animate', "pfp",
'teletype', 'widen', 'owo', 'flip', "bio",
'rng', 'aaa', 'tuxsay', 'coin', 'help', "rmpfp",
'lang', 'alias', 'id', 'uslog', 'log', "profile",
're', 'leave', 'hitokoto', 'apt', 'prune', 'selfprune', "block",
'yourprune', 'del', 'genqr', 'parseqr', "unblock",
'sb', 'sysinfo', 'status', "ghost",
'stats', 'speedtest', 'connection', "deny",
'pingdc', 'ping', 'topcloud', "convert",
's', 'sticker', 'sh', 'restart', "caption",
'trace', 'chat', 'update'] "ocr",
"highlight",
"time",
"translate",
"tts",
"google",
"animate",
"teletype",
"widen",
"owo",
"flip",
"rng",
"aaa",
"tuxsay",
"coin",
"help",
"lang",
"alias",
"id",
"uslog",
"log",
"re",
"leave",
"hitokoto",
"apt",
"prune",
"selfprune",
"yourprune",
"del",
"genqr",
"parseqr",
"sb",
"sysinfo",
"status",
"stats",
"speedtest",
"connection",
"pingdc",
"ping",
"topcloud",
"s",
"sticker",
"sh",
"restart",
"trace",
"chat",
"update",
]
for command in sorted(help_messages, reverse=False): for command in sorted(help_messages, reverse=False):
if str(command) in support_commands: if str(command) in support_commands:
continue continue
if from_self(message) or \ if from_self(message) or enforce_permission(
enforce_permission(from_msg_get_sudo_uid(message), help_messages[command]["permission"]): from_msg_get_sudo_uid(message), help_messages[command]["permission"]
):
result += f"`{command}`, " result += f"`{command}`, "
if result == f"**{lang('help_list')}: \n**": if result == f"**{lang('help_list')}: \n**":
"""The help raw command,""" """The help raw command,"""
for command in sorted(help_messages, reverse=False): for command in sorted(help_messages, reverse=False):
if from_self(message) or \ if from_self(message) or enforce_permission(
enforce_permission(from_msg_get_sudo_uid(message), help_messages[command]["permission"]): from_msg_get_sudo_uid(message), help_messages[command]["permission"]
):
result += f"`{command}`, " result += f"`{command}`, "
await message.edit(result[ await message.edit(
:-2] + f"\n**{lang('help_send')} \",help <{lang('command')}>\" {lang('help_see')}**\n" result[:-2]
+ f"\n**{lang('help_send')} \",help <{lang('command')}>\" {lang('help_see')}**\n"
f"[{lang('help_source')}](https://t.me/PagerMaid_Modify) " f"[{lang('help_source')}](https://t.me/PagerMaid_Modify) "
f"[{lang('help_plugin')}](https://index.xtaolabs.com/) " f"[{lang('help_plugin')}](https://index.xtaolabs.com/) "
f"[{lang('help_module')}](https://wiki.xtaolabs.com/)", f"[{lang('help_module')}](https://wiki.xtaolabs.com/)",
parse_mode=ParseMode.MARKDOWN, parse_mode=ParseMode.MARKDOWN,
disable_web_page_preview=True) disable_web_page_preview=True,
)
@listener(is_plugin=False, command="help_raw", @listener(
description=lang('help_des'), is_plugin=False,
parameters=f"<{lang('command')}>") command="help_raw",
description=lang("help_des"),
parameters=f"<{lang('command')}>",
)
async def help_raw_command(message: Message): async def help_raw_command(message: Message):
"""The help raw command,""" """The help raw command,"""
if message.arguments: if message.arguments:
if message.arguments in help_messages: if message.arguments in help_messages:
if from_self(message) or \ if from_self(message) or enforce_permission(
enforce_permission(from_msg_get_sudo_uid(message), help_messages[message.arguments]["permission"]): from_msg_get_sudo_uid(message),
help_messages[message.arguments]["permission"],
):
await message.edit(f"{help_messages[message.arguments]['use']}") await message.edit(f"{help_messages[message.arguments]['use']}")
else: else:
await message.edit(lang('help_no_permission')) await message.edit(lang("help_no_permission"))
else: else:
await message.edit(lang('arg_error')) await message.edit(lang("arg_error"))
else: else:
result = f"**{lang('help_list')}: \n**" result = f"**{lang('help_list')}: \n**"
for command in sorted(help_messages, reverse=False): for command in sorted(help_messages, reverse=False):
if from_self(message) or \ if from_self(message) or enforce_permission(
enforce_permission(from_msg_get_sudo_uid(message), help_messages[command]["permission"]): from_msg_get_sudo_uid(message), help_messages[command]["permission"]
):
result += f"`{command}`, " result += f"`{command}`, "
await message.edit( await message.edit(
f"""{result[:-2]}\n**{lang('help_send')} ",help <{lang('command')}>" {lang('help_see')}** [{lang('help_source')}](https://t.me/PagerMaid_Modify)""", f"""{result[:-2]}\n**{lang('help_send')} ",help <{lang('command')}>" {lang('help_see')}** [{lang('help_source')}](https://t.me/PagerMaid_Modify)""",
@ -93,57 +155,67 @@ async def help_raw_command(message: Message):
) )
@listener(is_plugin=False, command="lang", @listener(
need_admin=True, is_plugin=False, command="lang", need_admin=True, description=lang("lang_des")
description=lang('lang_des')) )
async def lang_change(message: Message): async def lang_change(message: Message):
to_lang = message.arguments to_lang = message.arguments
from_lang = Config.LANGUAGE from_lang = Config.LANGUAGE
dir_, dir__ = listdir('languages/built-in'), [] dir_, dir__ = listdir("languages/built-in"), []
for i in dir_: for i in dir_:
if i.find('yml') != -1: if i.find("yml") != -1:
dir__.append(i[:-4]) dir__.append(i[:-4])
file = pathlib.Path('config.yml').read_text() file = pathlib.Path("config.yml").read_text()
if to_lang in dir__: if to_lang in dir__:
file = file.replace(f'application_language: "{from_lang}"', f'application_language: "{to_lang}"') file = file.replace(
with open('config.yml', 'w', encoding="utf-8") as f: f'application_language: "{from_lang}"', f'application_language: "{to_lang}"'
)
with open("config.yml", "w", encoding="utf-8") as f:
f.write(file) f.write(file)
await message.edit(f"{lang('lang_change_to')} {to_lang}, {lang('lang_reboot')}") await message.edit(f"{lang('lang_change_to')} {to_lang}, {lang('lang_reboot')}")
await reload_all() await reload_all()
else: else:
await message.edit(f'{lang("lang_current_lang")} {Config.LANGUAGE}\n\n' await message.edit(
f'{lang("lang_all_lang")}{"".join(dir__)}') f'{lang("lang_current_lang")} {Config.LANGUAGE}\n\n'
f'{lang("lang_all_lang")}{"".join(dir__)}'
)
@listener(is_plugin=False, outgoing=True, command="alias", @listener(
is_plugin=False,
outgoing=True,
command="alias",
disallow_alias=True, disallow_alias=True,
need_admin=True, need_admin=True,
description=lang('alias_des'), description=lang("alias_des"),
parameters='{list|del|set} <source> <to>') parameters="{list|del|set} <source> <to>",
)
async def alias_commands(message: Message): async def alias_commands(message: Message):
alias_manager = AliasManager() alias_manager = AliasManager()
if len(message.parameter) == 0: if len(message.parameter) == 0:
await message.edit(lang('arg_error')) await message.edit(lang("arg_error"))
elif len(message.parameter) == 1: elif len(message.parameter) == 1:
if alias_manager.alias_list: if alias_manager.alias_list:
await message.edit(lang('alias_list') + '\n\n' + alias_manager.get_all_alias_text()) await message.edit(
lang("alias_list") + "\n\n" + alias_manager.get_all_alias_text()
)
else: else:
await message.edit(lang('alias_no')) await message.edit(lang("alias_no"))
elif len(message.parameter) == 2: elif len(message.parameter) == 2:
source_command = message.parameter[1] source_command = message.parameter[1]
try: try:
alias_manager.delete_alias(source_command) alias_manager.delete_alias(source_command)
await message.edit(lang('alias_success')) await message.edit(lang("alias_success"))
await reload_all() await reload_all()
except KeyError: except KeyError:
await message.edit(lang('alias_no_exist')) await message.edit(lang("alias_no_exist"))
return return
elif len(message.parameter) == 3: elif len(message.parameter) == 3:
source_command = message.parameter[1] source_command = message.parameter[1]
to_command = message.parameter[2] to_command = message.parameter[2]
if to_command in help_messages: if to_command in help_messages:
await message.edit(lang('alias_exist')) await message.edit(lang("alias_exist"))
return return
alias_manager.add_alias(source_command, to_command) alias_manager.add_alias(source_command, to_command)
await message.edit(lang('alias_success')) await message.edit(lang("alias_success"))
await reload_all() await reload_all()

View File

@ -10,8 +10,7 @@ from pagermaid.utils import lang
from pagermaid.enums import Client, Message from pagermaid.enums import Client, Message
@listener(is_plugin=False, outgoing=True, command="id", @listener(is_plugin=False, outgoing=True, command="id", description=lang("id_des"))
description=lang("id_des"))
async def userid(message: Message): async def userid(message: Message):
"""Query the UserID of the sender of the message you replied to.""" """Query the UserID of the sender of the message you replied to."""
reply = message.reply_to_message reply = message.reply_to_message
@ -36,7 +35,7 @@ async def userid(message: Message):
return await message.edit(lang("leave_not_group")) return await message.edit(lang("leave_not_group"))
text += f"protected: `{str(msg_from.has_protected_content)}" + "`\n" text += f"protected: `{str(msg_from.has_protected_content)}" + "`\n"
if reply: if reply:
text += "\n" + lang('id_hint') + "\nMessage ID: `" + str(reply.id) + "`" text += "\n" + lang("id_hint") + "\nMessage ID: `" + str(reply.id) + "`"
try: try:
text += "\n\n**User**\nid: `" + str(reply.from_user.id) + "`" text += "\n\n**User**\nid: `" + str(reply.from_user.id) + "`"
if reply.from_user.is_bot: if reply.from_user.is_bot:
@ -61,9 +60,14 @@ async def userid(message: Message):
except AttributeError: except AttributeError:
pass pass
if reply.forward_from_chat: if reply.forward_from_chat:
text += "\n\n**Forward From Channel**\n" \ text += (
"id: `" + str(reply.forward_from_chat.id) + \ "\n\n**Forward From Channel**\n"
"`\ntitle: `" + reply.forward_from_chat.title + "`" "id: `"
+ str(reply.forward_from_chat.id)
+ "`\ntitle: `"
+ reply.forward_from_chat.title
+ "`"
)
if reply.forward_from_chat.username: if reply.forward_from_chat.username:
text += "\nusername: @" + reply.forward_from_chat.username text += "\nusername: @" + reply.forward_from_chat.username
if reply.forward_from_message_id: if reply.forward_from_message_id:
@ -71,7 +75,9 @@ async def userid(message: Message):
if reply.forward_sender_name: if reply.forward_sender_name:
text += "\npost_author: `" + reply.forward_sender_name + "`" text += "\npost_author: `" + reply.forward_sender_name + "`"
elif reply.forward_from: elif reply.forward_from:
text += "\n\n**Forward From User**\nid: `" + str(reply.forward_from.id) + "`" text += (
"\n\n**Forward From User**\nid: `" + str(reply.forward_from.id) + "`"
)
try: try:
if reply.forward_from.is_bot: if reply.forward_from.is_bot:
text += f"\nis_bot: {lang('id_is_bot_yes')}" text += f"\nis_bot: {lang('id_is_bot_yes')}"
@ -88,13 +94,21 @@ async def userid(message: Message):
except AttributeError: except AttributeError:
pass pass
elif reply.forward_sender_name: elif reply.forward_sender_name:
text += "\n\n**Forward From User**\nsender_name: `" + str(reply.forward_sender_name) + "`" text += (
"\n\n**Forward From User**\nsender_name: `"
+ str(reply.forward_sender_name)
+ "`"
)
await message.edit(text) await message.edit(text)
@listener(is_plugin=False, outgoing=True, command="uslog", @listener(
description=lang('uslog_des'), is_plugin=False,
parameters="<string>") outgoing=True,
command="uslog",
description=lang("uslog_des"),
parameters="<string>",
)
async def uslog(message: Message): async def uslog(message: Message):
"""Forwards a message into log group""" """Forwards a message into log group"""
if Config.LOG: if Config.LOG:
@ -104,15 +118,19 @@ async def uslog(message: Message):
elif message.arguments: elif message.arguments:
await log(message.arguments) await log(message.arguments)
else: else:
return await message.edit(lang('arg_error')) return await message.edit(lang("arg_error"))
await message.edit(lang('uslog_success')) await message.edit(lang("uslog_success"))
else: else:
await message.edit(lang('uslog_log_disable')) await message.edit(lang("uslog_log_disable"))
@listener(is_plugin=False, outgoing=True, command="log", @listener(
description=lang('log_des'), is_plugin=False,
parameters="<string>") outgoing=True,
command="log",
description=lang("log_des"),
parameters="<string>",
)
async def logging(message: Message): async def logging(message: Message):
"""Forwards a message into log group""" """Forwards a message into log group"""
if Config.LOG: if Config.LOG:
@ -122,38 +140,45 @@ async def logging(message: Message):
elif message.arguments: elif message.arguments:
await log(message.arguments) await log(message.arguments)
else: else:
return await message.edit(lang('arg_error')) return await message.edit(lang("arg_error"))
await message.safe_delete() await message.safe_delete()
else: else:
await message.edit(lang('uslog_log_disable')) await message.edit(lang("uslog_log_disable"))
@listener(is_plugin=False, outgoing=True, command="re", @listener(
description=lang('re_des'), is_plugin=False,
parameters=lang('re_parameters')) outgoing=True,
command="re",
description=lang("re_des"),
parameters=lang("re_parameters"),
)
async def re(bot: Client, message: Message): async def re(bot: Client, message: Message):
"""Forwards a message into this group""" """Forwards a message into this group"""
if reply := message.reply_to_message: if reply := message.reply_to_message:
if message.arguments == '': if message.arguments == "":
num = 1 num = 1
else: else:
try: try:
num = int(message.arguments) num = int(message.arguments)
if num > 100: if num > 100:
await message.edit(lang('re_too_big')) await message.edit(lang("re_too_big"))
except Exception: except Exception:
return await message.edit(lang('re_arg_error')) return await message.edit(lang("re_arg_error"))
await message.safe_delete() await message.safe_delete()
for _ in range(num): for _ in range(num):
try: try:
if not message.chat.has_protected_content: if not message.chat.has_protected_content:
await forward_msg(bot, message.reply_to_message) await forward_msg(bot, message.reply_to_message)
else: else:
await reply.copy(reply.chat.id, reply_to_message_id=message.reply_to_top_message_id) await reply.copy(
reply.chat.id,
reply_to_message_id=message.reply_to_top_message_id,
)
except (Forbidden, FloodWait, Exception): except (Forbidden, FloodWait, Exception):
return return
else: else:
await message.edit(lang('not_reply')) await message.edit(lang("not_reply"))
async def forward_msg(bot: Client, message: Message): async def forward_msg(bot: Client, message: Message):

View File

@ -13,7 +13,7 @@ from pagermaid.hook import Hook
class DatetimeSerializer(json.JSONEncoder): class DatetimeSerializer(json.JSONEncoder):
def default(self, obj): def default(self, obj):
if isinstance(obj, datetime.datetime): if isinstance(obj, datetime.datetime):
fmt = '%Y-%m-%dT%H:%M:%S' fmt = "%Y-%m-%dT%H:%M:%S"
return obj.strftime(fmt) return obj.strftime(fmt)
return json.JSONEncoder.default(self, obj) return json.JSONEncoder.default(self, obj)
@ -37,57 +37,57 @@ class Mixpanel:
@staticmethod @staticmethod
def json_dumps(data, cls=None): def json_dumps(data, cls=None):
# Separators are specified to eliminate whitespace. # Separators are specified to eliminate whitespace.
return json.dumps(data, separators=(',', ':'), cls=cls) return json.dumps(data, separators=(",", ":"), cls=cls)
async def api_call(self, endpoint, json_message): async def api_call(self, endpoint, json_message):
_endpoints = { _endpoints = {
'events': f'https://{self.api_host}/track', "events": f"https://{self.api_host}/track",
'people': f'https://{self.api_host}/engage', "people": f"https://{self.api_host}/engage",
} }
request_url = _endpoints.get(endpoint) request_url = _endpoints.get(endpoint)
if request_url is None: if request_url is None:
return return
params = { params = {
'data': json_message, "data": json_message,
'verbose': 1, "verbose": 1,
'ip': 0, "ip": 0,
} }
start = self._now() start = self._now()
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
await self._request.post( await self._request.post(request_url, data=params, timeout=10.0)
request_url,
data=params,
timeout=10.0
)
logs.debug(f"Mixpanel request took {self._now() - start} seconds") logs.debug(f"Mixpanel request took {self._now() - start} seconds")
async def people_set(self, distinct_id: str, properties: dict): async def people_set(self, distinct_id: str, properties: dict):
message = { message = {
'$distinct_id': distinct_id, "$distinct_id": distinct_id,
'$set': properties, "$set": properties,
} }
record = {'$token': self._token, '$time': self._now()} record = {"$token": self._token, "$time": self._now()}
# sourcery skip: dict-assign-update-to-union # sourcery skip: dict-assign-update-to-union
record.update(message) record.update(message)
return await self.api_call('people', self.json_dumps(record, cls=self._serializer)) return await self.api_call(
"people", self.json_dumps(record, cls=self._serializer)
)
async def track(self, distinct_id: str, event_name: str, properties: dict): async def track(self, distinct_id: str, event_name: str, properties: dict):
all_properties = { all_properties = {
'token': self._token, "token": self._token,
'distinct_id': distinct_id, "distinct_id": distinct_id,
'time': self._now(), "time": self._now(),
'$insert_id': self._make_insert_id(), "$insert_id": self._make_insert_id(),
'mp_lib': 'python', "mp_lib": "python",
'$lib_version': '4.10.0', "$lib_version": "4.10.0",
} }
if properties: if properties:
# sourcery skip: dict-assign-update-to-union # sourcery skip: dict-assign-update-to-union
all_properties.update(properties) all_properties.update(properties)
event = { event = {
'event': event_name, "event": event_name,
'properties': all_properties, "properties": all_properties,
} }
return await self.api_call('events', self.json_dumps(event, cls=self._serializer)) return await self.api_call(
"events", self.json_dumps(event, cls=self._serializer)
)
mp = Mixpanel(Config.MIXPANEL_API) mp = Mixpanel(Config.MIXPANEL_API)
@ -97,7 +97,7 @@ mp = Mixpanel(Config.MIXPANEL_API)
async def mixpanel_init_id(bot: Client): async def mixpanel_init_id(bot: Client):
if not bot.me: if not bot.me:
bot.me = await bot.get_me() bot.me = await bot.get_me()
data = {'$first_name': bot.me.first_name} data = {"$first_name": bot.me.first_name}
if bot.me.username: if bot.me.username:
data["username"] = bot.me.username data["username"] = bot.me.username
bot.loop.create_task(mp.people_set(str(bot.me.id), data)) bot.loop.create_task(mp.people_set(str(bot.me.id), data))
@ -113,4 +113,10 @@ async def mixpanel_report(bot: Client, message: Message, command):
sender_id = message.sender_chat.id if message.sender_chat else sender_id sender_id = message.sender_chat.id if message.sender_chat else sender_id
if sender_id < 0 and message.outgoing: if sender_id < 0 and message.outgoing:
sender_id = bot.me.id sender_id = bot.me.id
bot.loop.create_task(mp.track(str(sender_id), f'Function {command}', {'command': command, "bot_id": bot.me.id})) bot.loop.create_task(
mp.track(
str(sender_id),
f"Function {command}",
{"command": command, "bot_id": bot.me.id},
)
)

View File

@ -33,50 +33,56 @@ def move_plugin(file_path):
def update_version(plugin_name, version): def update_version(plugin_name, version):
plugin_directory = f"{working_dir}{sep}plugins{sep}" plugin_directory = f"{working_dir}{sep}plugins{sep}"
with open(f"{plugin_directory}version.json", 'r', encoding="utf-8") as f: with open(f"{plugin_directory}version.json", "r", encoding="utf-8") as f:
version_json = json.load(f) version_json = json.load(f)
version_json[plugin_name] = version version_json[plugin_name] = version
with open(f"{plugin_directory}version.json", 'w') as f: with open(f"{plugin_directory}version.json", "w") as f:
json.dump(version_json, f) json.dump(version_json, f)
@listener(is_plugin=False, outgoing=True, command="apt", @listener(
is_plugin=False,
outgoing=True,
command="apt",
need_admin=True, need_admin=True,
diagnostics=False, diagnostics=False,
description=lang('apt_des'), description=lang("apt_des"),
parameters=lang('apt_parameters')) parameters=lang("apt_parameters"),
)
async def plugin(message: Message): async def plugin(message: Message):
if len(message.parameter) == 0: if len(message.parameter) == 0:
await message.edit(lang('arg_error')) await message.edit(lang("arg_error"))
return return
reply = message.reply_to_message reply = message.reply_to_message
plugin_directory = f"{working_dir}{sep}plugins{sep}" plugin_directory = f"{working_dir}{sep}plugins{sep}"
if message.parameter[0] == "install": if message.parameter[0] == "install":
if len(message.parameter) == 1: if len(message.parameter) == 1:
message = await message.edit(lang('apt_processing')) message = await message.edit(lang("apt_processing"))
file_path = None file_path = None
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
if reply: if reply:
file_path = await reply.download() file_path = await reply.download()
else: else:
file_path = await message.download() file_path = await message.download()
if file_path is None or not file_path.endswith('.py'): if file_path is None or not file_path.endswith(".py"):
await message.edit(lang('apt_no_py')) await message.edit(lang("apt_no_py"))
try: try:
remove(str(file_path)) remove(str(file_path))
except FileNotFoundError: except FileNotFoundError:
pass pass
return return
move_plugin(file_path) move_plugin(file_path)
await message.edit(f"<b>{lang('apt_name')}</b>\n\n" await message.edit(
f"<b>{lang('apt_name')}</b>\n\n"
f"{lang('apt_plugin')} " f"{lang('apt_plugin')} "
f"{path.basename(file_path)[:-3]} {lang('apt_installed')}") f"{path.basename(file_path)[:-3]} {lang('apt_installed')}"
)
await log(f"{lang('apt_install_success')} {path.basename(file_path)[:-3]}.") await log(f"{lang('apt_install_success')} {path.basename(file_path)[:-3]}.")
await reload_all() await reload_all()
elif len(message.parameter) >= 2: elif len(message.parameter) >= 2:
await plugin_manager.load_remote_plugins() await plugin_manager.load_remote_plugins()
process_list = message.parameter process_list = message.parameter
message = await message.edit(lang('apt_processing')) message = await message.edit(lang("apt_processing"))
del process_list[0] del process_list[0]
success_list = [] success_list = []
failed_list = [] failed_list = []
@ -98,30 +104,34 @@ async def plugin(message: Message):
failed_list.append(i) failed_list.append(i)
text = f"<b>{lang('apt_name')}</b>\n\n" text = f"<b>{lang('apt_name')}</b>\n\n"
if len(success_list) > 0: if len(success_list) > 0:
text += lang('apt_install_success') + " : %s\n" % ", ".join(success_list) text += lang("apt_install_success") + " : %s\n" % ", ".join(
success_list
)
if len(failed_list) > 0: if len(failed_list) > 0:
text += lang('apt_not_found') + " %s\n" % ", ".join(failed_list) text += lang("apt_not_found") + " %s\n" % ", ".join(failed_list)
if len(no_need_list) > 0: if len(no_need_list) > 0:
text += lang('apt_no_update') + " %s\n" % ", ".join(no_need_list) text += lang("apt_no_update") + " %s\n" % ", ".join(no_need_list)
await log(text) await log(text)
restart = len(success_list) > 0 restart = len(success_list) > 0
await message.edit(text) await message.edit(text)
if restart: if restart:
await reload_all() await reload_all()
else: else:
await message.edit(lang('arg_error')) await message.edit(lang("arg_error"))
elif message.parameter[0] == "remove": elif message.parameter[0] == "remove":
if len(message.parameter) == 2: if len(message.parameter) == 2:
if plugin_manager.remove_plugin(message.parameter[1]): if plugin_manager.remove_plugin(message.parameter[1]):
await message.edit(f"{lang('apt_remove_success')} {message.parameter[1]}") await message.edit(
f"{lang('apt_remove_success')} {message.parameter[1]}"
)
await log(f"{lang('apt_remove')} {message.parameter[1]}.") await log(f"{lang('apt_remove')} {message.parameter[1]}.")
await reload_all() await reload_all()
elif "/" in message.parameter[1]: elif "/" in message.parameter[1]:
await message.edit(lang('arg_error')) await message.edit(lang("arg_error"))
else: else:
await message.edit(lang('apt_not_exist')) await message.edit(lang("apt_not_exist"))
else: else:
await message.edit(lang('arg_error')) await message.edit(lang("arg_error"))
elif message.parameter[0] == "status": elif message.parameter[0] == "status":
if len(message.parameter) == 1: if len(message.parameter) == 1:
inactive_plugins = sorted(__list_plugins()) inactive_plugins = sorted(__list_plugins())
@ -151,35 +161,41 @@ async def plugin(message: Message):
inactive_plugins_string = f"`{lang('apt_no_load_failed_plugins')}`" inactive_plugins_string = f"`{lang('apt_no_load_failed_plugins')}`"
if len(disabled_plugins) == 0: if len(disabled_plugins) == 0:
disabled_plugins_string = f"`{lang('apt_no_disabled_plugins')}`" disabled_plugins_string = f"`{lang('apt_no_disabled_plugins')}`"
output = f"**{lang('apt_plugin_list')}**\n" \ output = (
f"{lang('apt_plugin_running')}: {active_plugins_string}\n" \ f"**{lang('apt_plugin_list')}**\n"
f"{lang('apt_plugin_disabled')}: {disabled_plugins_string}\n" \ f"{lang('apt_plugin_running')}: {active_plugins_string}\n"
f"{lang('apt_plugin_disabled')}: {disabled_plugins_string}\n"
f"{lang('apt_plugin_failed')}: {inactive_plugins_string}" f"{lang('apt_plugin_failed')}: {inactive_plugins_string}"
)
await message.edit(output) await message.edit(output)
else: else:
await message.edit(lang('arg_error')) await message.edit(lang("arg_error"))
elif message.parameter[0] == "enable": elif message.parameter[0] == "enable":
if len(message.parameter) == 2: if len(message.parameter) == 2:
if plugin_manager.enable_plugin(message.parameter[1]): if plugin_manager.enable_plugin(message.parameter[1]):
await message.edit(f"{lang('apt_plugin')} {message.parameter[1]} " await message.edit(
f"{lang('apt_enable')}") f"{lang('apt_plugin')} {message.parameter[1]} "
f"{lang('apt_enable')}"
)
await log(f"{lang('apt_enable')} {message.parameter[1]}.") await log(f"{lang('apt_enable')} {message.parameter[1]}.")
await reload_all() await reload_all()
else: else:
await message.edit(lang('apt_not_exist')) await message.edit(lang("apt_not_exist"))
else: else:
await message.edit(lang('arg_error')) await message.edit(lang("arg_error"))
elif message.parameter[0] == "disable": elif message.parameter[0] == "disable":
if len(message.parameter) == 2: if len(message.parameter) == 2:
if plugin_manager.disable_plugin(message.parameter[1]): if plugin_manager.disable_plugin(message.parameter[1]):
await message.edit(f"{lang('apt_plugin')} {message.parameter[1]} " await message.edit(
f"{lang('apt_disable')}") f"{lang('apt_plugin')} {message.parameter[1]} "
f"{lang('apt_disable')}"
)
await log(f"{lang('apt_disable')} {message.parameter[1]}.") await log(f"{lang('apt_disable')} {message.parameter[1]}.")
await reload_all() await reload_all()
else: else:
await message.edit(lang('apt_not_exist')) await message.edit(lang("apt_not_exist"))
else: else:
await message.edit(lang('arg_error')) await message.edit(lang("arg_error"))
elif message.parameter[0] == "upload": elif message.parameter[0] == "upload":
if len(message.parameter) == 2: if len(message.parameter) == 2:
file_name = f"{message.parameter[1]}.py" file_name = f"{message.parameter[1]}.py"
@ -191,31 +207,41 @@ async def plugin(message: Message):
elif exists(f"{plugin_directory}{file_name}.disabled"): elif exists(f"{plugin_directory}{file_name}.disabled"):
copyfile(f"{plugin_directory}{file_name}.disabled", file_name) copyfile(f"{plugin_directory}{file_name}.disabled", file_name)
if exists(file_name): if exists(file_name):
await message.edit(lang('apt_uploading')) await message.edit(lang("apt_uploading"))
await upload_attachment(file_name, await upload_attachment(
message.chat.id, reply_id, file_name,
message.chat.id,
reply_id,
thumb=f"pagermaid{sep}assets{sep}logo.jpg", thumb=f"pagermaid{sep}assets{sep}logo.jpg",
caption=f"<b>{lang('apt_name')}</b>\n\n" caption=f"<b>{lang('apt_name')}</b>\n\n"
f"PagerMaid-Pyro {message.parameter[1]} plugin.") f"PagerMaid-Pyro {message.parameter[1]} plugin.",
)
remove(file_name) remove(file_name)
await message.safe_delete() await message.safe_delete()
else: else:
await message.edit(lang('apt_not_exist')) await message.edit(lang("apt_not_exist"))
else: else:
await message.edit(lang('arg_error')) await message.edit(lang("arg_error"))
elif message.parameter[0] == "update": elif message.parameter[0] == "update":
if not exists(f"{plugin_directory}version.json"): if not exists(f"{plugin_directory}version.json"):
await message.edit(lang('apt_why_not_install_a_plugin')) await message.edit(lang("apt_why_not_install_a_plugin"))
return return
await plugin_manager.load_remote_plugins() await plugin_manager.load_remote_plugins()
updated_plugins = [i.name for i in await plugin_manager.update_all_remote_plugin() if i] updated_plugins = [
i.name for i in await plugin_manager.update_all_remote_plugin() if i
]
if len(updated_plugins) == 0: if len(updated_plugins) == 0:
await message.edit(f"<b>{lang('apt_name')}</b>\n\n" + await message.edit(
lang("apt_loading_from_online_but_nothing_need_to_update")) f"<b>{lang('apt_name')}</b>\n\n"
+ lang("apt_loading_from_online_but_nothing_need_to_update")
)
else: else:
message = await message.edit(lang("apt_loading_from_online_and_updating")) message = await message.edit(lang("apt_loading_from_online_and_updating"))
await message.edit( await message.edit(
f"<b>{lang('apt_name')}</b>\n\n" + lang("apt_reading_list") + "\n" + "".join(updated_plugins) f"<b>{lang('apt_name')}</b>\n\n"
+ lang("apt_reading_list")
+ "\n"
+ "".join(updated_plugins)
) )
await reload_all() await reload_all()
elif message.parameter[0] == "search": elif message.parameter[0] == "search":
@ -228,13 +254,25 @@ async def plugin(message: Message):
plugin_online = plugin_list.json()["list"] plugin_online = plugin_list.json()["list"]
for i in plugin_online: for i in plugin_online:
if search(plugin_name, i["name"], I): if search(plugin_name, i["name"], I):
search_result.extend(['`' + i['name'] + '` / `' + i['version'] + '`\n ' + i['des-short']]) search_result.extend(
[
"`"
+ i["name"]
+ "` / `"
+ i["version"]
+ "`\n "
+ i["des-short"]
]
)
if len(search_result) == 0: if len(search_result) == 0:
await message.edit(lang("apt_search_not_found")) await message.edit(lang("apt_search_not_found"))
else: else:
await message.edit(f"{lang('apt_search_result_hint')}:\n\n" + '\n\n'.join(search_result)) await message.edit(
f"{lang('apt_search_result_hint')}:\n\n"
+ "\n\n".join(search_result)
)
else: else:
await message.edit(lang('arg_error')) await message.edit(lang("arg_error"))
elif message.parameter[0] == "show": elif message.parameter[0] == "show":
if len(message.parameter) == 1: if len(message.parameter) == 1:
await message.edit(lang("apt_search_no_name")) await message.edit(lang("apt_search_no_name"))
@ -249,15 +287,17 @@ async def plugin(message: Message):
search_support = lang("apt_search_supporting") search_support = lang("apt_search_supporting")
else: else:
search_support = lang("apt_search_not_supporting") search_support = lang("apt_search_not_supporting")
search_result = f"{lang('apt_plugin_name')}:`{i['name']}`\n" \ search_result = (
f"{lang('apt_plugin_ver')}:`Ver {i['version']}`\n" \ f"{lang('apt_plugin_name')}:`{i['name']}`\n"
f"{lang('apt_plugin_section')}:`{i['section']}`\n" \ f"{lang('apt_plugin_ver')}:`Ver {i['version']}`\n"
f"{lang('apt_plugin_maintainer')}:`{i['maintainer']}`\n" \ f"{lang('apt_plugin_section')}:`{i['section']}`\n"
f"{lang('apt_plugin_size')}:`{i['size']}`\n" \ f"{lang('apt_plugin_maintainer')}:`{i['maintainer']}`\n"
f"{lang('apt_plugin_support')}:{search_support}\n" \ f"{lang('apt_plugin_size')}:`{i['size']}`\n"
f"{lang('apt_plugin_support')}:{search_support}\n"
f"{lang('apt_plugin_des_short')}:{i['des-short']}" f"{lang('apt_plugin_des_short')}:{i['des-short']}"
)
break break
if search_result == '': if search_result == "":
await message.edit(lang("apt_search_not_found")) await message.edit(lang("apt_search_not_found"))
else: else:
await message.edit(search_result) await message.edit(search_result)
@ -267,7 +307,7 @@ async def plugin(message: Message):
return return
message = await message.edit(lang("stats_loading")) message = await message.edit(lang("stats_loading"))
list_plugin = [] list_plugin = []
with open(f"{plugin_directory}version.json", 'r', encoding="utf-8") as f: with open(f"{plugin_directory}version.json", "r", encoding="utf-8") as f:
version_json = json.load(f) version_json = json.load(f)
plugin_list = await client.get(f"{Config.GIT_SOURCE}list.json") plugin_list = await client.get(f"{Config.GIT_SOURCE}list.json")
plugin_online = plugin_list.json()["list"] plugin_online = plugin_list.json()["list"]

View File

@ -10,20 +10,26 @@ from pagermaid.utils import lang
import contextlib import contextlib
@listener(is_plugin=False, outgoing=True, command="prune", @listener(
is_plugin=False,
outgoing=True,
command="prune",
need_admin=True, need_admin=True,
description=lang('prune_des')) description=lang("prune_des"),
)
async def prune(client: Client, message: Message): async def prune(client: Client, message: Message):
"""Purge every single message after the message you replied to.""" """Purge every single message after the message you replied to."""
if not message.reply_to_message: if not message.reply_to_message:
await message.edit(lang('not_reply')) await message.edit(lang("not_reply"))
return return
input_chat = message.chat.id input_chat = message.chat.id
messages = [] messages = []
count = 0 count = 0
limit = message.id - message.reply_to_message.id + 1 limit = message.id - message.reply_to_message.id + 1
if message.reply_to_top_message_id: if message.reply_to_top_message_id:
func = client.get_discussion_replies(input_chat, message.reply_to_top_message_id, limit=limit) func = client.get_discussion_replies(
input_chat, message.reply_to_top_message_id, limit=limit
)
else: else:
func = client.get_chat_history(input_chat, limit=limit) func = client.get_chat_history(input_chat, limit=limit)
async for msg in func: async for msg in func:
@ -45,10 +51,14 @@ async def prune(client: Client, message: Message):
await notification.delete() await notification.delete()
@listener(is_plugin=False, outgoing=True, command="selfprune", @listener(
is_plugin=False,
outgoing=True,
command="selfprune",
need_admin=True, need_admin=True,
description=lang('sp_des'), description=lang("sp_des"),
parameters=lang('sp_parameters')) parameters=lang("sp_parameters"),
)
async def self_prune(bot: Client, message: Message): async def self_prune(bot: Client, message: Message):
"""Deletes specific amount of messages you sent.""" """Deletes specific amount of messages you sent."""
msgs = [] msgs = []
@ -56,13 +66,13 @@ async def self_prune(bot: Client, message: Message):
offset = 0 offset = 0
if len(message.parameter) != 1: if len(message.parameter) != 1:
if not message.reply_to_message: if not message.reply_to_message:
return await message.edit(lang('arg_error')) return await message.edit(lang("arg_error"))
offset = message.reply_to_message.id offset = message.reply_to_message.id
try: try:
count = int(message.parameter[0]) count = int(message.parameter[0])
await message.delete() await message.delete()
except ValueError: except ValueError:
await message.edit(lang('arg_error')) await message.edit(lang("arg_error"))
return return
async for msg in bot.get_chat_history(message.chat.id, limit=100): async for msg in bot.get_chat_history(message.chat.id, limit=100):
if count_buffer == count: if count_buffer == count:
@ -73,7 +83,9 @@ async def self_prune(bot: Client, message: Message):
if len(msgs) == 100: if len(msgs) == 100:
await bot.delete_messages(message.chat.id, msgs) await bot.delete_messages(message.chat.id, msgs)
msgs = [] msgs = []
async for msg in bot.search_messages(message.chat.id, from_user="me", offset=offset): async for msg in bot.search_messages(
message.chat.id, from_user="me", offset=offset
):
if count_buffer == count: if count_buffer == count:
break break
msgs.append(msg.id) msgs.append(msg.id)
@ -93,25 +105,29 @@ async def self_prune(bot: Client, message: Message):
await notification.delete() await notification.delete()
@listener(is_plugin=False, outgoing=True, command="yourprune", @listener(
is_plugin=False,
outgoing=True,
command="yourprune",
need_admin=True, need_admin=True,
description=lang('yp_des'), description=lang("yp_des"),
parameters=lang('sp_parameters')) parameters=lang("sp_parameters"),
)
async def your_prune(bot: Client, message: Message): async def your_prune(bot: Client, message: Message):
"""Deletes specific amount of messages someone sent.""" """Deletes specific amount of messages someone sent."""
if not message.reply_to_message: if not message.reply_to_message:
return await message.edit(lang('not_reply')) return await message.edit(lang("not_reply"))
target = message.reply_to_message target = message.reply_to_message
if not target.from_user: if not target.from_user:
return await message.edit(lang('not_reply')) return await message.edit(lang("not_reply"))
if len(message.parameter) != 1: if len(message.parameter) != 1:
return await message.edit(lang('arg_error')) return await message.edit(lang("arg_error"))
count = 0 count = 0
try: try:
count = int(message.parameter[0]) count = int(message.parameter[0])
await message.delete() await message.delete()
except ValueError: except ValueError:
return await message.edit(lang('arg_error')) return await message.edit(lang("arg_error"))
except Exception: # noqa except Exception: # noqa
pass pass
count_buffer = 0 count_buffer = 0
@ -125,7 +141,9 @@ async def your_prune(bot: Client, message: Message):
if len(msgs) == 100: if len(msgs) == 100:
await bot.delete_messages(message.chat.id, msgs) await bot.delete_messages(message.chat.id, msgs)
msgs = [] msgs = []
async for msg in bot.search_messages(message.chat.id, from_user=target.from_user.id): async for msg in bot.search_messages(
message.chat.id, from_user=target.from_user.id
):
if count_buffer == count: if count_buffer == count:
break break
count_buffer += 1 count_buffer += 1
@ -145,16 +163,20 @@ async def your_prune(bot: Client, message: Message):
await notification.delete() await notification.delete()
@listener(is_plugin=False, outgoing=True, command="del", @listener(
is_plugin=False,
outgoing=True,
command="del",
need_admin=True, need_admin=True,
description=lang('del_des')) description=lang("del_des"),
)
async def delete(message: Message): async def delete(message: Message):
"""Deletes the message you replied to.""" """Deletes the message you replied to."""
if target := message.reply_to_message: if target := message.reply_to_message:
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
await target.delete() await target.delete()
await message.delete() await message.delete()
await log(lang('del_notification')) await log(lang("del_notification"))
else: else:
await message.delete() await message.delete()

View File

@ -6,9 +6,9 @@ from pagermaid.services import scheduler
from pagermaid.utils import lang from pagermaid.utils import lang
@listener(is_plugin=False, command="reload", @listener(
need_admin=True, is_plugin=False, command="reload", need_admin=True, description=lang("reload_des")
description=lang('reload_des')) )
async def reload_plugins(message: Message): async def reload_plugins(message: Message):
"""To reload plugins.""" """To reload plugins."""
await reload_all() await reload_all()

View File

@ -17,7 +17,7 @@ def sentry_before_send(event, hint):
exc_info = hint.get("exc_info") exc_info = hint.get("exc_info")
if exc_info and isinstance(exc_info[1], (Unauthorized, UsernameInvalid)): if exc_info and isinstance(exc_info[1], (Unauthorized, UsernameInvalid)):
# The user has been deleted/deactivated or session revoked # The user has been deleted/deactivated or session revoked
safe_remove('pagermaid.session') safe_remove("pagermaid.session")
exit(1) exit(1)
if time() <= sentry_sdk_report_time + 30: if time() <= sentry_sdk_report_time + 30:
sentry_sdk_report_time = time() sentry_sdk_report_time = time()
@ -28,7 +28,9 @@ def sentry_before_send(event, hint):
sentry_sdk_report_time = time() sentry_sdk_report_time = time()
sentry_sdk_git_hash = run("git rev-parse HEAD", stdout=PIPE, shell=True).stdout.decode().strip() sentry_sdk_git_hash = (
run("git rev-parse HEAD", stdout=PIPE, shell=True).stdout.decode().strip()
)
sentry_sdk.init( sentry_sdk.init(
Config.SENTRY_API, Config.SENTRY_API,
traces_sample_rate=1.0, traces_sample_rate=1.0,
@ -55,9 +57,14 @@ async def sentry_init_id(bot: Client):
async def sentry_report(message: Message, command, exc_info, **_): async def sentry_report(message: Message, command, exc_info, **_):
sender_id = message.from_user.id if message.from_user else "" sender_id = message.from_user.id if message.from_user else ""
sender_id = message.sender_chat.id if message.sender_chat else sender_id sender_id = message.sender_chat.id if message.sender_chat else sender_id
sentry_sdk.set_context("Target", {"ChatID": str(message.chat.id), sentry_sdk.set_context(
"Target",
{
"ChatID": str(message.chat.id),
"UserID": str(sender_id), "UserID": str(sender_id),
"Msg": message.text or ""}) "Msg": message.text or "",
},
)
if command: if command:
sentry_sdk.set_tag("com", command) sentry_sdk.set_tag("com", command)
sentry_sdk.capture_exception(exc_info) sentry_sdk.capture_exception(exc_info)

View File

@ -29,30 +29,29 @@ DCs = {
2: "149.154.167.51", 2: "149.154.167.51",
3: "149.154.175.100", 3: "149.154.175.100",
4: "149.154.167.91", 4: "149.154.167.91",
5: "91.108.56.130" 5: "91.108.56.130",
} }
@listener(is_plugin=False, command="sysinfo", @listener(is_plugin=False, command="sysinfo", description=lang("sysinfo_des"))
description=lang('sysinfo_des'))
async def sysinfo(message: Message): async def sysinfo(message: Message):
"""Retrieve system information via neofetch.""" """Retrieve system information via neofetch."""
if not Config.SILENT: if not Config.SILENT:
message = await message.edit(lang("sysinfo_loading")) message = await message.edit(lang("sysinfo_loading"))
if platform == 'win32': if platform == "win32":
return await message.edit(neofetch_win(), parse_mode=ParseMode.HTML) return await message.edit(neofetch_win(), parse_mode=ParseMode.HTML)
result = await execute("neofetch --config none --stdout") result = await execute("neofetch --config none --stdout")
await message.edit(f"`{result}`") await message.edit(f"`{result}`")
@listener(is_plugin=False, command="status", @listener(is_plugin=False, command="status", description=lang("status_des"))
description=lang('status_des'))
async def status(message: Message): async def status(message: Message):
# database # database
# database = lang('status_online') if redis_status() else lang('status_offline') # database = lang('status_online') if redis_status() else lang('status_offline')
# uptime https://gist.github.com/borgstrom/936ca741e885a1438c374824efb038b3 # uptime https://gist.github.com/borgstrom/936ca741e885a1438c374824efb038b3
uptime = await get_bot_uptime() uptime = await get_bot_uptime()
text = (f"**{lang('status_hint')}** \n" text = (
f"**{lang('status_hint')}** \n"
f"{lang('status_name')}: `{uname().node}` \n" f"{lang('status_name')}: `{uname().node}` \n"
f"{lang('status_platform')}: `{platform}` \n" f"{lang('status_platform')}: `{platform}` \n"
f"{lang('status_release')}: `{uname().release}` \n" f"{lang('status_release')}: `{uname().release}` \n"
@ -64,8 +63,7 @@ async def status(message: Message):
await message.edit(text) await message.edit(text)
@listener(is_plugin=False, command="stats", @listener(is_plugin=False, command="stats", description=lang("stats_des"))
description=lang("stats_des"))
async def stats(client: Client, message: Message): async def stats(client: Client, message: Message):
msg = await message.edit(lang("stats_loading")) msg = await message.edit(lang("stats_loading"))
a, u, g, s, c, b = 0, 0, 0, 0, 0, 0 a, u, g, s, c, b = 0, 0, 0, 0, 0, 0
@ -82,7 +80,8 @@ async def stats(client: Client, message: Message):
elif chat_type == ChatType.CHANNEL: elif chat_type == ChatType.CHANNEL:
c += 1 c += 1
a += 1 a += 1
text = (f"**{lang('stats_hint')}** \n" text = (
f"**{lang('stats_hint')}** \n"
f"{lang('stats_dialogs')}: `{a}` \n" f"{lang('stats_dialogs')}: `{a}` \n"
f"{lang('stats_private')}: `{u}` \n" f"{lang('stats_private')}: `{u}` \n"
f"{lang('stats_group')}: `{g}` \n" f"{lang('stats_group')}: `{g}` \n"
@ -93,8 +92,7 @@ async def stats(client: Client, message: Message):
await msg.edit(text) await msg.edit(text)
@listener(is_plugin=False, command="pingdc", @listener(is_plugin=False, command="pingdc", description=lang("pingdc_des"))
description=lang("pingdc_des"))
async def ping_dc(message: Message): async def ping_dc(message: Message):
"""Ping your or other data center's IP addresses.""" """Ping your or other data center's IP addresses."""
data = [] data = []
@ -107,7 +105,9 @@ async def ping_dc(message: Message):
else: else:
data.append("0") data.append("0")
else: else:
result = await execute(f"ping -c 1 {DCs[dc]} | awk -F '/' " + "'END {print $5}'") result = await execute(
f"ping -c 1 {DCs[dc]} | awk -F '/' " + "'END {print $5}'"
)
try: try:
data.append(str(float(result))) data.append(str(float(result)))
except ValueError: except ValueError:
@ -121,8 +121,7 @@ async def ping_dc(message: Message):
) )
@listener(is_plugin=False, command="ping", @listener(is_plugin=False, command="ping", description=lang("ping_des"))
description=lang("ping_des"))
async def ping(client: Client, message: Message): async def ping(client: Client, message: Message):
"""Calculates latency between PagerMaid and Telegram.""" """Calculates latency between PagerMaid and Telegram."""
start = datetime.now() start = datetime.now()
@ -179,13 +178,13 @@ def get_uptime():
return output return output
def readable(num, suffix='B'): def readable(num, suffix="B"):
"""Convert Bytes into human-readable formats""" """Convert Bytes into human-readable formats"""
for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']: for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
if abs(num) < 1024.0: if abs(num) < 1024.0:
return "%3.1f%s%s" % (num, unit, suffix) return "%3.1f%s%s" % (num, unit, suffix)
num /= 1024.0 num /= 1024.0
return "%.1f%s%s" % (num, 'Yi', suffix) return "%.1f%s%s" % (num, "Yi", suffix)
def get_ram(): def get_ram():
@ -208,7 +207,9 @@ def partitions():
try: try:
total, used, free = disk_usage(g.device) total, used, free = disk_usage(g.device)
percent_used = round(used / total * 100, 2) percent_used = round(used / total * 100, 2)
listparts.append(f" {g.device[:2]} {readable(used)} / {readable(total)} ({percent_used}%)") listparts.append(
f" {g.device[:2]} {readable(used)} / {readable(total)} ({percent_used}%)"
)
except PermissionError: except PermissionError:
continue continue
@ -228,11 +229,11 @@ def neofetch_win():
mboard = "Unknown..." mboard = "Unknown..."
cpu = wmic("wmic cpu get name")[-1] cpu = wmic("wmic cpu get name")[-1]
gpu = wmic("wmic path win32_VideoController get name") gpu = wmic("wmic path win32_VideoController get name")
gpu = [f' {g.strip()}' for g in gpu[1:]][0].strip() gpu = [f" {g.strip()}" for g in gpu[1:]][0].strip()
ram = get_ram() ram = get_ram()
disks = '\n'.join(partitions()) disks = "\n".join(partitions())
return ( return (
f'<code>{user_name}@{host_name}\n---------\nOS: {os}\nUptime: {uptime}\n' f"<code>{user_name}@{host_name}\n---------\nOS: {os}\nUptime: {uptime}\n"
f'Motherboard: {mboard}\nCPU: {cpu}\nGPU: {gpu}\nMemory: {ram}\n' f"Motherboard: {mboard}\nCPU: {cpu}\nGPU: {gpu}\nMemory: {ram}\n"
f'Disk:\n{disks}</code>' f"Disk:\n{disks}</code>"
) )

View File

@ -1,8 +1,15 @@
from pagermaid.single_utils import sqlite from pagermaid.single_utils import sqlite
from pagermaid.listener import listener from pagermaid.listener import listener
from pagermaid.group_manager import add_permission_for_group, Permission, remove_permission_for_group, \ from pagermaid.group_manager import (
add_user_to_group, remove_user_from_group, add_permission_for_user, remove_permission_for_user, \ add_permission_for_group,
permissions Permission,
remove_permission_for_group,
add_user_to_group,
remove_user_from_group,
add_permission_for_user,
remove_permission_for_user,
permissions,
)
from pagermaid.enums import Client, Message from pagermaid.enums import Client, Message
from pagermaid.utils import lang, edit_delete, _status_sudo from pagermaid.utils import lang, edit_delete, _status_sudo
from pagermaid.single_utils import get_sudo_list from pagermaid.single_utils import get_sudo_list
@ -15,17 +22,20 @@ def from_msg_get_sudo_id(message: Message) -> int:
return message.chat.id return message.chat.id
@listener(is_plugin=False, command="sudo", @listener(
is_plugin=False,
command="sudo",
need_admin=True, need_admin=True,
parameters="{on|off|add|remove|gaddp|gaddu|gdelp|gdelu|glist|uaddp|udelp|list}", parameters="{on|off|add|remove|gaddp|gaddu|gdelp|gdelu|glist|uaddp|udelp|list}",
description=lang('sudo_des')) description=lang("sudo_des"),
)
async def sudo_change(client: Client, message: Message): async def sudo_change(client: Client, message: Message):
"""To enable or disable sudo of your userbot.""" """To enable or disable sudo of your userbot."""
input_str = message.arguments input_str = message.arguments
sudo = get_sudo_list() sudo = get_sudo_list()
if input_str == "on": if input_str == "on":
if _status_sudo(): if _status_sudo():
return await edit_delete(message, lang('sudo_has_enabled')) return await edit_delete(message, lang("sudo_has_enabled"))
sqlite["sudo_enable"] = True sqlite["sudo_enable"] = True
text = f"__{lang('sudo_enable')}__\n" text = f"__{lang('sudo_enable')}__\n"
if len(sudo) != 0: if len(sudo) != 0:
@ -48,7 +58,7 @@ async def sudo_change(client: Client, message: Message):
return await message.edit( return await message.edit(
text, text,
) )
await edit_delete(message, lang('sudo_has_disabled')) await edit_delete(message, lang("sudo_has_disabled"))
elif input_str == "add": elif input_str == "add":
from_id = from_msg_get_sudo_id(message) from_id = from_msg_get_sudo_id(message)
if from_id in sudo: if from_id in sudo:
@ -85,7 +95,9 @@ async def sudo_change(client: Client, message: Message):
for j in permissions.get_permissions_for_user(str(i)): for j in permissions.get_permissions_for_user(str(i)):
text += f"{'-' if j[2] == 'ejection' else ''}{j[1]}\n" text += f"{'-' if j[2] == 'ejection' else ''}{j[1]}\n"
except Exception: except Exception:
text += f"• `{i}` - {' '.join(permissions.get_roles_for_user(str(i)))}\n" text += (
f"• `{i}` - {' '.join(permissions.get_roles_for_user(str(i)))}\n"
)
await message.edit(text) await message.edit(text)
elif len(message.parameter) > 0: elif len(message.parameter) > 0:
if len(message.parameter) == 2: if len(message.parameter) == 2:
@ -113,20 +125,26 @@ async def sudo_change(client: Client, message: Message):
add_permission_for_user(str(from_id), Permission(message.parameter[1])) add_permission_for_user(str(from_id), Permission(message.parameter[1]))
return await message.edit(lang("sudo_user_add_per")) return await message.edit(lang("sudo_user_add_per"))
elif message.parameter[0] == "udelp": elif message.parameter[0] == "udelp":
remove_permission_for_user(str(from_id), Permission(message.parameter[1])) remove_permission_for_user(
str(from_id), Permission(message.parameter[1])
)
return await message.edit(lang("sudo_user_del_per")) return await message.edit(lang("sudo_user_del_per"))
else: else:
return await edit_delete(message, lang('arg_error')) return await edit_delete(message, lang("arg_error"))
if len(message.parameter) == 3: if len(message.parameter) == 3:
if message.parameter[0] == "gaddp": if message.parameter[0] == "gaddp":
add_permission_for_group(message.parameter[1], Permission(message.parameter[2])) add_permission_for_group(
message.parameter[1], Permission(message.parameter[2])
)
return await message.edit(lang("sudo_group_add_per")) return await message.edit(lang("sudo_group_add_per"))
elif message.parameter[0] == "gdelp": elif message.parameter[0] == "gdelp":
remove_permission_for_group(message.parameter[1], Permission(message.parameter[2])) remove_permission_for_group(
message.parameter[1], Permission(message.parameter[2])
)
return await message.edit(lang("sudo_group_del_per")) return await message.edit(lang("sudo_group_del_per"))
else: else:
return await edit_delete(message, lang('arg_error')) return await edit_delete(message, lang("arg_error"))
else: else:
await edit_delete(message, lang('arg_error')) await edit_delete(message, lang("arg_error"))
else: else:
await edit_delete(message, lang('arg_error')) await edit_delete(message, lang("arg_error"))

View File

@ -11,10 +11,13 @@ from pagermaid.enums import Message
from pagermaid.utils import attach_log, execute, lang, upload_attachment from pagermaid.utils import attach_log, execute, lang, upload_attachment
@listener(is_plugin=False, command="sh", @listener(
is_plugin=False,
command="sh",
need_admin=True, need_admin=True,
description=lang('sh_des'), description=lang("sh_des"),
parameters=lang('sh_parameters')) parameters=lang("sh_parameters"),
)
async def sh(message: Message): async def sh(message: Message):
"""Use the command-line from Telegram.""" """Use the command-line from Telegram."""
user = getuser() user = getuser()
@ -22,13 +25,10 @@ async def sh(message: Message):
hostname = node() hostname = node()
if not command: if not command:
await message.edit(lang('arg_error')) await message.edit(lang("arg_error"))
return return
message = await message.edit( message = await message.edit(f"`{user}`@{hostname} ~" f"\n> `$` {command}")
f"`{user}`@{hostname} ~"
f"\n> `$` {command}"
)
result = await execute(command) result = await execute(command)
@ -38,28 +38,29 @@ async def sh(message: Message):
return return
await message.edit( await message.edit(
f"`{user}`@{hostname} ~" f"`{user}`@{hostname} ~" f"\n> `#` {command}" f"\n`{result}`"
f"\n> `#` {command}"
f"\n`{result}`"
) )
else: else:
return return
@listener(is_plugin=False, command="restart", @listener(
need_admin=True, is_plugin=False, command="restart", need_admin=True, description=lang("restart_des")
description=lang('restart_des')) )
async def restart(message: Message): async def restart(message: Message):
"""To re-execute PagerMaid.""" """To re-execute PagerMaid."""
if not message.text[0].isalpha(): if not message.text[0].isalpha():
await message.edit(lang('restart_log')) await message.edit(lang("restart_log"))
exit(0) exit(0)
@listener(is_plugin=False, command="eval", @listener(
is_plugin=False,
command="eval",
need_admin=True, need_admin=True,
description=lang('eval_des'), description=lang("eval_des"),
parameters=lang('eval_parameters')) parameters=lang("eval_parameters"),
)
async def sh_eval(message: Message): async def sh_eval(message: Message):
"""Run python commands from Telegram.""" """Run python commands from Telegram."""
dev_mode = exists(f"data{sep}dev") dev_mode = exists(f"data{sep}dev")
@ -67,7 +68,7 @@ async def sh_eval(message: Message):
assert dev_mode assert dev_mode
cmd = message.text.split(" ", maxsplit=1)[1] cmd = message.text.split(" ", maxsplit=1)[1]
except (IndexError, AssertionError): except (IndexError, AssertionError):
return await message.edit(lang('eval_need_dev')) return await message.edit(lang("eval_need_dev"))
final_output = await run_eval(cmd, message) final_output = await run_eval(cmd, message)
if len(final_output) > 4096: if len(final_output) > 4096:
message = await message.edit(f"**>>>** `{cmd}`", parse_mode=ParseMode.MARKDOWN) message = await message.edit(f"**>>>** `{cmd}`", parse_mode=ParseMode.MARKDOWN)
@ -76,16 +77,21 @@ async def sh_eval(message: Message):
await message.edit(final_output) await message.edit(final_output)
@listener(is_plugin=False, command="send_log", @listener(
is_plugin=False,
command="send_log",
need_admin=True, need_admin=True,
description=lang("send_log_des")) description=lang("send_log_des"),
)
async def send_log(message: Message): async def send_log(message: Message):
"""Send log to a chat.""" """Send log to a chat."""
if not exists("pagermaid.log.txt"): if not exists("pagermaid.log.txt"):
return await message.edit(lang("send_log_not_found")) return await message.edit(lang("send_log_not_found"))
await upload_attachment("pagermaid.log.txt", await upload_attachment(
"pagermaid.log.txt",
message.chat.id, message.chat.id,
message.reply_to_message_id or message.reply_to_top_message_id, message.reply_to_message_id or message.reply_to_top_message_id,
thumb=f"pagermaid{sep}assets{sep}logo.jpg", thumb=f"pagermaid{sep}assets{sep}logo.jpg",
caption=lang("send_log_caption")) caption=lang("send_log_caption"),
)
await message.safe_delete() await message.safe_delete()

View File

@ -5,11 +5,15 @@ from pagermaid.listener import listener
from pagermaid.utils import lang, Message, alias_command from pagermaid.utils import lang, Message, alias_command
@listener(is_plugin=False, outgoing=True, command=alias_command("update"), @listener(
is_plugin=False,
outgoing=True,
command=alias_command("update"),
need_admin=True, need_admin=True,
description=lang('update_des'), description=lang("update_des"),
parameters="<true/debug>") parameters="<true/debug>",
)
async def update(message: Message): async def update(message: Message):
await update_function(len(message.parameter) > 0) await update_function(len(message.parameter) > 0)
await message.edit(lang('update_success')) await message.edit(lang("update_success"))
exit(0) exit(0)

View File

@ -14,5 +14,7 @@ async def init_web():
from pagermaid.web import app, init_web from pagermaid.web import app, init_web
init_web() init_web()
server = uvicorn.Server(config=uvicorn.Config(app, host=Config.WEB_HOST, port=Config.WEB_PORT)) server = uvicorn.Server(
config=uvicorn.Config(app, host=Config.WEB_HOST, port=Config.WEB_PORT)
)
bot.loop.create_task(server.serve()) bot.loop.create_task(server.serve())

View File

@ -19,9 +19,12 @@ async def delete_message(message: Message) -> bool:
def add_delete_message_job(message: Message, delete_seconds: int = 60): def add_delete_message_job(message: Message, delete_seconds: int = 60):
scheduler.add_job( scheduler.add_job(
delete_message, "date", delete_message,
"date",
id=f"{message.chat.id}|{message.id}|delete_message", id=f"{message.chat.id}|{message.id}|delete_message",
name=f"{message.chat.id}|{message.id}|delete_message", name=f"{message.chat.id}|{message.id}|delete_message",
args=[message], args=[message],
run_date=datetime.datetime.now(pytz.timezone(Config.TIME_ZONE)) + datetime.timedelta(seconds=delete_seconds), run_date=datetime.datetime.now(pytz.timezone(Config.TIME_ZONE))
replace_existing=True) + datetime.timedelta(seconds=delete_seconds),
replace_existing=True,
)

View File

@ -6,5 +6,11 @@ from pagermaid.utils import client
def get(name: str): def get(name: str):
data = {"Client": bot, "Logger": logs, "SqliteDict": sqlite, "AsyncIOScheduler": scheduler, "AsyncClient": client} data = {
"Client": bot,
"Logger": logs,
"SqliteDict": sqlite,
"AsyncIOScheduler": scheduler,
"AsyncClient": client,
}
return data.get(name, None) return data.get(name, None)

View File

@ -9,7 +9,11 @@ from pyrogram import Client as OldClient
from pyrogram.types import Chat as OldChat, Message as OldMessage, Dialog from pyrogram.types import Chat as OldChat, Message as OldMessage, Dialog
from pyromod.utils.conversation import Conversation from pyromod.utils.conversation import Conversation
from pyromod.utils.errors import AlreadyInConversationError, TimeoutConversationError, ListenerCanceled from pyromod.utils.errors import (
AlreadyInConversationError,
TimeoutConversationError,
ListenerCanceled,
)
from sqlitedict import SqliteDict from sqlitedict import SqliteDict
@ -57,7 +61,9 @@ class Client(OldClient):
async def listen(self, chat_id, filters=None, timeout=None) -> Optional[Message]: async def listen(self, chat_id, filters=None, timeout=None) -> Optional[Message]:
"""Listen for a message in a conversation.""" """Listen for a message in a conversation."""
async def ask(self, chat_id, text, filters=None, timeout=None, *args, **kwargs) -> Optional[Message]: async def ask(
self, chat_id, text, filters=None, timeout=None, *args, **kwargs
) -> Optional[Message]:
"""Ask a message in a conversation.""" """Ask a message in a conversation."""
def cancel_listener(self, chat_id): def cancel_listener(self, chat_id):
@ -66,8 +72,9 @@ class Client(OldClient):
def cancel_all_listeners(self): def cancel_all_listeners(self):
"""Cancel all conversations.""" """Cancel all conversations."""
def conversation(self, chat_id: Union[int, str], def conversation(
once_timeout: int = 60, filters=None) -> Optional[Conversation]: self, chat_id: Union[int, str], once_timeout: int = 60, filters=None
) -> Optional[Conversation]:
"""Initialize a conversation with the given chat_id.""" """Initialize a conversation with the given chat_id."""
async def get_dialogs_list(self) -> List[Dialog]: async def get_dialogs_list(self) -> List[Dialog]:
@ -80,7 +87,9 @@ class Chat(OldChat):
async def listen(self, chat_id, filters=None, timeout=None) -> Optional[Message]: async def listen(self, chat_id, filters=None, timeout=None) -> Optional[Message]:
"""Listen for a message in a conversation.""" """Listen for a message in a conversation."""
async def ask(self, chat_id, text, filters=None, timeout=None, *args, **kwargs) -> Optional[Message]: async def ask(
self, chat_id, text, filters=None, timeout=None, *args, **kwargs
) -> Optional[Message]:
"""Ask a message in a conversation.""" """Ask a message in a conversation."""
def cancel_listener(self, chat_id): def cancel_listener(self, chat_id):

View File

@ -36,7 +36,7 @@ async def attach_report(plaintext, file_name, reply_id=None, caption=None):
"PagerMaid_Modify_bot", "PagerMaid_Modify_bot",
file_name, file_name,
reply_to_message_id=reply_id, reply_to_message_id=reply_id,
caption=caption caption=caption,
) )
except Exception: # noqa except Exception: # noqa
return return
@ -45,13 +45,10 @@ async def attach_report(plaintext, file_name, reply_id=None, caption=None):
async def attach_log(plaintext, chat_id, file_name, reply_id=None, caption=None): async def attach_log(plaintext, chat_id, file_name, reply_id=None, caption=None):
"""Attach plaintext as logs.""" """Attach plaintext as logs."""
with open(file_name, "w+", encoding='utf-8') as file: with open(file_name, "w+", encoding="utf-8") as file:
file.write(plaintext) file.write(plaintext)
await bot.send_document( await bot.send_document(
chat_id, chat_id, file_name, reply_to_message_id=reply_id, caption=caption
file_name,
reply_to_message_id=reply_id,
caption=caption
) )
remove(file_name) remove(file_name)
@ -66,7 +63,7 @@ async def upload_attachment(file_path, chat_id, reply_id, caption=None, thumb=No
file_path, file_path,
thumb=thumb, thumb=thumb,
reply_to_message_id=reply_id, reply_to_message_id=reply_id,
caption=caption caption=caption,
) )
except BaseException as exception: except BaseException as exception:
raise exception raise exception
@ -76,29 +73,28 @@ async def upload_attachment(file_path, chat_id, reply_id, caption=None, thumb=No
async def execute(command, pass_error=True): async def execute(command, pass_error=True):
"""Executes command and returns output, with the option of enabling stderr.""" """Executes command and returns output, with the option of enabling stderr."""
executor = await create_subprocess_shell( executor = await create_subprocess_shell(
command, command, stdout=PIPE, stderr=PIPE, stdin=PIPE
stdout=PIPE,
stderr=PIPE,
stdin=PIPE
) )
stdout, stderr = await executor.communicate() stdout, stderr = await executor.communicate()
if pass_error: if pass_error:
try: try:
result = str(stdout.decode().strip()) \ result = str(stdout.decode().strip()) + str(stderr.decode().strip())
+ str(stderr.decode().strip())
except UnicodeDecodeError: except UnicodeDecodeError:
result = str(stdout.decode('gbk').strip()) \ result = str(stdout.decode("gbk").strip()) + str(
+ str(stderr.decode('gbk').strip()) stderr.decode("gbk").strip()
)
else: else:
try: try:
result = str(stdout.decode().strip()) result = str(stdout.decode().strip())
except UnicodeDecodeError: except UnicodeDecodeError:
result = str(stdout.decode('gbk').strip()) result = str(stdout.decode("gbk").strip())
return result return result
def pip_install(package: str, version: Optional[str] = "", alias: Optional[str] = "") -> bool: def pip_install(
package: str, version: Optional[str] = "", alias: Optional[str] = ""
) -> bool:
"""Auto install extra pypi packages""" """Auto install extra pypi packages"""
if not alias: if not alias:
# when import name is not provided, use package name # when import name is not provided, use package name
@ -110,25 +106,35 @@ def pip_install(package: str, version: Optional[str] = "", alias: Optional[str]
return True return True
async def edit_delete(message: Message, async def edit_delete(
message: Message,
text: str, text: str,
time: int = 5, time: int = 5,
parse_mode: Optional["enums.ParseMode"] = None, parse_mode: Optional["enums.ParseMode"] = None,
disable_web_page_preview: bool = None): disable_web_page_preview: bool = None,
):
sudo_users = get_sudo_list() sudo_users = get_sudo_list()
from_id = message.from_user.id if message.from_user else message.sender_chat.id from_id = message.from_user.id if message.from_user else message.sender_chat.id
if from_id in sudo_users: if from_id in sudo_users:
reply_to = message.reply_to_message reply_to = message.reply_to_message
event = ( event = (
await reply_to.reply(text, disable_web_page_preview=disable_web_page_preview, parse_mode=parse_mode) await reply_to.reply(
text,
disable_web_page_preview=disable_web_page_preview,
parse_mode=parse_mode,
)
if reply_to if reply_to
else await message.reply( else await message.reply(
text, disable_web_page_preview=disable_web_page_preview, parse_mode=parse_mode text,
disable_web_page_preview=disable_web_page_preview,
parse_mode=parse_mode,
) )
) )
else: else:
event = await message.edit( event = await message.edit(
text, disable_web_page_preview=disable_web_page_preview, parse_mode=parse_mode text,
disable_web_page_preview=disable_web_page_preview,
parse_mode=parse_mode,
) )
await sleep(time) await sleep(time)
return await event.delete() return await event.delete()
@ -147,7 +153,9 @@ def sudo_filter(permission: str):
if not _status_sudo(): if not _status_sudo():
return False return False
try: try:
from_id = message.from_user.id if message.from_user else message.sender_chat.id from_id = (
message.from_user.id if message.from_user else message.sender_chat.id
)
sudo_list = get_sudo_list() sudo_list = get_sudo_list()
if from_id not in sudo_list: if from_id not in sudo_list:
if message.chat.id in sudo_list: if message.chat.id in sudo_list:
@ -173,7 +181,9 @@ def from_msg_get_sudo_uid(message: Message) -> int:
def check_manage_subs(message: Message) -> bool: def check_manage_subs(message: Message) -> bool:
return from_self(message) or enforce_permission(from_msg_get_sudo_uid(message), "modules.manage_subs") return from_self(message) or enforce_permission(
from_msg_get_sudo_uid(message), "modules.manage_subs"
)
async def process_exit(start: int, _client, message=None): async def process_exit(start: int, _client, message=None):
@ -184,8 +194,13 @@ async def process_exit(start: int, _client, message=None):
msg: Message = await _client.get_messages(cid, mid) msg: Message = await _client.get_messages(cid, mid)
if msg: if msg:
await msg.edit( await msg.edit(
((msg.text or msg.caption) if msg.from_user.is_self and (msg.text or msg.caption) else "") + (
f'\n\n> {lang("restart_complete")}') (msg.text or msg.caption)
if msg.from_user.is_self and (msg.text or msg.caption)
else ""
)
+ f'\n\n> {lang("restart_complete")}'
)
del sqlite["exit_msg"] del sqlite["exit_msg"]
if message: if message:
sqlite["exit_msg"] = {"cid": message.chat.id, "mid": message.id} sqlite["exit_msg"] = {"cid": message.chat.id, "mid": message.id}

View File

@ -7,13 +7,13 @@ from pagermaid.config import Config
from pagermaid.web.api import base_api_router from pagermaid.web.api import base_api_router
from pagermaid.web.pages import admin_app, login_page from pagermaid.web.pages import admin_app, login_page
requestAdaptor = ''' requestAdaptor = """
requestAdaptor(api) { requestAdaptor(api) {
api.headers["token"] = localStorage.getItem("token"); api.headers["token"] = localStorage.getItem("token");
return api; return api;
}, },
''' """
responseAdaptor = ''' responseAdaptor = """
responseAdaptor(api, payload, query, request, response) { responseAdaptor(api, payload, query, request, response) {
if (response.data.detail == '登录验证失败或已失效,请重新登录') { if (response.data.detail == '登录验证失败或已失效,请重新登录') {
window.location.href = '/login' window.location.href = '/login'
@ -23,8 +23,8 @@ responseAdaptor(api, payload, query, request, response) {
} }
return payload return payload
}, },
''' """
icon_path = 'https://xtaolabs.com/pagermaid-logo.png' icon_path = "https://xtaolabs.com/pagermaid-logo.png"
app: FastAPI = FastAPI() app: FastAPI = FastAPI()
@ -36,25 +36,25 @@ def init_web():
allow_origins=Config.WEB_ORIGINS, allow_origins=Config.WEB_ORIGINS,
allow_credentials=True, allow_credentials=True,
allow_methods=["*"], allow_methods=["*"],
allow_headers=["*"] allow_headers=["*"],
) )
@app.get('/', response_class=RedirectResponse) @app.get("/", response_class=RedirectResponse)
async def index(): async def index():
return '/admin' return "/admin"
@app.get('/admin', response_class=HTMLResponse) @app.get("/admin", response_class=HTMLResponse)
async def admin(): async def admin():
return admin_app.render( return admin_app.render(
site_title='PagerMaid-Pyro 后台管理', site_title="PagerMaid-Pyro 后台管理",
site_icon=icon_path, site_icon=icon_path,
requestAdaptor=requestAdaptor, requestAdaptor=requestAdaptor,
responseAdaptor=responseAdaptor responseAdaptor=responseAdaptor,
) )
@app.get('/login', response_class=HTMLResponse) @app.get("/login", response_class=HTMLResponse)
async def login(): async def login():
return login_page.render( return login_page.render(
site_title='登录 | PagerMaid-Pyro 后台管理', site_title="登录 | PagerMaid-Pyro 后台管理",
site_icon=icon_path, site_icon=icon_path,
) )

View File

@ -8,7 +8,7 @@ from pagermaid.web.api.login import route as login_route
from pagermaid.web.api.plugin import route as plugin_route from pagermaid.web.api.plugin import route as plugin_route
from pagermaid.web.api.status import route as status_route from pagermaid.web.api.status import route as status_route
base_api_router = APIRouter(prefix='/pagermaid/api') base_api_router = APIRouter(prefix="/pagermaid/api")
base_api_router.include_router(plugin_route) base_api_router.include_router(plugin_route)
base_api_router.include_router(bot_info_route) base_api_router.include_router(bot_info_route)

View File

@ -10,16 +10,15 @@ from pagermaid.common.update import update
route = APIRouter() route = APIRouter()
@route.post('/bot_update', response_class=JSONResponse, dependencies=[authentication()]) @route.post("/bot_update", response_class=JSONResponse, dependencies=[authentication()])
async def bot_update(): async def bot_update():
await update() await update()
return { return {"status": 0, "msg": "更新成功,请重启 PagerMaid-Pyro 以应用更新。"}
"status": 0,
"msg": "更新成功,请重启 PagerMaid-Pyro 以应用更新。"
}
@route.post('/bot_restart', response_class=JSONResponse, dependencies=[authentication()]) @route.post(
"/bot_restart", response_class=JSONResponse, dependencies=[authentication()]
)
async def bot_restart(): async def bot_restart():
os.kill(os.getpid(), signal.SIGINT) os.kill(os.getpid(), signal.SIGINT)
return {} return {}

View File

@ -7,41 +7,41 @@ from pagermaid.web.api.utils import authentication
route = APIRouter() route = APIRouter()
@route.get('/command_alias', response_class=JSONResponse, dependencies=[authentication()]) @route.get(
"/command_alias", response_class=JSONResponse, dependencies=[authentication()]
)
async def get_command_alias(): async def get_command_alias():
alias = AliasManager() alias = AliasManager()
return { return {
'status': 0, "status": 0,
'msg': 'ok', "msg": "ok",
'data': { "data": {
'items': alias.get_all_alias_dict(), "items": alias.get_all_alias_dict(),
} },
} }
@route.post('/command_alias', response_class=JSONResponse, dependencies=[authentication()]) @route.post(
"/command_alias", response_class=JSONResponse, dependencies=[authentication()]
)
async def add_command_alias(data: dict): async def add_command_alias(data: dict):
data = data['items'] data = data["items"]
try: try:
await AliasManager.save_from_web(data) await AliasManager.save_from_web(data)
return { return {"status": 0, "msg": "命令别名保存成功"}
'status': 0,
'msg': '命令别名保存成功'
}
except Exception: except Exception:
return { return {"status": 1, "msg": "命令别名保存失败"}
'status': 1,
'msg': '命令别名保存失败'
}
@route.get('/test_command_alias', response_class=JSONResponse, dependencies=[authentication()]) @route.get(
"/test_command_alias", response_class=JSONResponse, dependencies=[authentication()]
)
async def test_command_alias(message: str): async def test_command_alias(message: str):
alias = AliasManager() alias = AliasManager()
return { return {
'status': 0, "status": 0,
'msg': '测试成功', "msg": "测试成功",
'data': { "data": {
'new_msg': alias.test_alias(message), "new_msg": alias.test_alias(message),
} },
} }

View File

@ -7,39 +7,40 @@ from pagermaid.web.api import authentication
route = APIRouter() route = APIRouter()
@route.get("/get_ignore_group_list", response_class=JSONResponse, dependencies=[authentication()]) @route.get(
"/get_ignore_group_list",
response_class=JSONResponse,
dependencies=[authentication()],
)
async def get_ignore_group_list(): async def get_ignore_group_list():
try: try:
groups = [] groups = []
for data in await get_group_list(): for data in await get_group_list():
data["status"] = ignore_groups_manager.check_id(data["id"]) data["status"] = ignore_groups_manager.check_id(data["id"])
groups.append(data) groups.append(data)
return { return {"status": 0, "msg": "ok", "data": {"groups": groups}}
'status': 0,
'msg': 'ok',
'data': {
'groups': groups
}
}
except BaseException: except BaseException:
return { return {"status": -100, "msg": "获取群组列表失败"}
'status': -100,
'msg': '获取群组列表失败'
}
@route.post('/set_ignore_group_status', response_class=JSONResponse, dependencies=[authentication()]) @route.post(
"/set_ignore_group_status",
response_class=JSONResponse,
dependencies=[authentication()],
)
async def set_ignore_group_status(data: dict): async def set_ignore_group_status(data: dict):
cid: int = data.get('id') cid: int = data.get("id")
status: bool = data.get('status') status: bool = data.get("status")
if status: if status:
ignore_groups_manager.add_id(cid) ignore_groups_manager.add_id(cid)
else: else:
ignore_groups_manager.del_id(cid) ignore_groups_manager.del_id(cid)
return {'status': 0, 'msg': f'成功{"忽略" if status else "取消忽略"} {cid}'} return {"status": 0, "msg": f'成功{"忽略" if status else "取消忽略"} {cid}'}
@route.post('/clear_ignore_group', response_class=JSONResponse, dependencies=[authentication()]) @route.post(
"/clear_ignore_group", response_class=JSONResponse, dependencies=[authentication()]
)
async def clear_ignore_group(): async def clear_ignore_group():
ignore_groups_manager.clear_subs() ignore_groups_manager.clear_subs()
return {'status': 0, 'msg': '成功清空忽略列表'} return {"status": 0, "msg": "成功清空忽略列表"}

View File

@ -14,19 +14,13 @@ class UserModel(BaseModel):
route = APIRouter() route = APIRouter()
@route.post('/login', response_class=JSONResponse) @route.post("/login", response_class=JSONResponse)
async def login(user: UserModel): async def login(user: UserModel):
if user.password != Config.WEB_SECRET_KEY: if user.password != Config.WEB_SECRET_KEY:
return { return {"status": -100, "msg": "登录失败,请重新输入密钥"}
"status": -100,
"msg": "登录失败,请重新输入密钥"
}
token = create_token() token = create_token()
return { return {
"status": 0, "status": 0,
"msg": "登录成功", "msg": "登录成功",
"data": { "data": {"version": pgm_version_code, "token": token},
"version": pgm_version_code,
"token": token
}
} }

View File

@ -8,68 +8,68 @@ from pagermaid.web.api.utils import authentication
route = APIRouter() route = APIRouter()
@route.get('/get_local_plugins', response_class=JSONResponse, dependencies=[authentication()]) @route.get(
"/get_local_plugins", response_class=JSONResponse, dependencies=[authentication()]
)
async def get_local_plugins(): async def get_local_plugins():
plugins = [i.dict() for i in plugin_manager.plugins] plugins = [i.dict() for i in plugin_manager.plugins]
plugins.sort(key=lambda x: x['name']) plugins.sort(key=lambda x: x["name"])
return { return {"status": 0, "msg": "ok", "data": {"rows": plugins, "total": len(plugins)}}
'status': 0,
'msg': 'ok',
'data': {
'rows': plugins,
'total': len(plugins)
}
}
@route.post('/set_local_plugin_status', response_class=JSONResponse, dependencies=[authentication()]) @route.post(
"/set_local_plugin_status",
response_class=JSONResponse,
dependencies=[authentication()],
)
async def set_local_plugin_status(data: dict): async def set_local_plugin_status(data: dict):
module_name: str = data.get('plugin') module_name: str = data.get("plugin")
status: bool = data.get('status') status: bool = data.get("status")
if not (plugin := plugin_manager.get_local_plugin(module_name)): if not (plugin := plugin_manager.get_local_plugin(module_name)):
return {'status': -100, 'msg': f'插件 {module_name} 不存在'} return {"status": -100, "msg": f"插件 {module_name} 不存在"}
if status: if status:
plugin.enable() plugin.enable()
else: else:
plugin.disable() plugin.disable()
await reload_all() await reload_all()
return {'status': 0, 'msg': f'成功{"开启" if status else "关闭"} {module_name}'} return {"status": 0, "msg": f'成功{"开启" if status else "关闭"} {module_name}'}
@route.post('/remove_local_plugin', response_class=JSONResponse, dependencies=[authentication()]) @route.post(
"/remove_local_plugin", response_class=JSONResponse, dependencies=[authentication()]
)
async def remove_local_plugin(data: dict): async def remove_local_plugin(data: dict):
module_name: str = data.get('plugin') module_name: str = data.get("plugin")
if not (plugin := plugin_manager.get_local_plugin(module_name)): if not (plugin := plugin_manager.get_local_plugin(module_name)):
return {'status': -100, 'msg': f'插件 {module_name} 不存在'} return {"status": -100, "msg": f"插件 {module_name} 不存在"}
plugin_manager.remove_plugin(plugin.name) plugin_manager.remove_plugin(plugin.name)
await reload_all() await reload_all()
return {'status': 0, 'msg': f'成功卸载 {module_name}'} return {"status": 0, "msg": f"成功卸载 {module_name}"}
@route.get('/get_remote_plugins', response_class=JSONResponse, dependencies=[authentication()]) @route.get(
"/get_remote_plugins", response_class=JSONResponse, dependencies=[authentication()]
)
async def get_remote_plugins(): async def get_remote_plugins():
await plugin_manager.load_remote_plugins() await plugin_manager.load_remote_plugins()
plugins = [i.dict() for i in plugin_manager.remote_plugins] plugins = [i.dict() for i in plugin_manager.remote_plugins]
plugins.sort(key=lambda x: x['name']) plugins.sort(key=lambda x: x["name"])
return { return {"status": 0, "msg": "ok", "data": {"rows": plugins, "total": len(plugins)}}
'status': 0,
'msg': 'ok',
'data': {
'rows': plugins,
'total': len(plugins)
}
}
@route.post('/set_remote_plugin_status', response_class=JSONResponse, dependencies=[authentication()]) @route.post(
"/set_remote_plugin_status",
response_class=JSONResponse,
dependencies=[authentication()],
)
async def set_remote_plugin_status(data: dict): async def set_remote_plugin_status(data: dict):
module_name: str = data.get('plugin') module_name: str = data.get("plugin")
status: bool = data.get('status') status: bool = data.get("status")
if not plugin_manager.get_remote_plugin(module_name): if not plugin_manager.get_remote_plugin(module_name):
return {'status': 1, 'msg': f'插件 {module_name} 不存在'} return {"status": 1, "msg": f"插件 {module_name} 不存在"}
if status: if status:
await plugin_manager.install_remote_plugin(module_name) await plugin_manager.install_remote_plugin(module_name)
else: else:
plugin_manager.remove_plugin(module_name) plugin_manager.remove_plugin(module_name)
await reload_all() await reload_all()
return {'status': 0, 'msg': f'成功{"开启" if status else "关闭"} {module_name}'} return {"status": 0, "msg": f'成功{"开启" if status else "关闭"} {module_name}'}

View File

@ -13,7 +13,7 @@ from pagermaid.web.api.utils import authentication
route = APIRouter() route = APIRouter()
@route.get('/log') @route.get("/log")
async def get_log(token: Optional[str] = Header(...), num: Union[int, str] = 100): async def get_log(token: Optional[str] = Header(...), num: Union[int, str] = 100):
if token != Config.WEB_SECRET_KEY: if token != Config.WEB_SECRET_KEY:
return "非法请求" return "非法请求"
@ -31,8 +31,8 @@ async def get_log(token: Optional[str] = Header(...), num: Union[int, str] = 100
return StreamingResponse(streaming_logs()) return StreamingResponse(streaming_logs())
@route.get('/run_eval') @route.get("/run_eval")
async def run_cmd(token: Optional[str] = Header(...), cmd: str = ''): async def run_cmd(token: Optional[str] = Header(...), cmd: str = ""):
if token != Config.WEB_SECRET_KEY: if token != Config.WEB_SECRET_KEY:
return "非法请求" return "非法请求"
@ -45,8 +45,8 @@ async def run_cmd(token: Optional[str] = Header(...), cmd: str = ''):
return StreamingResponse(run_cmd_func()) if cmd else "无效命令" return StreamingResponse(run_cmd_func()) if cmd else "无效命令"
@route.get('/run_sh') @route.get("/run_sh")
async def run_sh(token: Optional[str] = Header(...), cmd: str = ''): async def run_sh(token: Optional[str] = Header(...), cmd: str = ""):
if token != Config.WEB_SECRET_KEY: if token != Config.WEB_SECRET_KEY:
return "非法请求" return "非法请求"
@ -59,6 +59,6 @@ async def run_sh(token: Optional[str] = Header(...), cmd: str = ''):
return StreamingResponse(run_sh_func()) if cmd else "无效命令" return StreamingResponse(run_sh_func()) if cmd else "无效命令"
@route.get('/status', response_class=JSONResponse, dependencies=[authentication()]) @route.get("/status", response_class=JSONResponse, dependencies=[authentication()])
async def status(): async def status():
return (await get_status()).dict() return (await get_status()).dict()

View File

@ -6,7 +6,7 @@ from jose import jwt
from pagermaid.config import Config from pagermaid.config import Config
ALGORITHM = 'HS256' ALGORITHM = "HS256"
TOKEN_EXPIRE_MINUTES = 30 TOKEN_EXPIRE_MINUTES = 30
@ -18,13 +18,14 @@ def authentication():
try: try:
jwt.decode(token, Config.WEB_SECRET_KEY, algorithms=ALGORITHM) jwt.decode(token, Config.WEB_SECRET_KEY, algorithms=ALGORITHM)
except (jwt.JWTError, jwt.ExpiredSignatureError, AttributeError): except (jwt.JWTError, jwt.ExpiredSignatureError, AttributeError):
raise HTTPException(status_code=400, detail='登录验证失败或已失效,请重新登录') raise HTTPException(status_code=400, detail="登录验证失败或已失效,请重新登录")
return Depends(inner) return Depends(inner)
def create_token(): def create_token():
data = { data = {
"exp": datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(minutes=TOKEN_EXPIRE_MINUTES), "exp": datetime.datetime.now(datetime.timezone.utc)
+ datetime.timedelta(minutes=TOKEN_EXPIRE_MINUTES),
} }
return jwt.encode(data, Config.WEB_SECRET_KEY, algorithm=ALGORITHM) return jwt.encode(data, Config.WEB_SECRET_KEY, algorithm=ALGORITHM)

View File

@ -5,20 +5,20 @@ html_base_path = Path(__file__).parent
def get_html(path: Path) -> str: def get_html(path: Path) -> str:
"""获取 HTML 模板。""" """获取 HTML 模板。"""
with open(path, 'r', encoding='utf-8') as f: with open(path, "r", encoding="utf-8") as f:
return f.read() return f.read()
def get_logo() -> str: def get_logo() -> str:
"""获取 logo。""" """获取 logo。"""
return get_html(html_base_path / 'logo.html') return get_html(html_base_path / "logo.html")
def get_github_logo() -> str: def get_github_logo() -> str:
"""获取 github logo。""" """获取 github logo。"""
return get_html(html_base_path / 'github_logo.html') return get_html(html_base_path / "github_logo.html")
def get_footer() -> str: def get_footer() -> str:
"""获取 footer。""" """获取 footer。"""
return get_html(html_base_path / 'footer.html') return get_html(html_base_path / "footer.html")

View File

@ -1,50 +1,52 @@
from amis import Form, InputSubForm, InputText, Static, Alert, PageSchema, Page from amis import Form, InputSubForm, InputText, Static, Alert, PageSchema, Page
main_form = Form( main_form = Form(
title='命令别名', title="命令别名",
initApi='get:/pagermaid/api/command_alias', initApi="get:/pagermaid/api/command_alias",
api='post:/pagermaid/api/command_alias', api="post:/pagermaid/api/command_alias",
submitText='保存', submitText="保存",
body=[ body=[
InputSubForm( InputSubForm(
name='items', name="items",
label='已设置的命令别名', label="已设置的命令别名",
multiple=True, multiple=True,
btnLabel='${alias} >> ${command}', btnLabel="${alias} >> ${command}",
draggable=True, draggable=True,
addable=True, addable=True,
removable=True, removable=True,
addButtonText='添加命令别名', addButtonText="添加命令别名",
showErrorMsg=False, showErrorMsg=False,
form=Form( form=Form(
title='命令别名', title="命令别名",
body=[ body=[
InputText(name='alias', label='命令别名', required=True), InputText(name="alias", label="命令别名", required=True),
InputText(name='command', label='原命令', required=True), InputText(name="command", label="原命令", required=True),
] ],
),
) )
) ],
]
) )
test_form = Form( test_form = Form(
title='测试', title="测试",
api='get:/pagermaid/api/test_command_alias?message=${message}', api="get:/pagermaid/api/test_command_alias?message=${message}",
submitText='测试', submitText="测试",
body=[ body=[
InputText(name='message', label='测试消息(无需输入逗号前缀)', required=True), InputText(name="message", label="测试消息(无需输入逗号前缀)", required=True),
Static(className='text-red-600', name='new_msg', label='命令别名修改后消息', Static(
visibleOn="typeof data.new_msg !== 'undefined'") className="text-red-600",
] name="new_msg",
label="命令别名修改后消息",
visibleOn="typeof data.new_msg !== 'undefined'",
),
],
) )
tips = Alert(level='info') tips = Alert(level="info")
page = PageSchema( page = PageSchema(
url='/bot_config/command_alias', url="/bot_config/command_alias",
icon='fa fa-link', label='命令别名', icon="fa fa-link",
schema=Page( label="命令别名",
title='', schema=Page(title="", body=[tips, main_form, test_form]),
body=[tips, main_form, test_form]
)
) )

View File

@ -1,49 +1,50 @@
from amis import Page, PageSchema, Html, Property, Service, Flex, ActionType, LevelEnum, Divider, Log, Alert, Form, \ from amis import (
Dialog, Select, Group, InputText, DisplayModeEnum, Horizontal Page,
PageSchema,
Html,
Property,
Service,
Flex,
ActionType,
LevelEnum,
Divider,
Log,
Alert,
Form,
Dialog,
Select,
Group,
InputText,
DisplayModeEnum,
Horizontal,
)
from pagermaid.config import Config from pagermaid.config import Config
from pagermaid.web.html import get_logo from pagermaid.web.html import get_logo
logo = Html(html=get_logo()) logo = Html(html=get_logo())
select_log_num = Select( select_log_num = Select(
label='日志数量', label="日志数量",
name='log_num', name="log_num",
value=100, value=100,
options=[ options=[
{ {"label": 100, "value": 100},
'label': 100, {"label": 200, "value": 200},
'value': 100 {"label": 300, "value": 300},
}, {"label": 400, "value": 400},
{ {"label": 500, "value": 500},
'label': 200, ],
'value': 200
},
{
'label': 300,
'value': 300
},
{
'label': 400,
'value': 400
},
{
'label': 500,
'value': 500
}
]
) )
log_page = Log( log_page = Log(
autoScroll=True, autoScroll=True,
placeholder='暂无日志数据...', placeholder="暂无日志数据...",
operation=['stop', 'showLineNumber', 'filter'], operation=["stop", "showLineNumber", "filter"],
source={ source={
'method': 'get', "method": "get",
'url': '/pagermaid/api/log?num=${log_num | raw}', "url": "/pagermaid/api/log?num=${log_num | raw}",
'headers': { "headers": {"token": Config.WEB_SECRET_KEY},
'token': Config.WEB_SECRET_KEY },
}
}
) )
cmd_input = Form( cmd_input = Form(
@ -51,132 +52,126 @@ cmd_input = Form(
horizontal=Horizontal(left=0), horizontal=Horizontal(left=0),
wrapWithPanel=False, wrapWithPanel=False,
body=[ body=[
InputText(name='command', required=True, clearable=True, addOn=ActionType.Dialog( InputText(
label='执行', name="command",
required=True,
clearable=True,
addOn=ActionType.Dialog(
label="执行",
level=LevelEnum.primary, level=LevelEnum.primary,
dialog=Dialog( dialog=Dialog(
title='命令执行结果', title="命令执行结果",
size='xl', size="xl",
body=Log( body=Log(
autoScroll=True, autoScroll=True,
placeholder='执行命令中,请稍候...', placeholder="执行命令中,请稍候...",
operation=['stop', 'showLineNumber', 'filter'], operation=["stop", "showLineNumber", "filter"],
source={ source={
'method': 'get', "method": "get",
'url': '/pagermaid/api/run_sh?cmd=${command | raw}', "url": "/pagermaid/api/run_sh?cmd=${command | raw}",
'headers': { "headers": {"token": Config.WEB_SECRET_KEY},
'token': Config.WEB_SECRET_KEY },
} ),
}), ),
),
) )
)) ],
]
) )
eval_input = Form( eval_input = Form(
mode=DisplayModeEnum.horizontal, mode=DisplayModeEnum.horizontal,
horizontal=Horizontal(left=0), horizontal=Horizontal(left=0),
wrapWithPanel=False, wrapWithPanel=False,
body=[ body=[
InputText(name='command', required=True, clearable=True, addOn=ActionType.Dialog( InputText(
label='执行', name="command",
required=True,
clearable=True,
addOn=ActionType.Dialog(
label="执行",
level=LevelEnum.primary, level=LevelEnum.primary,
dialog=Dialog( dialog=Dialog(
title='命令执行结果', title="命令执行结果",
size='xl', size="xl",
body=Log( body=Log(
autoScroll=True, autoScroll=True,
placeholder='执行命令中,请稍候...', placeholder="执行命令中,请稍候...",
operation=['stop', 'showLineNumber', 'filter'], operation=["stop", "showLineNumber", "filter"],
source={ source={
'method': 'get', "method": "get",
'url': '/pagermaid/api/run_eval?cmd=${command | raw}', "url": "/pagermaid/api/run_eval?cmd=${command | raw}",
'headers': { "headers": {"token": Config.WEB_SECRET_KEY},
'token': Config.WEB_SECRET_KEY },
} ),
}), ),
),
) )
)) ],
]
) )
operation_button = Flex(justify='center', items=[ operation_button = Flex(
justify="center",
items=[
ActionType.Ajax( ActionType.Ajax(
label='更新', label="更新",
api='/pagermaid/api/bot_update', api="/pagermaid/api/bot_update",
confirmText='该操作会更新 PagerMaid-Pyro ,请在更新完成后手动重启,请确认执行该操作', confirmText="该操作会更新 PagerMaid-Pyro ,请在更新完成后手动重启,请确认执行该操作",
level=LevelEnum.info level=LevelEnum.info,
), ),
ActionType.Ajax( ActionType.Ajax(
label='重启', label="重启",
className='m-l', className="m-l",
api='/pagermaid/api/bot_restart', api="/pagermaid/api/bot_restart",
confirmText='该操作会重启 PagerMaid-Pyro ,请耐心等待重启', confirmText="该操作会重启 PagerMaid-Pyro ,请耐心等待重启",
level=LevelEnum.danger level=LevelEnum.danger,
), ),
ActionType.Dialog( ActionType.Dialog(
label='日志', label="日志",
className='m-l', className="m-l",
level=LevelEnum.primary, level=LevelEnum.primary,
dialog=Dialog(title='查看日志', dialog=Dialog(
size='xl', title="查看日志",
size="xl",
actions=[], actions=[],
body=[ body=[
Alert(level=LevelEnum.info, Alert(
body='查看最近最多500条日志不会自动刷新需要手动点击两次"暂停键"来进行刷新。'), level=LevelEnum.info,
Form( body='查看最近最多500条日志不会自动刷新需要手动点击两次"暂停键"来进行刷新。',
body=[Group(body=[select_log_num]), log_page] ),
)]) Form(body=[Group(body=[select_log_num]), log_page]),
],
),
), ),
ActionType.Dialog( ActionType.Dialog(
label='shell', label="shell",
className='m-l', className="m-l",
level=LevelEnum.warning, level=LevelEnum.warning,
dialog=Dialog(title='shell', dialog=Dialog(title="shell", size="lg", actions=[], body=[cmd_input]),
size='lg',
actions=[],
body=[cmd_input])
), ),
ActionType.Dialog( ActionType.Dialog(
label='eval', label="eval",
className='m-l', className="m-l",
level=LevelEnum.warning, level=LevelEnum.warning,
dialog=Dialog(title='eval', dialog=Dialog(title="eval", size="lg", actions=[], body=[eval_input]),
size='lg', ),
actions=[], ],
body=[eval_input])
) )
])
status = Service( status = Service(
api='/pagermaid/api/status', api="/pagermaid/api/status",
body=Property( body=Property(
title='运行信息', title="运行信息",
column=2, column=2,
items=[ items=[
Property.Item( Property.Item(label="Bot 版本", content="${version}"),
label='Bot 版本', Property.Item(label="Bot 运行时间", content="${run_time}"),
content='${version}' Property.Item(label="CPU占用率", content="${cpu_percent}"),
Property.Item(label="RAM占用率", content="${ram_percent}"),
Property.Item(label="SWAP占用率", content="${swap_percent}", span=2),
],
), ),
Property.Item(
label='Bot 运行时间',
content='${run_time}'
),
Property.Item(
label='CPU占用率',
content='${cpu_percent}'
),
Property.Item(
label='RAM占用率',
content='${ram_percent}'
),
Property.Item(
label='SWAP占用率',
content='${swap_percent}',
span=2
),
]
)
) )
page_detail = Page(title='', body=[logo, operation_button, Divider(), status]) page_detail = Page(title="", body=[logo, operation_button, Divider(), status])
page = PageSchema(url='/home', label='首页', icon='fa fa-home', isDefaultPage=True, schema=page_detail) page = PageSchema(
url="/home", label="首页", icon="fa fa-home", isDefaultPage=True, schema=page_detail
)

View File

@ -1,63 +1,71 @@
from amis import InputText, Switch, Card, Tpl, CardsCRUD, PageSchema, Page, Button, Select from amis import (
InputText,
Switch,
Card,
Tpl,
CardsCRUD,
PageSchema,
Page,
Button,
Select,
)
card = Card( card = Card(
header=Card.Header( header=Card.Header(
title='$title', title="$title",
description='$id', description="$id",
avatarText='$title', avatarText="$title",
avatarTextClassName='overflow-hidden' avatarTextClassName="overflow-hidden",
), ),
actions=[], actions=[],
toolbar=[ toolbar=[
Switch( Switch(
name='enable', name="enable",
value='${status}', value="${status}",
onText='已忽略', onText="已忽略",
offText='未忽略', offText="未忽略",
onEvent={ onEvent={
'change': { "change": {
'actions': { "actions": {
'actionType': 'ajax', "actionType": "ajax",
'args': { "args": {
'api': { "api": {
'url': '/pagermaid/api/set_ignore_group_status', "url": "/pagermaid/api/set_ignore_group_status",
'method': 'post' "method": "post",
}, },
'messages': { "messages": {
'success': '成功${IF(event.data.value, "忽略", "取消忽略")}了 ${title}', "success": '成功${IF(event.data.value, "忽略", "取消忽略")}了 ${title}',
'failed': '操作失败' "failed": "操作失败",
},
"status": "${event.data.value}",
"id": "${id}",
}, },
'status': '${event.data.value}',
'id': '${id}'
}
}
} }
} }
},
) )
] ],
) )
cards_curd = CardsCRUD( cards_curd = CardsCRUD(
mode='cards', mode="cards",
title='', title="",
syncLocation=False, syncLocation=False,
api='/pagermaid/api/get_ignore_group_list', api="/pagermaid/api/get_ignore_group_list",
loadDataOnce=True, loadDataOnce=True,
source='${groups | filter:title:match:keywords_name}', source="${groups | filter:title:match:keywords_name}",
filter={ filter={"body": [InputText(name="keywords_name", label="群组名")]},
'body': [
InputText(name='keywords_name', label='群组名')
]
},
perPage=12, perPage=12,
autoJumpToTopOnPagerChange=True, autoJumpToTopOnPagerChange=True,
placeholder='群组列表为空', placeholder="群组列表为空",
footerToolbar=['switch-per-page', 'pagination'], footerToolbar=["switch-per-page", "pagination"],
columnsCount=3, columnsCount=3,
card=card card=card,
) )
page = PageSchema( page = PageSchema(
url='/bot_config/ignore_groups', url="/bot_config/ignore_groups",
icon='fa fa-ban', icon="fa fa-ban",
label='忽略群组', label="忽略群组",
schema=Page(title='忽略群组', subTitle="忽略后Bot 不再响应指定群组的消息(群组列表将会缓存一小时)", body=cards_curd) schema=Page(
title="忽略群组", subTitle="忽略后Bot 不再响应指定群组的消息(群组列表将会缓存一小时)", body=cards_curd
),
) )

View File

@ -1,32 +1,42 @@
from amis import Form, InputPassword, DisplayModeEnum, Horizontal, Remark, Html, Page, AmisAPI, Wrapper from amis import (
Form,
InputPassword,
DisplayModeEnum,
Horizontal,
Remark,
Html,
Page,
AmisAPI,
Wrapper,
)
from pagermaid.web.html import get_logo from pagermaid.web.html import get_logo
logo = Html(html=get_logo()) logo = Html(html=get_logo())
login_api = AmisAPI( login_api = AmisAPI(
url='/pagermaid/api/login', url="/pagermaid/api/login",
method='post', method="post",
adaptor=''' adaptor="""
if (payload.status == 0) { if (payload.status == 0) {
localStorage.setItem("token", payload.data.token); localStorage.setItem("token", payload.data.token);
} }
return payload; return payload;
''' """,
) )
login_form = Form( login_form = Form(
api=login_api, api=login_api,
title='', title="",
body=[ body=[
InputPassword( InputPassword(
name='password', name="password",
label='密码', label="密码",
labelRemark=Remark(shape='circle', content='登录密码') labelRemark=Remark(shape="circle", content="登录密码"),
), ),
], ],
mode=DisplayModeEnum.horizontal, mode=DisplayModeEnum.horizontal,
horizontal=Horizontal(left=3, right=9, offset=5), horizontal=Horizontal(left=3, right=9, offset=5),
redirect='/admin', redirect="/admin",
) )
body = Wrapper(className='w-2/5 mx-auto my-0 m:w-full', body=login_form) body = Wrapper(className="w-2/5 mx-auto my-0 m:w-full", body=login_form)
login_page = Page(title='', body=[logo, body]) login_page = Page(title="", body=[logo, body])

View File

@ -8,25 +8,33 @@ from pagermaid.web.pages.plugin_local_manage import page as plugin_local_manage_
from pagermaid.web.pages.plugin_remote_manage import page as plugin_remote_manage_page from pagermaid.web.pages.plugin_remote_manage import page as plugin_remote_manage_page
github_logo = Tpl( github_logo = Tpl(
className='w-full', className="w-full",
tpl=get_github_logo(), tpl=get_github_logo(),
) )
header = Flex(className='w-full', justify='flex-end', alignItems='flex-end', items=[github_logo]) header = Flex(
className="w-full", justify="flex-end", alignItems="flex-end", items=[github_logo]
)
admin_app = App( admin_app = App(
brandName='pagermaid', brandName="pagermaid",
logo='https://xtaolabs.com/pagermaid-logo.png', logo="https://xtaolabs.com/pagermaid-logo.png",
header=header, header=header,
pages=[ pages=[
{ {
'children': [ "children": [
home_page, home_page,
PageSchema(label='Bot 设置', icon='fa fa-wrench', PageSchema(
children=[command_alias_page, ignore_groups_page]), label="Bot 设置",
PageSchema(label='插件管理', icon='fa fa-cube', icon="fa fa-wrench",
children=[plugin_local_manage_page, plugin_remote_manage_page]), children=[command_alias_page, ignore_groups_page],
),
PageSchema(
label="插件管理",
icon="fa fa-cube",
children=[plugin_local_manage_page, plugin_remote_manage_page],
),
] ]
} }
], ],
footer=get_footer(), footer=get_footer(),
) )
blank_page = Page(title='PagerMaid-Pyro 404', body='404') blank_page = Page(title="PagerMaid-Pyro 404", body="404")

View File

@ -2,63 +2,57 @@ from amis import InputText, Switch, Card, CardsCRUD, PageSchema, Page
card = Card( card = Card(
header=Card.Header( header=Card.Header(
title='$name', title="$name", avatarText="$name", avatarTextClassName="overflow-hidden"
avatarText='$name',
avatarTextClassName='overflow-hidden'
), ),
actions=[], actions=[],
toolbar=[ toolbar=[
Switch( Switch(
name='enable', name="enable",
value='${status}', value="${status}",
onText='启用', onText="启用",
offText='禁用', offText="禁用",
onEvent={ onEvent={
'change': { "change": {
'actions': [ "actions": [
{ {
'actionType': 'ajax', "actionType": "ajax",
'args': { "args": {
'api': { "api": {
'url': '/pagermaid/api/set_local_plugin_status', "url": "/pagermaid/api/set_local_plugin_status",
'method': 'post' "method": "post",
}, },
'messages': { "messages": {
'success': '成功${IF(event.data.value, "开启", "禁用")}了 ${name}', "success": '成功${IF(event.data.value, "开启", "禁用")}了 ${name}',
'failed': '操作失败' "failed": "操作失败",
},
"status": "${event.data.value}",
"plugin": "${name}",
}, },
'status': '${event.data.value}',
'plugin': '${name}'
}
}, },
] ]
} }
} },
) )
] ],
) )
cards_curd = CardsCRUD( cards_curd = CardsCRUD(
mode='cards', mode="cards",
title='', title="",
syncLocation=False, syncLocation=False,
api='/pagermaid/api/get_local_plugins', api="/pagermaid/api/get_local_plugins",
loadDataOnce=True, loadDataOnce=True,
source='${rows | filter:name:match:keywords_name}', source="${rows | filter:name:match:keywords_name}",
filter={ filter={"body": [InputText(name="keywords_name", label="插件名")]},
'body': [
InputText(name='keywords_name', label='插件名')
]
},
perPage=12, perPage=12,
autoJumpToTopOnPagerChange=True, autoJumpToTopOnPagerChange=True,
placeholder='暂无插件信息', placeholder="暂无插件信息",
footerToolbar=['switch-per-page', 'pagination'], footerToolbar=["switch-per-page", "pagination"],
columnsCount=3, columnsCount=3,
card=card card=card,
) )
page = PageSchema( page = PageSchema(
url='/plugins/local', url="/plugins/local",
icon='fa fa-database', icon="fa fa-database",
label='本地插件管理', label="本地插件管理",
schema=Page(title='本地插件管理', body=cards_curd) schema=Page(title="本地插件管理", body=cards_curd),
) )

View File

@ -2,63 +2,63 @@ from amis import InputText, Switch, Card, Tpl, CardsCRUD, PageSchema, Page, Butt
card = Card( card = Card(
header=Card.Header( header=Card.Header(
title='$name', title="$name",
description='$des', description="$des",
avatarText='$name', avatarText="$name",
avatarTextClassName='overflow-hidden' avatarTextClassName="overflow-hidden",
), ),
actions=[], actions=[],
toolbar=[ toolbar=[
Switch( Switch(
name='enable', name="enable",
value='${status}', value="${status}",
onText='已安装', onText="已安装",
offText='未安装', offText="未安装",
onEvent={ onEvent={
'change': { "change": {
'actions': { "actions": {
'actionType': 'ajax', "actionType": "ajax",
'args': { "args": {
'api': { "api": {
'url': '/pagermaid/api/set_remote_plugin_status', "url": "/pagermaid/api/set_remote_plugin_status",
'method': 'post' "method": "post",
}, },
'messages': { "messages": {
'success': '成功${IF(event.data.value, "安装", "卸载")}了 ${name}', "success": '成功${IF(event.data.value, "安装", "卸载")}了 ${name}',
'failed': '操作失败' "failed": "操作失败",
},
"status": "${event.data.value}",
"plugin": "${name}",
}, },
'status': '${event.data.value}',
'plugin': '${name}'
}
}
} }
} }
},
) )
] ],
) )
cards_curd = CardsCRUD( cards_curd = CardsCRUD(
mode='cards', mode="cards",
title='', title="",
syncLocation=False, syncLocation=False,
api='/pagermaid/api/get_remote_plugins', api="/pagermaid/api/get_remote_plugins",
loadDataOnce=True, loadDataOnce=True,
source='${rows | filter:name:match:keywords_name | filter:des:match:keywords_description}', source="${rows | filter:name:match:keywords_name | filter:des:match:keywords_description}",
filter={ filter={
'body': [ "body": [
InputText(name='keywords_name', label='插件名'), InputText(name="keywords_name", label="插件名"),
InputText(name='keywords_description', label='插件描述') InputText(name="keywords_description", label="插件描述"),
] ]
}, },
perPage=12, perPage=12,
autoJumpToTopOnPagerChange=True, autoJumpToTopOnPagerChange=True,
placeholder='暂无插件信息', placeholder="暂无插件信息",
footerToolbar=['switch-per-page', 'pagination'], footerToolbar=["switch-per-page", "pagination"],
columnsCount=3, columnsCount=3,
card=card card=card,
) )
page = PageSchema( page = PageSchema(
url='/plugins/remote', url="/plugins/remote",
icon='fa fa-cloud-download', icon="fa fa-cloud-download",
label='插件仓库', label="插件仓库",
schema=Page(title='插件仓库', body=cards_curd) schema=Page(title="插件仓库", body=cards_curd),
) )

View File

@ -22,7 +22,7 @@ import pyrogram
def dice(ctx, message): def dice(ctx, message):
return hasattr(message, 'dice') and message.dice return hasattr(message, "dice") and message.dice
pyrogram.filters.dice = dice pyrogram.filters.dice = dice

View File

@ -54,12 +54,8 @@ class Client:
chat_id = chat.id chat_id = chat.id
future = self.loop.create_future() future = self.loop.create_future()
future.add_done_callback( future.add_done_callback(functools.partial(self.clear_listener, chat_id))
functools.partial(self.clear_listener, chat_id) self.listening.update({chat_id: {"future": future, "filters": filters}})
)
self.listening.update({
chat_id: {"future": future, "filters": filters}
})
try: try:
return await asyncio.wait_for(future, timeout) return await asyncio.wait_for(future, timeout)
except asyncio.exceptions.TimeoutError as e: except asyncio.exceptions.TimeoutError as e:
@ -81,11 +77,11 @@ class Client:
@patchable @patchable
def cancel_listener(self, chat_id): def cancel_listener(self, chat_id):
listener = self.listening.get(chat_id) listener = self.listening.get(chat_id)
if not listener or listener['future'].done(): if not listener or listener["future"].done():
return return
listener['future'].set_exception(ListenerCanceled()) listener["future"].set_exception(ListenerCanceled())
self.clear_listener(chat_id, listener['future']) self.clear_listener(chat_id, listener["future"])
@patchable @patchable
def cancel_all_listener(self): def cancel_all_listener(self):
@ -93,14 +89,14 @@ class Client:
self.cancel_listener(chat_id) self.cancel_listener(chat_id)
@patchable @patchable
def conversation(self, chat_id: Union[int, str], once_timeout: int = 60, filters=None): def conversation(
self, chat_id: Union[int, str], once_timeout: int = 60, filters=None
):
return Conversation(self, chat_id, once_timeout, filters) return Conversation(self, chat_id, once_timeout, filters)
@patchable @patchable
async def read_chat_history( async def read_chat_history(
self: "pyrogram.Client", self: "pyrogram.Client", chat_id: Union[int, str], max_id: int = 0
chat_id: Union[int, str],
max_id: int = 0
) -> bool: ) -> bool:
peer = await self.resolve_peer(chat_id) peer = await self.resolve_peer(chat_id)
if isinstance(peer, pyrogram.raw.types.InputPeerChannel): if isinstance(peer, pyrogram.raw.types.InputPeerChannel):
@ -111,7 +107,7 @@ class Client:
offset_date=0, offset_date=0,
offset_id=0, offset_id=0,
offset_topic=0, offset_topic=0,
limit=0 limit=0,
) )
) )
for i in topics.topics: for i in topics.topics:
@ -139,26 +135,26 @@ class MessageHandler:
@patchable @patchable
async def resolve_listener(self, client, message, *args): async def resolve_listener(self, client, message, *args):
listener = client.listening.get(message.chat.id) listener = client.listening.get(message.chat.id)
if listener and not listener['future'].done(): if listener and not listener["future"].done():
listener['future'].set_result(message) listener["future"].set_result(message)
else: else:
if listener and listener['future'].done(): if listener and listener["future"].done():
client.clear_listener(message.chat.id, listener['future']) client.clear_listener(message.chat.id, listener["future"])
await self.user_callback(client, message, *args) await self.user_callback(client, message, *args)
@patchable @patchable
async def check(self, client, update): async def check(self, client, update):
listener = client.listening.get(update.chat.id) listener = client.listening.get(update.chat.id)
if listener and not listener['future'].done(): if listener and not listener["future"].done():
return await listener['filters'](client, update) if callable(listener['filters']) else True
return ( return (
await self.filters(client, update) await listener["filters"](client, update)
if callable(self.filters) if callable(listener["filters"])
else True else True
) )
return await self.filters(client, update) if callable(self.filters) else True
@patch(pyrogram.handlers.edited_message_handler.EditedMessageHandler) @patch(pyrogram.handlers.edited_message_handler.EditedMessageHandler)
class EditedMessageHandler: class EditedMessageHandler:
@ -170,26 +166,26 @@ class EditedMessageHandler:
@patchable @patchable
async def resolve_listener(self, client, message, *args): async def resolve_listener(self, client, message, *args):
listener = client.listening.get(message.chat.id) listener = client.listening.get(message.chat.id)
if listener and not listener['future'].done(): if listener and not listener["future"].done():
listener['future'].set_result(message) listener["future"].set_result(message)
else: else:
if listener and listener['future'].done(): if listener and listener["future"].done():
client.clear_listener(message.chat.id, listener['future']) client.clear_listener(message.chat.id, listener["future"])
await self.user_callback(client, message, *args) await self.user_callback(client, message, *args)
@patchable @patchable
async def check(self, client, update): async def check(self, client, update):
listener = client.listening.get(update.chat.id) listener = client.listening.get(update.chat.id)
if listener and not listener['future'].done(): if listener and not listener["future"].done():
return await listener['filters'](client, update) if callable(listener['filters']) else True
return ( return (
await self.filters(client, update) await listener["filters"](client, update)
if callable(self.filters) if callable(listener["filters"])
else True else True
) )
return await self.filters(client, update) if callable(self.filters) else True
@patch(pyrogram.types.user_and_chats.chat.Chat) @patch(pyrogram.types.user_and_chats.chat.Chat)
class Chat(pyrogram.types.Chat): class Chat(pyrogram.types.Chat):
@ -208,21 +204,27 @@ class Chat(pyrogram.types.Chat):
@patchable @patchable
@staticmethod @staticmethod
def _parse_user_chat(client, user: pyrogram.raw.types.User) -> "Chat": def _parse_user_chat(client, user: pyrogram.raw.types.User) -> "Chat":
chat = pyrogram.types.user_and_chats.chat.Chat.old_parse_user_chat(client, user) # noqa chat = pyrogram.types.user_and_chats.chat.Chat.old_parse_user_chat(
client, user
) # noqa
chat.is_forum = None chat.is_forum = None
return chat return chat
@patchable @patchable
@staticmethod @staticmethod
def _parse_chat_chat(client, chat: pyrogram.raw.types.Chat) -> "Chat": def _parse_chat_chat(client, chat: pyrogram.raw.types.Chat) -> "Chat":
chat = pyrogram.types.user_and_chats.chat.Chat.old_parse_chat_chat(client, chat) # noqa chat = pyrogram.types.user_and_chats.chat.Chat.old_parse_chat_chat(
client, chat
) # noqa
chat.is_forum = None chat.is_forum = None
return chat return chat
@patchable @patchable
@staticmethod @staticmethod
def _parse_channel_chat(client, channel: pyrogram.raw.types.Channel) -> "Chat": def _parse_channel_chat(client, channel: pyrogram.raw.types.Channel) -> "Chat":
chat = pyrogram.types.user_and_chats.chat.Chat.old_parse_channel_chat(client, channel) # noqa chat = pyrogram.types.user_and_chats.chat.Chat.old_parse_channel_chat(
client, channel
) # noqa
chat.is_forum = getattr(channel, "forum", None) chat.is_forum = getattr(channel, "forum", None)
return chat return chat
@ -251,9 +253,7 @@ class Message(pyrogram.types.Message):
async def safe_delete(self, revoke: bool = True): async def safe_delete(self, revoke: bool = True):
try: try:
return await self._client.delete_messages( return await self._client.delete_messages(
chat_id=self.chat.id, chat_id=self.chat.id, message_ids=self.id, revoke=revoke
message_ids=self.id,
revoke=revoke
) )
except Exception: # noqa except Exception: # noqa
return False return False
@ -279,9 +279,14 @@ class Message(pyrogram.types.Message):
if raw_user.isnumeric(): if raw_user.isnumeric():
user = int(raw_user) user = int(raw_user)
elif self.entities is not None: elif self.entities is not None:
if self.entities[0].type == pyrogram.enums.MessageEntityType.TEXT_MENTION: if (
self.entities[0].type
== pyrogram.enums.MessageEntityType.TEXT_MENTION
):
user = self.entities[0].user.id user = self.entities[0].user.id
if not user and self.chat.type == pyrogram.enums.ChatType.PRIVATE: # Current chat if (
not user and self.chat.type == pyrogram.enums.ChatType.PRIVATE
): # Current chat
user = self.chat.id user = self.chat.id
return user return user
@ -317,7 +322,7 @@ class Message(pyrogram.types.Message):
text=text, text=text,
parse_mode=parse_mode, parse_mode=parse_mode,
disable_web_page_preview=disable_web_page_preview, disable_web_page_preview=disable_web_page_preview,
quote=True quote=True,
) )
elif is_self: elif is_self:
msg = await self._client.edit_message_text( msg = await self._client.edit_message_text(
@ -327,14 +332,14 @@ class Message(pyrogram.types.Message):
parse_mode=parse_mode, parse_mode=parse_mode,
entities=entities, entities=entities,
disable_web_page_preview=disable_web_page_preview, disable_web_page_preview=disable_web_page_preview,
reply_markup=reply_markup reply_markup=reply_markup,
) )
elif not no_reply: elif not no_reply:
msg = await self.reply( msg = await self.reply(
text=text, text=text,
parse_mode=parse_mode, parse_mode=parse_mode,
disable_web_page_preview=disable_web_page_preview, disable_web_page_preview=disable_web_page_preview,
quote=True quote=True,
) )
else: else:
try: try:
@ -345,9 +350,11 @@ class Message(pyrogram.types.Message):
parse_mode=parse_mode, parse_mode=parse_mode,
entities=entities, entities=entities,
disable_web_page_preview=disable_web_page_preview, disable_web_page_preview=disable_web_page_preview,
reply_markup=reply_markup reply_markup=reply_markup,
) )
except pyrogram.errors.exceptions.forbidden_403.MessageAuthorRequired: # noqa except (
pyrogram.errors.exceptions.forbidden_403.MessageAuthorRequired
): # noqa
if not no_reply: if not no_reply:
msg = await self.reply( msg = await self.reply(
text=text, text=text,
@ -355,15 +362,13 @@ class Message(pyrogram.types.Message):
entities=entities, entities=entities,
disable_web_page_preview=disable_web_page_preview, disable_web_page_preview=disable_web_page_preview,
reply_markup=reply_markup, reply_markup=reply_markup,
quote=True quote=True,
) )
else: else:
with open("output.log", "w+") as file: with open("output.log", "w+") as file:
file.write(text) file.write(text)
msg = await self._client.send_document( msg = await self._client.send_document(
chat_id=self.chat.id, chat_id=self.chat.id, document="output.log", reply_to_message_id=self.id
document="output.log",
reply_to_message_id=self.id
) )
if not msg: if not msg:
return self return self
@ -381,9 +386,11 @@ class Message(pyrogram.types.Message):
users: dict, users: dict,
chats: dict, chats: dict,
is_scheduled: bool = False, is_scheduled: bool = False,
replies: int = 1 replies: int = 1,
): ):
parsed = await pyrogram.types.Message.old_parse(client, message, users, chats, is_scheduled, replies) # noqa parsed = await pyrogram.types.Message.old_parse(
client, message, users, chats, is_scheduled, replies
) # noqa
# forum topic # forum topic
if isinstance(message, pyrogram.raw.types.Message): if isinstance(message, pyrogram.raw.types.Message):
parsed.forum_topic = getattr(message.reply_to, "forum_topic", None) parsed.forum_topic = getattr(message.reply_to, "forum_topic", None)
@ -414,8 +421,8 @@ class Message(pyrogram.types.Message):
"pyrogram.types.InlineKeyboardMarkup", "pyrogram.types.InlineKeyboardMarkup",
"pyrogram.types.ReplyKeyboardMarkup", "pyrogram.types.ReplyKeyboardMarkup",
"pyrogram.types.ReplyKeyboardRemove", "pyrogram.types.ReplyKeyboardRemove",
"pyrogram.types.ForceReply" "pyrogram.types.ForceReply",
] = object ] = object,
) -> Union["pyrogram.types.Message", List["pyrogram.types.Message"]]: ) -> Union["pyrogram.types.Message", List["pyrogram.types.Message"]]:
if self.media: if self.media:
self.text = None self.text = None

View File

@ -30,14 +30,20 @@ async def sign_in_qrcode(
await client.session.stop() await client.session.stop()
await client.storage.dc_id(req.dc_id) await client.storage.dc_id(req.dc_id)
await client.storage.auth_key( await client.storage.auth_key(
await Auth(client, await client.storage.dc_id(), await client.storage.test_mode()).create() await Auth(
client, await client.storage.dc_id(), await client.storage.test_mode()
).create()
) )
client.session = Session( client.session = Session(
client, await client.storage.dc_id(), client,
await client.storage.auth_key(), await client.storage.test_mode() await client.storage.dc_id(),
await client.storage.auth_key(),
await client.storage.test_mode(),
) )
await client.session.start() await client.session.start()
req = await client.invoke(pyrogram.raw.functions.auth.ImportLoginToken(token=req.token)) req = await client.invoke(
pyrogram.raw.functions.auth.ImportLoginToken(token=req.token)
)
await client.storage.user_id(req.authorization.user.id) await client.storage.user_id(req.authorization.user.id)
await client.storage.is_bot(False) await client.storage.is_bot(False)
return pyrogram.types.User._parse(client, req.authorization.user) return pyrogram.types.User._parse(client, req.authorization.user)
@ -51,8 +57,10 @@ async def authorize_by_qrcode(
client: Client, client: Client,
): ):
print(f"Welcome to Pyrogram (version {pyrogram.__version__})") print(f"Welcome to Pyrogram (version {pyrogram.__version__})")
print(f"Pyrogram is free software and comes with ABSOLUTELY NO WARRANTY. Licensed\n" print(
f"under the terms of the {pyrogram.__license__}.\n") f"Pyrogram is free software and comes with ABSOLUTELY NO WARRANTY. Licensed\n"
f"under the terms of the {pyrogram.__license__}.\n"
)
while True: while True:
qrcode = None qrcode = None
@ -66,7 +74,9 @@ async def authorize_by_qrcode(
print(f"Password hint: {await client.get_password_hint()}") print(f"Password hint: {await client.get_password_hint()}")
if not client.password: if not client.password:
client.password = await ainput("Enter password (empty to recover): ", hide=client.hide_password) client.password = await ainput(
"Enter password (empty to recover): ", hide=client.hide_password
)
try: try:
if client.password: if client.password:
@ -96,8 +106,12 @@ async def authorize_by_qrcode(
except Exception: except Exception:
print("Save qrcode.png failed.") print("Save qrcode.png failed.")
print(qr_obj.terminal()) print(qr_obj.terminal())
print(f"Scan the QR code above, the qrcode.png file or visit {qrcode} to log in.\n") print(
print("QR code will expire in 20 seconds. If you have scanned it, please wait...") f"Scan the QR code above, the qrcode.png file or visit {qrcode} to log in.\n"
)
print(
"QR code will expire in 20 seconds. If you have scanned it, please wait..."
)
await asyncio.sleep(20) await asyncio.sleep(20)
elif isinstance(qrcode, pyrogram.types.User): elif isinstance(qrcode, pyrogram.types.User):
return qrcode return qrcode
@ -114,7 +128,9 @@ async def start_client(client: Client):
await client.authorize() await client.authorize()
if not await client.storage.is_bot() and client.takeout: if not await client.storage.is_bot() and client.takeout:
client.takeout_id = (await client.invoke(pyrogram.raw.functions.account.InitTakeoutSession())).id client.takeout_id = (
await client.invoke(pyrogram.raw.functions.account.InitTakeoutSession())
).id
await client.invoke(pyrogram.raw.functions.updates.GetState()) await client.invoke(pyrogram.raw.functions.updates.GetState())
except (Exception, KeyboardInterrupt): except (Exception, KeyboardInterrupt):

View File

@ -12,12 +12,14 @@ def _checks_cancelled(f):
raise asyncio.CancelledError("The conversation was cancelled before") raise asyncio.CancelledError("The conversation was cancelled before")
return f(self, *args, **kwargs) return f(self, *args, **kwargs)
return wrapper return wrapper
class Conversation: class Conversation:
def __init__(self, client, chat_id: Union[int, str], def __init__(
once_timeout: int = 60, filters=None): self, client, chat_id: Union[int, str], once_timeout: int = 60, filters=None
):
self._client = client self._client = client
self._chat_id = chat_id self._chat_id = chat_id
self._once_timeout = once_timeout self._once_timeout = once_timeout
@ -56,7 +58,9 @@ class Conversation:
async def ask(self, text, filters=None, timeout=None, *args, **kwargs): async def ask(self, text, filters=None, timeout=None, *args, **kwargs):
filters = filters or self._filters filters = filters or self._filters
timeout = timeout or self._once_timeout timeout = timeout or self._once_timeout
return await self._client.ask(self._chat_id, text, filters=filters, timeout=timeout, *args, **kwargs) return await self._client.ask(
self._chat_id, text, filters=filters, timeout=timeout, *args, **kwargs
)
@_checks_cancelled @_checks_cancelled
async def get_response(self, filters=None, timeout=None): async def get_response(self, filters=None, timeout=None):
@ -65,7 +69,9 @@ class Conversation:
return await self._client.listen(self._chat_id, filters, timeout) return await self._client.listen(self._chat_id, filters, timeout)
def mark_as_read(self, message=None): def mark_as_read(self, message=None):
return self._client.read_chat_history(self._chat_id, max_id=message.id if message else 0) return self._client.read_chat_history(
self._chat_id, max_id=message.id if message else 0
)
def cancel(self): def cancel(self):
self._cancelled = True self._cancelled = True

View File

@ -2,6 +2,7 @@ class AlreadyInConversationError(Exception):
""" """
Occurs when another exclusive conversation is opened in the same chat. Occurs when another exclusive conversation is opened in the same chat.
""" """
def __init__(self): def __init__(self):
super().__init__( super().__init__(
"Cannot open exclusive conversation in a " "Cannot open exclusive conversation in a "
@ -13,10 +14,9 @@ class TimeoutConversationError(Exception):
""" """
Occurs when the conversation times out. Occurs when the conversation times out.
""" """
def __init__(self): def __init__(self):
super().__init__( super().__init__("Response read timed out")
"Response read timed out"
)
class ListenerCanceled(Exception): class ListenerCanceled(Exception):
@ -25,6 +25,4 @@ class ListenerCanceled(Exception):
""" """
def __init__(self): def __init__(self):
super().__init__( super().__init__("Listener was canceled")
"Listener was canceled"
)

View File

@ -21,12 +21,12 @@ along with pyromod. If not, see <https://www.gnu.org/licenses/>.
def patch(obj): def patch(obj):
def is_patchable(item): def is_patchable(item):
return getattr(item[1], 'patchable', False) return getattr(item[1], "patchable", False)
def wrapper(container): def wrapper(container):
for name, func in filter(is_patchable, container.__dict__.items()): for name, func in filter(is_patchable, container.__dict__.items()):
old = getattr(obj, name, None) old = getattr(obj, name, None)
setattr(obj, f'old{name}', old) setattr(obj, f"old{name}", old)
setattr(obj, name, func) setattr(obj, name, func)
return container return container

View File

@ -1,4 +1,4 @@
pyrogram==2.0.100 pyrogram==2.0.101
TgCrypto==1.2.5 TgCrypto==1.2.5
Pillow>=8.4.0 Pillow>=8.4.0
pytz>=2021.3 pytz>=2021.3
@ -6,15 +6,15 @@ PyYAML>=6.0
coloredlogs>=15.0.1 coloredlogs>=15.0.1
psutil>=5.8.0 psutil>=5.8.0
httpx~=0.23.3 httpx~=0.23.3
apscheduler==3.10.0 apscheduler>=3.10.1
sqlitedict~=2.1.0 sqlitedict~=2.1.0
casbin==1.17.6 casbin==1.18.0
sentry-sdk==1.16.0 sentry-sdk==1.16.0
PyQRCode>=1.2.1 PyQRCode>=1.2.1
PyPng PyPng
fastapi==0.92.0 fastapi==0.94.0
amis-python==1.0.7 amis-python==1.0.7
python-jose python-jose
uvicorn uvicorn
pydantic==1.10.5 pydantic==1.10.6
starlette==0.25.0 starlette==0.26.0.post1

View File

@ -5,6 +5,7 @@ from sys import executable, exit
try: try:
from pyrogram.errors import ApiIdInvalid, PhoneNumberInvalid from pyrogram.errors import ApiIdInvalid, PhoneNumberInvalid
from pyrogram import Client from pyrogram import Client
print("Found an existing installation of Pyrogram...\nSuccessfully Imported.") print("Found an existing installation of Pyrogram...\nSuccessfully Imported.")
except ImportError: except ImportError:
print("Installing Pyrogram...") print("Installing Pyrogram...")
@ -32,10 +33,14 @@ async def main():
"me", "me",
f"**PagerMaid** `String SESSION`:\n\n`{await bot.export_session_string()}`", f"**PagerMaid** `String SESSION`:\n\n`{await bot.export_session_string()}`",
) )
print("Your SESSION has been generated. Check your telegram saved messages!") print(
"Your SESSION has been generated. Check your telegram saved messages!"
)
exit(0) exit(0)
except ApiIdInvalid: except ApiIdInvalid:
print("Your API ID/API HASH combination is invalid. Kindly recheck.\nQuitting...") print(
"Your API ID/API HASH combination is invalid. Kindly recheck.\nQuitting..."
)
exit(0) exit(0)
except ValueError: except ValueError:
print("API HASH must not be empty!\nQuitting...") print("API HASH must not be empty!\nQuitting...")