nonebot2/none/command.py

128 lines
3.6 KiB
Python
Raw Normal View History

2018-06-15 06:58:24 +08:00
import re
2018-06-15 10:40:53 +08:00
from typing import Tuple, Union, Callable, Iterable, Dict, Any, Optional
2018-06-15 06:58:24 +08:00
from aiocqhttp import CQHttp
2018-06-15 10:40:53 +08:00
from . import permissions as perm
2018-06-15 06:58:24 +08:00
2018-06-15 10:40:53 +08:00
# Key: str (one segment of command name)
# Value: subtree or a leaf Command object
_registry = {}
2018-06-15 06:58:24 +08:00
# Key: str
# Value: tuple that identifies a command
2018-06-15 10:40:53 +08:00
_aliases = {}
2018-06-15 06:58:24 +08:00
# Key: context source
# Value: Command object
2018-06-15 10:40:53 +08:00
_sessions = {}
2018-06-15 06:58:24 +08:00
class Command:
2018-06-15 10:40:53 +08:00
__slots__ = ('name', 'func', 'permission')
2018-06-15 06:58:24 +08:00
2018-06-15 10:40:53 +08:00
def __init__(self, name: Tuple[str], func: Callable, permission: int):
2018-06-15 06:58:24 +08:00
self.name = name
2018-06-15 10:40:53 +08:00
self.func = func
self.permission = permission
2018-06-15 06:58:24 +08:00
2018-06-15 10:40:53 +08:00
async def run(self, bot, ctx, session,
*args, **kwargs) -> Any:
2018-06-15 06:58:24 +08:00
# TODO: check permission
2018-06-15 10:40:53 +08:00
if isinstance(self.func, Callable):
return await self.func(bot, ctx, session)
return None
def _find_command(name: Tuple[str]) -> Optional[Command]:
if not name:
return None
cmd_tree = _registry
for part in name[:-1]:
if part not in cmd_tree:
return False
cmd_tree = cmd_tree[part]
return cmd_tree.get(name[-1])
class Session:
__slots__ = ('cmd', 'arg', 'images', 'data', 'last_interaction')
def __init__(self, cmd: Command, arg: str = ''):
self.cmd = cmd
self.arg = arg
self.images = []
self.data = {}
self.last_interaction = None
2018-06-15 06:58:24 +08:00
async def handle_command(bot: CQHttp, ctx: Dict[str, Any]) -> bool:
# TODO: check if there is a session
msg_text = ctx['message'].extract_plain_text().lstrip()
for start in bot.config.COMMAND_START:
if isinstance(start, type(re.compile(''))):
m = start.search(msg_text)
if m:
full_command = msg_text[len(m.group(0)):].lstrip()
break
elif isinstance(start, str):
if msg_text.startswith(start):
full_command = msg_text[len(start):].lstrip()
break
else:
# it's not a command
return False
if not full_command:
# command is empty
return False
cmd_name_text, *cmd_remained = full_command.split(maxsplit=1)
2018-06-15 10:40:53 +08:00
cmd_name = _aliases.get(cmd_name_text)
2018-06-15 06:58:24 +08:00
if not cmd_name:
for sep in bot.config.COMMAND_SEP:
if isinstance(sep, type(re.compile(''))):
cmd_name = tuple(sep.split(cmd_name_text))
break
elif isinstance(sep, str):
cmd_name = tuple(cmd_name_text.split(sep))
break
else:
cmd_name = (cmd_name_text,)
2018-06-15 10:40:53 +08:00
cmd = _find_command(cmd_name)
if not cmd:
return False
session = Session(cmd, ''.join(cmd_remained))
session.images = [s.data['url'] for s in ctx['message']
if s.type == 'image' and 'url' in s.data]
await cmd.run(bot, ctx, session)
return True
2018-06-15 06:58:24 +08:00
def on_command(name: Union[str, Tuple[str]], aliases: Iterable = (),
permission: int = perm.EVERYONE) -> Callable:
def deco(func: Callable) -> Callable:
if not isinstance(name, (str, tuple)):
raise TypeError('the name of a command must be a str or tuple')
if not name:
raise ValueError('the name of a command must not be empty')
cmd_name = name if isinstance(name, tuple) else (name,)
2018-06-15 10:40:53 +08:00
current_parent = _registry
2018-06-15 06:58:24 +08:00
for parent_key in cmd_name[:-1]:
current_parent[parent_key] = {}
current_parent = current_parent[parent_key]
2018-06-15 10:40:53 +08:00
current_parent[cmd_name[-1]] = Command(
name=cmd_name, func=func, permission=permission)
2018-06-15 06:58:24 +08:00
for alias in aliases:
2018-06-15 10:40:53 +08:00
_aliases[alias] = cmd_name
2018-06-15 06:58:24 +08:00
return func
return deco