🔖 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 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 pagermaid.config import Config
@ -12,8 +21,8 @@ from pagermaid.scheduler import scheduler
import pyromod.listen
from pyrogram import Client
pgm_version = "1.3.0"
pgm_version_code = 1300
pgm_version = "1.3.1"
pgm_version_code = 1301
CMD_LIST = {}
module_dir = __path__[0]
working_dir = getcwd()
@ -21,7 +30,11 @@ working_dir = getcwd()
read_context = {}
help_messages = {}
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(),
}
all_permissions = []
@ -51,6 +64,7 @@ start_time = datetime.now(timezone.utc)
with contextlib.suppress(ImportError):
import uvloop # noqa
uvloop.install()
if not scheduler.running:
@ -68,16 +82,11 @@ bot.job = scheduler
async def log(message):
logs.info(
message.replace('`', '\"')
)
logs.info(message.replace("`", '"'))
if not Config.LOG:
return
try:
await bot.send_message(
Config.LOG_ID,
message
)
await bot.send_message(Config.LOG_ID, message)
except Exception:
Config.LOG = False
Config.LOG_ID = "me"

View File

@ -17,7 +17,7 @@ path.insert(1, f"{working_dir}{sep}plugins")
async def main():
logs.info(lang('platform') + platform + lang('platform_load'))
logs.info(lang("platform") + platform + lang("platform_load"))
try:
await start_client(bot)
@ -35,7 +35,9 @@ async def main():
try:
import_module(f"pagermaid.modules.{module_name}")
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():
try:
import_module(f"plugins.{plugin_name}")
@ -45,11 +47,12 @@ async def main():
plugin_manager.load_local_plugins()
await process_exit(start=True, _client=bot)
logs.info(lang('start'))
logs.info(lang("start"))
await Hook.load_success_exec()
await Hook.startup()
await idle()
await bot.stop()
bot.run(main())

View File

@ -28,15 +28,12 @@ class AliasManager:
def get_all_alias_text(self) -> str:
texts = []
texts.extend(
f'`{i.command}` > `{i.alias}`'
for i in self.alias_list
)
return '\n'.join(texts)
texts.extend(f"`{i.command}` > `{i.alias}`" for i in self.alias_list)
return "\n".join(texts)
@staticmethod
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)
@staticmethod

View File

@ -20,7 +20,7 @@ def cache(ttl=datetime.timedelta(minutes=15)):
nonlocal cache_data
bound = inspect.signature(func).bind(*args, **kw)
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))
now = datetime.datetime.now()
if (not data.time) or ((now - data.time) > ttl):
@ -31,5 +31,7 @@ def cache(ttl=datetime.timedelta(minutes=15)):
except Exception as e:
raise e
return data.value
return wrapped
return wrap

View File

@ -10,7 +10,7 @@ from pagermaid import Config
from pagermaid.common.cache import cache
from pagermaid.utils import client
plugins_path = Path('plugins')
plugins_path = Path("plugins")
class LocalPlugin(BaseModel):
@ -57,11 +57,11 @@ class RemotePlugin(LocalPlugin):
...
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:
self.remove()
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 False
@ -76,11 +76,11 @@ class PluginManager:
def load_local_version_map(self):
if not os.path.exists(plugins_path / "version.json"):
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)
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)
def get_local_version(self, name: str) -> Optional[float]:
@ -116,15 +116,17 @@ class PluginManager:
def load_local_plugins(self) -> List[LocalPlugin]:
self.load_local_version_map()
self.plugins = []
for plugin in os.listdir('plugins'):
if plugin.endswith('.py') or plugin.endswith('.py.disabled'):
plugin = plugin[:-12] if plugin.endswith('.py.disabled') else plugin[:-3]
for plugin in os.listdir("plugins"):
if plugin.endswith(".py") or plugin.endswith(".py.disabled"):
plugin = (
plugin[:-12] if plugin.endswith(".py.disabled") else plugin[:-3]
)
self.plugins.append(
LocalPlugin(
name=plugin,
installed=self.get_plugin_install_status(plugin),
status=self.get_plugin_load_status(plugin),
version=self.get_local_version(plugin)
version=self.get_local_version(plugin),
)
)
return self.plugins
@ -140,7 +142,8 @@ class PluginManager:
RemotePlugin(
**plugin,
status=False,
) for plugin in plugin_list
)
for plugin in plugin_list
]
self.remote_plugins = plugins
self.remote_version_map = {}

View File

@ -4,7 +4,14 @@ import os
import pagermaid.config
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.hook import Hook
from pagermaid.utils import lang
@ -32,7 +39,9 @@ async def reload_all():
if module_name in loaded_plugins:
importlib.reload(module)
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():
try:
plugin = importlib.import_module(f"plugins.{plugin_name}")

View File

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

View File

@ -33,11 +33,11 @@ async def run_eval(cmd: str, message=None, only_result: bool = False) -> str:
async def aexec(code, event, client):
exec(
(
(
("async def __aexec(e, client): " + "\n msg = message = e")
+ "\n reply = message.reply_to_message if message else None"
)
+ "\n chat = e.chat if e else None"
(
("async def __aexec(e, client): " + "\n msg = message = e")
+ "\n reply = message.reply_to_message if message else None"
)
+ "\n chat = e.chat if e else None"
)
+ "".join(f"\n {x}" for x in code.split("\n"))
)

View File

@ -4,9 +4,9 @@ from pagermaid.utils import execute
async def update(force: bool = False):
await execute('git fetch --all')
await execute("git fetch --all")
if force:
await execute('git reset --hard origin/master')
await execute('git pull --all')
await execute("git reset --hard origin/master")
await execute("git pull --all")
await execute(f"{executable} -m pip install --upgrade -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 = val.lower()
if val in ('y', 'yes', 't', 'true', 'on', '1'):
if val in ("y", "yes", "t", "true", "on", "1"):
return 1
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
elif val in ("n", "no", "f", "false", "off", "0"):
return 0
else:
print("[Degrade] invalid truth value %r" % (val,))
@ -27,7 +27,9 @@ def strtobool(val, default=False):
try:
config: Dict = load(open(r"config.yml", encoding="utf-8"), Loader=FullLoader)
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")
sys.exit(1)
@ -43,19 +45,29 @@ class Config:
# TGX
API_ID = DEFAULT_API_ID
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")
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"])
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"])
LOG = strtobool(os.environ.get("PGM_LOG", config["log"]))
LOG_ID = int(os.environ.get("PGM_LOG_ID", config["log_chatid"]))
IPV6 = strtobool(os.environ.get("PGM_IPV6", config["ipv6"]))
ALLOW_ANALYTIC = strtobool(os.environ.get("PGM_ALLOW_ANALYTIC", config["allow_analytic"]), True)
SENTRY_API = "https://2e13a517aeb542e7a307cba8996b6d1a@o1342815.ingest.sentry.io/6617119"
ALLOW_ANALYTIC = strtobool(
os.environ.get("PGM_ALLOW_ANALYTIC", config["allow_analytic"]), True
)
SENTRY_API = (
"https://2e13a517aeb542e7a307cba8996b6d1a@o1342815.ingest.sentry.io/6617119"
)
MIXPANEL_API = "c79162511383b0fa1e9c062a2a86c855"
TIME_FORM = os.environ.get("PGM_TIME_FORM", config["time_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)
PROXY_ADDRESS = os.environ.get("PGM_PROXY_ADDRESS", config["proxy_addr"])
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 = None
if PROXY_ADDRESS and PROXY_PORT:
PROXY = dict(
scheme="socks5",
hostname=PROXY_ADDRESS,
port=int(PROXY_PORT)
)
PROXY = dict(scheme="socks5", hostname=PROXY_ADDRESS, port=int(PROXY_PORT))
elif PROXY_HTTP_ADDRESS and PROXY_HTTP_PORT:
PROXY = dict(
scheme="http",
hostname=PROXY_HTTP_ADDRESS,
port=int(PROXY_HTTP_PORT)
scheme="http", hostname=PROXY_HTTP_ADDRESS, port=int(PROXY_HTTP_PORT)
)
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:
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)
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)
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)
except Exception as e:
print("[Error] Reading English language YAML file failed.")
@ -99,8 +119,12 @@ class Config:
except Exception as e:
alias_dict = {}
web_interface = config.get("web_interface", {})
WEB_ENABLE = strtobool(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_ENABLE = strtobool(
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_PORT = int(os.environ.get("WEB_PORT", web_interface.get("port", 3333)))
WEB_ORIGINS = web_interface.get("origins", ["*"])

View File

@ -9,8 +9,13 @@ from pagermaid import all_permissions, module_dir
# init permissions
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")
permissions = casbin.Enforcer(f"pagermaid{sep}assets{sep}gm_model.conf", f"data{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",
)
permissions = casbin.Enforcer(
f"pagermaid{sep}assets{sep}gm_model.conf", f"data{sep}gm_policy.csv"
)
permissions.logger.setLevel(CRITICAL)
@ -27,15 +32,14 @@ def enforce_permission(user: int, permission: str):
data = permission.split(".")
if len(data) != 2:
raise ValueError("Invalid permission format")
if permissions.enforce(
str(user), data[0], "access"
) and not permissions.enforce(str(user), permission, "ejection"):
if permissions.enforce(str(user), data[0], "access") and not permissions.enforce(
str(user), permission, "ejection"
):
return True
if permissions.enforce(
str(user), permission, "access"
) and not permissions.enforce(str(user), permission, "ejection"):
return True
return False
return bool(
permissions.enforce(str(user), permission, "access")
and not permissions.enforce(str(user), permission, "ejection")
)
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")
datas = []
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)
if not datas:
raise ValueError("No permission found")

View File

@ -14,9 +14,11 @@ class Hook:
"""
注册一个启动钩子
"""
def decorator(function):
hook_functions["startup"].add(function)
return function
return decorator
@staticmethod
@ -24,9 +26,11 @@ class Hook:
"""
注册一个关闭钩子
"""
def decorator(function):
hook_functions["shutdown"].add(function)
return function
return decorator
@staticmethod
@ -79,7 +83,9 @@ class Hook:
@staticmethod
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:
await asyncio.gather(*cors)
except Exception as exception:
@ -87,7 +93,10 @@ class Hook:
@staticmethod
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:
await asyncio.gather(*cors)
except Exception as exception:
@ -136,12 +145,20 @@ class Hook:
logs.info(f"[command_post]: {type(exception)}: {exception}")
@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 = []
try:
for error in hook_functions["process_error"]:
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:
logs.info(f"[process_error]: {type(exception)}: {exception}")
continue
@ -158,7 +175,10 @@ class Hook:
@staticmethod
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:
await asyncio.gather(*cors)
except Exception as exception:

View File

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

View File

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

View File

@ -11,16 +11,19 @@ from pagermaid.utils import lang
import contextlib
@listener(is_plugin=False, command="profile",
description=lang('profile_des'),
parameters="<username>")
@listener(
is_plugin=False,
command="profile",
description=lang("profile_des"),
parameters="<username>",
)
async def profile(client: Client, message: Message):
""" Queries profile of a user. """
"""Queries profile of a user."""
if len(message.parameter) > 1:
await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")
return
if not Config.SILENT:
message = await message.edit(lang('profile_process'))
message = await message.edit(lang("profile_process"))
if message.reply_to_message:
user = message.reply_to_message.from_user
if not user:
@ -43,39 +46,54 @@ async def profile(client: Client, message: Message):
try:
user = await client.get_users(user)
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:
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:
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:
raise exception
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')
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")
)
if not user.first_name:
await message.edit(f"{lang('error_prefix')}{lang('profile_e_no')}")
return
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')
verified = lang('profile_yes') if user.is_verified else lang('profile_no')
restricted = lang('profile_yes') if user.is_restricted else lang('profile_no')
caption = f"**{lang('profile_name')}:** \n" \
f"{lang('profile_username')}: {username_system} \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})"
last_name = (
user.last_name.replace("\u2060", "")
if user.last_name is not None
else lang("profile_noset")
)
verified = lang("profile_yes") if user.is_verified else lang("profile_no")
restricted = lang("profile_yes") if user.is_restricted else lang("profile_no")
caption = (
f"**{lang('profile_name')}:** \n"
f"{lang('profile_username')}: {username_system} \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})"
)
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:
await client.send_photo(
message.chat.id,
photo,
caption=caption,
reply_to_message_id=reply_to
message.chat.id, photo, caption=caption, reply_to_message_id=reply_to
)
await message.delete()
return remove(photo)
@ -83,17 +101,21 @@ async def profile(client: Client, message: Message):
await message.edit(caption)
@listener(is_plugin=False, outgoing=True, command="block",
need_admin=True,
description=lang('block_des'),
parameters="(username/uid/reply)")
@listener(
is_plugin=False,
outgoing=True,
command="block",
need_admin=True,
description=lang("block_des"),
parameters="(username/uid/reply)",
)
async def block_user(client: Client, message: Message):
""" Block a user. """
"""Block a user."""
if len(message.parameter) > 1:
await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")
return
if not Config.SILENT:
message = await message.edit(lang('block_process'))
message = await message.edit(lang("block_process"))
user = message.obtain_user()
if not user:
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')}")
@listener(is_plugin=False, outgoing=True, command="unblock",
need_admin=True,
description=lang('unblock_des'),
parameters="<username/uid/reply>")
@listener(
is_plugin=False,
outgoing=True,
command="unblock",
need_admin=True,
description=lang("unblock_des"),
parameters="<username/uid/reply>",
)
async def unblock_user(client: Client, message: Message):
""" Unblock a user. """
"""Unblock a user."""
if len(message.parameter) > 1:
await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")
return
if not Config.SILENT:
message = await message.edit(lang('unblock_process'))
message = await message.edit(lang("unblock_process"))
user = message.obtain_user()
if not user:
return await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")

View File

@ -7,11 +7,15 @@ from pagermaid.enums import Client, Message
from pagermaid.listener import listener
@listener(is_plugin=False, outgoing=True, command="ghost",
description=lang('ghost_des'),
parameters="<true|false|status>")
@listener(
is_plugin=False,
outgoing=True,
command="ghost",
description=lang("ghost_des"),
parameters="<true|false|status>",
)
async def ghost(client: Client, message: Message):
""" Toggles ghosting of a user. """
"""Toggles ghosting of a user."""
if len(message.parameter) != 1:
await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")
return
@ -19,35 +23,43 @@ async def ghost(client: Client, message: Message):
self_user_id = myself.id
if message.parameter[0] == "true":
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
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":
if message.chat.id == self_user_id:
await message.edit(lang('ghost_e_mark'))
await message.edit(lang("ghost_e_mark"))
return
try:
del sqlite[f"ghosted.chat_id.{str(message.chat.id)}"]
except KeyError:
return await message.edit(lang('ghost_e_noexist'))
return await message.edit(lang("ghost_e_noexist"))
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":
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:
await message.edit(lang('ghost_e_noexist'))
await message.edit(lang("ghost_e_noexist"))
else:
await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")
@listener(is_plugin=False, outgoing=True, command="deny",
need_admin=True,
description=lang('deny_des'),
parameters="<true|false|status>")
@listener(
is_plugin=False,
outgoing=True,
command="deny",
need_admin=True,
description=lang("deny_des"),
parameters="<true|false|status>",
)
async def deny(client: Client, message: Message):
""" Toggles denying of a user. """
"""Toggles denying of a user."""
if len(message.parameter) != 1:
await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")
return
@ -55,38 +67,38 @@ async def deny(client: Client, message: Message):
self_user_id = myself.id
if message.parameter[0] == "true":
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
await message.safe_delete()
await log(f"ChatID {str(message.chat.id)} {lang('deny_set')}")
elif message.parameter[0] == "false":
if message.chat.id == self_user_id:
await message.edit(lang('ghost_e_mark'))
await message.edit(lang("ghost_e_mark"))
return
try:
del sqlite[f"denied.chat_id.{str(message.chat.id)}"]
except KeyError:
return await message.edit(lang('deny_e_noexist'))
return await message.edit(lang("deny_e_noexist"))
await message.safe_delete()
await log(f"ChatID {str(message.chat.id)} {lang('deny_cancel')}")
elif message.parameter[0] == "status":
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:
await message.edit(lang('deny_e_noexist'))
await message.edit(lang("deny_e_noexist"))
else:
await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")
@listener(is_plugin=False, incoming=True, outgoing=False, ignore_edited=True)
async def set_read_acknowledgement(client: Client, message: Message):
""" Event handler to infinitely read ghosted messages. """
"""Event handler to infinitely read ghosted messages."""
if sqlite.get(f"ghosted.chat_id.{str(message.chat.id)}", None):
await client.read_chat_history(message.chat.id)
@listener(is_plugin=False, incoming=True, outgoing=False, ignore_edited=True)
async def message_removal(message: Message):
""" Event handler to infinitely delete denied messages. """
"""Event handler to infinitely delete denied messages."""
if sqlite.get(f"denied.chat_id.{str(message.chat.id)}", None):
await message.safe_delete()

View File

@ -39,10 +39,11 @@ def un_tar_gz(filename, dirs):
return False
@listener(is_plugin=False, outgoing=True, command="backup",
description=lang('backup_des'))
@listener(
is_plugin=False, outgoing=True, command="backup", description=lang("backup_des")
)
async def backup(message: Message):
await message.edit(lang('backup_process'))
await message.edit(lang("backup_process"))
# Remove old backup
if os.path.exists(pgm_backup_zip_name):
@ -50,7 +51,12 @@ async def backup(message: Message):
# remove mp3 , they are so big !
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}")
# run backup function
@ -65,37 +71,41 @@ async def backup(message: Message):
await message.edit(lang("backup_success"))
@listener(is_plugin=False, outgoing=True, command="recovery",
need_admin=True,
description=lang('recovery_des'))
@listener(
is_plugin=False,
outgoing=True,
command="recovery",
need_admin=True,
description=lang("recovery_des"),
)
async def recovery(message: Message):
reply = message.reply_to_message
if not reply:
return await message.edit(lang('recovery_file_error'))
return await message.edit(lang("recovery_file_error"))
if not reply.document:
return await message.edit(lang('recovery_file_error'))
return await message.edit(lang("recovery_file_error"))
try:
if ".tar.gz" not in reply.document.file_name:
return await message.edit(lang('recovery_file_error'))
await message.edit(lang('recovery_down'))
return await message.edit(lang("recovery_file_error"))
await message.edit(lang("recovery_down"))
# Start download process
pgm_backup_zip_name = await reply.download() # noqa
except Exception as e: # noqa
print(e, format_exc())
return await message.edit(lang('recovery_file_error'))
return await message.edit(lang("recovery_file_error"))
# Extract backup files
await message.edit(lang('recovery_process'))
await message.edit(lang("recovery_process"))
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, ""):
os.remove(pgm_backup_zip_name)
return await message.edit(lang('recovery_file_error'))
return await message.edit(lang("recovery_file_error"))
# Cleanup
if os.path.exists(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)

View File

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

View File

@ -15,76 +15,138 @@ from pagermaid.listener import listener
import pathlib
@listener(is_plugin=False, command="help",
description=lang('help_des'),
parameters=f"<{lang('command')}>")
@listener(
is_plugin=False,
command="help",
description=lang("help_des"),
parameters=f"<{lang('command')}>",
)
async def help_command(message: Message):
""" The help new command,"""
"""The help new command,"""
if message.arguments:
if message.arguments in help_messages:
if from_self(message) or \
enforce_permission(from_msg_get_sudo_uid(message), help_messages[message.arguments]["permission"]):
if from_self(message) or enforce_permission(
from_msg_get_sudo_uid(message),
help_messages[message.arguments]["permission"],
):
await message.edit(f"{help_messages[message.arguments]['use']}")
else:
await message.edit(lang('help_no_permission'))
await message.edit(lang("help_no_permission"))
else:
await message.edit(lang('arg_error'))
await message.edit(lang("arg_error"))
else:
result = f"**{lang('help_list')}: \n**"
support_commands = ['username', 'name', 'pfp', 'bio', 'rmpfp',
'profile', 'block', 'unblock', 'ghost', 'deny', 'convert',
'caption', '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']
support_commands = [
"username",
"name",
"pfp",
"bio",
"rmpfp",
"profile",
"block",
"unblock",
"ghost",
"deny",
"convert",
"caption",
"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):
if str(command) in support_commands:
continue
if from_self(message) or \
enforce_permission(from_msg_get_sudo_uid(message), help_messages[command]["permission"]):
if from_self(message) or enforce_permission(
from_msg_get_sudo_uid(message), help_messages[command]["permission"]
):
result += f"`{command}`, "
if result == f"**{lang('help_list')}: \n**":
""" The help raw command,"""
"""The help raw command,"""
for command in sorted(help_messages, reverse=False):
if from_self(message) or \
enforce_permission(from_msg_get_sudo_uid(message), help_messages[command]["permission"]):
if from_self(message) or enforce_permission(
from_msg_get_sudo_uid(message), help_messages[command]["permission"]
):
result += f"`{command}`, "
await message.edit(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_plugin')}](https://index.xtaolabs.com/) "
f"[{lang('help_module')}](https://wiki.xtaolabs.com/)",
parse_mode=ParseMode.MARKDOWN,
disable_web_page_preview=True)
await message.edit(
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_plugin')}](https://index.xtaolabs.com/) "
f"[{lang('help_module')}](https://wiki.xtaolabs.com/)",
parse_mode=ParseMode.MARKDOWN,
disable_web_page_preview=True,
)
@listener(is_plugin=False, command="help_raw",
description=lang('help_des'),
parameters=f"<{lang('command')}>")
@listener(
is_plugin=False,
command="help_raw",
description=lang("help_des"),
parameters=f"<{lang('command')}>",
)
async def help_raw_command(message: Message):
""" The help raw command,"""
"""The help raw command,"""
if message.arguments:
if message.arguments in help_messages:
if from_self(message) or \
enforce_permission(from_msg_get_sudo_uid(message), help_messages[message.arguments]["permission"]):
if from_self(message) or enforce_permission(
from_msg_get_sudo_uid(message),
help_messages[message.arguments]["permission"],
):
await message.edit(f"{help_messages[message.arguments]['use']}")
else:
await message.edit(lang('help_no_permission'))
await message.edit(lang("help_no_permission"))
else:
await message.edit(lang('arg_error'))
await message.edit(lang("arg_error"))
else:
result = f"**{lang('help_list')}: \n**"
for command in sorted(help_messages, reverse=False):
if from_self(message) or \
enforce_permission(from_msg_get_sudo_uid(message), help_messages[command]["permission"]):
if from_self(message) or enforce_permission(
from_msg_get_sudo_uid(message), help_messages[command]["permission"]
):
result += f"`{command}`, "
await message.edit(
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",
need_admin=True,
description=lang('lang_des'))
@listener(
is_plugin=False, command="lang", need_admin=True, description=lang("lang_des")
)
async def lang_change(message: Message):
to_lang = message.arguments
from_lang = Config.LANGUAGE
dir_, dir__ = listdir('languages/built-in'), []
dir_, dir__ = listdir("languages/built-in"), []
for i in dir_:
if i.find('yml') != -1:
if i.find("yml") != -1:
dir__.append(i[:-4])
file = pathlib.Path('config.yml').read_text()
file = pathlib.Path("config.yml").read_text()
if to_lang in dir__:
file = file.replace(f'application_language: "{from_lang}"', f'application_language: "{to_lang}"')
with open('config.yml', 'w', encoding="utf-8") as f:
file = file.replace(
f'application_language: "{from_lang}"', f'application_language: "{to_lang}"'
)
with open("config.yml", "w", encoding="utf-8") as f:
f.write(file)
await message.edit(f"{lang('lang_change_to')} {to_lang}, {lang('lang_reboot')}")
await reload_all()
else:
await message.edit(f'{lang("lang_current_lang")} {Config.LANGUAGE}\n\n'
f'{lang("lang_all_lang")}{"".join(dir__)}')
await message.edit(
f'{lang("lang_current_lang")} {Config.LANGUAGE}\n\n'
f'{lang("lang_all_lang")}{"".join(dir__)}'
)
@listener(is_plugin=False, outgoing=True, command="alias",
disallow_alias=True,
need_admin=True,
description=lang('alias_des'),
parameters='{list|del|set} <source> <to>')
@listener(
is_plugin=False,
outgoing=True,
command="alias",
disallow_alias=True,
need_admin=True,
description=lang("alias_des"),
parameters="{list|del|set} <source> <to>",
)
async def alias_commands(message: Message):
alias_manager = AliasManager()
if len(message.parameter) == 0:
await message.edit(lang('arg_error'))
await message.edit(lang("arg_error"))
elif len(message.parameter) == 1:
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:
await message.edit(lang('alias_no'))
await message.edit(lang("alias_no"))
elif len(message.parameter) == 2:
source_command = message.parameter[1]
try:
alias_manager.delete_alias(source_command)
await message.edit(lang('alias_success'))
await message.edit(lang("alias_success"))
await reload_all()
except KeyError:
await message.edit(lang('alias_no_exist'))
await message.edit(lang("alias_no_exist"))
return
elif len(message.parameter) == 3:
source_command = message.parameter[1]
to_command = message.parameter[2]
if to_command in help_messages:
await message.edit(lang('alias_exist'))
await message.edit(lang("alias_exist"))
return
alias_manager.add_alias(source_command, to_command)
await message.edit(lang('alias_success'))
await message.edit(lang("alias_success"))
await reload_all()

View File

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

View File

@ -13,7 +13,7 @@ from pagermaid.hook import Hook
class DatetimeSerializer(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
fmt = '%Y-%m-%dT%H:%M:%S'
fmt = "%Y-%m-%dT%H:%M:%S"
return obj.strftime(fmt)
return json.JSONEncoder.default(self, obj)
@ -37,57 +37,57 @@ class Mixpanel:
@staticmethod
def json_dumps(data, cls=None):
# 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):
_endpoints = {
'events': f'https://{self.api_host}/track',
'people': f'https://{self.api_host}/engage',
"events": f"https://{self.api_host}/track",
"people": f"https://{self.api_host}/engage",
}
request_url = _endpoints.get(endpoint)
if request_url is None:
return
params = {
'data': json_message,
'verbose': 1,
'ip': 0,
"data": json_message,
"verbose": 1,
"ip": 0,
}
start = self._now()
with contextlib.suppress(Exception):
await self._request.post(
request_url,
data=params,
timeout=10.0
)
await self._request.post(request_url, data=params, timeout=10.0)
logs.debug(f"Mixpanel request took {self._now() - start} seconds")
async def people_set(self, distinct_id: str, properties: dict):
message = {
'$distinct_id': distinct_id,
'$set': properties,
"$distinct_id": distinct_id,
"$set": properties,
}
record = {'$token': self._token, '$time': self._now()}
record = {"$token": self._token, "$time": self._now()}
# sourcery skip: dict-assign-update-to-union
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):
all_properties = {
'token': self._token,
'distinct_id': distinct_id,
'time': self._now(),
'$insert_id': self._make_insert_id(),
'mp_lib': 'python',
'$lib_version': '4.10.0',
"token": self._token,
"distinct_id": distinct_id,
"time": self._now(),
"$insert_id": self._make_insert_id(),
"mp_lib": "python",
"$lib_version": "4.10.0",
}
if properties:
# sourcery skip: dict-assign-update-to-union
all_properties.update(properties)
event = {
'event': event_name,
'properties': all_properties,
"event": event_name,
"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)
@ -97,7 +97,7 @@ mp = Mixpanel(Config.MIXPANEL_API)
async def mixpanel_init_id(bot: Client):
if not bot.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:
data["username"] = bot.me.username
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
if sender_id < 0 and message.outgoing:
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):
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[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)
@listener(is_plugin=False, outgoing=True, command="apt",
need_admin=True,
diagnostics=False,
description=lang('apt_des'),
parameters=lang('apt_parameters'))
@listener(
is_plugin=False,
outgoing=True,
command="apt",
need_admin=True,
diagnostics=False,
description=lang("apt_des"),
parameters=lang("apt_parameters"),
)
async def plugin(message: Message):
if len(message.parameter) == 0:
await message.edit(lang('arg_error'))
await message.edit(lang("arg_error"))
return
reply = message.reply_to_message
plugin_directory = f"{working_dir}{sep}plugins{sep}"
if message.parameter[0] == "install":
if len(message.parameter) == 1:
message = await message.edit(lang('apt_processing'))
message = await message.edit(lang("apt_processing"))
file_path = None
with contextlib.suppress(Exception):
if reply:
file_path = await reply.download()
else:
file_path = await message.download()
if file_path is None or not file_path.endswith('.py'):
await message.edit(lang('apt_no_py'))
if file_path is None or not file_path.endswith(".py"):
await message.edit(lang("apt_no_py"))
try:
remove(str(file_path))
except FileNotFoundError:
pass
return
move_plugin(file_path)
await message.edit(f"<b>{lang('apt_name')}</b>\n\n"
f"{lang('apt_plugin')} "
f"{path.basename(file_path)[:-3]} {lang('apt_installed')}")
await message.edit(
f"<b>{lang('apt_name')}</b>\n\n"
f"{lang('apt_plugin')} "
f"{path.basename(file_path)[:-3]} {lang('apt_installed')}"
)
await log(f"{lang('apt_install_success')} {path.basename(file_path)[:-3]}.")
await reload_all()
elif len(message.parameter) >= 2:
await plugin_manager.load_remote_plugins()
process_list = message.parameter
message = await message.edit(lang('apt_processing'))
message = await message.edit(lang("apt_processing"))
del process_list[0]
success_list = []
failed_list = []
@ -98,30 +104,34 @@ async def plugin(message: Message):
failed_list.append(i)
text = f"<b>{lang('apt_name')}</b>\n\n"
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:
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:
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)
restart = len(success_list) > 0
await message.edit(text)
if restart:
await reload_all()
else:
await message.edit(lang('arg_error'))
await message.edit(lang("arg_error"))
elif message.parameter[0] == "remove":
if len(message.parameter) == 2:
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 reload_all()
elif "/" in message.parameter[1]:
await message.edit(lang('arg_error'))
await message.edit(lang("arg_error"))
else:
await message.edit(lang('apt_not_exist'))
await message.edit(lang("apt_not_exist"))
else:
await message.edit(lang('arg_error'))
await message.edit(lang("arg_error"))
elif message.parameter[0] == "status":
if len(message.parameter) == 1:
inactive_plugins = sorted(__list_plugins())
@ -151,35 +161,41 @@ async def plugin(message: Message):
inactive_plugins_string = f"`{lang('apt_no_load_failed_plugins')}`"
if len(disabled_plugins) == 0:
disabled_plugins_string = f"`{lang('apt_no_disabled_plugins')}`"
output = f"**{lang('apt_plugin_list')}**\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}"
output = (
f"**{lang('apt_plugin_list')}**\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}"
)
await message.edit(output)
else:
await message.edit(lang('arg_error'))
await message.edit(lang("arg_error"))
elif message.parameter[0] == "enable":
if len(message.parameter) == 2:
if plugin_manager.enable_plugin(message.parameter[1]):
await message.edit(f"{lang('apt_plugin')} {message.parameter[1]} "
f"{lang('apt_enable')}")
await message.edit(
f"{lang('apt_plugin')} {message.parameter[1]} "
f"{lang('apt_enable')}"
)
await log(f"{lang('apt_enable')} {message.parameter[1]}.")
await reload_all()
else:
await message.edit(lang('apt_not_exist'))
await message.edit(lang("apt_not_exist"))
else:
await message.edit(lang('arg_error'))
await message.edit(lang("arg_error"))
elif message.parameter[0] == "disable":
if len(message.parameter) == 2:
if plugin_manager.disable_plugin(message.parameter[1]):
await message.edit(f"{lang('apt_plugin')} {message.parameter[1]} "
f"{lang('apt_disable')}")
await message.edit(
f"{lang('apt_plugin')} {message.parameter[1]} "
f"{lang('apt_disable')}"
)
await log(f"{lang('apt_disable')} {message.parameter[1]}.")
await reload_all()
else:
await message.edit(lang('apt_not_exist'))
await message.edit(lang("apt_not_exist"))
else:
await message.edit(lang('arg_error'))
await message.edit(lang("arg_error"))
elif message.parameter[0] == "upload":
if len(message.parameter) == 2:
file_name = f"{message.parameter[1]}.py"
@ -191,31 +207,41 @@ async def plugin(message: Message):
elif exists(f"{plugin_directory}{file_name}.disabled"):
copyfile(f"{plugin_directory}{file_name}.disabled", file_name)
if exists(file_name):
await message.edit(lang('apt_uploading'))
await upload_attachment(file_name,
message.chat.id, reply_id,
thumb=f"pagermaid{sep}assets{sep}logo.jpg",
caption=f"<b>{lang('apt_name')}</b>\n\n"
f"PagerMaid-Pyro {message.parameter[1]} plugin.")
await message.edit(lang("apt_uploading"))
await upload_attachment(
file_name,
message.chat.id,
reply_id,
thumb=f"pagermaid{sep}assets{sep}logo.jpg",
caption=f"<b>{lang('apt_name')}</b>\n\n"
f"PagerMaid-Pyro {message.parameter[1]} plugin.",
)
remove(file_name)
await message.safe_delete()
else:
await message.edit(lang('apt_not_exist'))
await message.edit(lang("apt_not_exist"))
else:
await message.edit(lang('arg_error'))
await message.edit(lang("arg_error"))
elif message.parameter[0] == "update":
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
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:
await message.edit(f"<b>{lang('apt_name')}</b>\n\n" +
lang("apt_loading_from_online_but_nothing_need_to_update"))
await message.edit(
f"<b>{lang('apt_name')}</b>\n\n"
+ lang("apt_loading_from_online_but_nothing_need_to_update")
)
else:
message = await message.edit(lang("apt_loading_from_online_and_updating"))
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()
elif message.parameter[0] == "search":
@ -228,13 +254,25 @@ async def plugin(message: Message):
plugin_online = plugin_list.json()["list"]
for i in plugin_online:
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:
await message.edit(lang("apt_search_not_found"))
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:
await message.edit(lang('arg_error'))
await message.edit(lang("arg_error"))
elif message.parameter[0] == "show":
if len(message.parameter) == 1:
await message.edit(lang("apt_search_no_name"))
@ -249,15 +287,17 @@ async def plugin(message: Message):
search_support = lang("apt_search_supporting")
else:
search_support = lang("apt_search_not_supporting")
search_result = f"{lang('apt_plugin_name')}:`{i['name']}`\n" \
f"{lang('apt_plugin_ver')}:`Ver {i['version']}`\n" \
f"{lang('apt_plugin_section')}:`{i['section']}`\n" \
f"{lang('apt_plugin_maintainer')}:`{i['maintainer']}`\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']}"
search_result = (
f"{lang('apt_plugin_name')}:`{i['name']}`\n"
f"{lang('apt_plugin_ver')}:`Ver {i['version']}`\n"
f"{lang('apt_plugin_section')}:`{i['section']}`\n"
f"{lang('apt_plugin_maintainer')}:`{i['maintainer']}`\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']}"
)
break
if search_result == '':
if search_result == "":
await message.edit(lang("apt_search_not_found"))
else:
await message.edit(search_result)
@ -267,7 +307,7 @@ async def plugin(message: Message):
return
message = await message.edit(lang("stats_loading"))
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)
plugin_list = await client.get(f"{Config.GIT_SOURCE}list.json")
plugin_online = plugin_list.json()["list"]

View File

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

View File

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

View File

@ -17,7 +17,7 @@ def sentry_before_send(event, hint):
exc_info = hint.get("exc_info")
if exc_info and isinstance(exc_info[1], (Unauthorized, UsernameInvalid)):
# The user has been deleted/deactivated or session revoked
safe_remove('pagermaid.session')
safe_remove("pagermaid.session")
exit(1)
if time() <= sentry_sdk_report_time + 30:
sentry_sdk_report_time = time()
@ -28,7 +28,9 @@ def sentry_before_send(event, hint):
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(
Config.SENTRY_API,
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, **_):
sender_id = message.from_user.id if message.from_user else ""
sender_id = message.sender_chat.id if message.sender_chat else sender_id
sentry_sdk.set_context("Target", {"ChatID": str(message.chat.id),
"UserID": str(sender_id),
"Msg": message.text or ""})
sentry_sdk.set_context(
"Target",
{
"ChatID": str(message.chat.id),
"UserID": str(sender_id),
"Msg": message.text or "",
},
)
if command:
sentry_sdk.set_tag("com", command)
sentry_sdk.capture_exception(exc_info)

View File

@ -29,43 +29,41 @@ DCs = {
2: "149.154.167.51",
3: "149.154.175.100",
4: "149.154.167.91",
5: "91.108.56.130"
5: "91.108.56.130",
}
@listener(is_plugin=False, command="sysinfo",
description=lang('sysinfo_des'))
@listener(is_plugin=False, command="sysinfo", description=lang("sysinfo_des"))
async def sysinfo(message: Message):
""" Retrieve system information via neofetch. """
"""Retrieve system information via neofetch."""
if not Config.SILENT:
message = await message.edit(lang("sysinfo_loading"))
if platform == 'win32':
if platform == "win32":
return await message.edit(neofetch_win(), parse_mode=ParseMode.HTML)
result = await execute("neofetch --config none --stdout")
await message.edit(f"`{result}`")
@listener(is_plugin=False, command="status",
description=lang('status_des'))
@listener(is_plugin=False, command="status", description=lang("status_des"))
async def status(message: Message):
# database
# database = lang('status_online') if redis_status() else lang('status_offline')
# uptime https://gist.github.com/borgstrom/936ca741e885a1438c374824efb038b3
uptime = await get_bot_uptime()
text = (f"**{lang('status_hint')}** \n"
f"{lang('status_name')}: `{uname().node}` \n"
f"{lang('status_platform')}: `{platform}` \n"
f"{lang('status_release')}: `{uname().release}` \n"
f"{lang('status_python')}: `{python_version()}` \n"
f"{lang('status_pyrogram')}: `{__version__}` \n"
f"{lang('status_pgm')}: `{pgm_version}`\n"
f"{lang('status_uptime')}: `{uptime}`"
)
text = (
f"**{lang('status_hint')}** \n"
f"{lang('status_name')}: `{uname().node}` \n"
f"{lang('status_platform')}: `{platform}` \n"
f"{lang('status_release')}: `{uname().release}` \n"
f"{lang('status_python')}: `{python_version()}` \n"
f"{lang('status_pyrogram')}: `{__version__}` \n"
f"{lang('status_pgm')}: `{pgm_version}`\n"
f"{lang('status_uptime')}: `{uptime}`"
)
await message.edit(text)
@listener(is_plugin=False, command="stats",
description=lang("stats_des"))
@listener(is_plugin=False, command="stats", description=lang("stats_des"))
async def stats(client: Client, message: Message):
msg = await message.edit(lang("stats_loading"))
a, u, g, s, c, b = 0, 0, 0, 0, 0, 0
@ -82,21 +80,21 @@ async def stats(client: Client, message: Message):
elif chat_type == ChatType.CHANNEL:
c += 1
a += 1
text = (f"**{lang('stats_hint')}** \n"
f"{lang('stats_dialogs')}: `{a}` \n"
f"{lang('stats_private')}: `{u}` \n"
f"{lang('stats_group')}: `{g}` \n"
f"{lang('stats_supergroup')}: `{s}` \n"
f"{lang('stats_channel')}: `{c}` \n"
f"{lang('stats_bot')}: `{b}`"
)
text = (
f"**{lang('stats_hint')}** \n"
f"{lang('stats_dialogs')}: `{a}` \n"
f"{lang('stats_private')}: `{u}` \n"
f"{lang('stats_group')}: `{g}` \n"
f"{lang('stats_supergroup')}: `{s}` \n"
f"{lang('stats_channel')}: `{c}` \n"
f"{lang('stats_bot')}: `{b}`"
)
await msg.edit(text)
@listener(is_plugin=False, command="pingdc",
description=lang("pingdc_des"))
@listener(is_plugin=False, command="pingdc", description=lang("pingdc_des"))
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 = []
print("1")
for dc in range(1, 6):
@ -107,7 +105,9 @@ async def ping_dc(message: Message):
else:
data.append("0")
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:
data.append(str(float(result)))
except ValueError:
@ -121,10 +121,9 @@ async def ping_dc(message: Message):
)
@listener(is_plugin=False, command="ping",
description=lang("ping_des"))
@listener(is_plugin=False, command="ping", description=lang("ping_des"))
async def ping(client: Client, message: Message):
""" Calculates latency between PagerMaid and Telegram. """
"""Calculates latency between PagerMaid and Telegram."""
start = datetime.now()
await client.invoke(Ping(ping_id=0))
end = datetime.now()
@ -137,7 +136,7 @@ async def ping(client: Client, message: Message):
def wmic(command: str):
""" Fetch the wmic command to cmd """
"""Fetch the wmic command to cmd"""
try:
p = Popen(command.split(" "), stdout=PIPE)
except FileNotFoundError:
@ -152,7 +151,7 @@ def wmic(command: str):
def get_uptime():
""" Get the device uptime """
"""Get the device uptime"""
delta = round(time() - boot_time())
hours, remainder = divmod(int(delta), 3600)
@ -179,17 +178,17 @@ def get_uptime():
return output
def readable(num, suffix='B'):
""" Convert Bytes into human-readable formats """
for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
def readable(num, suffix="B"):
"""Convert Bytes into human-readable formats"""
for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
if abs(num) < 1024.0:
return "%3.1f%s%s" % (num, unit, suffix)
num /= 1024.0
return "%.1f%s%s" % (num, 'Yi', suffix)
return "%.1f%s%s" % (num, "Yi", suffix)
def get_ram():
""" Get RAM used/free/total """
"""Get RAM used/free/total"""
ram = virtual_memory()
used = readable(ram.used)
total = readable(ram.total)
@ -200,7 +199,7 @@ def get_ram():
def partitions():
""" Find the disk partitions on current OS """
"""Find the disk partitions on current OS"""
parts = disk_partitions()
listparts = []
@ -208,7 +207,9 @@ def partitions():
try:
total, used, free = disk_usage(g.device)
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:
continue
@ -228,11 +229,11 @@ def neofetch_win():
mboard = "Unknown..."
cpu = wmic("wmic cpu get name")[-1]
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()
disks = '\n'.join(partitions())
disks = "\n".join(partitions())
return (
f'<code>{user_name}@{host_name}\n---------\nOS: {os}\nUptime: {uptime}\n'
f'Motherboard: {mboard}\nCPU: {cpu}\nGPU: {gpu}\nMemory: {ram}\n'
f'Disk:\n{disks}</code>'
f"<code>{user_name}@{host_name}\n---------\nOS: {os}\nUptime: {uptime}\n"
f"Motherboard: {mboard}\nCPU: {cpu}\nGPU: {gpu}\nMemory: {ram}\n"
f"Disk:\n{disks}</code>"
)

View File

@ -1,8 +1,15 @@
from pagermaid.single_utils import sqlite
from pagermaid.listener import listener
from pagermaid.group_manager import add_permission_for_group, Permission, remove_permission_for_group, \
add_user_to_group, remove_user_from_group, add_permission_for_user, remove_permission_for_user, \
permissions
from pagermaid.group_manager import (
add_permission_for_group,
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.utils import lang, edit_delete, _status_sudo
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
@listener(is_plugin=False, command="sudo",
need_admin=True,
parameters="{on|off|add|remove|gaddp|gaddu|gdelp|gdelu|glist|uaddp|udelp|list}",
description=lang('sudo_des'))
@listener(
is_plugin=False,
command="sudo",
need_admin=True,
parameters="{on|off|add|remove|gaddp|gaddu|gdelp|gdelu|glist|uaddp|udelp|list}",
description=lang("sudo_des"),
)
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
sudo = get_sudo_list()
if input_str == "on":
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
text = f"__{lang('sudo_enable')}__\n"
if len(sudo) != 0:
@ -48,7 +58,7 @@ async def sudo_change(client: Client, message: Message):
return await message.edit(
text,
)
await edit_delete(message, lang('sudo_has_disabled'))
await edit_delete(message, lang("sudo_has_disabled"))
elif input_str == "add":
from_id = from_msg_get_sudo_id(message)
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)):
text += f"{'-' if j[2] == 'ejection' else ''}{j[1]}\n"
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)
elif len(message.parameter) > 0:
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]))
return await message.edit(lang("sudo_user_add_per"))
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"))
else:
return await edit_delete(message, lang('arg_error'))
return await edit_delete(message, lang("arg_error"))
if len(message.parameter) == 3:
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"))
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"))
else:
return await edit_delete(message, lang('arg_error'))
return await edit_delete(message, lang("arg_error"))
else:
await edit_delete(message, lang('arg_error'))
await edit_delete(message, lang("arg_error"))
else:
await edit_delete(message, lang('arg_error'))
await edit_delete(message, lang("arg_error"))

View File

@ -11,24 +11,24 @@ from pagermaid.enums import Message
from pagermaid.utils import attach_log, execute, lang, upload_attachment
@listener(is_plugin=False, command="sh",
need_admin=True,
description=lang('sh_des'),
parameters=lang('sh_parameters'))
@listener(
is_plugin=False,
command="sh",
need_admin=True,
description=lang("sh_des"),
parameters=lang("sh_parameters"),
)
async def sh(message: Message):
""" Use the command-line from Telegram. """
"""Use the command-line from Telegram."""
user = getuser()
command = message.arguments
hostname = node()
if not command:
await message.edit(lang('arg_error'))
await message.edit(lang("arg_error"))
return
message = await message.edit(
f"`{user}`@{hostname} ~"
f"\n> `$` {command}"
)
message = await message.edit(f"`{user}`@{hostname} ~" f"\n> `$` {command}")
result = await execute(command)
@ -38,36 +38,37 @@ async def sh(message: Message):
return
await message.edit(
f"`{user}`@{hostname} ~"
f"\n> `#` {command}"
f"\n`{result}`"
f"`{user}`@{hostname} ~" f"\n> `#` {command}" f"\n`{result}`"
)
else:
return
@listener(is_plugin=False, command="restart",
need_admin=True,
description=lang('restart_des'))
@listener(
is_plugin=False, command="restart", need_admin=True, description=lang("restart_des")
)
async def restart(message: Message):
""" To re-execute PagerMaid. """
"""To re-execute PagerMaid."""
if not message.text[0].isalpha():
await message.edit(lang('restart_log'))
await message.edit(lang("restart_log"))
exit(0)
@listener(is_plugin=False, command="eval",
need_admin=True,
description=lang('eval_des'),
parameters=lang('eval_parameters'))
@listener(
is_plugin=False,
command="eval",
need_admin=True,
description=lang("eval_des"),
parameters=lang("eval_parameters"),
)
async def sh_eval(message: Message):
""" Run python commands from Telegram. """
"""Run python commands from Telegram."""
dev_mode = exists(f"data{sep}dev")
try:
assert dev_mode
cmd = message.text.split(" ", maxsplit=1)[1]
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)
if len(final_output) > 4096:
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)
@listener(is_plugin=False, command="send_log",
need_admin=True,
description=lang("send_log_des"))
@listener(
is_plugin=False,
command="send_log",
need_admin=True,
description=lang("send_log_des"),
)
async def send_log(message: Message):
""" Send log to a chat. """
"""Send log to a chat."""
if not exists("pagermaid.log.txt"):
return await message.edit(lang("send_log_not_found"))
await upload_attachment("pagermaid.log.txt",
message.chat.id,
message.reply_to_message_id or message.reply_to_top_message_id,
thumb=f"pagermaid{sep}assets{sep}logo.jpg",
caption=lang("send_log_caption"))
await upload_attachment(
"pagermaid.log.txt",
message.chat.id,
message.reply_to_message_id or message.reply_to_top_message_id,
thumb=f"pagermaid{sep}assets{sep}logo.jpg",
caption=lang("send_log_caption"),
)
await message.safe_delete()

View File

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

View File

@ -14,5 +14,7 @@ async def init_web():
from pagermaid.web import app, 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())

View File

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

View File

@ -6,5 +6,11 @@ from pagermaid.utils import client
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)

View File

@ -9,7 +9,11 @@ from pyrogram import Client as OldClient
from pyrogram.types import Chat as OldChat, Message as OldMessage, Dialog
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
@ -39,49 +43,54 @@ class Message(OldMessage):
chat: "Chat"
def obtain_message(self) -> Optional[str]:
""" Obtains a message from either the reply message or command arguments. """
"""Obtains a message from either the reply message or command arguments."""
def obtain_user(self) -> Optional[int]:
""" Obtains a user from either the reply message or command arguments. """
"""Obtains a user from either the reply message or command arguments."""
async def delay_delete(self, delete_seconds: int = 60) -> Optional[bool]:
""" Deletes the message after a specified amount of seconds. """
"""Deletes the message after a specified amount of seconds."""
async def safe_delete(self, revoke: bool = True) -> None:
""" Safely deletes the message. """
"""Safely deletes the message."""
class Client(OldClient):
job: Optional[AsyncIOScheduler] = None
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]:
""" Ask a message in a conversation. """
async def ask(
self, chat_id, text, filters=None, timeout=None, *args, **kwargs
) -> Optional[Message]:
"""Ask a message in a conversation."""
def cancel_listener(self, chat_id):
""" Cancel the conversation with the given chat_id. """
"""Cancel the conversation with the given chat_id."""
def cancel_all_listeners(self):
""" Cancel all conversations. """
"""Cancel all conversations."""
def conversation(self, chat_id: Union[int, str],
once_timeout: int = 60, filters=None) -> Optional[Conversation]:
""" Initialize a conversation with the given chat_id. """
def conversation(
self, chat_id: Union[int, str], once_timeout: int = 60, filters=None
) -> Optional[Conversation]:
"""Initialize a conversation with the given chat_id."""
async def get_dialogs_list(self) -> List[Dialog]:
""" Get a list of all dialogs. """
"""Get a list of all dialogs."""
class Chat(OldChat):
is_forum: Optional[bool] = None
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]:
""" Ask a message in a conversation. """
async def ask(
self, chat_id, text, filters=None, timeout=None, *args, **kwargs
) -> Optional[Message]:
"""Ask a message in a conversation."""
def cancel_listener(self, chat_id):
""" Cancel the conversation with the given chat_id. """
"""Cancel the conversation with the given chat_id."""

View File

@ -18,17 +18,17 @@ from pagermaid.single_utils import _status_sudo, get_sudo_list, Message, sqlite
def lang(text: str) -> str:
""" i18n """
"""i18n"""
return Config.lang_dict.get(text, text)
def alias_command(command: str, disallow_alias: bool = False) -> str:
""" alias """
"""alias"""
return command if disallow_alias else Config.alias_dict.get(command, command)
async def attach_report(plaintext, file_name, reply_id=None, caption=None):
""" Attach plaintext as logs. """
"""Attach plaintext as logs."""
with open(file_name, "w+") as file:
file.write(plaintext)
try:
@ -36,7 +36,7 @@ async def attach_report(plaintext, file_name, reply_id=None, caption=None):
"PagerMaid_Modify_bot",
file_name,
reply_to_message_id=reply_id,
caption=caption
caption=caption,
)
except Exception: # noqa
return
@ -44,20 +44,17 @@ 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):
""" Attach plaintext as logs. """
with open(file_name, "w+", encoding='utf-8') as file:
"""Attach plaintext as logs."""
with open(file_name, "w+", encoding="utf-8") as file:
file.write(plaintext)
await bot.send_document(
chat_id,
file_name,
reply_to_message_id=reply_id,
caption=caption
chat_id, file_name, reply_to_message_id=reply_id, caption=caption
)
remove(file_name)
async def upload_attachment(file_path, chat_id, reply_id, caption=None, thumb=None):
""" Uploads a local attachment file. """
"""Uploads a local attachment file."""
if not exists(file_path):
return False
try:
@ -66,7 +63,7 @@ async def upload_attachment(file_path, chat_id, reply_id, caption=None, thumb=No
file_path,
thumb=thumb,
reply_to_message_id=reply_id,
caption=caption
caption=caption,
)
except BaseException as exception:
raise exception
@ -74,32 +71,31 @@ async def upload_attachment(file_path, chat_id, reply_id, caption=None, thumb=No
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(
command,
stdout=PIPE,
stderr=PIPE,
stdin=PIPE
command, stdout=PIPE, stderr=PIPE, stdin=PIPE
)
stdout, stderr = await executor.communicate()
if pass_error:
try:
result = str(stdout.decode().strip()) \
+ str(stderr.decode().strip())
result = str(stdout.decode().strip()) + str(stderr.decode().strip())
except UnicodeDecodeError:
result = str(stdout.decode('gbk').strip()) \
+ str(stderr.decode('gbk').strip())
result = str(stdout.decode("gbk").strip()) + str(
stderr.decode("gbk").strip()
)
else:
try:
result = str(stdout.decode().strip())
except UnicodeDecodeError:
result = str(stdout.decode('gbk').strip())
result = str(stdout.decode("gbk").strip())
return result
def pip_install(package: str, version: Optional[str] = "", alias: Optional[str] = "") -> bool:
""" Auto install extra pypi packages """
def pip_install(
package: str, version: Optional[str] = "", alias: Optional[str] = ""
) -> bool:
"""Auto install extra pypi packages"""
if not alias:
# when import name is not provided, use package name
alias = package
@ -110,32 +106,42 @@ def pip_install(package: str, version: Optional[str] = "", alias: Optional[str]
return True
async def edit_delete(message: Message,
text: str,
time: int = 5,
parse_mode: Optional["enums.ParseMode"] = None,
disable_web_page_preview: bool = None):
async def edit_delete(
message: Message,
text: str,
time: int = 5,
parse_mode: Optional["enums.ParseMode"] = None,
disable_web_page_preview: bool = None,
):
sudo_users = get_sudo_list()
from_id = message.from_user.id if message.from_user else message.sender_chat.id
if from_id in sudo_users:
reply_to = message.reply_to_message
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
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:
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)
return await event.delete()
def get_permission_name(is_plugin: bool, need_admin: bool, command: str) -> str:
""" Get permission name. """
"""Get permission name."""
if is_plugin:
return f"plugins_root.{command}" if need_admin else f"plugins.{command}"
else:
@ -147,7 +153,9 @@ def sudo_filter(permission: str):
if not _status_sudo():
return False
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()
if from_id not in sudo_list:
if message.chat.id in sudo_list:
@ -167,13 +175,15 @@ def from_self(message: Message) -> bool:
def from_msg_get_sudo_uid(message: Message) -> int:
""" Get the sudo uid from the message. """
"""Get the sudo uid from the message."""
from_id = message.from_user.id if message.from_user else message.sender_chat.id
return from_id if from_id in get_sudo_list() else message.chat.id
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):
@ -184,8 +194,13 @@ async def process_exit(start: int, _client, message=None):
msg: Message = await _client.get_messages(cid, mid)
if msg:
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"]
if message:
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.pages import admin_app, login_page
requestAdaptor = '''
requestAdaptor = """
requestAdaptor(api) {
api.headers["token"] = localStorage.getItem("token");
return api;
},
'''
responseAdaptor = '''
"""
responseAdaptor = """
responseAdaptor(api, payload, query, request, response) {
if (response.data.detail == '登录验证失败或已失效,请重新登录') {
window.location.href = '/login'
@ -23,8 +23,8 @@ responseAdaptor(api, payload, query, request, response) {
}
return payload
},
'''
icon_path = 'https://xtaolabs.com/pagermaid-logo.png'
"""
icon_path = "https://xtaolabs.com/pagermaid-logo.png"
app: FastAPI = FastAPI()
@ -36,25 +36,25 @@ def init_web():
allow_origins=Config.WEB_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"]
allow_headers=["*"],
)
@app.get('/', response_class=RedirectResponse)
@app.get("/", response_class=RedirectResponse)
async def index():
return '/admin'
return "/admin"
@app.get('/admin', response_class=HTMLResponse)
@app.get("/admin", response_class=HTMLResponse)
async def admin():
return admin_app.render(
site_title='PagerMaid-Pyro 后台管理',
site_title="PagerMaid-Pyro 后台管理",
site_icon=icon_path,
requestAdaptor=requestAdaptor,
responseAdaptor=responseAdaptor
responseAdaptor=responseAdaptor,
)
@app.get('/login', response_class=HTMLResponse)
@app.get("/login", response_class=HTMLResponse)
async def login():
return login_page.render(
site_title='登录 | PagerMaid-Pyro 后台管理',
site_title="登录 | PagerMaid-Pyro 后台管理",
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.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(bot_info_route)

View File

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

View File

@ -7,41 +7,41 @@ from pagermaid.web.api.utils import authentication
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():
alias = AliasManager()
return {
'status': 0,
'msg': 'ok',
'data': {
'items': alias.get_all_alias_dict(),
}
"status": 0,
"msg": "ok",
"data": {
"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):
data = data['items']
data = data["items"]
try:
await AliasManager.save_from_web(data)
return {
'status': 0,
'msg': '命令别名保存成功'
}
return {"status": 0, "msg": "命令别名保存成功"}
except Exception:
return {
'status': 1,
'msg': '命令别名保存失败'
}
return {"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):
alias = AliasManager()
return {
'status': 0,
'msg': '测试成功',
'data': {
'new_msg': alias.test_alias(message),
}
"status": 0,
"msg": "测试成功",
"data": {
"new_msg": alias.test_alias(message),
},
}

View File

@ -7,39 +7,40 @@ from pagermaid.web.api import authentication
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():
try:
groups = []
for data in await get_group_list():
data["status"] = ignore_groups_manager.check_id(data["id"])
groups.append(data)
return {
'status': 0,
'msg': 'ok',
'data': {
'groups': groups
}
}
return {"status": 0, "msg": "ok", "data": {"groups": groups}}
except BaseException:
return {
'status': -100,
'msg': '获取群组列表失败'
}
return {"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):
cid: int = data.get('id')
status: bool = data.get('status')
cid: int = data.get("id")
status: bool = data.get("status")
if status:
ignore_groups_manager.add_id(cid)
else:
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():
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.post('/login', response_class=JSONResponse)
@route.post("/login", response_class=JSONResponse)
async def login(user: UserModel):
if user.password != Config.WEB_SECRET_KEY:
return {
"status": -100,
"msg": "登录失败,请重新输入密钥"
}
return {"status": -100, "msg": "登录失败,请重新输入密钥"}
token = create_token()
return {
"status": 0,
"msg": "登录成功",
"data": {
"version": pgm_version_code,
"token": token
}
"msg": "登录成功",
"data": {"version": pgm_version_code, "token": token},
}

View File

@ -8,68 +8,68 @@ from pagermaid.web.api.utils import authentication
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():
plugins = [i.dict() for i in plugin_manager.plugins]
plugins.sort(key=lambda x: x['name'])
return {
'status': 0,
'msg': 'ok',
'data': {
'rows': plugins,
'total': len(plugins)
}
}
plugins.sort(key=lambda x: x["name"])
return {"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):
module_name: str = data.get('plugin')
status: bool = data.get('status')
module_name: str = data.get("plugin")
status: bool = data.get("status")
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:
plugin.enable()
else:
plugin.disable()
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):
module_name: str = data.get('plugin')
module_name: str = data.get("plugin")
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)
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():
await plugin_manager.load_remote_plugins()
plugins = [i.dict() for i in plugin_manager.remote_plugins]
plugins.sort(key=lambda x: x['name'])
return {
'status': 0,
'msg': 'ok',
'data': {
'rows': plugins,
'total': len(plugins)
}
}
plugins.sort(key=lambda x: x["name"])
return {"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):
module_name: str = data.get('plugin')
status: bool = data.get('status')
module_name: str = data.get("plugin")
status: bool = data.get("status")
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:
await plugin_manager.install_remote_plugin(module_name)
else:
plugin_manager.remove_plugin(module_name)
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.get('/log')
@route.get("/log")
async def get_log(token: Optional[str] = Header(...), num: Union[int, str] = 100):
if token != Config.WEB_SECRET_KEY:
return "非法请求"
@ -31,8 +31,8 @@ async def get_log(token: Optional[str] = Header(...), num: Union[int, str] = 100
return StreamingResponse(streaming_logs())
@route.get('/run_eval')
async def run_cmd(token: Optional[str] = Header(...), cmd: str = ''):
@route.get("/run_eval")
async def run_cmd(token: Optional[str] = Header(...), cmd: str = ""):
if token != Config.WEB_SECRET_KEY:
return "非法请求"
@ -45,8 +45,8 @@ async def run_cmd(token: Optional[str] = Header(...), cmd: str = ''):
return StreamingResponse(run_cmd_func()) if cmd else "无效命令"
@route.get('/run_sh')
async def run_sh(token: Optional[str] = Header(...), cmd: str = ''):
@route.get("/run_sh")
async def run_sh(token: Optional[str] = Header(...), cmd: str = ""):
if token != Config.WEB_SECRET_KEY:
return "非法请求"
@ -59,6 +59,6 @@ async def run_sh(token: Optional[str] = Header(...), cmd: str = ''):
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():
return (await get_status()).dict()

View File

@ -6,7 +6,7 @@ from jose import jwt
from pagermaid.config import Config
ALGORITHM = 'HS256'
ALGORITHM = "HS256"
TOKEN_EXPIRE_MINUTES = 30
@ -18,13 +18,14 @@ def authentication():
try:
jwt.decode(token, Config.WEB_SECRET_KEY, algorithms=ALGORITHM)
except (jwt.JWTError, jwt.ExpiredSignatureError, AttributeError):
raise HTTPException(status_code=400, detail='登录验证失败或已失效,请重新登录')
raise HTTPException(status_code=400, detail="登录验证失败或已失效,请重新登录")
return Depends(inner)
def create_token():
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)

View File

@ -5,20 +5,20 @@ html_base_path = Path(__file__).parent
def get_html(path: Path) -> str:
"""获取 HTML 模板。"""
with open(path, 'r', encoding='utf-8') as f:
with open(path, "r", encoding="utf-8") as f:
return f.read()
def get_logo() -> str:
"""获取 logo。"""
return get_html(html_base_path / 'logo.html')
return get_html(html_base_path / "logo.html")
def get_github_logo() -> str:
"""获取 github logo。"""
return get_html(html_base_path / 'github_logo.html')
return get_html(html_base_path / "github_logo.html")
def get_footer() -> str:
"""获取 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
main_form = Form(
title='命令别名',
initApi='get:/pagermaid/api/command_alias',
api='post:/pagermaid/api/command_alias',
submitText='保存',
title="命令别名",
initApi="get:/pagermaid/api/command_alias",
api="post:/pagermaid/api/command_alias",
submitText="保存",
body=[
InputSubForm(
name='items',
label='已设置的命令别名',
name="items",
label="已设置的命令别名",
multiple=True,
btnLabel='${alias} >> ${command}',
btnLabel="${alias} >> ${command}",
draggable=True,
addable=True,
removable=True,
addButtonText='添加命令别名',
addButtonText="添加命令别名",
showErrorMsg=False,
form=Form(
title='命令别名',
title="命令别名",
body=[
InputText(name='alias', label='命令别名', required=True),
InputText(name='command', label='原命令', required=True),
]
)
InputText(name="alias", label="命令别名", required=True),
InputText(name="command", label="原命令", required=True),
],
),
)
]
],
)
test_form = Form(
title='测试',
api='get:/pagermaid/api/test_command_alias?message=${message}',
submitText='测试',
title="测试",
api="get:/pagermaid/api/test_command_alias?message=${message}",
submitText="测试",
body=[
InputText(name='message', label='测试消息(无需输入逗号前缀)', required=True),
Static(className='text-red-600', name='new_msg', label='命令别名修改后消息',
visibleOn="typeof data.new_msg !== 'undefined'")
]
InputText(name="message", label="测试消息(无需输入逗号前缀)", required=True),
Static(
className="text-red-600",
name="new_msg",
label="命令别名修改后消息",
visibleOn="typeof data.new_msg !== 'undefined'",
),
],
)
tips = Alert(level='info')
tips = Alert(level="info")
page = PageSchema(
url='/bot_config/command_alias',
icon='fa fa-link', label='命令别名',
schema=Page(
title='',
body=[tips, main_form, test_form]
)
url="/bot_config/command_alias",
icon="fa fa-link",
label="命令别名",
schema=Page(title="", 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, \
Dialog, Select, Group, InputText, DisplayModeEnum, Horizontal
from amis import (
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.web.html import get_logo
logo = Html(html=get_logo())
select_log_num = Select(
label='日志数量',
name='log_num',
label="日志数量",
name="log_num",
value=100,
options=[
{
'label': 100,
'value': 100
},
{
'label': 200,
'value': 200
},
{
'label': 300,
'value': 300
},
{
'label': 400,
'value': 400
},
{
'label': 500,
'value': 500
}
]
{"label": 100, "value": 100},
{"label": 200, "value": 200},
{"label": 300, "value": 300},
{"label": 400, "value": 400},
{"label": 500, "value": 500},
],
)
log_page = Log(
autoScroll=True,
placeholder='暂无日志数据...',
operation=['stop', 'showLineNumber', 'filter'],
placeholder="暂无日志数据...",
operation=["stop", "showLineNumber", "filter"],
source={
'method': 'get',
'url': '/pagermaid/api/log?num=${log_num | raw}',
'headers': {
'token': Config.WEB_SECRET_KEY
}
}
"method": "get",
"url": "/pagermaid/api/log?num=${log_num | raw}",
"headers": {"token": Config.WEB_SECRET_KEY},
},
)
cmd_input = Form(
@ -51,132 +52,126 @@ cmd_input = Form(
horizontal=Horizontal(left=0),
wrapWithPanel=False,
body=[
InputText(name='command', required=True, clearable=True, addOn=ActionType.Dialog(
label='执行',
level=LevelEnum.primary,
dialog=Dialog(
title='命令执行结果',
size='xl',
body=Log(
autoScroll=True,
placeholder='执行命令中,请稍候...',
operation=['stop', 'showLineNumber', 'filter'],
source={
'method': 'get',
'url': '/pagermaid/api/run_sh?cmd=${command | raw}',
'headers': {
'token': Config.WEB_SECRET_KEY
}
}),
)
))
]
InputText(
name="command",
required=True,
clearable=True,
addOn=ActionType.Dialog(
label="执行",
level=LevelEnum.primary,
dialog=Dialog(
title="命令执行结果",
size="xl",
body=Log(
autoScroll=True,
placeholder="执行命令中,请稍候...",
operation=["stop", "showLineNumber", "filter"],
source={
"method": "get",
"url": "/pagermaid/api/run_sh?cmd=${command | raw}",
"headers": {"token": Config.WEB_SECRET_KEY},
},
),
),
),
)
],
)
eval_input = Form(
mode=DisplayModeEnum.horizontal,
horizontal=Horizontal(left=0),
wrapWithPanel=False,
body=[
InputText(name='command', required=True, clearable=True, addOn=ActionType.Dialog(
label='执行',
InputText(
name="command",
required=True,
clearable=True,
addOn=ActionType.Dialog(
label="执行",
level=LevelEnum.primary,
dialog=Dialog(
title="命令执行结果",
size="xl",
body=Log(
autoScroll=True,
placeholder="执行命令中,请稍候...",
operation=["stop", "showLineNumber", "filter"],
source={
"method": "get",
"url": "/pagermaid/api/run_eval?cmd=${command | raw}",
"headers": {"token": Config.WEB_SECRET_KEY},
},
),
),
),
)
],
)
operation_button = Flex(
justify="center",
items=[
ActionType.Ajax(
label="更新",
api="/pagermaid/api/bot_update",
confirmText="该操作会更新 PagerMaid-Pyro ,请在更新完成后手动重启,请确认执行该操作",
level=LevelEnum.info,
),
ActionType.Ajax(
label="重启",
className="m-l",
api="/pagermaid/api/bot_restart",
confirmText="该操作会重启 PagerMaid-Pyro ,请耐心等待重启",
level=LevelEnum.danger,
),
ActionType.Dialog(
label="日志",
className="m-l",
level=LevelEnum.primary,
dialog=Dialog(
title='命令执行结果',
size='xl',
body=Log(
autoScroll=True,
placeholder='执行命令中,请稍候...',
operation=['stop', 'showLineNumber', 'filter'],
source={
'method': 'get',
'url': '/pagermaid/api/run_eval?cmd=${command | raw}',
'headers': {
'token': Config.WEB_SECRET_KEY
}
}),
)
))
]
title="查看日志",
size="xl",
actions=[],
body=[
Alert(
level=LevelEnum.info,
body='查看最近最多500条日志不会自动刷新需要手动点击两次"暂停键"来进行刷新。',
),
Form(body=[Group(body=[select_log_num]), log_page]),
],
),
),
ActionType.Dialog(
label="shell",
className="m-l",
level=LevelEnum.warning,
dialog=Dialog(title="shell", size="lg", actions=[], body=[cmd_input]),
),
ActionType.Dialog(
label="eval",
className="m-l",
level=LevelEnum.warning,
dialog=Dialog(title="eval", size="lg", actions=[], body=[eval_input]),
),
],
)
operation_button = Flex(justify='center', items=[
ActionType.Ajax(
label='更新',
api='/pagermaid/api/bot_update',
confirmText='该操作会更新 PagerMaid-Pyro ,请在更新完成后手动重启,请确认执行该操作',
level=LevelEnum.info
),
ActionType.Ajax(
label='重启',
className='m-l',
api='/pagermaid/api/bot_restart',
confirmText='该操作会重启 PagerMaid-Pyro ,请耐心等待重启',
level=LevelEnum.danger
),
ActionType.Dialog(
label='日志',
className='m-l',
level=LevelEnum.primary,
dialog=Dialog(title='查看日志',
size='xl',
actions=[],
body=[
Alert(level=LevelEnum.info,
body='查看最近最多500条日志不会自动刷新需要手动点击两次"暂停键"来进行刷新。'),
Form(
body=[Group(body=[select_log_num]), log_page]
)])
),
ActionType.Dialog(
label='shell',
className='m-l',
level=LevelEnum.warning,
dialog=Dialog(title='shell',
size='lg',
actions=[],
body=[cmd_input])
),
ActionType.Dialog(
label='eval',
className='m-l',
level=LevelEnum.warning,
dialog=Dialog(title='eval',
size='lg',
actions=[],
body=[eval_input])
)
])
status = Service(
api='/pagermaid/api/status',
api="/pagermaid/api/status",
body=Property(
title='运行信息',
title="运行信息",
column=2,
items=[
Property.Item(
label='Bot 版本',
content='${version}'
),
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
),
]
)
Property.Item(label="Bot 版本", content="${version}"),
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 = PageSchema(url='/home', label='首页', icon='fa fa-home', isDefaultPage=True, schema=page_detail)
page_detail = Page(title="", body=[logo, operation_button, Divider(), status])
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(
header=Card.Header(
title='$title',
description='$id',
avatarText='$title',
avatarTextClassName='overflow-hidden'
title="$title",
description="$id",
avatarText="$title",
avatarTextClassName="overflow-hidden",
),
actions=[],
toolbar=[
Switch(
name='enable',
value='${status}',
onText='已忽略',
offText='未忽略',
name="enable",
value="${status}",
onText="已忽略",
offText="未忽略",
onEvent={
'change': {
'actions': {
'actionType': 'ajax',
'args': {
'api': {
'url': '/pagermaid/api/set_ignore_group_status',
'method': 'post'
"change": {
"actions": {
"actionType": "ajax",
"args": {
"api": {
"url": "/pagermaid/api/set_ignore_group_status",
"method": "post",
},
'messages': {
'success': '成功${IF(event.data.value, "忽略", "取消忽略")}了 ${title}',
'failed': '操作失败'
"messages": {
"success": '成功${IF(event.data.value, "忽略", "取消忽略")}了 ${title}',
"failed": "操作失败",
},
'status': '${event.data.value}',
'id': '${id}'
}
"status": "${event.data.value}",
"id": "${id}",
},
}
}
}
},
)
]
],
)
cards_curd = CardsCRUD(
mode='cards',
title='',
mode="cards",
title="",
syncLocation=False,
api='/pagermaid/api/get_ignore_group_list',
api="/pagermaid/api/get_ignore_group_list",
loadDataOnce=True,
source='${groups | filter:title:match:keywords_name}',
filter={
'body': [
InputText(name='keywords_name', label='群组名')
]
},
source="${groups | filter:title:match:keywords_name}",
filter={"body": [InputText(name="keywords_name", label="群组名")]},
perPage=12,
autoJumpToTopOnPagerChange=True,
placeholder='群组列表为空',
footerToolbar=['switch-per-page', 'pagination'],
placeholder="群组列表为空",
footerToolbar=["switch-per-page", "pagination"],
columnsCount=3,
card=card
card=card,
)
page = PageSchema(
url='/bot_config/ignore_groups',
icon='fa fa-ban',
label='忽略群组',
schema=Page(title='忽略群组', subTitle="忽略后Bot 不再响应指定群组的消息(群组列表将会缓存一小时)", body=cards_curd)
url="/bot_config/ignore_groups",
icon="fa fa-ban",
label="忽略群组",
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
logo = Html(html=get_logo())
login_api = AmisAPI(
url='/pagermaid/api/login',
method='post',
adaptor='''
url="/pagermaid/api/login",
method="post",
adaptor="""
if (payload.status == 0) {
localStorage.setItem("token", payload.data.token);
}
return payload;
'''
""",
)
login_form = Form(
api=login_api,
title='',
title="",
body=[
InputPassword(
name='password',
label='密码',
labelRemark=Remark(shape='circle', content='登录密码')
name="password",
label="密码",
labelRemark=Remark(shape="circle", content="登录密码"),
),
],
mode=DisplayModeEnum.horizontal,
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)
login_page = Page(title='', body=[logo, body])
body = Wrapper(className="w-2/5 mx-auto my-0 m:w-full", body=login_form)
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
github_logo = Tpl(
className='w-full',
className="w-full",
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(
brandName='pagermaid',
logo='https://xtaolabs.com/pagermaid-logo.png',
brandName="pagermaid",
logo="https://xtaolabs.com/pagermaid-logo.png",
header=header,
pages=[
{
'children': [
"children": [
home_page,
PageSchema(label='Bot 设置', icon='fa fa-wrench',
children=[command_alias_page, ignore_groups_page]),
PageSchema(label='插件管理', icon='fa fa-cube',
children=[plugin_local_manage_page, plugin_remote_manage_page]),
PageSchema(
label="Bot 设置",
icon="fa fa-wrench",
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(),
)
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(
header=Card.Header(
title='$name',
avatarText='$name',
avatarTextClassName='overflow-hidden'
title="$name", avatarText="$name", avatarTextClassName="overflow-hidden"
),
actions=[],
toolbar=[
Switch(
name='enable',
value='${status}',
onText='启用',
offText='禁用',
name="enable",
value="${status}",
onText="启用",
offText="禁用",
onEvent={
'change': {
'actions': [
"change": {
"actions": [
{
'actionType': 'ajax',
'args': {
'api': {
'url': '/pagermaid/api/set_local_plugin_status',
'method': 'post'
"actionType": "ajax",
"args": {
"api": {
"url": "/pagermaid/api/set_local_plugin_status",
"method": "post",
},
'messages': {
'success': '成功${IF(event.data.value, "开启", "禁用")}了 ${name}',
'failed': '操作失败'
"messages": {
"success": '成功${IF(event.data.value, "开启", "禁用")}了 ${name}',
"failed": "操作失败",
},
'status': '${event.data.value}',
'plugin': '${name}'
}
"status": "${event.data.value}",
"plugin": "${name}",
},
},
]
}
}
},
)
]
],
)
cards_curd = CardsCRUD(
mode='cards',
title='',
mode="cards",
title="",
syncLocation=False,
api='/pagermaid/api/get_local_plugins',
api="/pagermaid/api/get_local_plugins",
loadDataOnce=True,
source='${rows | filter:name:match:keywords_name}',
filter={
'body': [
InputText(name='keywords_name', label='插件名')
]
},
source="${rows | filter:name:match:keywords_name}",
filter={"body": [InputText(name="keywords_name", label="插件名")]},
perPage=12,
autoJumpToTopOnPagerChange=True,
placeholder='暂无插件信息',
footerToolbar=['switch-per-page', 'pagination'],
placeholder="暂无插件信息",
footerToolbar=["switch-per-page", "pagination"],
columnsCount=3,
card=card
card=card,
)
page = PageSchema(
url='/plugins/local',
icon='fa fa-database',
label='本地插件管理',
schema=Page(title='本地插件管理', body=cards_curd)
url="/plugins/local",
icon="fa fa-database",
label="本地插件管理",
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(
header=Card.Header(
title='$name',
description='$des',
avatarText='$name',
avatarTextClassName='overflow-hidden'
title="$name",
description="$des",
avatarText="$name",
avatarTextClassName="overflow-hidden",
),
actions=[],
toolbar=[
Switch(
name='enable',
value='${status}',
onText='已安装',
offText='未安装',
name="enable",
value="${status}",
onText="已安装",
offText="未安装",
onEvent={
'change': {
'actions': {
'actionType': 'ajax',
'args': {
'api': {
'url': '/pagermaid/api/set_remote_plugin_status',
'method': 'post'
"change": {
"actions": {
"actionType": "ajax",
"args": {
"api": {
"url": "/pagermaid/api/set_remote_plugin_status",
"method": "post",
},
'messages': {
'success': '成功${IF(event.data.value, "安装", "卸载")}了 ${name}',
'failed': '操作失败'
"messages": {
"success": '成功${IF(event.data.value, "安装", "卸载")}了 ${name}',
"failed": "操作失败",
},
'status': '${event.data.value}',
'plugin': '${name}'
}
"status": "${event.data.value}",
"plugin": "${name}",
},
}
}
}
},
)
]
],
)
cards_curd = CardsCRUD(
mode='cards',
title='',
mode="cards",
title="",
syncLocation=False,
api='/pagermaid/api/get_remote_plugins',
api="/pagermaid/api/get_remote_plugins",
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={
'body': [
InputText(name='keywords_name', label='插件名'),
InputText(name='keywords_description', label='插件描述')
"body": [
InputText(name="keywords_name", label="插件名"),
InputText(name="keywords_description", label="插件描述"),
]
},
perPage=12,
autoJumpToTopOnPagerChange=True,
placeholder='暂无插件信息',
footerToolbar=['switch-per-page', 'pagination'],
placeholder="暂无插件信息",
footerToolbar=["switch-per-page", "pagination"],
columnsCount=3,
card=card
card=card,
)
page = PageSchema(
url='/plugins/remote',
icon='fa fa-cloud-download',
label='插件仓库',
schema=Page(title='插件仓库', body=cards_curd)
url="/plugins/remote",
icon="fa fa-cloud-download",
label="插件仓库",
schema=Page(title="插件仓库", body=cards_curd),
)

View File

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

View File

@ -54,12 +54,8 @@ class Client:
chat_id = chat.id
future = self.loop.create_future()
future.add_done_callback(
functools.partial(self.clear_listener, chat_id)
)
self.listening.update({
chat_id: {"future": future, "filters": filters}
})
future.add_done_callback(functools.partial(self.clear_listener, chat_id))
self.listening.update({chat_id: {"future": future, "filters": filters}})
try:
return await asyncio.wait_for(future, timeout)
except asyncio.exceptions.TimeoutError as e:
@ -81,11 +77,11 @@ class Client:
@patchable
def cancel_listener(self, chat_id):
listener = self.listening.get(chat_id)
if not listener or listener['future'].done():
if not listener or listener["future"].done():
return
listener['future'].set_exception(ListenerCanceled())
self.clear_listener(chat_id, listener['future'])
listener["future"].set_exception(ListenerCanceled())
self.clear_listener(chat_id, listener["future"])
@patchable
def cancel_all_listener(self):
@ -93,25 +89,25 @@ class Client:
self.cancel_listener(chat_id)
@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)
@patchable
async def read_chat_history(
self: "pyrogram.Client",
chat_id: Union[int, str],
max_id: int = 0
self: "pyrogram.Client", chat_id: Union[int, str], max_id: int = 0
) -> bool:
peer = await self.resolve_peer(chat_id)
if isinstance(peer, pyrogram.raw.types.InputPeerChannel):
with contextlib.suppress(pyrogram.errors.BadRequest): # noqa
with contextlib.suppress(pyrogram.errors.BadRequest): # noqa
topics: pyrogram.raw.types.messages.ForumTopics = await self.invoke(
pyrogram.raw.functions.channels.GetForumTopics(
channel=peer, # noqa
channel=peer, # noqa
offset_date=0,
offset_id=0,
offset_topic=0,
limit=0
limit=0,
)
)
for i in topics.topics:
@ -139,25 +135,25 @@ class MessageHandler:
@patchable
async def resolve_listener(self, client, message, *args):
listener = client.listening.get(message.chat.id)
if listener and not listener['future'].done():
listener['future'].set_result(message)
if listener and not listener["future"].done():
listener["future"].set_result(message)
else:
if listener and listener['future'].done():
client.clear_listener(message.chat.id, listener['future'])
if listener and listener["future"].done():
client.clear_listener(message.chat.id, listener["future"])
await self.user_callback(client, message, *args)
@patchable
async def check(self, client, update):
listener = client.listening.get(update.chat.id)
if listener and not listener['future'].done():
return await listener['filters'](client, update) if callable(listener['filters']) else True
if listener and not listener["future"].done():
return (
await listener["filters"](client, update)
if callable(listener["filters"])
else True
)
return (
await self.filters(client, update)
if callable(self.filters)
else True
)
return await self.filters(client, update) if callable(self.filters) else True
@patch(pyrogram.handlers.edited_message_handler.EditedMessageHandler)
@ -170,25 +166,25 @@ class EditedMessageHandler:
@patchable
async def resolve_listener(self, client, message, *args):
listener = client.listening.get(message.chat.id)
if listener and not listener['future'].done():
listener['future'].set_result(message)
if listener and not listener["future"].done():
listener["future"].set_result(message)
else:
if listener and listener['future'].done():
client.clear_listener(message.chat.id, listener['future'])
if listener and listener["future"].done():
client.clear_listener(message.chat.id, listener["future"])
await self.user_callback(client, message, *args)
@patchable
async def check(self, client, update):
listener = client.listening.get(update.chat.id)
if listener and not listener['future'].done():
return await listener['filters'](client, update) if callable(listener['filters']) else True
if listener and not listener["future"].done():
return (
await listener["filters"](client, update)
if callable(listener["filters"])
else True
)
return (
await self.filters(client, update)
if callable(self.filters)
else True
)
return await self.filters(client, update) if callable(self.filters) else True
@patch(pyrogram.types.user_and_chats.chat.Chat)
@ -208,21 +204,27 @@ class Chat(pyrogram.types.Chat):
@patchable
@staticmethod
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
return chat
@patchable
@staticmethod
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
return chat
@patchable
@staticmethod
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)
return chat
@ -251,23 +253,21 @@ class Message(pyrogram.types.Message):
async def safe_delete(self, revoke: bool = True):
try:
return await self._client.delete_messages(
chat_id=self.chat.id,
message_ids=self.id,
revoke=revoke
chat_id=self.chat.id, message_ids=self.id, revoke=revoke
)
except Exception: # noqa
return False
@patchable
def obtain_message(self) -> Optional[str]:
""" Obtains a message from either the reply message or command arguments. """
"""Obtains a message from either the reply message or command arguments."""
return self.arguments or (
self.reply_to_message.text if self.reply_to_message else None
)
@patchable
def obtain_user(self) -> Optional[int]:
""" Obtains a user from either the reply message or command arguments. """
"""Obtains a user from either the reply message or command arguments."""
user = None
# Priority: reply > argument > current_chat
if self.reply_to_message: # Reply to a user
@ -279,9 +279,14 @@ class Message(pyrogram.types.Message):
if raw_user.isnumeric():
user = int(raw_user)
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
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
return user
@ -291,13 +296,13 @@ class Message(pyrogram.types.Message):
@patchable
async def edit_text(
self,
text: str,
parse_mode: Optional["pyrogram.enums.ParseMode"] = None,
entities: List["pyrogram.types.MessageEntity"] = None,
disable_web_page_preview: bool = None,
reply_markup: "pyrogram.types.InlineKeyboardMarkup" = None,
no_reply: bool = None,
self,
text: str,
parse_mode: Optional["pyrogram.enums.ParseMode"] = None,
entities: List["pyrogram.types.MessageEntity"] = None,
disable_web_page_preview: bool = None,
reply_markup: "pyrogram.types.InlineKeyboardMarkup" = None,
no_reply: bool = None,
) -> "Message":
msg = None
sudo_users = get_sudo_list()
@ -317,7 +322,7 @@ class Message(pyrogram.types.Message):
text=text,
parse_mode=parse_mode,
disable_web_page_preview=disable_web_page_preview,
quote=True
quote=True,
)
elif is_self:
msg = await self._client.edit_message_text(
@ -327,14 +332,14 @@ class Message(pyrogram.types.Message):
parse_mode=parse_mode,
entities=entities,
disable_web_page_preview=disable_web_page_preview,
reply_markup=reply_markup
reply_markup=reply_markup,
)
elif not no_reply:
msg = await self.reply(
text=text,
parse_mode=parse_mode,
disable_web_page_preview=disable_web_page_preview,
quote=True
quote=True,
)
else:
try:
@ -345,9 +350,11 @@ class Message(pyrogram.types.Message):
parse_mode=parse_mode,
entities=entities,
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:
msg = await self.reply(
text=text,
@ -355,15 +362,13 @@ class Message(pyrogram.types.Message):
entities=entities,
disable_web_page_preview=disable_web_page_preview,
reply_markup=reply_markup,
quote=True
quote=True,
)
else:
with open("output.log", "w+") as file:
file.write(text)
msg = await self._client.send_document(
chat_id=self.chat.id,
document="output.log",
reply_to_message_id=self.id
chat_id=self.chat.id, document="output.log", reply_to_message_id=self.id
)
if not msg:
return self
@ -381,9 +386,11 @@ class Message(pyrogram.types.Message):
users: dict,
chats: dict,
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
if isinstance(message, pyrogram.raw.types.Message):
parsed.forum_topic = getattr(message.reply_to, "forum_topic", None)
@ -414,8 +421,8 @@ class Message(pyrogram.types.Message):
"pyrogram.types.InlineKeyboardMarkup",
"pyrogram.types.ReplyKeyboardMarkup",
"pyrogram.types.ReplyKeyboardRemove",
"pyrogram.types.ForceReply"
] = object
"pyrogram.types.ForceReply",
] = object,
) -> Union["pyrogram.types.Message", List["pyrogram.types.Message"]]:
if self.media:
self.text = None

View File

@ -30,14 +30,20 @@ async def sign_in_qrcode(
await client.session.stop()
await client.storage.dc_id(req.dc_id)
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, await client.storage.dc_id(),
await client.storage.auth_key(), await client.storage.test_mode()
client,
await client.storage.dc_id(),
await client.storage.auth_key(),
await client.storage.test_mode(),
)
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.is_bot(False)
return pyrogram.types.User._parse(client, req.authorization.user)
@ -51,8 +57,10 @@ async def authorize_by_qrcode(
client: Client,
):
print(f"Welcome to Pyrogram (version {pyrogram.__version__})")
print(f"Pyrogram is free software and comes with ABSOLUTELY NO WARRANTY. Licensed\n"
f"under the terms of the {pyrogram.__license__}.\n")
print(
f"Pyrogram is free software and comes with ABSOLUTELY NO WARRANTY. Licensed\n"
f"under the terms of the {pyrogram.__license__}.\n"
)
while True:
qrcode = None
@ -66,7 +74,9 @@ async def authorize_by_qrcode(
print(f"Password hint: {await client.get_password_hint()}")
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:
if client.password:
@ -96,8 +106,12 @@ async def authorize_by_qrcode(
except Exception:
print("Save qrcode.png failed.")
print(qr_obj.terminal())
print(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...")
print(
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)
elif isinstance(qrcode, pyrogram.types.User):
return qrcode
@ -114,7 +128,9 @@ async def start_client(client: Client):
await client.authorize()
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())
except (Exception, KeyboardInterrupt):

View File

@ -12,12 +12,14 @@ def _checks_cancelled(f):
raise asyncio.CancelledError("The conversation was cancelled before")
return f(self, *args, **kwargs)
return wrapper
class Conversation:
def __init__(self, client, chat_id: Union[int, str],
once_timeout: int = 60, filters=None):
def __init__(
self, client, chat_id: Union[int, str], once_timeout: int = 60, filters=None
):
self._client = client
self._chat_id = chat_id
self._once_timeout = once_timeout
@ -56,7 +58,9 @@ class Conversation:
async def ask(self, text, filters=None, timeout=None, *args, **kwargs):
filters = filters or self._filters
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
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)
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):
self._cancelled = True

View File

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

View File

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

View File

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

View File

@ -5,6 +5,7 @@ from sys import executable, exit
try:
from pyrogram.errors import ApiIdInvalid, PhoneNumberInvalid
from pyrogram import Client
print("Found an existing installation of Pyrogram...\nSuccessfully Imported.")
except ImportError:
print("Installing Pyrogram...")
@ -32,10 +33,14 @@ async def main():
"me",
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)
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)
except ValueError:
print("API HASH must not be empty!\nQuitting...")