mirror of
https://github.com/nonebot/nonebot2.git
synced 2024-11-27 18:45:05 +08:00
Add lots of comments and logs
This commit is contained in:
parent
1c78eb1b80
commit
21db23168f
@ -16,9 +16,11 @@ class NoneBot(CQHttp):
|
|||||||
if config_object is None:
|
if config_object is None:
|
||||||
from . import default_config as config_object
|
from . import default_config as config_object
|
||||||
|
|
||||||
super_kwargs = {k.lower(): v for k, v in config_object.__dict__.items()
|
config_dict = {k: v for k, v in config_object.__dict__.items()
|
||||||
if k.isupper() and not k.startswith('_')}
|
if k.isupper() and not k.startswith('_')}
|
||||||
super().__init__(message_class=Message, **super_kwargs)
|
logger.debug(f'Loaded configurations: {config_dict}')
|
||||||
|
super().__init__(message_class=Message,
|
||||||
|
**{k.lower(): v for k, v in config_dict.items()})
|
||||||
|
|
||||||
self.config = config_object
|
self.config = config_object
|
||||||
self.asgi.debug = self.config.DEBUG
|
self.asgi.debug = self.config.DEBUG
|
||||||
@ -39,29 +41,10 @@ class NoneBot(CQHttp):
|
|||||||
asyncio.ensure_future(handle_notice_or_request(self, ctx))
|
asyncio.ensure_future(handle_notice_or_request(self, ctx))
|
||||||
|
|
||||||
def run(self, host=None, port=None, *args, **kwargs):
|
def run(self, host=None, port=None, *args, **kwargs):
|
||||||
|
logger.info(f'Running on {host}:{port}')
|
||||||
super().run(host=host, port=port, loop=asyncio.get_event_loop(),
|
super().run(host=host, port=port, loop=asyncio.get_event_loop(),
|
||||||
*args, **kwargs)
|
*args, **kwargs)
|
||||||
|
|
||||||
def get_data_folder(self,
|
|
||||||
*sub_folder: str) -> Optional[str]:
|
|
||||||
folder = self.config.DATA_FOLDER
|
|
||||||
if not folder:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if sub_folder:
|
|
||||||
folder = os.path.join(folder, *sub_folder)
|
|
||||||
|
|
||||||
if not os.path.isdir(folder):
|
|
||||||
os.makedirs(folder, 0o755, exist_ok=True)
|
|
||||||
return folder
|
|
||||||
|
|
||||||
def get_data_file(self, path: str, *others: str) -> Optional[str]:
|
|
||||||
rel_path = os.path.join(path, *others)
|
|
||||||
parent = self.get_data_folder(os.path.dirname(rel_path))
|
|
||||||
if not parent:
|
|
||||||
return None
|
|
||||||
return os.path.join(parent, os.path.basename(rel_path))
|
|
||||||
|
|
||||||
|
|
||||||
_bot: Optional[NoneBot] = None
|
_bot: Optional[NoneBot] = None
|
||||||
|
|
||||||
@ -103,11 +86,14 @@ def run(host: str = None, port: int = None, *args, **kwargs) -> None:
|
|||||||
_plugins = set()
|
_plugins = set()
|
||||||
|
|
||||||
|
|
||||||
def clear_plugins() -> None:
|
|
||||||
_plugins.clear()
|
|
||||||
|
|
||||||
|
|
||||||
def load_plugins(plugin_dir: str, module_prefix: str) -> None:
|
def load_plugins(plugin_dir: str, module_prefix: str) -> None:
|
||||||
|
"""
|
||||||
|
Find all non-hidden modules or packages in a given directory,
|
||||||
|
and import them with the given module prefix.
|
||||||
|
|
||||||
|
:param plugin_dir: plugin directory to search
|
||||||
|
:param module_prefix: module prefix used while importing
|
||||||
|
"""
|
||||||
for name in os.listdir(plugin_dir):
|
for name in os.listdir(plugin_dir):
|
||||||
path = os.path.join(plugin_dir, name)
|
path = os.path.join(plugin_dir, name)
|
||||||
if os.path.isfile(path) and \
|
if os.path.isfile(path) and \
|
||||||
@ -125,12 +111,15 @@ def load_plugins(plugin_dir: str, module_prefix: str) -> None:
|
|||||||
mod_name = f'{module_prefix}.{m.group(1)}'
|
mod_name = f'{module_prefix}.{m.group(1)}'
|
||||||
try:
|
try:
|
||||||
_plugins.add(importlib.import_module(mod_name))
|
_plugins.add(importlib.import_module(mod_name))
|
||||||
logger.info('Succeeded to import "{}"'.format(mod_name))
|
logger.info(f'Succeeded to import "{mod_name}"')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logger.warning('Failed to import "{}"'.format(mod_name))
|
logger.warning(f'Failed to import "{mod_name}"')
|
||||||
|
|
||||||
|
|
||||||
def load_builtin_plugins() -> None:
|
def load_builtin_plugins() -> None:
|
||||||
|
"""
|
||||||
|
Load built-in plugins distributed along with "none" package.
|
||||||
|
"""
|
||||||
plugin_dir = os.path.join(os.path.dirname(__file__), 'plugins')
|
plugin_dir = os.path.join(os.path.dirname(__file__), 'plugins')
|
||||||
load_plugins(plugin_dir, 'none.plugins')
|
load_plugins(plugin_dir, 'none.plugins')
|
||||||
|
|
||||||
|
@ -8,6 +8,11 @@ class ParserExit(RuntimeError):
|
|||||||
|
|
||||||
|
|
||||||
class ArgumentParser(ArgumentParser):
|
class ArgumentParser(ArgumentParser):
|
||||||
|
"""
|
||||||
|
An ArgumentParser wrapper that avoid printing messages to
|
||||||
|
standard I/O.
|
||||||
|
"""
|
||||||
|
|
||||||
def _print_message(self, *args, **kwargs):
|
def _print_message(self, *args, **kwargs):
|
||||||
# do nothing
|
# do nothing
|
||||||
pass
|
pass
|
||||||
|
@ -8,6 +8,7 @@ from typing import (
|
|||||||
from aiocqhttp.message import Message
|
from aiocqhttp.message import Message
|
||||||
|
|
||||||
from . import NoneBot, permission as perm
|
from . import NoneBot, permission as perm
|
||||||
|
from .log import logger
|
||||||
from .expression import render
|
from .expression import render
|
||||||
from .helpers import context_id
|
from .helpers import context_id
|
||||||
from .session import BaseSession
|
from .session import BaseSession
|
||||||
@ -26,8 +27,8 @@ _sessions = {}
|
|||||||
|
|
||||||
|
|
||||||
class Command:
|
class Command:
|
||||||
__slots__ = (
|
__slots__ = ('name', 'func', 'permission',
|
||||||
'name', 'func', 'permission', 'only_to_me', 'args_parser_func')
|
'only_to_me', 'args_parser_func')
|
||||||
|
|
||||||
def __init__(self, *, name: Tuple[str], func: Callable, permission: int,
|
def __init__(self, *, name: Tuple[str], func: Callable, permission: int,
|
||||||
only_to_me: bool):
|
only_to_me: bool):
|
||||||
@ -266,6 +267,8 @@ def parse_command(bot: NoneBot,
|
|||||||
:param cmd_string: command string
|
:param cmd_string: command string
|
||||||
:return: (Command object, current arg string)
|
:return: (Command object, current arg string)
|
||||||
"""
|
"""
|
||||||
|
logger.debug(f'Parsing command: {cmd_string}')
|
||||||
|
|
||||||
matched_start = None
|
matched_start = None
|
||||||
for start in bot.config.COMMAND_START:
|
for start in bot.config.COMMAND_START:
|
||||||
# loop through COMMAND_START to find the longest matched start
|
# loop through COMMAND_START to find the longest matched start
|
||||||
@ -286,8 +289,11 @@ def parse_command(bot: NoneBot,
|
|||||||
|
|
||||||
if matched_start is None:
|
if matched_start is None:
|
||||||
# it's not a command
|
# it's not a command
|
||||||
|
logger.debug('It\'s not a command')
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
logger.debug(f'Matched command start: '
|
||||||
|
f'{matched_start}{"(space)" if not matched_start else ""}')
|
||||||
full_command = cmd_string[len(matched_start):].lstrip()
|
full_command = cmd_string[len(matched_start):].lstrip()
|
||||||
|
|
||||||
if not full_command:
|
if not full_command:
|
||||||
@ -314,10 +320,13 @@ def parse_command(bot: NoneBot,
|
|||||||
if not cmd_name:
|
if not cmd_name:
|
||||||
cmd_name = (cmd_name_text,)
|
cmd_name = (cmd_name_text,)
|
||||||
|
|
||||||
|
logger.debug(f'Split command name: {cmd_name}')
|
||||||
cmd = _find_command(cmd_name)
|
cmd = _find_command(cmd_name)
|
||||||
if not cmd:
|
if not cmd:
|
||||||
|
logger.debug(f'Command {cmd_name} not found')
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
logger.debug(f'Command {cmd.name} found, function: {cmd.func}')
|
||||||
return cmd, ''.join(cmd_remained)
|
return cmd, ''.join(cmd_remained)
|
||||||
|
|
||||||
|
|
||||||
@ -337,11 +346,13 @@ async def handle_command(bot: NoneBot, ctx: Dict[str, Any]) -> bool:
|
|||||||
if _sessions.get(ctx_id):
|
if _sessions.get(ctx_id):
|
||||||
session = _sessions[ctx_id]
|
session = _sessions[ctx_id]
|
||||||
if session and session.is_valid:
|
if session and session.is_valid:
|
||||||
|
logger.debug(f'Session of command {session.cmd.name} exists')
|
||||||
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
|
# there is no need to check permission for existing session
|
||||||
check_perm = False
|
check_perm = False
|
||||||
else:
|
else:
|
||||||
# the session is expired, remove it
|
# the session is expired, remove it
|
||||||
|
logger.debug(f'Session of command {session.cmd.name} is expired')
|
||||||
del _sessions[ctx_id]
|
del _sessions[ctx_id]
|
||||||
session = None
|
session = None
|
||||||
if not session:
|
if not session:
|
||||||
@ -349,6 +360,7 @@ async def handle_command(bot: NoneBot, ctx: Dict[str, Any]) -> bool:
|
|||||||
if not cmd or cmd.only_to_me and not ctx['to_me']:
|
if not cmd or cmd.only_to_me and not ctx['to_me']:
|
||||||
return False
|
return False
|
||||||
session = CommandSession(bot, ctx, cmd, current_arg=current_arg)
|
session = CommandSession(bot, ctx, cmd, current_arg=current_arg)
|
||||||
|
logger.debug(f'New session of command {session.cmd.name} created')
|
||||||
return await _real_run_command(session, ctx_id, check_perm=check_perm)
|
return await _real_run_command(session, ctx_id, check_perm=check_perm)
|
||||||
|
|
||||||
|
|
||||||
@ -395,6 +407,7 @@ async def _real_run_command(session: CommandSession,
|
|||||||
# override session only when not disabling interaction
|
# override session only when not disabling interaction
|
||||||
_sessions[ctx_id] = session
|
_sessions[ctx_id] = session
|
||||||
try:
|
try:
|
||||||
|
logger.debug(f'Running command {session.cmd.name}')
|
||||||
res = await session.cmd.run(session, **kwargs)
|
res = await session.cmd.run(session, **kwargs)
|
||||||
if not disable_interaction:
|
if not disable_interaction:
|
||||||
# the command is finished, remove the session
|
# the command is finished, remove the session
|
||||||
@ -404,8 +417,11 @@ async def _real_run_command(session: CommandSession,
|
|||||||
if disable_interaction:
|
if disable_interaction:
|
||||||
# if the command needs further interaction, we view it as failed
|
# if the command needs further interaction, we view it as failed
|
||||||
return False
|
return False
|
||||||
|
logger.debug(f'Further interaction needed for '
|
||||||
|
f'command {session.cmd.name}')
|
||||||
session.last_interaction = datetime.now()
|
session.last_interaction = datetime.now()
|
||||||
# return True because this step of the session is successful
|
# return True because this step of the session is successful
|
||||||
return True
|
return True
|
||||||
except _FinishException:
|
except _FinishException:
|
||||||
|
logger.debug(f'Session of command {session.cmd.name} finished')
|
||||||
return True
|
return True
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
"""
|
||||||
|
Default configurations.
|
||||||
|
|
||||||
|
Any derived configurations must import everything from this module
|
||||||
|
at the very beginning of their code, and then set their own value
|
||||||
|
to override the default one.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
>>> from none.default_config import *
|
||||||
|
>>> PORT = 9090
|
||||||
|
>>> DEBUG = False
|
||||||
|
>>> SUPERUSERS.add(123456)
|
||||||
|
>>> NICKNAME = '小明'
|
||||||
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
|
"""
|
||||||
|
Provide logger object.
|
||||||
|
|
||||||
|
Any other modules in "none" should use "logger" from this module
|
||||||
|
to log messages.
|
||||||
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@ from .natural_language import handle_natural_language
|
|||||||
|
|
||||||
|
|
||||||
async def handle_message(bot: NoneBot, ctx: Dict[str, Any]) -> None:
|
async def handle_message(bot: NoneBot, ctx: Dict[str, Any]) -> None:
|
||||||
|
_log_message(ctx)
|
||||||
|
|
||||||
if ctx['message_type'] != 'private':
|
if ctx['message_type'] != 'private':
|
||||||
# group or discuss
|
# group or discuss
|
||||||
ctx['to_me'] = False
|
ctx['to_me'] = False
|
||||||
@ -23,10 +25,21 @@ async def handle_message(bot: NoneBot, 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.info(f'Message {ctx["message_id"]} is handled as a command')
|
||||||
return
|
return
|
||||||
|
|
||||||
handled = await handle_natural_language(bot, ctx)
|
handled = await handle_natural_language(bot, ctx)
|
||||||
if handled:
|
if handled:
|
||||||
logger.debug('Message is handled as natural language')
|
logger.info(f'Message {ctx["message_id"]} is handled '
|
||||||
|
f'as natural language')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def _log_message(ctx: Dict[str, Any]) -> None:
|
||||||
|
msg_from = f'{ctx["user_id"]}'
|
||||||
|
if ctx['message_type'] == 'group':
|
||||||
|
msg_from += f'@[群:{ctx["group_id"]}]'
|
||||||
|
elif ctx['message_type'] == 'discuss':
|
||||||
|
msg_from += f'@[讨论组:{ctx["discuss_id"]}]'
|
||||||
|
logger.info(f'Message {ctx["message_id"]} from {msg_from}: '
|
||||||
|
f'{ctx["message"]}')
|
||||||
|
@ -89,6 +89,7 @@ async def handle_natural_language(bot: NoneBot, ctx: Dict[str, Any]) -> bool:
|
|||||||
nicknames = filter(lambda n: n, bot.config.NICKNAME)
|
nicknames = filter(lambda n: n, bot.config.NICKNAME)
|
||||||
m = re.search(rf'^({"|".join(nicknames)})[\s,,]+', msg)
|
m = re.search(rf'^({"|".join(nicknames)})[\s,,]+', msg)
|
||||||
if m:
|
if m:
|
||||||
|
logger.debug(f'User is calling me {m.group(1)}')
|
||||||
ctx['to_me'] = True
|
ctx['to_me'] = True
|
||||||
msg = msg[m.end():]
|
msg = msg[m.end():]
|
||||||
|
|
||||||
@ -114,10 +115,13 @@ async def handle_natural_language(bot: NoneBot, ctx: Dict[str, Any]) -> bool:
|
|||||||
# wait for possible results, and sort them by confidence
|
# wait for possible results, and sort them by confidence
|
||||||
results = sorted(filter(lambda r: r, await asyncio.gather(*coros)),
|
results = sorted(filter(lambda r: r, await asyncio.gather(*coros)),
|
||||||
key=lambda r: r.confidence, reverse=True)
|
key=lambda r: r.confidence, reverse=True)
|
||||||
logger.debug(results)
|
logger.debug(f'NLP results: {results}')
|
||||||
if results and results[0].confidence >= 60.0:
|
if results and results[0].confidence >= 60.0:
|
||||||
# choose the result with highest confidence
|
# choose the result with highest confidence
|
||||||
|
logger.debug(f'NLP result with highest confidence: {results[0]}')
|
||||||
return await call_command(bot, ctx, results[0].cmd_name,
|
return await call_command(bot, ctx, results[0].cmd_name,
|
||||||
args=results[0].cmd_args,
|
args=results[0].cmd_args,
|
||||||
check_perm=False)
|
check_perm=False)
|
||||||
|
else:
|
||||||
|
logger.debug('No NLP result having enough confidence')
|
||||||
return False
|
return False
|
||||||
|
@ -76,9 +76,19 @@ async def handle_notice_or_request(bot: NoneBot, ctx: Dict[str, Any]) -> None:
|
|||||||
event += f'.{ctx["sub_type"]}'
|
event += f'.{ctx["sub_type"]}'
|
||||||
|
|
||||||
if post_type == 'notice':
|
if post_type == 'notice':
|
||||||
|
_log_notice(ctx)
|
||||||
session = NoticeSession(bot, ctx)
|
session = NoticeSession(bot, ctx)
|
||||||
else: # must be 'request'
|
else: # must be 'request'
|
||||||
|
_log_request(ctx)
|
||||||
session = RequestSession(bot, ctx)
|
session = RequestSession(bot, ctx)
|
||||||
|
|
||||||
logger.debug(f'Emitting event: {event}')
|
logger.debug(f'Emitting event: {event}')
|
||||||
await _bus.emit(event, session)
|
await _bus.emit(event, session)
|
||||||
|
|
||||||
|
|
||||||
|
def _log_notice(ctx: Dict[str, Any]) -> None:
|
||||||
|
logger.info(f'Notice: {ctx}')
|
||||||
|
|
||||||
|
|
||||||
|
def _log_request(ctx: Dict[str, Any]) -> None:
|
||||||
|
logger.info(f'Request: {ctx}')
|
||||||
|
Loading…
Reference in New Issue
Block a user