mirror of
https://github.com/nonebot/nonebot2.git
synced 2025-07-28 00:31:14 +00:00
New generation init
This commit is contained in:
76
none/__init__.py
Normal file
76
none/__init__.py
Normal file
@ -0,0 +1,76 @@
|
||||
import os
|
||||
import importlib
|
||||
import logging
|
||||
import re
|
||||
import asyncio
|
||||
from typing import Any
|
||||
|
||||
from aiocqhttp import CQHttp
|
||||
from aiocqhttp.message import Message
|
||||
|
||||
logger = logging.getLogger('none')
|
||||
|
||||
from .plugin import handle_message, handle_notice, handle_request
|
||||
from .command import on_command
|
||||
|
||||
|
||||
def create_bot(config_object: Any = None):
|
||||
if config_object is None:
|
||||
from . import default_config as config_object
|
||||
|
||||
kwargs = {k.lower(): v for k, v in config_object.__dict__.items()
|
||||
if k.isupper() and not k.startswith('_')}
|
||||
|
||||
bot = CQHttp(message_class=Message, **kwargs)
|
||||
bot.config = config_object
|
||||
if bot.config.DEBUG:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
else:
|
||||
logger.setLevel(logging.INFO)
|
||||
bot.asgi.debug = bot.config.DEBUG
|
||||
|
||||
@bot.on_message
|
||||
async def _(ctx):
|
||||
asyncio.ensure_future(handle_message(bot, ctx))
|
||||
|
||||
@bot.on_notice
|
||||
async def _(ctx):
|
||||
asyncio.ensure_future(handle_notice(bot, ctx))
|
||||
|
||||
@bot.on_request
|
||||
async def _(ctx):
|
||||
asyncio.ensure_future(handle_request(bot, ctx))
|
||||
|
||||
return bot
|
||||
|
||||
|
||||
_plugins = set()
|
||||
|
||||
|
||||
def load_plugins():
|
||||
_plugins.clear()
|
||||
root_dir = os.path.dirname(__path__[0])
|
||||
plugins_dir = os.path.join(root_dir, 'plugins')
|
||||
saved_cwd = os.getcwd()
|
||||
os.chdir(root_dir)
|
||||
for item in os.listdir(plugins_dir):
|
||||
path = os.path.join(plugins_dir, item)
|
||||
if os.path.isfile(path) and \
|
||||
(path.startswith('_') or not path.endswith('.py')):
|
||||
continue
|
||||
if os.path.isdir(path) and \
|
||||
(path.startswith('_') or not os.path.exists(
|
||||
os.path.join(path, '__init__.py'))):
|
||||
continue
|
||||
|
||||
m = re.match(r'([_A-Z0-9a-z]+)(.py)?', item)
|
||||
if not m:
|
||||
continue
|
||||
|
||||
mod_name = 'plugins.' + m.group(1)
|
||||
try:
|
||||
_plugins.add(importlib.import_module(mod_name))
|
||||
logger.info('Succeeded to import "{}"'.format(mod_name))
|
||||
except ImportError:
|
||||
logger.warning('Failed to import "{}"'.format(mod_name))
|
||||
os.chdir(saved_cwd)
|
108
none/command.py
Normal file
108
none/command.py
Normal file
@ -0,0 +1,108 @@
|
||||
import re
|
||||
from typing import Tuple, Union, Callable, Iterable, Dict, Any
|
||||
|
||||
from aiocqhttp import CQHttp
|
||||
|
||||
from . import permissions as perm, logger
|
||||
|
||||
_command_tree = {}
|
||||
|
||||
# Key: str
|
||||
# Value: tuple that identifies a command
|
||||
_command_aliases = {}
|
||||
|
||||
# Key: context source
|
||||
# Value: Command object
|
||||
_command_sessions = {}
|
||||
|
||||
|
||||
# TODO: Command 类只用来表示注册的命令,Session 类用来在运行时表示命令的参数等
|
||||
|
||||
class Command:
|
||||
__slots__ = ('name', 'arg', 'images', 'data', 'last_interaction')
|
||||
|
||||
def __init__(self, name: Tuple[str]):
|
||||
self.name = name
|
||||
|
||||
async def __call__(self, bot: CQHttp, ctx: Dict[str, Any],
|
||||
*args, **kwargs) -> bool:
|
||||
logger.info(repr(self.images))
|
||||
cmd_tree = _command_tree
|
||||
for part in self.name:
|
||||
if part not in cmd_tree:
|
||||
return False
|
||||
cmd_tree = cmd_tree[part]
|
||||
cmd = cmd_tree
|
||||
if 'func' not in cmd or not isinstance(cmd['func'], Callable):
|
||||
return False
|
||||
# TODO: check permission
|
||||
await cmd['func'](bot, ctx, self)
|
||||
return True
|
||||
|
||||
|
||||
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)
|
||||
cmd_name = _command_aliases.get(cmd_name_text)
|
||||
|
||||
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,)
|
||||
|
||||
cmd = Command(cmd_name)
|
||||
cmd.arg = ''.join(cmd_remained)
|
||||
cmd.images = [s.data['url'] for s in ctx['message']
|
||||
if s.type == 'image' and 'url' in s.data]
|
||||
return await cmd(bot, ctx)
|
||||
|
||||
|
||||
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,)
|
||||
current_parent = _command_tree
|
||||
for parent_key in cmd_name[:-1]:
|
||||
current_parent[parent_key] = {}
|
||||
current_parent = current_parent[parent_key]
|
||||
current_parent[cmd_name[-1]] = {
|
||||
'name': cmd_name,
|
||||
'func': func,
|
||||
'permission': permission
|
||||
}
|
||||
for alias in aliases:
|
||||
_command_aliases[alias] = cmd_name
|
||||
return func
|
||||
|
||||
return deco
|
9
none/default_config.py
Normal file
9
none/default_config.py
Normal file
@ -0,0 +1,9 @@
|
||||
API_ROOT = ''
|
||||
SECRET = ''
|
||||
HOST = '127.0.0.1'
|
||||
PORT = 8080
|
||||
DEBUG = True
|
||||
|
||||
SUPERUSERS = set()
|
||||
COMMAND_START = {'/', '!', '/', '!'}
|
||||
COMMAND_SEP = {'/', '.'}
|
12
none/helpers.py
Normal file
12
none/helpers.py
Normal file
@ -0,0 +1,12 @@
|
||||
from typing import Dict, Any
|
||||
|
||||
|
||||
def context_source(ctx: Dict[str, Any]) -> str:
|
||||
src = ''
|
||||
if ctx.get('group_id'):
|
||||
src += 'g%s' % ctx['group_id']
|
||||
elif ctx.get('discuss_id'):
|
||||
src += 'd%s' % ctx['discuss_id']
|
||||
if ctx.get('user_id'):
|
||||
src += 'p%s' % ctx['user_id']
|
||||
return src
|
12
none/permissions.py
Normal file
12
none/permissions.py
Normal file
@ -0,0 +1,12 @@
|
||||
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
|
||||
EVERYONE = 0xFFFF
|
31
none/plugin.py
Normal file
31
none/plugin.py
Normal file
@ -0,0 +1,31 @@
|
||||
from typing import Dict, Any
|
||||
|
||||
from aiocqhttp import CQHttp
|
||||
from aiocqhttp.message import MessageSegment
|
||||
|
||||
from . import command, logger
|
||||
|
||||
|
||||
async def handle_message(bot: CQHttp, ctx: Dict[str, Any]):
|
||||
if ctx['message_type'] != 'private':
|
||||
# group or discuss
|
||||
first_message_seg = ctx['message'][0]
|
||||
if first_message_seg != MessageSegment.at(ctx['self_id']):
|
||||
return
|
||||
del ctx['message'][0]
|
||||
if not ctx['message']:
|
||||
ctx['message'].append(MessageSegment.text(''))
|
||||
|
||||
handled = await command.handle_command(bot, ctx)
|
||||
if handled:
|
||||
logger.debug('Message is handled as a command')
|
||||
else:
|
||||
await bot.send(ctx, '你在说什么我看不懂诶')
|
||||
|
||||
|
||||
async def handle_notice(bot: CQHttp, ctx: Dict[str, Any]):
|
||||
pass
|
||||
|
||||
|
||||
async def handle_request(bot: CQHttp, ctx: Dict[str, Any]):
|
||||
pass
|
Reference in New Issue
Block a user