🔖 Update to v1.4.11

feat: support sub command
fix: reload plugin
This commit is contained in:
xtaodada 2024-04-29 22:05:03 +08:00
parent 7cb481d0fc
commit 19b102486d
Signed by: xtaodada
GPG Key ID: 4CBB3F4FA8C85659
9 changed files with 458 additions and 200 deletions

View File

@ -22,8 +22,8 @@ from pagermaid.scheduler import scheduler
import pyromod.listen
from pyrogram import Client
pgm_version = "1.4.10"
pgm_version_code = 1410
pgm_version = "1.4.11"
pgm_version_code = 1411
CMD_LIST = {}
module_dir = __path__[0]
working_dir = getcwd()

View File

@ -6,10 +6,10 @@ from typing import Optional, List, Tuple, Dict
from pydantic import BaseModel, ValidationError
import pagermaid.modules
from pagermaid import Config, logs
from pagermaid.enums import Message
from pagermaid.common.cache import cache
from pagermaid.modules import plugin_list as active_plugins
from pagermaid.utils import client
from pagermaid.services import sqlite
@ -33,6 +33,7 @@ class LocalPlugin(BaseModel):
@property
def load_status(self) -> bool:
"""插件加载状态"""
active_plugins = pagermaid.modules.plugin_list
return self.name in active_plugins
def remove(self):
@ -307,6 +308,7 @@ class PluginManager:
) -> Tuple[List[str], List[LocalPlugin], List[LocalPlugin]]:
"""Get plugins status"""
all_local_plugins = self.plugins
active_plugins = pagermaid.modules.plugin_list
disabled_plugins = []
inactive_plugins = []
for plugin in all_local_plugins:

View File

@ -0,0 +1,52 @@
from typing import NewType, Callable, Any, Awaitable, Union, TYPE_CHECKING, Optional
from ..inject import inject
if TYPE_CHECKING:
from . import Client, Message
CommandHandlerFunc = NewType("CommandHandlerFunc", Callable[[Any, Any], Awaitable[Any]])
CommandHandlerDecorator = NewType(
"CommandHandlerDecorator",
Callable[[Union["CommandHandler", CommandHandlerFunc]], "CommandHandler"],
)
class CommandHandler:
def __init__(self, func: CommandHandlerFunc, command: Optional[str]) -> None:
self._pgp_func__: CommandHandlerFunc = func
self._pgp_command__: Optional[str] = command
self._pgp_raw_handler = None
def func(self) -> CommandHandlerFunc:
return self._pgp_func__
def set_handler(self, handler):
self._pgp_raw_handler = handler
def get_handler(self):
return self._pgp_raw_handler
async def handler(self, client: "Client", message: "Message"):
func = self.func()
if data := inject(message, func):
await func(**data)
else:
if func.__code__.co_argcount == 0:
await func()
if func.__code__.co_argcount == 1:
await func(message)
elif func.__code__.co_argcount == 2:
await func(client, message)
def sub_command(self, **kwargs) -> CommandHandlerDecorator:
if self._pgp_command__ is None:
raise ValueError("Cannot add subcommand to a handler without a command")
if self._pgp_raw_handler is None:
raise ValueError("Cannot add subcommand to a handler without init")
from pagermaid.listener import listener
def decorator(func: CommandHandlerFunc) -> CommandHandlerFunc:
return listener(__parent_command=self._pgp_command__, **kwargs)(func)
return decorator

View File

@ -103,12 +103,14 @@ class Hook:
logs.info(f"[shutdown]: {type(exception)}: {exception}")
@staticmethod
async def command_pre(message: Message, command):
async def command_pre(message: Message, command, sub_command):
cors = []
try:
for pre in hook_functions["command_pre"]:
try:
data = inject(message, pre, command=command)
data = inject(
message, pre, command=command, sub_command=sub_command
)
except Exception as exception:
logs.info(f"[process_error]: {type(exception)}: {exception}")
continue
@ -124,12 +126,14 @@ class Hook:
logs.info(f"[command_pre]: {type(exception)}: {exception}")
@staticmethod
async def command_post(message: Message, command):
async def command_post(message: Message, command, sub_command):
cors = []
try:
for post in hook_functions["command_post"]:
try:
data = inject(message, post, command=command)
data = inject(
message, post, command=command, sub_command=sub_command
)
except Exception as exception:
logs.info(f"[process_error]: {type(exception)}: {exception}")
continue

View File

@ -17,8 +17,8 @@ from pyrogram.handlers import MessageHandler, EditedMessageHandler
from pagermaid import help_messages, logs, Config, bot, read_context, all_permissions
from pagermaid.common.ignore import ignore_groups_manager
from pagermaid.enums.command import CommandHandler, CommandHandlerDecorator
from pagermaid.group_manager import Permission
from pagermaid.inject import inject
from pagermaid.single_utils import (
Message,
AlreadyInConversationError,
@ -41,9 +41,11 @@ from pyromod.utils import mod_filters
_lock = asyncio.Lock()
def listener(**args):
def listener(**args) -> CommandHandlerDecorator:
"""Register an event listener."""
parent_command = args.get("__parent_command")
command = args.get("command")
allow_parent = args.get("allow_parent", False)
disallow_alias = args.get("disallow_alias", False)
need_admin = args.get("need_admin", False)
description = args.get("description")
@ -70,15 +72,20 @@ def listener(**args):
priority = 0
if command is not None:
if command in help_messages:
if parent_command is None and command in help_messages:
if help_messages[alias_command(command)]["priority"] <= priority:
raise ValueError(
f"{lang('error_prefix')} {lang('command')} \"{command}\" {lang('has_reg')}"
)
else:
block_process = True
pattern = rf"^(,|){alias_command(command, disallow_alias)}(?: |$)([\s\S]*)"
sudo_pattern = rf"^(/){alias_command(command, disallow_alias)}(?: |$)([\s\S]*)"
real_command = (
alias_command(command, disallow_alias)
if parent_command is None
else f"{parent_command} {command}"
)
pattern = rf"^(,|){real_command}(?: |$)([\s\S]*)"
sudo_pattern = rf"^(/){real_command}(?: |$)([\s\S]*)"
if pattern is not None and not pattern.startswith("(?i)"):
args["pattern"] = f"(?i){pattern}"
else:
@ -137,7 +144,14 @@ def listener(**args):
if "block_process" in args:
del args["block_process"]
def decorator(function):
def decorator(function) -> CommandHandler:
func = CommandHandler(
function,
alias_command(command, disallow_alias)
if command and parent_command is None
else None,
)
async def handler(client: Client, message: Message):
try:
# ignore
@ -149,11 +163,15 @@ def listener(**args):
except BaseException:
pass
try:
parameter = message.matches[0].group(2).split(" ")
arguments = message.matches[0].group(2)
parameter = arguments.split(" ")
if parameter == [""]:
parameter = []
if parent_command is not None and command is not None:
parameter.insert(0, command)
arguments = f"{command} {arguments}".strip()
message.parameter = parameter
message.arguments = message.matches[0].group(2)
message.arguments = arguments
except BaseException:
message.parameter = None
message.arguments = None
@ -164,18 +182,18 @@ def listener(**args):
read_context[(message.chat.id, message.id)] = True
if command:
await Hook.command_pre(message, command)
if data := inject(message, function):
await function(**data)
else:
if function.__code__.co_argcount == 0:
await function()
if function.__code__.co_argcount == 1:
await function(message)
elif function.__code__.co_argcount == 2:
await function(client, message)
await Hook.command_pre(
message,
parent_command or command,
command if parent_command else None,
)
await func.handler(client, message)
if command:
await Hook.command_post(message, command)
await Hook.command_post(
message,
parent_command or command,
command if parent_command else None,
)
except StopPropagation as e:
raise StopPropagation from e
except KeyboardInterrupt as e:
@ -235,30 +253,38 @@ def listener(**args):
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:
if block_process or (parent_command and not allow_parent):
message.stop_propagation()
message.continue_propagation()
bot.add_handler(
MessageHandler(handler, filters=base_filters), group=0 + priority
bot.dispatcher.add_handler(
MessageHandler(handler, filters=base_filters),
group=0 + priority,
first=parent_command and not allow_parent,
)
if command:
bot.add_handler(
MessageHandler(handler, filters=sudo_filters), group=50 + priority
bot.dispatcher.add_handler(
MessageHandler(handler, filters=sudo_filters),
group=50 + priority,
first=parent_command and not allow_parent,
)
if not ignore_edited:
bot.add_handler(
EditedMessageHandler(handler, filters=base_filters), group=1 + priority
bot.dispatcher.add_handler(
EditedMessageHandler(handler, filters=base_filters),
group=1 + priority,
first=parent_command and not allow_parent,
)
if command:
bot.add_handler(
bot.dispatcher.add_handler(
EditedMessageHandler(handler, filters=sudo_filters),
group=51 + priority,
first=parent_command and not allow_parent,
)
return handler
func.set_handler(handler)
return func
if description is not None and command is not None:
if description is not None and command is not None and parent_command is None:
if parameters is None:
parameters = ""
help_messages.update(

View File

@ -1,6 +1,7 @@
""" PagerMaid module for different ways to avoid users. """
from pagermaid import log
from pagermaid.enums.command import CommandHandler
from pagermaid.single_utils import sqlite
from pagermaid.utils import lang
from pagermaid.enums import Client, Message
@ -14,14 +15,25 @@ from pagermaid.listener import listener
description=lang("ghost_des"),
parameters="<true|false|status>",
)
async def ghost(client: Client, message: Message):
async def ghost(message: Message):
"""Toggles ghosting of a user."""
if len(message.parameter) != 1:
await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")
return
await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")
ghost: "CommandHandler"
@ghost.sub_command(
is_plugin=False,
outgoing=True,
command="true",
)
async def ghost_true(client: Client, message: Message):
myself = await client.get_me()
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"))
sqlite[f"ghosted.chat_id.{str(message.chat.id)}"] = True
@ -29,7 +41,16 @@ async def ghost(client: Client, message: Message):
await log(
f"{lang('ghost_set_f')} ChatID {str(message.chat.id)} {lang('ghost_set_l')}"
)
elif message.parameter[0] == "false":
@ghost.sub_command(
is_plugin=False,
outgoing=True,
command="false",
)
async def ghost_false(client: Client, message: Message):
myself = await client.get_me()
self_user_id = myself.id
if message.chat.id == self_user_id:
await message.edit(lang("ghost_e_mark"))
return
@ -41,13 +62,18 @@ async def ghost(client: Client, message: Message):
await log(
f"{lang('ghost_set_f')} ChatID {str(message.chat.id)} {lang('ghost_cancel')}"
)
elif message.parameter[0] == "status":
@ghost.sub_command(
is_plugin=False,
outgoing=True,
command="status",
)
async def ghost_status(message: Message):
if sqlite.get(f"ghosted.chat_id.{str(message.chat.id)}", None):
await message.edit(lang("ghost_e_exist"))
else:
await message.edit(lang("ghost_e_noexist"))
else:
await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")
@listener(
@ -58,20 +84,42 @@ async def ghost(client: Client, message: Message):
description=lang("deny_des"),
parameters="<true|false|status>",
)
async def deny(client: Client, message: Message):
async def deny(message: Message):
"""Toggles denying of a user."""
if len(message.parameter) != 1:
await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")
return
await message.edit(f"{lang('error_prefix')}{lang('arg_error')}")
deny: "CommandHandler"
@deny.sub_command(
is_plugin=False,
outgoing=True,
need_admin=True,
command="true",
)
async def deny_true(client: Client, message: Message):
myself = await client.get_me()
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"))
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":
@deny.sub_command(
is_plugin=False,
outgoing=True,
need_admin=True,
command="false",
)
async def deny_false(client: Client, message: Message):
myself = await client.get_me()
self_user_id = myself.id
if message.chat.id == self_user_id:
await message.edit(lang("ghost_e_mark"))
return
@ -81,13 +129,19 @@ async def deny(client: Client, message: Message):
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":
@deny.sub_command(
is_plugin=False,
outgoing=True,
need_admin=True,
command="status",
)
async def deny_status(message: Message):
if sqlite.get(f"denied.chat_id.{str(message.chat.id)}", None):
await message.edit(lang("deny_e_exist"))
else:
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)

View File

@ -134,7 +134,7 @@ async def mixpanel_init_id(bot: Client):
@Hook.command_postprocessor()
async def mixpanel_report(bot: Client, message: Message, command):
async def mixpanel_report(bot: Client, message: Message, command, sub_command):
if not Config.ALLOW_ANALYTIC:
return
await set_people(bot)
@ -144,11 +144,14 @@ 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
properties = {"command": command, "bot_id": bot.me.id}
if sub_command:
properties["sub_command"] = sub_command
bot.loop.create_task(
mp.track(
str(sender_id),
f"Function {command}",
{"command": command, "bot_id": bot.me.id},
properties,
)
)

View File

@ -1,3 +1,4 @@
from pagermaid.enums.command import CommandHandler
from pagermaid.single_utils import sqlite
from pagermaid.listener import listener
from pagermaid.group_manager import (
@ -29,11 +30,21 @@ def from_msg_get_sudo_id(message: Message) -> int:
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):
async def sudo_change(message: Message):
"""To enable or disable sudo of your userbot."""
input_str = message.arguments
await edit_delete(message, lang("arg_error"))
sudo_change: "CommandHandler"
@sudo_change.sub_command(
is_plugin=False,
command="on",
need_admin=True,
)
async def sudo_on(message: Message):
sudo = get_sudo_list()
if input_str == "on":
if _status_sudo():
return await edit_delete(message, lang("sudo_has_enabled"))
sqlite["sudo_enable"] = True
@ -46,7 +57,15 @@ async def sudo_change(client: Client, message: Message):
return await message.edit(
text,
)
elif input_str == "off":
@sudo_change.sub_command(
is_plugin=False,
command="off",
need_admin=True,
)
async def sudo_off(message: Message):
sudo = get_sudo_list()
if _status_sudo():
del sqlite["sudo_enable"]
text = f"__{lang('sudo_disable')}__\n"
@ -59,7 +78,15 @@ async def sudo_change(client: Client, message: Message):
text,
)
await edit_delete(message, lang("sudo_has_disabled"))
elif input_str == "add":
@sudo_change.sub_command(
is_plugin=False,
command="add",
need_admin=True,
)
async def sudo_add(message: Message):
sudo = get_sudo_list()
from_id = from_msg_get_sudo_id(message)
if from_id in sudo:
return await edit_delete(message, f"__{lang('sudo_add')}__")
@ -70,7 +97,15 @@ async def sudo_change(client: Client, message: Message):
await message.edit(f"__{lang('sudo_add')}__")
else:
await message.edit(f"__{lang('sudo_add_chat')}__")
elif input_str == "remove":
@sudo_change.sub_command(
is_plugin=False,
command="remove",
need_admin=True,
)
async def sudo_remove(message: Message):
sudo = get_sudo_list()
from_id = from_msg_get_sudo_id(message)
if from_id not in sudo:
return await edit_delete(message, f"__{lang('sudo_no')}__")
@ -80,7 +115,15 @@ async def sudo_change(client: Client, message: Message):
await message.edit(f"__{lang('sudo_remove')}__")
else:
await message.edit(f"__{lang('sudo_remove_chat')}__")
elif input_str == "list":
@sudo_change.sub_command(
is_plugin=False,
command="list",
need_admin=True,
)
async def sudo_list(client: Client, message: Message):
sudo = get_sudo_list()
if len(sudo) == 0:
return await edit_delete(message, f"__{lang('sudo_no_one')}__")
text = f"**{lang('sudo_list')}**\n\n"
@ -99,56 +142,108 @@ 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:
def check_parameter_length(length: int, check_permission: bool):
def decorator(func):
async def wrapper(message: Message):
if len(message.parameter) != length:
return await edit_delete(message, lang("arg_error"))
if check_permission:
sudo = get_sudo_list()
from_id = from_msg_get_sudo_id(message)
if message.parameter[0] == "glist":
if not (
data := permissions.get_permissions_for_user(
str(message.parameter[1])
if from_id not in sudo:
return await edit_delete(message, f"__{lang('sudo_no')}__")
return await func(message)
return wrapper
return decorator
@sudo_change.sub_command(
is_plugin=False,
command="glist",
need_admin=True,
)
):
@check_parameter_length(2, False)
async def sudo_glist(message: Message):
if not (data := permissions.get_permissions_for_user(str(message.parameter[1]))):
return await edit_delete(message, f"__{lang('sudo_group_list')}__")
text = f"**{message.parameter[1]} {lang('sudo_group_list')}**\n\n"
for i in data:
text += f" • `{'-' if i[2] == 'ejection' else ''}{i[1]}`\n"
return await message.edit(text)
if from_id not in sudo:
return await edit_delete(message, f"__{lang('sudo_no')}__")
elif message.parameter[0] == "gaddu":
@sudo_change.sub_command(
is_plugin=False,
command="gaddu",
need_admin=True,
)
@check_parameter_length(2, True)
async def sudo_gaddu(message: Message):
from_id = from_msg_get_sudo_id(message)
add_user_to_group(str(from_id), message.parameter[1])
return await message.edit(lang("sudo_group_add_user"))
elif message.parameter[0] == "gdelu":
@sudo_change.sub_command(
is_plugin=False,
command="gdelu",
need_admin=True,
)
@check_parameter_length(2, True)
async def sudo_gdelu(message: Message):
from_id = from_msg_get_sudo_id(message)
remove_user_from_group(str(from_id), message.parameter[1])
return await message.edit(lang("sudo_group_del_user"))
elif message.parameter[0] == "uaddp":
@sudo_change.sub_command(
is_plugin=False,
command="uaddp",
need_admin=True,
)
@check_parameter_length(2, True)
async def sudo_uaddp(message: Message):
from_id = from_msg_get_sudo_id(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])
@sudo_change.sub_command(
is_plugin=False,
command="udelp",
need_admin=True,
)
@check_parameter_length(2, True)
async def sudo_udelp(message: Message):
from_id = from_msg_get_sudo_id(message)
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"))
if len(message.parameter) == 3:
if message.parameter[0] == "gaddp":
add_permission_for_group(
message.parameter[1], Permission(message.parameter[2])
@sudo_change.sub_command(
is_plugin=False,
command="gaddp",
need_admin=True,
)
@check_parameter_length(3, False)
async def sudo_gaddp(message: Message):
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])
@sudo_change.sub_command(
is_plugin=False,
command="gdelp",
need_admin=True,
)
@check_parameter_length(3, False)
async def sudo_gdelp(message: Message):
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"))
else:
await edit_delete(message, lang("arg_error"))
else:
await edit_delete(message, lang("arg_error"))

View File

@ -21,6 +21,7 @@ along with pyromod. If not, see <https://www.gnu.org/licenses/>.
import asyncio
import contextlib
import functools
from collections import OrderedDict
from datetime import datetime
from typing import Optional, List, Union
@ -425,3 +426,24 @@ class Dispatcher(pyrogram.dispatcher.Dispatcher): # noqa
lock.release()
self.loop.create_task(fn())
@patchable
def add_handler(self, handler, group: int, first: bool):
if not first:
return self.oldadd_handler(handler, group)
async def fn():
for lock in self.locks_list:
await lock.acquire()
try:
if group not in self.groups:
self.groups[group] = []
self.groups = OrderedDict(sorted(self.groups.items()))
self.groups[group].insert(0, handler)
finally:
for lock in self.locks_list:
lock.release()
self.loop.create_task(fn())