mirror of
https://github.com/nonebot/nonebot2.git
synced 2025-01-19 17:58:26 +08:00
Lots of updates
This commit is contained in:
parent
1835a4c33d
commit
a13128f356
@ -74,7 +74,7 @@ def load_builtin_plugins():
|
|||||||
load_plugins(plugin_dir, 'none.plugins')
|
load_plugins(plugin_dir, 'none.plugins')
|
||||||
|
|
||||||
|
|
||||||
from .command import on_command, CommandSession
|
from .command import on_command, CommandSession, CommandGroup
|
||||||
from .notice_request import (
|
from .notice_request import (
|
||||||
on_notice, NoticeSession,
|
on_notice, NoticeSession,
|
||||||
on_request, RequestSession,
|
on_request, RequestSession,
|
||||||
|
@ -5,13 +5,13 @@ from typing import (
|
|||||||
Tuple, Union, Callable, Iterable, Dict, Any, Optional, Sequence
|
Tuple, Union, Callable, Iterable, Dict, Any, Optional, Sequence
|
||||||
)
|
)
|
||||||
|
|
||||||
from aiocqhttp import CQHttp, Error as CQHttpError
|
from aiocqhttp import CQHttp
|
||||||
from aiocqhttp.message import Message
|
from aiocqhttp.message import Message
|
||||||
|
|
||||||
from . import permissions as perm
|
from . import permission as perm
|
||||||
from ..helpers import context_source
|
from .helpers import context_source
|
||||||
from ..expression import render
|
from .expression import render
|
||||||
from ..session import BaseSession
|
from .session import BaseSession
|
||||||
|
|
||||||
# Key: str (one segment of command name)
|
# Key: str (one segment of command name)
|
||||||
# Value: subtree or a leaf Command object
|
# Value: subtree or a leaf Command object
|
||||||
@ -37,17 +37,20 @@ class Command:
|
|||||||
self.only_to_me = only_to_me
|
self.only_to_me = only_to_me
|
||||||
self.args_parser_func = None
|
self.args_parser_func = None
|
||||||
|
|
||||||
async def run(self, session, *, permission: Optional[int] = None) -> bool:
|
async def run(self, session, check_perm: bool = True) -> bool:
|
||||||
"""
|
"""
|
||||||
Run the command in a given session.
|
Run the command in a given session.
|
||||||
|
|
||||||
:param session: CommandSession object
|
:param session: CommandSession object
|
||||||
:param permission: the permission the caller owns
|
:param check_perm: should check permission before running
|
||||||
:return: the command is finished
|
:return: the command is finished
|
||||||
"""
|
"""
|
||||||
if permission is None:
|
if check_perm:
|
||||||
permission = await _calculate_permission(session.bot, session.ctx)
|
has_perm = await perm.check_permission(
|
||||||
if self.func and permission & self.permission:
|
session.bot, session.ctx, self.permission)
|
||||||
|
else:
|
||||||
|
has_perm = True
|
||||||
|
if self.func and has_perm:
|
||||||
if self.args_parser_func:
|
if self.args_parser_func:
|
||||||
await self.args_parser_func(session)
|
await self.args_parser_func(session)
|
||||||
await self.func(session)
|
await self.func(session)
|
||||||
@ -55,48 +58,6 @@ class Command:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
async def _calculate_permission(bot: CQHttp, ctx: Dict[str, Any]) -> int:
|
|
||||||
"""
|
|
||||||
Calculate the permission OWNED by the current context.
|
|
||||||
|
|
||||||
This is different from the permission REQUIRED by a command.
|
|
||||||
The result of this function should be made a bit-and with
|
|
||||||
the permission required by some command to check whether
|
|
||||||
the context is allowed to call the command.
|
|
||||||
|
|
||||||
:param bot: CQHttp instance
|
|
||||||
:param ctx: message context
|
|
||||||
:return: the calculated permission value
|
|
||||||
"""
|
|
||||||
permission = 0
|
|
||||||
if ctx['user_id'] in bot.config.SUPERUSERS:
|
|
||||||
permission |= perm.IS_SUPERUSER
|
|
||||||
if ctx['message_type'] == 'private':
|
|
||||||
if ctx['sub_type'] == 'friend':
|
|
||||||
permission |= perm.IS_PRIVATE_FRIEND
|
|
||||||
elif ctx['sub_type'] == 'group':
|
|
||||||
permission |= perm.IS_PRIVATE_GROUP
|
|
||||||
elif ctx['sub_type'] == 'discuss':
|
|
||||||
permission |= perm.IS_PRIVATE_DISCUSS
|
|
||||||
elif ctx['sub_type'] == 'other':
|
|
||||||
permission |= perm.IS_PRIVATE_OTHER
|
|
||||||
elif ctx['message_type'] == 'group':
|
|
||||||
permission |= perm.IS_GROUP_MEMBER
|
|
||||||
if not ctx['anonymous']:
|
|
||||||
try:
|
|
||||||
member_info = await bot.get_group_member_info(**ctx)
|
|
||||||
if member_info:
|
|
||||||
if member_info['role'] == 'owner':
|
|
||||||
permission |= perm.IS_GROUP_OWNER
|
|
||||||
elif member_info['role'] == 'admin':
|
|
||||||
permission |= perm.IS_GROUP_ADMIN
|
|
||||||
except CQHttpError:
|
|
||||||
pass
|
|
||||||
elif ctx['message_type'] == 'discuss':
|
|
||||||
permission |= perm.IS_DISCUSS
|
|
||||||
return permission
|
|
||||||
|
|
||||||
|
|
||||||
def on_command(name: Union[str, Tuple[str]], *,
|
def on_command(name: Union[str, Tuple[str]], *,
|
||||||
aliases: Iterable = (),
|
aliases: Iterable = (),
|
||||||
permission: int = perm.EVERYBODY,
|
permission: int = perm.EVERYBODY,
|
||||||
@ -325,10 +286,13 @@ async def handle_command(bot: CQHttp, ctx: Dict[str, Any]) -> bool:
|
|||||||
"""
|
"""
|
||||||
src = context_source(ctx)
|
src = context_source(ctx)
|
||||||
session = None
|
session = None
|
||||||
|
check_perm = True
|
||||||
if _sessions.get(src):
|
if _sessions.get(src):
|
||||||
session = _sessions[src]
|
session = _sessions[src]
|
||||||
if session and session.is_valid:
|
if session and session.is_valid:
|
||||||
session.refresh(ctx, current_arg=str(ctx['message']))
|
session.refresh(ctx, current_arg=str(ctx['message']))
|
||||||
|
# there is no need to check permission for existing session
|
||||||
|
check_perm = False
|
||||||
else:
|
else:
|
||||||
# the session is expired, remove it
|
# the session is expired, remove it
|
||||||
del _sessions[src]
|
del _sessions[src]
|
||||||
@ -340,7 +304,7 @@ async def handle_command(bot: CQHttp, ctx: Dict[str, Any]) -> bool:
|
|||||||
_sessions[src] = session
|
_sessions[src] = session
|
||||||
|
|
||||||
try:
|
try:
|
||||||
res = await session.cmd.run(session)
|
res = await session.cmd.run(session, check_perm=check_perm)
|
||||||
# the command is finished, remove the session
|
# the command is finished, remove the session
|
||||||
del _sessions[src]
|
del _sessions[src]
|
||||||
return res
|
return res
|
@ -1,25 +0,0 @@
|
|||||||
PRIVATE_FRIEND = 0x0001
|
|
||||||
PRIVATE_GROUP = 0x0002
|
|
||||||
PRIVATE_DISCUSS = 0x0004
|
|
||||||
PRIVATE_OTHER = 0x0008
|
|
||||||
PRIVATE = 0x000F
|
|
||||||
DISCUSS = 0x00F0
|
|
||||||
GROUP_MEMBER = 0x0100
|
|
||||||
GROUP_ADMIN = 0x0200
|
|
||||||
GROUP_OWNER = 0x0400
|
|
||||||
GROUP = 0x0F00
|
|
||||||
SUPERUSER = 0xF000
|
|
||||||
EVERYBODY = 0xFFFF
|
|
||||||
|
|
||||||
IS_NOBODY = 0x0000
|
|
||||||
IS_PRIVATE_FRIEND = PRIVATE_FRIEND
|
|
||||||
IS_PRIVATE_GROUP = PRIVATE_GROUP
|
|
||||||
IS_PRIVATE_DISCUSS = PRIVATE_DISCUSS
|
|
||||||
IS_PRIVATE_OTHER = PRIVATE_OTHER
|
|
||||||
IS_PRIVATE = PRIVATE
|
|
||||||
IS_DISCUSS = DISCUSS
|
|
||||||
IS_GROUP_MEMBER = GROUP_MEMBER
|
|
||||||
IS_GROUP_ADMIN = GROUP_MEMBER | GROUP_ADMIN
|
|
||||||
IS_GROUP_OWNER = GROUP_ADMIN | GROUP_OWNER
|
|
||||||
IS_GROUP = GROUP
|
|
||||||
IS_SUPERUSER = 0xFFFF
|
|
@ -4,8 +4,8 @@ from aiocqhttp import CQHttp
|
|||||||
from aiocqhttp.message import MessageSegment
|
from aiocqhttp.message import MessageSegment
|
||||||
|
|
||||||
from .command import handle_command
|
from .command import handle_command
|
||||||
|
from .natural_language import handle_natural_language
|
||||||
from .log import logger
|
from .log import logger
|
||||||
from .helpers import send
|
|
||||||
|
|
||||||
|
|
||||||
async def handle_message(bot: CQHttp, ctx: Dict[str, Any]) -> None:
|
async def handle_message(bot: CQHttp, ctx: Dict[str, Any]) -> None:
|
||||||
@ -23,9 +23,10 @@ async def handle_message(bot: CQHttp, ctx: Dict[str, Any]) -> None:
|
|||||||
|
|
||||||
handled = await handle_command(bot, ctx)
|
handled = await handle_command(bot, ctx)
|
||||||
if handled:
|
if handled:
|
||||||
logger.debug('Message is handled as a command')
|
logger.debug('Message is handled as command')
|
||||||
return
|
return
|
||||||
elif ctx['to_me']:
|
|
||||||
await send(bot, ctx, '你在说什么我看不懂诶')
|
|
||||||
|
|
||||||
# TODO: NLP
|
handled = await handle_natural_language(bot, ctx)
|
||||||
|
if handled:
|
||||||
|
logger.debug('Message is handled as natural language')
|
||||||
|
return
|
||||||
|
22
none/natural_language.py
Normal file
22
none/natural_language.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
from collections import namedtuple
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
from aiocqhttp import CQHttp
|
||||||
|
|
||||||
|
_nl_processors = set()
|
||||||
|
|
||||||
|
|
||||||
|
class NLProcessor:
|
||||||
|
__slots__ = ('func', 'permission', 'only_to_me', 'keywords',
|
||||||
|
'precondition_func')
|
||||||
|
|
||||||
|
|
||||||
|
NLPResult = namedtuple('NLPResult', (
|
||||||
|
'confidence',
|
||||||
|
'cmd_name',
|
||||||
|
'cmd_args',
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_natural_language(bot: CQHttp, ctx: Dict[str, Any]) -> None:
|
||||||
|
pass
|
91
none/permission.py
Normal file
91
none/permission.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
from collections import namedtuple
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
from aiocache import cached
|
||||||
|
from aiocqhttp import CQHttp, Error as CQHttpError
|
||||||
|
|
||||||
|
PRIVATE_FRIEND = 0x0001
|
||||||
|
PRIVATE_GROUP = 0x0002
|
||||||
|
PRIVATE_DISCUSS = 0x0004
|
||||||
|
PRIVATE_OTHER = 0x0008
|
||||||
|
PRIVATE = 0x000F
|
||||||
|
DISCUSS = 0x00F0
|
||||||
|
GROUP_MEMBER = 0x0100
|
||||||
|
GROUP_ADMIN = 0x0200
|
||||||
|
GROUP_OWNER = 0x0400
|
||||||
|
GROUP = 0x0F00
|
||||||
|
SUPERUSER = 0xF000
|
||||||
|
EVERYBODY = 0xFFFF
|
||||||
|
|
||||||
|
IS_NOBODY = 0x0000
|
||||||
|
IS_PRIVATE_FRIEND = PRIVATE_FRIEND
|
||||||
|
IS_PRIVATE_GROUP = PRIVATE_GROUP
|
||||||
|
IS_PRIVATE_DISCUSS = PRIVATE_DISCUSS
|
||||||
|
IS_PRIVATE_OTHER = PRIVATE_OTHER
|
||||||
|
IS_PRIVATE = PRIVATE
|
||||||
|
IS_DISCUSS = DISCUSS
|
||||||
|
IS_GROUP_MEMBER = GROUP_MEMBER
|
||||||
|
IS_GROUP_ADMIN = GROUP_MEMBER | GROUP_ADMIN
|
||||||
|
IS_GROUP_OWNER = GROUP_ADMIN | GROUP_OWNER
|
||||||
|
IS_GROUP = GROUP
|
||||||
|
IS_SUPERUSER = 0xFFFF
|
||||||
|
|
||||||
|
_min_context_fields = (
|
||||||
|
'message_type',
|
||||||
|
'sub_type',
|
||||||
|
'user_id',
|
||||||
|
'discuss_id',
|
||||||
|
'group_id',
|
||||||
|
'anonymous',
|
||||||
|
)
|
||||||
|
|
||||||
|
_MinContext = namedtuple('MinContext', _min_context_fields)
|
||||||
|
|
||||||
|
|
||||||
|
async def check_permission(bot: CQHttp, ctx: Dict[str, Any],
|
||||||
|
permission_required: int) -> bool:
|
||||||
|
min_ctx_kwargs = {}
|
||||||
|
for field in _min_context_fields:
|
||||||
|
if field in ctx:
|
||||||
|
min_ctx_kwargs[field] = ctx[field]
|
||||||
|
else:
|
||||||
|
min_ctx_kwargs[field] = None
|
||||||
|
min_ctx = _MinContext(**min_ctx_kwargs)
|
||||||
|
return await _check(bot, min_ctx, permission_required)
|
||||||
|
|
||||||
|
|
||||||
|
@cached(ttl=2 * 60) # cache the result for 1 minute
|
||||||
|
async def _check(bot: CQHttp, min_ctx: _MinContext,
|
||||||
|
permission_required: int) -> bool:
|
||||||
|
permission = 0
|
||||||
|
if min_ctx.user_id in bot.config.SUPERUSERS:
|
||||||
|
permission |= IS_SUPERUSER
|
||||||
|
if min_ctx.message_type == 'private':
|
||||||
|
if min_ctx.sub_type == 'friend':
|
||||||
|
permission |= IS_PRIVATE_FRIEND
|
||||||
|
elif min_ctx.sub_type == 'group':
|
||||||
|
permission |= IS_PRIVATE_GROUP
|
||||||
|
elif min_ctx.sub_type == 'discuss':
|
||||||
|
permission |= IS_PRIVATE_DISCUSS
|
||||||
|
elif min_ctx.sub_type == 'other':
|
||||||
|
permission |= IS_PRIVATE_OTHER
|
||||||
|
elif min_ctx.message_type == 'group':
|
||||||
|
permission |= IS_GROUP_MEMBER
|
||||||
|
if not min_ctx.anonymous:
|
||||||
|
try:
|
||||||
|
member_info = await bot.get_group_member_info(
|
||||||
|
group_id=min_ctx.group_id,
|
||||||
|
user_id=min_ctx.user_id,
|
||||||
|
no_cache=True
|
||||||
|
)
|
||||||
|
if member_info:
|
||||||
|
if member_info['role'] == 'owner':
|
||||||
|
permission |= IS_GROUP_OWNER
|
||||||
|
elif member_info['role'] == 'admin':
|
||||||
|
permission |= IS_GROUP_ADMIN
|
||||||
|
except CQHttpError:
|
||||||
|
pass
|
||||||
|
elif min_ctx.message_type == 'discuss':
|
||||||
|
permission |= IS_DISCUSS
|
||||||
|
|
||||||
|
return bool(permission & permission_required)
|
@ -1,15 +1,14 @@
|
|||||||
from aiocqhttp.message import unescape
|
from aiocqhttp.message import unescape
|
||||||
|
|
||||||
from none import on_command, CommandSession
|
from none import on_command, CommandSession, permission as perm
|
||||||
from none.command import permissions as perm
|
|
||||||
|
|
||||||
|
|
||||||
@on_command('echo', only_to_me=False)
|
@on_command('echo', only_to_me=False)
|
||||||
async def echo(session: CommandSession):
|
async def echo(session: CommandSession):
|
||||||
await session.send(session.args.get('text') or session.current_arg)
|
await session.send(session.args.get('message') or session.current_arg)
|
||||||
|
|
||||||
|
|
||||||
@on_command('say', permission=perm.SUPERUSER, only_to_me=False)
|
@on_command('say', permission=perm.SUPERUSER, only_to_me=False)
|
||||||
async def _(session: CommandSession):
|
async def _(session: CommandSession):
|
||||||
await session.send(
|
await session.send(
|
||||||
unescape(session.args.get('text') or session.current_arg))
|
unescape(session.args.get('message') or session.current_arg))
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from none.command import CommandSession, CommandGroup
|
from none import CommandSession, CommandGroup
|
||||||
|
|
||||||
from . import expressions as expr
|
from . import expressions as expr
|
||||||
|
|
||||||
@ -17,6 +17,26 @@ async def _(session: CommandSession):
|
|||||||
session.args[session.current_key] = session.current_arg_text.strip()
|
session.args[session.current_key] = session.current_arg_text.strip()
|
||||||
|
|
||||||
|
|
||||||
|
# @on_natural_language(keywords={'天气', '雨', '雪', '晴', '阴', '多云', '冰雹'},
|
||||||
|
# only_to_me=False)
|
||||||
|
# async def weather_nlp(session: NaturalLanguageSession):
|
||||||
|
# return NLPResult(89.5, ('weather', 'weather'), {'city': '南京'})
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# @weather_nlp.condition
|
||||||
|
# async def _(session: NaturalLanguageSession):
|
||||||
|
# keywords = {'天气', '雨', '雪', '晴', '阴', '多云', '冰雹'}
|
||||||
|
# for kw in keywords:
|
||||||
|
# if kw in session.text:
|
||||||
|
# keyword_hit = True
|
||||||
|
# break
|
||||||
|
# else:
|
||||||
|
# keyword_hit = False
|
||||||
|
# if session.ctx['to_me'] and keyword_hit:
|
||||||
|
# return True
|
||||||
|
# return False
|
||||||
|
|
||||||
|
|
||||||
@w.command('suggestion', aliases=('生活指数', '生活建议', '生活提示'))
|
@w.command('suggestion', aliases=('生活指数', '生活建议', '生活提示'))
|
||||||
async def suggestion(session: CommandSession):
|
async def suggestion(session: CommandSession):
|
||||||
await session.send('suggestion')
|
await session.send('suggestion')
|
||||||
|
2
setup.py
2
setup.py
@ -14,7 +14,7 @@ setup(
|
|||||||
description='A QQ bot framework',
|
description='A QQ bot framework',
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
install_requires=['aiocqhttp>=0.3'],
|
install_requires=['aiocqhttp>=0.3', 'aiocache'],
|
||||||
python_requires='>=3.6',
|
python_requires='>=3.6',
|
||||||
platforms='any',
|
platforms='any',
|
||||||
classifiers=(
|
classifiers=(
|
||||||
|
Loading…
Reference in New Issue
Block a user