From ae2d177d5ad74d41a13889083674143eb5aaa03c Mon Sep 17 00:00:00 2001 From: Richard Chien Date: Wed, 27 Jun 2018 22:05:12 +0800 Subject: [PATCH] Improve a lot --- none/__init__.py | 24 ++++++------- none/command.py | 33 +++++------------ none/helpers.py | 52 ++++++++++++++++++++++++++- none/logger.py | 9 +++++ none/{plugin.py => message.py} | 16 ++------- none/notice.py | 28 +++++++++++++++ none/plugins/base.py | 6 ++-- none/session.py | 24 +++++++++++++ none_demo/plugins/__init__.py | 0 none_demo/plugins/greeting.py | 22 ++++++++++++ none_demo/plugins/weather/__init__.py | 8 ++--- 11 files changed, 163 insertions(+), 59 deletions(-) create mode 100644 none/logger.py rename none/{plugin.py => message.py} (72%) create mode 100644 none/notice.py create mode 100644 none/session.py delete mode 100644 none_demo/plugins/__init__.py create mode 100644 none_demo/plugins/greeting.py diff --git a/none/__init__.py b/none/__init__.py index e284d5b7..d2bf2b1d 100644 --- a/none/__init__.py +++ b/none/__init__.py @@ -1,5 +1,4 @@ import os -import sys import importlib import logging import re @@ -9,15 +8,9 @@ from typing import Any from aiocqhttp import CQHttp from aiocqhttp.message import Message -logger = logging.getLogger('none') -default_handler = logging.StreamHandler(sys.stdout) -default_handler.setFormatter(logging.Formatter( - '[%(asctime)s] %(levelname)s: %(message)s' -)) -logger.addHandler(default_handler) - -from . import plugin -from .plugin import * +from .message import handle_message +from .notice import handle_notice +from .logger import logger def create_bot(config_object: Any = None): @@ -37,15 +30,16 @@ def create_bot(config_object: Any = None): @bot.on_message async def _(ctx): - asyncio.ensure_future(plugin.handle_message(bot, ctx)) + asyncio.ensure_future(handle_message(bot, ctx)) @bot.on_notice async def _(ctx): - asyncio.ensure_future(plugin.handle_notice(bot, ctx)) + asyncio.ensure_future(handle_notice(bot, ctx)) @bot.on_request async def _(ctx): - asyncio.ensure_future(plugin.handle_request(bot, ctx)) + pass + # asyncio.ensure_future(plugin.handle_request(bot, ctx)) return bot @@ -79,3 +73,7 @@ def load_plugins(plugin_dir: str, module_prefix: str): def load_builtin_plugins(): plugin_dir = os.path.join(os.path.dirname(__file__), 'plugins') load_plugins(plugin_dir, 'none.plugins') + + +from .command import on_command +from .notice import on_notice diff --git a/none/command.py b/none/command.py index b7ef1a68..fe3f4056 100644 --- a/none/command.py +++ b/none/command.py @@ -2,7 +2,7 @@ import re import asyncio from datetime import datetime from typing import ( - Tuple, Union, Callable, Iterable, Dict, Any, Optional, List, Sequence + Tuple, Union, Callable, Iterable, Dict, Any, Optional, Sequence ) from aiocqhttp import CQHttp, Error as CQHttpError @@ -11,6 +11,7 @@ from aiocqhttp.message import Message from . import permissions as perm from .helpers import context_source from .expression import render +from .session import BaseSession # Key: str (one segment of command name) # Value: subtree or a leaf Command object @@ -148,16 +149,14 @@ class _FurtherInteractionNeeded(Exception): pass -class Session: - __slots__ = ('bot', 'cmd', 'ctx', - 'current_key', 'current_arg', 'current_arg_text', +class CommandSession(BaseSession): + __slots__ = ('cmd', 'current_key', 'current_arg', 'current_arg_text', 'images', 'args', 'last_interaction') - def __init__(self, bot: CQHttp, cmd: Command, ctx: Dict[str, Any], *, + def __init__(self, bot: CQHttp, ctx: Dict[str, Any], cmd: Command, *, current_arg: str = '', args: Dict[str, Any] = None): - self.bot = bot + super().__init__(bot, ctx) self.cmd = cmd - self.ctx = ctx self.current_key = None self.current_arg = current_arg self.current_arg_text = Message(current_arg).extract_plain_text() @@ -218,23 +217,9 @@ class Session: asyncio.ensure_future(self.send(prompt)) raise _FurtherInteractionNeeded - async def send(self, - message: Union[str, Dict[str, Any], List[Dict[str, Any]]], - *, ignore_failure: bool = True) -> None: - try: - await self.bot.send(self.ctx, message) - except CQHttpError: - if not ignore_failure: - raise - - async def send_expr(self, - expr: Union[str, Sequence[str], Callable], - **kwargs): - return await self.send(render(expr, **kwargs)) - def _new_command_session(bot: CQHttp, - ctx: Dict[str, Any]) -> Optional[Session]: + ctx: Dict[str, Any]) -> Optional[CommandSession]: """ Create a new session for a command. @@ -245,7 +230,7 @@ def _new_command_session(bot: CQHttp, :param bot: CQHttp instance :param ctx: message context - :return: Session object or None + :return: CommandSession object or None """ msg_text = str(ctx['message']).lstrip() @@ -285,7 +270,7 @@ def _new_command_session(bot: CQHttp, if not cmd: return None - return Session(bot, cmd, ctx, current_arg=''.join(cmd_remained)) + return CommandSession(bot, ctx, cmd, current_arg=''.join(cmd_remained)) async def handle_command(bot: CQHttp, ctx: Dict[str, Any]) -> bool: diff --git a/none/helpers.py b/none/helpers.py index a9535ada..a8b580e5 100644 --- a/none/helpers.py +++ b/none/helpers.py @@ -1,4 +1,9 @@ -from typing import Dict, Any +from typing import Dict, Any, Union, List, Sequence, Callable, Optional + +from aiocqhttp import CQHttp, Error as CQHttpError +from aiocqhttp.bus import EventBus + +from . import expression def context_source(ctx: Dict[str, Any]) -> str: @@ -10,3 +15,48 @@ def context_source(ctx: Dict[str, Any]) -> str: if ctx.get('user_id'): src += f'/user/{ctx["user_id"]}' return src + + +async def send(bot: CQHttp, ctx: Dict[str, Any], + message: Union[str, Dict[str, Any], List[Dict[str, Any]]], + *, ignore_failure: bool = True) -> None: + try: + if ctx.get('post_type') == 'message': + await bot.send(ctx, message) + else: + ctx = ctx.copy() + if 'message' in ctx: + del ctx['message'] + if 'group_id' in ctx: + await bot.send_group_msg(**ctx, message=message) + elif 'discuss_id' in ctx: + await bot.send_discuss_msg(**ctx, message=message) + elif 'user_id' in ctx: + await bot.send_private_msg(**ctx, message=message) + except CQHttpError: + if not ignore_failure: + raise + + +async def send_expr(bot: CQHttp, ctx: Dict[str, Any], + expr: Union[str, Sequence[str], Callable], + **kwargs): + return await send(bot, ctx, expression.render(expr, **kwargs)) + + +def make_event_deco(post_type: str, bus: EventBus) -> Callable: + def deco_deco(arg: Optional[Union[str, Callable]] = None, + *events: str) -> Callable: + def deco(func: Callable) -> Callable: + if isinstance(arg, str): + for e in [arg] + list(events): + bus.subscribe(f'{post_type}.{e}', func) + else: + bus.subscribe(post_type, func) + return func + + if isinstance(arg, Callable): + return deco(arg) + return deco + + return deco_deco diff --git a/none/logger.py b/none/logger.py new file mode 100644 index 00000000..5842aeb0 --- /dev/null +++ b/none/logger.py @@ -0,0 +1,9 @@ +import sys +import logging + +logger = logging.getLogger('none') +default_handler = logging.StreamHandler(sys.stdout) +default_handler.setFormatter(logging.Formatter( + '[%(asctime)s] %(levelname)s: %(message)s' +)) +logger.addHandler(default_handler) diff --git a/none/plugin.py b/none/message.py similarity index 72% rename from none/plugin.py rename to none/message.py index 71995518..d1827966 100644 --- a/none/plugin.py +++ b/none/message.py @@ -3,12 +3,8 @@ from typing import Dict, Any from aiocqhttp import CQHttp from aiocqhttp.message import MessageSegment -from . import logger -from .command import on_command, handle_command - -__all__ = [ - 'on_command', -] +from .command import handle_command +from .logger import logger async def handle_message(bot: CQHttp, ctx: Dict[str, Any]) -> None: @@ -27,11 +23,3 @@ async def handle_message(bot: CQHttp, ctx: Dict[str, Any]) -> None: return else: await bot.send(ctx, '你在说什么我看不懂诶') - - -async def handle_notice(bot: CQHttp, ctx: Dict[str, Any]) -> None: - pass - - -async def handle_request(bot: CQHttp, ctx: Dict[str, Any]) -> None: - pass diff --git a/none/notice.py b/none/notice.py new file mode 100644 index 00000000..809fa19b --- /dev/null +++ b/none/notice.py @@ -0,0 +1,28 @@ +from typing import Dict, Any + +from aiocqhttp import CQHttp +from aiocqhttp.bus import EventBus + +from .session import BaseSession +from .helpers import make_event_deco +from .logger import logger + +_bus = EventBus() +on_notice = make_event_deco('notice', _bus) + + +class NoticeSession(BaseSession): + __slots__ = () + + def __init__(self, bot: CQHttp, ctx: Dict[str, Any]): + super().__init__(bot, ctx) + + +async def handle_notice(bot: CQHttp, ctx: Dict[str, Any]) -> None: + event = f'notice.{ctx["notice_type"]}' + if ctx.get('sub_type'): + event += f'.{ctx["sub_type"]}' + + session = NoticeSession(bot, ctx) + logger.debug(f'Emitting event: {event}') + await _bus.emit(event, session) diff --git a/none/plugins/base.py b/none/plugins/base.py index 2757d114..71a2806a 100644 --- a/none/plugins/base.py +++ b/none/plugins/base.py @@ -2,14 +2,14 @@ from aiocqhttp.message import unescape import none from none import permissions as perm -from none.command import Session +from none.command import CommandSession @none.on_command('echo') -async def echo(session: Session): +async def echo(session: CommandSession): await session.send(session.current_arg) @none.on_command('say', permission=perm.SUPERUSER) -async def _(session: Session): +async def _(session: CommandSession): await session.send(unescape(session.current_arg)) diff --git a/none/session.py b/none/session.py new file mode 100644 index 00000000..30a5b8c8 --- /dev/null +++ b/none/session.py @@ -0,0 +1,24 @@ +from typing import Union, Callable, Dict, Any, List, Sequence + +from aiocqhttp import CQHttp + +from .helpers import send, send_expr + + +class BaseSession: + __slots__ = ('bot', 'ctx') + + def __init__(self, bot: CQHttp, ctx: Dict[str, Any]): + self.bot = bot + self.ctx = ctx + + async def send(self, + message: Union[str, Dict[str, Any], List[Dict[str, Any]]], + *, ignore_failure: bool = True) -> None: + return await send(self.bot, self.ctx, message, + ignore_failure=ignore_failure) + + async def send_expr(self, + expr: Union[str, Sequence[str], Callable], + **kwargs): + return await send_expr(self.bot, self.ctx, expr, **kwargs) diff --git a/none_demo/plugins/__init__.py b/none_demo/plugins/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/none_demo/plugins/greeting.py b/none_demo/plugins/greeting.py new file mode 100644 index 00000000..a05cd473 --- /dev/null +++ b/none_demo/plugins/greeting.py @@ -0,0 +1,22 @@ +from aiocqhttp import Error as CQHttpError + +import none +from none.notice import NoticeSession + +GROUP_GREETING = ( + '欢迎新同学 {name}[]![CQ:face,id=63][CQ:face,id=63][CQ:face,id=63]', + '[CQ:face,id=99]欢迎新成员~', + '欢迎 {name}👏👏~', + '[CQ:at,qq={user_id}] 欢迎欢迎👏', +) + + +@none.on_notice('group_increase') +async def _(session: NoticeSession): + try: + info = await session.bot.get_group_member_info(**session.ctx, + no_cache=True) + name = info['card'] or info['nickname'] or '新成员' + await session.send_expr(GROUP_GREETING, name=name, **session.ctx) + except CQHttpError: + pass diff --git a/none_demo/plugins/weather/__init__.py b/none_demo/plugins/weather/__init__.py index 2ae39a27..dc51b79a 100644 --- a/none_demo/plugins/weather/__init__.py +++ b/none_demo/plugins/weather/__init__.py @@ -1,4 +1,4 @@ -from none.command import Session, CommandGroup +from none.command import CommandSession, CommandGroup from . import expressions as expr @@ -6,17 +6,17 @@ w = CommandGroup('weather') @w.command('weather', aliases=('天气', '天气预报')) -async def weather(session: Session): +async def weather(session: CommandSession): city = session.require_arg('city', prompt_expr=expr.WHICH_CITY) await session.send_expr(expr.REPORT, city=city) @weather.args_parser -async def _(session: Session): +async def _(session: CommandSession): if session.current_key: session.args[session.current_key] = session.current_arg_text.strip() @w.command('suggestion', aliases=('生活指数', '生活建议', '生活提示')) -async def suggestion(session: Session): +async def suggestion(session: CommandSession): await session.send('suggestion')