This commit is contained in:
Richard Chien 2018-06-25 12:41:12 +08:00
parent 844ade5c31
commit 492fed9b50
4 changed files with 102 additions and 45 deletions

View File

@ -17,7 +17,7 @@ default_handler.setFormatter(logging.Formatter(
logger.addHandler(default_handler) logger.addHandler(default_handler)
from .plugin import handle_message, handle_notice, handle_request from .plugin import handle_message, handle_notice, handle_request
from .command import on_command from .command import on_command, call_command
def create_bot(config_object: Any = None): def create_bot(config_object: Any = None):

View File

@ -19,17 +19,33 @@ _aliases = {}
_sessions = {} _sessions = {}
# TODO: session 保存为一个栈,命令可以调用命令,进入新的 session命令执行完毕
# 中间没有抛出异常(标志进入交互模式的异常),则从栈中 pop
class Command: class Command:
__slots__ = ('name', 'func', 'permission') __slots__ = ('name', 'func', 'permission', 'args_parser')
def __init__(self, name: Tuple[str], func: Callable, permission: int): def __init__(self, name: Tuple[str], func: Callable, permission: int):
self.name = name self.name = name
self.func = func self.func = func
self.permission = permission self.permission = permission
self.args_parser = None
async def run(self, bot, session) -> bool: async def run(self, bot, session, *,
permission: int = None) -> bool:
if permission is None:
permission = await calculate_permission(bot, session.ctx)
if isinstance(self.func, Callable) and permission & self.permission:
if isinstance(self.args_parser, Callable):
self.args_parser(session)
await self.func(bot, session)
return True
return False
async def calculate_permission(bot: CQHttp, ctx: Dict[str, Any]) -> int:
permission = 0 permission = 0
ctx = session.ctx
if ctx['user_id'] in bot.config.SUPERUSERS: if ctx['user_id'] in bot.config.SUPERUSERS:
permission |= perm.IS_SUPERUSER permission |= perm.IS_SUPERUSER
if ctx['message_type'] == 'private': if ctx['message_type'] == 'private':
@ -55,24 +71,22 @@ class Command:
pass pass
elif ctx['message_type'] == 'discuss': elif ctx['message_type'] == 'discuss':
permission |= perm.IS_DISCUSS permission |= perm.IS_DISCUSS
return permission
if isinstance(self.func, Callable) and permission & self.permission:
await self.func(bot, session)
return True
return False
def _find_command(name: Tuple[str]) -> Optional[Command]: def _find_command(name: Union[str, Tuple[str]]) -> Optional[Command]:
if not name: cmd_name = name if isinstance(name, tuple) else (name,)
if not cmd_name:
return None return None
cmd_tree = _registry cmd_tree = _registry
for part in name[:-1]: for part in cmd_name[:-1]:
if part not in cmd_tree: if part not in cmd_tree:
return None return None
cmd_tree = cmd_tree[part] cmd_tree = cmd_tree[part]
return cmd_tree.get(name[-1]) return cmd_tree.get(cmd_name[-1])
class Session: class Session:
@ -80,15 +94,16 @@ class Session:
'current_key', 'current_arg', 'current_arg_text', 'current_key', 'current_arg', 'current_arg_text',
'images', 'args', 'last_interaction') 'images', 'args', 'last_interaction')
def __init__(self, cmd: Command, ctx: Dict[str, Any], def __init__(self, cmd: Command, ctx: Dict[str, Any], *,
current_arg: str = ''): current_arg: str = '', args: Dict[str, Any] = None):
self.cmd = cmd self.cmd = cmd
self.ctx = ctx self.ctx = ctx
self.current_key = None self.current_key = None
self.current_arg = current_arg self.current_arg = current_arg
self.current_arg_text = Message(current_arg).extract_plain_text() self.current_arg_text = Message(current_arg).extract_plain_text()
self.images = [] self.images = [s.data['url'] for s in ctx['message']
self.args = {} if s.type == 'image' and 'url' in s.data]
self.args = args or {}
self.last_interaction = None self.last_interaction = None
def require_arg(self, key: str, prompt: str = '', *, def require_arg(self, key: str, prompt: str = '', *,
@ -137,9 +152,8 @@ async def handle_command(bot: CQHttp, ctx: Dict[str, Any]) -> bool:
if not cmd: if not cmd:
return False return False
session = Session(cmd=cmd, ctx=ctx, current_arg=''.join(cmd_remained)) session = Session(cmd, ctx, current_arg=''.join(cmd_remained))
session.images = [s.data['url'] for s in ctx['message'] # TODO: 插入 session
if s.type == 'image' and 'url' in s.data]
return await cmd.run(bot, session) return await cmd.run(bot, session)
@ -156,13 +170,40 @@ def on_command(name: Union[str, Tuple[str]], aliases: Iterable = (),
for parent_key in cmd_name[:-1]: for parent_key in cmd_name[:-1]:
current_parent[parent_key] = {} current_parent[parent_key] = {}
current_parent = current_parent[parent_key] current_parent = current_parent[parent_key]
current_parent[cmd_name[-1]] = Command( cmd = Command(name=cmd_name, func=func, permission=permission)
name=cmd_name, func=func, permission=permission) current_parent[cmd_name[-1]] = cmd
for alias in aliases: for alias in aliases:
_aliases[alias] = cmd_name _aliases[alias] = cmd_name
# TODO: 给 func 添加一个 argparser 装饰器,用于注册它的参数解析器 def args_parser(parser_func: Callable):
cmd.args_parser = parser_func
return parser_func
func.args_parser = args_parser
return func return func
return deco return deco
async def call_command(name: Union[str, Tuple[str]],
bot: CQHttp, ctx: Dict[str, Any], **kwargs) -> bool:
"""
Call a command internally.
There is no permission restriction on this function,
which means any command can be called from any other command.
Unexpected users should be handled by the caller command's permission
option.
:param name: command name (str or tuple of str)
:param bot: CQHttp instance
:param ctx: event context
:param kwargs: other keyword args that will be passed to Session()
:return: the command is successfully called
"""
cmd = _find_command(name)
if cmd:
session = Session(cmd, ctx, **kwargs)
# TODO: 插入 session
return await cmd.run(bot, session, permission=perm.IS_SUPERUSER)
return False

View File

@ -11,6 +11,7 @@ GROUP = 0x0F00
SUPERUSER = 0xF000 SUPERUSER = 0xF000
EVERYONE = 0xFFFF EVERYONE = 0xFFFF
IS_NOBODY = 0x0000
IS_PRIVATE_FRIEND = PRIVATE_FRIEND IS_PRIVATE_FRIEND = PRIVATE_FRIEND
IS_PRIVATE_GROUP = PRIVATE_GROUP IS_PRIVATE_GROUP = PRIVATE_GROUP
IS_PRIVATE_DISCUSS = PRIVATE_DISCUSS IS_PRIVATE_DISCUSS = PRIVATE_DISCUSS

15
plugins/weather.py Normal file
View File

@ -0,0 +1,15 @@
import none
from none.command import Session
from none.helpers import send
@none.on_command('weather', aliases=('天气',))
async def weather(bot, session: Session):
city = session.require_arg('city', prompt='你想知道哪个城市的天气呢?')
await send(bot, session.ctx, f'你查询了{city}的天气')
@weather.args_parser
def _(session: Session):
if session.current_key:
session.args[session.current_key] = session.current_arg.strip()