From 286036a936f1d816bcaf8320c928207722155d73 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Tue, 10 Nov 2020 14:29:50 +0800 Subject: [PATCH 01/34] :pencil2: fix module docstring --- nonebot/__init__.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/nonebot/__init__.py b/nonebot/__init__.py index 9e25268f..eefb5f8f 100644 --- a/nonebot/__init__.py +++ b/nonebot/__init__.py @@ -10,10 +10,9 @@ - ``on_metaevent`` => ``nonebot.plugin.on_metaevent`` - ``on_startswith`` => ``nonebot.plugin.on_startswith`` - ``on_endswith`` => ``nonebot.plugin.on_endswith`` +- ``on_keyword`` => ``nonebot.plugin.on_keyword`` - ``on_command`` => ``nonebot.plugin.on_command`` - ``on_regex`` => ``nonebot.plugin.on_regex`` -- ``on_regex`` => ``nonebot.plugin.on_regex`` -- ``on_regex`` => ``nonebot.plugin.on_regex`` - ``CommandGroup`` => ``nonebot.plugin.CommandGroup`` - ``load_plugin`` => ``nonebot.plugin.load_plugin`` - ``load_plugins`` => ``nonebot.plugin.load_plugins`` @@ -233,6 +232,6 @@ async def _start_scheduler(): logger.opt(colors=True).info("Scheduler Started") -from nonebot.plugin import on_message, on_notice, on_request, on_metaevent -from nonebot.plugin import on_startswith, on_endswith, on_command, on_regex, CommandGroup +from nonebot.plugin import on_message, on_notice, on_request, on_metaevent, CommandGroup +from nonebot.plugin import on_startswith, on_endswith, on_keyword, on_command, on_regex from nonebot.plugin import load_plugin, load_plugins, load_builtin_plugins, get_loaded_plugins From b3f7aaa580c5bc7a32010a356c34280f518cdd5b Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Tue, 10 Nov 2020 14:30:18 +0800 Subject: [PATCH 02/34] :memo: add create handler guide --- docs/.vuepress/config.js | 3 ++- docs/guide/creating-a-handler.md | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 docs/guide/creating-a-handler.md diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index fe33e166..53bb7bfb 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -104,7 +104,8 @@ module.exports = context => ({ children: [ "loading-a-plugin", "creating-a-plugin", - "creating-a-matcher" + "creating-a-matcher", + "creating-a-handler" ] } ], diff --git a/docs/guide/creating-a-handler.md b/docs/guide/creating-a-handler.md new file mode 100644 index 00000000..1cbe6415 --- /dev/null +++ b/docs/guide/creating-a-handler.md @@ -0,0 +1,32 @@ +# 事件处理 + +在上一章中,我们已经注册了事件响应器,现在我们可以正式编写事件处理逻辑了! + +## [事件处理函数](../api/typing.md#handler) + +```python{1,2,8,9} +@weather.handle() +async def handle_first_receive(bot: Bot, event: Event, state: dict): + args = str(event.message).strip() # 首次发送命令时跟随的参数,例:/天气 上海,则args为上海 + if args: + state["city"] = args # 如果用户发送了参数则直接赋值 + + +@weather.got("city", prompt="你想查询哪个城市的天气呢?") +async def handle_city(bot: Bot, event: Event, state: dict): + city = state["city"] + if city not in ["上海", "北京"]: + await weather.reject("你想查询的城市暂不支持,请重新输入!") + city_weather = await get_weather(city) + await weather.finish(city_weather) +``` + +在之前的样例中,我们定义了两个函数,他们被事件响应器的装饰器装饰从而成为事件响应器的事件处理函数。 + +### 装饰器 + +事件响应器提供了三种装饰事件处理函数的装饰器,分别是: + +1. [handle()](../api/matcher.md#classmethod-handle) +2. [receive()](../api/matcher.md#classmethod-receive) +3. [got(key, prompt, args_parser)](../api/matcher.md#classmethod-got-key-prompt-none-args-parser-none) From 09a511acf893dfaa8acb51e8fefbca0f99c4d17b Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Tue, 10 Nov 2020 14:44:24 +0800 Subject: [PATCH 03/34] :memo: add poetry install --- docs/guide/installation.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 4f6e68e1..9d640dc2 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -13,7 +13,16 @@ pip uninstall nonebot pip install nonebot2 ``` -如果你需要使用最新的(可能尚未发布的)特性,可以克隆 Git 仓库后手动安装: +如果你需要使用最新的(可能尚未发布的)特性,可以直接从GitHub仓库安装: + +```bash +# master +poetry add git+https://github.com/nonebot/nonebot2.git#master +# dev +poetry add git+https://github.com/nonebot/nonebot2.git#dev +``` + +或者克隆 Git 仓库后手动安装: ```bash git clone https://github.com/nonebot/nonebot2.git From c3c2c7935d7d312651ed5b32dae5facd63618efe Mon Sep 17 00:00:00 2001 From: Artin Date: Tue, 10 Nov 2020 17:34:02 +0800 Subject: [PATCH 04/34] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20add=20adapters/cqhtt?= =?UTF-8?q?p=20directory?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot/adapters/{cqhttp.py => cqhttp/__init__.py} | 0 nonebot/adapters/{cqhttp.pyi => cqhttp/__init__.pyi} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename nonebot/adapters/{cqhttp.py => cqhttp/__init__.py} (100%) rename nonebot/adapters/{cqhttp.pyi => cqhttp/__init__.pyi} (100%) diff --git a/nonebot/adapters/cqhttp.py b/nonebot/adapters/cqhttp/__init__.py similarity index 100% rename from nonebot/adapters/cqhttp.py rename to nonebot/adapters/cqhttp/__init__.py diff --git a/nonebot/adapters/cqhttp.pyi b/nonebot/adapters/cqhttp/__init__.pyi similarity index 100% rename from nonebot/adapters/cqhttp.pyi rename to nonebot/adapters/cqhttp/__init__.pyi From 3903b718cb257856b3c8ffa6250ba7e266478723 Mon Sep 17 00:00:00 2001 From: nonebot Date: Tue, 10 Nov 2020 09:36:13 +0000 Subject: [PATCH 05/34] :memo: update api docs --- docs/api/nonebot.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/api/nonebot.md b/docs/api/nonebot.md index 7c89c882..cdb72bdb 100644 --- a/docs/api/nonebot.md +++ b/docs/api/nonebot.md @@ -28,18 +28,15 @@ sidebarDepth: 0 * `on_endswith` => `nonebot.plugin.on_endswith` +* `on_keyword` => `nonebot.plugin.on_keyword` + + * `on_command` => `nonebot.plugin.on_command` * `on_regex` => `nonebot.plugin.on_regex` -* `on_regex` => `nonebot.plugin.on_regex` - - -* `on_regex` => `nonebot.plugin.on_regex` - - * `CommandGroup` => `nonebot.plugin.CommandGroup` From b2a2234d5cd0c445628b294be4fc6a234f411628 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Wed, 11 Nov 2020 15:14:29 +0800 Subject: [PATCH 06/34] :art: change permission check from driver into adapter #46 --- nonebot/adapters/__init__.py | 9 ++- nonebot/adapters/cqhttp/__init__.py | 59 ++++++++++++++-- nonebot/adapters/cqhttp/__init__.pyi | 11 +++ nonebot/drivers/fastapi.py | 102 +++++++++------------------ nonebot/exception.py | 25 ++++++- 5 files changed, 130 insertions(+), 76 deletions(-) diff --git a/nonebot/adapters/__init__.py b/nonebot/adapters/__init__.py index 1f34d01e..ca2ef828 100644 --- a/nonebot/adapters/__init__.py +++ b/nonebot/adapters/__init__.py @@ -11,7 +11,7 @@ from dataclasses import dataclass, field from nonebot.config import Config from nonebot.typing import Driver, Message, WebSocket -from nonebot.typing import Any, Dict, Union, Optional, Callable, Iterable, Awaitable +from nonebot.typing import Any, Dict, Union, Optional, NoReturn, Callable, Iterable, Awaitable class BaseBot(abc.ABC): @@ -55,6 +55,13 @@ class BaseBot(abc.ABC): """Adapter 类型""" raise NotImplementedError + @classmethod + @abc.abstractmethod + async def check_permission(cls, driver: Driver, connection_type: str, + headers: dict, + body: Optional[dict]) -> Union[str, NoReturn]: + raise NotImplementedError + @abc.abstractmethod async def handle_message(self, message: dict): """ diff --git a/nonebot/adapters/cqhttp/__init__.py b/nonebot/adapters/cqhttp/__init__.py index 8a895f17..aa47e9cf 100644 --- a/nonebot/adapters/cqhttp/__init__.py +++ b/nonebot/adapters/cqhttp/__init__.py @@ -12,6 +12,8 @@ CQHTTP (OneBot) v11 协议适配 import re import sys +import hmac +import json import asyncio import httpx @@ -19,10 +21,10 @@ import httpx from nonebot.log import logger from nonebot.config import Config from nonebot.message import handle_event -from nonebot.typing import Any, Dict, Union, Tuple, Iterable, Optional -from nonebot.exception import NetworkError, ActionFailed, ApiNotAvailable from nonebot.typing import overrides, Driver, WebSocket, NoReturn +from nonebot.typing import Any, Dict, Union, Tuple, Iterable, Optional from nonebot.adapters import BaseBot, BaseEvent, BaseMessage, BaseMessageSegment +from nonebot.exception import NetworkError, ActionFailed, RequestDenied, ApiNotAvailable def log(level: str, message: str): @@ -39,6 +41,16 @@ def log(level: str, message: str): return logger.opt(colors=True).log(level, "CQHTTP | " + message) +def get_auth_bearer( + access_token: Optional[str] = None) -> Union[Optional[str], NoReturn]: + if not access_token: + return None + scheme, _, param = access_token.partition(" ") + if scheme.lower() not in ["bearer", "token"]: + raise RequestDenied(401, "Not authenticated") + return param + + def escape(s: str, *, escape_comma: bool = True) -> str: """ :说明: @@ -264,8 +276,6 @@ class Bot(BaseBot): self_id: str, *, websocket: Optional[WebSocket] = None): - if connection_type not in ["http", "websocket"]: - raise ValueError("Unsupported connection type") super().__init__(driver, connection_type, @@ -281,6 +291,47 @@ class Bot(BaseBot): """ return "cqhttp" + @classmethod + @overrides(BaseBot) + async def check_permission(cls, driver: Driver, connection_type: str, + headers: dict, + body: Optional[dict]) -> Union[str, NoReturn]: + x_self_id = headers.get("x-self-id") + x_signature = headers.get("x-signature") + access_token = get_auth_bearer(headers.get("authorization")) + + # 检查连接方式 + if connection_type not in ["http", "websocket"]: + log("WARNING", "Unsupported connection type") + raise RequestDenied(405, "Unsupported connection type") + + # 检查self_id + if not x_self_id: + log("WARNING", "Missing X-Self-ID Header") + raise RequestDenied(400, "Missing X-Self-ID Header") + + # 检查签名 + secret = driver.config.secret + if secret and connection_type == "http": + if not x_signature: + log("WARNING", "Missing Signature Header") + raise RequestDenied(401, "Missing Signature") + sig = hmac.new(secret.encode("utf-8"), + json.dumps(body).encode(), "sha1").hexdigest() + if x_signature != "sha1=" + sig: + log("WARNING", "Signature Header is invalid") + raise RequestDenied(403, "Signature is invalid") + + access_token = driver.config.access_token + if access_token and access_token != access_token: + log( + "WARNING", "Authorization Header is invalid" + if access_token else "Missing Authorization Header") + raise RequestDenied( + 403, "Authorization Header is invalid" + if access_token else "Missing Authorization Header") + return str(x_self_id) + @overrides(BaseBot) async def handle_message(self, message: dict): """ diff --git a/nonebot/adapters/cqhttp/__init__.pyi b/nonebot/adapters/cqhttp/__init__.pyi index 7920bfe3..e5398588 100644 --- a/nonebot/adapters/cqhttp/__init__.pyi +++ b/nonebot/adapters/cqhttp/__init__.pyi @@ -9,6 +9,11 @@ def log(level: str, message: str): ... +def get_auth_bearer( + access_token: Optional[str] = ...) -> Union[Optional[str], NoReturn]: + ... + + def escape(s: str, *, escape_comma: bool = ...) -> str: ... @@ -69,6 +74,12 @@ class Bot(BaseBot): def type(self) -> str: ... + @classmethod + async def check_permission(cls, driver: Driver, connection_type: str, + headers: dict, + body: Optional[dict]) -> Union[str, NoReturn]: + ... + async def handle_message(self, message: dict): ... diff --git a/nonebot/drivers/fastapi.py b/nonebot/drivers/fastapi.py index bb0009b4..6a5585dd 100644 --- a/nonebot/drivers/fastapi.py +++ b/nonebot/drivers/fastapi.py @@ -15,12 +15,13 @@ import logging import uvicorn from fastapi.responses import Response -from fastapi import Body, status, Header, FastAPI, Depends, HTTPException +from fastapi import Body, status, Header, Request, FastAPI, Depends, HTTPException from starlette.websockets import WebSocketDisconnect, WebSocket as FastAPIWebSocket from nonebot.log import logger from nonebot.config import Env, Config from nonebot.utils import DataclassEncoder +from nonebot.exception import RequestDenied from nonebot.drivers import BaseDriver, BaseWebSocket from nonebot.typing import Optional, Callable, overrides @@ -127,97 +128,58 @@ class Driver(BaseDriver): @overrides(BaseDriver) async def _handle_http(self, adapter: str, - data: dict = Body(...), - x_self_id: Optional[str] = Header(None), - x_signature: Optional[str] = Header(None), - auth: Optional[str] = Depends(get_auth_bearer)): - # 检查self_id - if not x_self_id: - logger.warning("Missing X-Self-ID Header") - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, - detail="Missing X-Self-ID Header") - - # 检查签名 - secret = self.config.secret - if secret: - if not x_signature: - logger.warning("Missing Signature Header") - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, - detail="Missing Signature") - sig = hmac.new(secret.encode("utf-8"), - json.dumps(data).encode(), "sha1").hexdigest() - if x_signature != "sha1=" + sig: - logger.warning("Signature Header is invalid") - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, - detail="Signature is invalid") - - access_token = self.config.access_token - if access_token and access_token != auth: - logger.warning("Authorization Header is invalid" - if auth else "Missing Authorization Header") - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, - detail="Authorization Header is invalid" - if auth else "Missing Authorization Header") - + request: Request, + data: dict = Body(...)): if not isinstance(data, dict): logger.warning("Data received is invalid") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST) + if adapter not in self._adapters: + logger.warning("Unknown adapter") + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, + detail="adapter not found") + + # 创建 Bot 对象 + BotClass = self._adapters[adapter] + headers = dict(request.headers) + try: + x_self_id = await BotClass.check_permission(self, "http", headers, + data) + except RequestDenied as e: + raise HTTPException(status_code=e.status_code, + detail=e.reason) from None + if x_self_id in self._clients: logger.warning("There's already a reverse websocket api connection," "so the event may be handled twice.") - # 创建 Bot 对象 - if adapter in self._adapters: - BotClass = self._adapters[adapter] - bot = BotClass(self, "http", self.config, x_self_id) - else: - logger.warning("Unknown adapter") - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, - detail="adapter not found") + bot = BotClass(self, "http", self.config, x_self_id) asyncio.create_task(bot.handle_message(data)) return Response("", 204) @overrides(BaseDriver) - async def _handle_ws_reverse( - self, - adapter: str, - websocket: FastAPIWebSocket, - x_self_id: str = Header(None), - auth: Optional[str] = Depends(get_auth_bearer)): + async def _handle_ws_reverse(self, adapter: str, + websocket: FastAPIWebSocket): ws = WebSocket(websocket) - access_token = self.config.access_token - if access_token and access_token != auth: - logger.warning("Authorization Header is invalid" - if auth else "Missing Authorization Header") - await ws.close(code=status.WS_1008_POLICY_VIOLATION) - return - - if not x_self_id: - logger.warning(f"Missing X-Self-ID Header") - await ws.close(code=status.WS_1008_POLICY_VIOLATION) - return - - if x_self_id in self._clients: - logger.warning(f"Connection Conflict: self_id {x_self_id}") + if adapter not in self._adapters: + logger.warning("Unknown adapter") await ws.close(code=status.WS_1008_POLICY_VIOLATION) return # Create Bot Object - if adapter in self._adapters: - BotClass = self._adapters[adapter] - bot = BotClass(self, - "websocket", - self.config, - x_self_id, - websocket=ws) - else: - logger.warning("Unknown adapter") + BotClass = self._adapters[adapter] + headers = dict(websocket.headers) + try: + x_self_id = await BotClass.check_permission(self, "websocket", + headers, None) + except RequestDenied: await ws.close(code=status.WS_1008_POLICY_VIOLATION) return + bot = BotClass(self, "websocket", self.config, x_self_id, websocket=ws) + await ws.accept() self._clients[x_self_id] = bot logger.opt(colors=True).info( diff --git a/nonebot/exception.py b/nonebot/exception.py index 45062635..4d862e41 100644 --- a/nonebot/exception.py +++ b/nonebot/exception.py @@ -105,6 +105,29 @@ class StopPropagation(Exception): pass +class RequestDenied(Exception): + """ + :说明: + + Bot 连接请求不合法。 + + :参数: + + * ``status_code: int``: HTTP 状态码 + * ``reason: str``: 拒绝原因 + """ + + def __init__(self, status_code: int, reason: str): + self.status_code = status_code + self.reason = reason + + def __repr__(self): + return f"" + + def __str__(self): + return self.__repr__() + + class ApiNotAvailable(Exception): """ :说明: @@ -131,7 +154,7 @@ class ActionFailed(Exception): :参数: - * ``retcode``: 错误代码 + * ``retcode: Optional[int]``: 错误代码 """ def __init__(self, retcode: Optional[int]): From 47b1affc56fb40ac5f0eb65c4a8b27730c10a564 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Wed, 11 Nov 2020 16:49:11 +0800 Subject: [PATCH 07/34] :memo: update handler create doc --- docs/guide/creating-a-handler.md | 34 ++++++++++++++++++++++++++++++-- docs/guide/creating-a-matcher.md | 8 ++++---- docs/guide/installation.md | 2 +- 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/docs/guide/creating-a-handler.md b/docs/guide/creating-a-handler.md index 1cbe6415..e1c4b77d 100644 --- a/docs/guide/creating-a-handler.md +++ b/docs/guide/creating-a-handler.md @@ -21,12 +21,42 @@ async def handle_city(bot: Bot, event: Event, state: dict): await weather.finish(city_weather) ``` -在之前的样例中,我们定义了两个函数,他们被事件响应器的装饰器装饰从而成为事件响应器的事件处理函数。 +在之前的样例中,我们定义了两个函数 `handle_first_receive`, `handle_city`,他们被事件响应器的装饰器装饰从而成为事件响应器的事件处理函数。 -### 装饰器 +:::tips 提示 +在事件响应器中,事件处理函数是**顺序**执行的! +::: + +### 添加一个事件处理函数 事件响应器提供了三种装饰事件处理函数的装饰器,分别是: 1. [handle()](../api/matcher.md#classmethod-handle) 2. [receive()](../api/matcher.md#classmethod-receive) 3. [got(key, prompt, args_parser)](../api/matcher.md#classmethod-got-key-prompt-none-args-parser-none) + +#### handle() + +简单的为事件响应器添加一个事件处理函数,这个函数将会在上一个处理函数正常返回执行完毕后立即执行。 + +#### receive() + +指示 NoneBot 接收一条新的用户消息后继续执行该处理函数。此时函数将会接收到新的消息而非前一条消息,之前相关信息可以存储在 state 中。 + +特别的,当装饰的函数前没有其他事件处理函数,那么 `receive()` 不会接收一条新的消息而是直接使用第一条接收到的消息。 + +#### got(key, prompt, args_parser) + +### 事件处理函数参数 + +事件处理函数类型为 `Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` 。 + +参数分别为: + +1. [nonebot.typing.Bot](../api/typing.md#bot): 即事件上报连接对应的 Bot 对象,为 BaseBot 的子类。特别注意,此处的类型注释可以替换为指定的 Bot 类型,例如:`nonebot.adapters.cqhttp.Bot`,只有在上报事件的 Bot 类型与类型注释相符时才会执行该处理函数!可用于多平台进行不同的处理。 +2. [nonebot.typing.Event](../api/typing.md#event): 即上报事件对象,可以获取到上报的所有信息。 +3. `state`: 状态字典,可以存储任意的信息 + +### 参数处理函数 args_parser + +### 逻辑控制 diff --git a/docs/guide/creating-a-matcher.md b/docs/guide/creating-a-matcher.md index 6c0929e1..1a27246a 100644 --- a/docs/guide/creating-a-matcher.md +++ b/docs/guide/creating-a-matcher.md @@ -106,8 +106,8 @@ rule 的出现使得 nonebot 对事件的响应可以非常自由,nonebot 内 - [startswith(msg)](../api/rule.md#startswith-msg) - [endswith(msg)](../api/rule.md#endswith-msg) -- [keyword(*keywords)](../api/rule.md#keyword-keywords) -- [command(*cmds)](../api/rule.md#command-cmds) +- [keyword(\*keywords)](../api/rule.md#keyword-keywords) +- [command(\*cmds)](../api/rule.md#command-cmds) - [regex(regex, flag)](../api/rule.md#regex-regex-flags-0) 以上规则都是返回类型为 `Rule` 的函数,`Rule` 由非负个 `RuleChecker` 组成,当所有 `RuleChecker` 返回 `True` 时匹配成功。这些 `Rule`, `RuleChecker` 的形式如下: @@ -125,7 +125,7 @@ def check(arg1, args2): async def _checker(bot: Bot, event: Event, state: dict) -> bool: return bool(arg1 + arg2) - + return Rule(_check) ``` @@ -137,7 +137,7 @@ from nonebot.rule import Rule Rule(async_checker1) & sync_checker & async_checker2 ``` -***请勿将事件处理的逻辑写入 `rule` 中,这会使得事件处理返回奇怪的响应。*** +**_请勿将事件处理的逻辑写入 `rule` 中,这会使得事件处理返回奇怪的响应。_** :::danger 警告 `Rule(*checkers)` 只接受 async function,或使用 `nonebot.utils.run_sync` 自行包裹 sync function。在使用 `与 &` 时,NoneBot 会自动包裹 sync function diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 9d640dc2..907630c9 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -13,7 +13,7 @@ pip uninstall nonebot pip install nonebot2 ``` -如果你需要使用最新的(可能尚未发布的)特性,可以直接从GitHub仓库安装: +如果你需要使用最新的(可能尚未发布的)特性,可以直接从 GitHub 仓库安装: ```bash # master From 9803925bbb04732003fa43b1feb12f67bbd0e611 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Thu, 12 Nov 2020 09:32:35 +0800 Subject: [PATCH 08/34] :memo: finish plugin guide --- docs/.vuepress/config.js | 3 +- docs/guide/creating-a-handler.md | 99 +++++++++++++++++++++++++++++++- docs/guide/creating-a-matcher.md | 86 +-------------------------- docs/guide/end-or-start.md | 7 +++ 4 files changed, 108 insertions(+), 87 deletions(-) create mode 100644 docs/guide/end-or-start.md diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 53bb7bfb..528d5101 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -105,7 +105,8 @@ module.exports = context => ({ "loading-a-plugin", "creating-a-plugin", "creating-a-matcher", - "creating-a-handler" + "creating-a-handler", + "end-or-start" ] } ], diff --git a/docs/guide/creating-a-handler.md b/docs/guide/creating-a-handler.md index e1c4b77d..3950dd67 100644 --- a/docs/guide/creating-a-handler.md +++ b/docs/guide/creating-a-handler.md @@ -47,6 +47,33 @@ async def handle_city(bot: Bot, event: Event, state: dict): #### got(key, prompt, args_parser) +指示 NoneBot 当 `state` 中不存在 `key` 时向用户发送 `prompt` 等待用户回复并赋值给 `state[key]`。 + +`prompt` 可以为 `str`, `Message`, `MessageSegment`,若为空则不会向用户发送,若不为空则会在 format 之后发送,即 `prompt.format(**state)`,注意对 `{}` 进行转义。示例: + +```python +@matcher.receive() +async def handle(bot: Bot, event: Event, state: dict): + state["key"] = "hello" + + +@matcher.got("key2", prompt="{key}!") +async def handle2(bot: Bot, event: Event, state: dict): + pass +``` + +`args_parser` 为参数处理函数,在这里传入一个新的函数以覆盖默认的参数处理。详情参照 [args_parser](#参数处理函数-args-parser) + + +特别的,这些装饰器都可以套娃使用: + +```python +@matcher.got("key1") +@matcher.got("key2") +async def handle(bot: Bot, event: Event, state: dict): + pass +``` + ### 事件处理函数参数 事件处理函数类型为 `Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` 。 @@ -55,8 +82,78 @@ async def handle_city(bot: Bot, event: Event, state: dict): 1. [nonebot.typing.Bot](../api/typing.md#bot): 即事件上报连接对应的 Bot 对象,为 BaseBot 的子类。特别注意,此处的类型注释可以替换为指定的 Bot 类型,例如:`nonebot.adapters.cqhttp.Bot`,只有在上报事件的 Bot 类型与类型注释相符时才会执行该处理函数!可用于多平台进行不同的处理。 2. [nonebot.typing.Event](../api/typing.md#event): 即上报事件对象,可以获取到上报的所有信息。 -3. `state`: 状态字典,可以存储任意的信息 +3. `state`: 状态字典,可以存储任意的信息,其中还包含一些特殊的值以获取 NoneBot 内部处理时的一些信息,如: + - `state["_current_key"]`: 存储当前 `got` 获取的参数名 + - `state["_prefix"]`, `state["_suffix"]`: 存储当前 TRIE 匹配的前缀/后缀,可以通过该值获取用户命令的原始命令 ### 参数处理函数 args_parser +在使用 `got` 获取用户输入参数时,需要对用户的消息进行处理以转换为我们所需要的信息。在默认情况下,NoneBot 会把用户的消息字符串原封不动的赋值给 `state[key]` 。可以通过以下两种方式修改默认处理逻辑: + +- `@matcher.args_parser` 装饰器:直接装饰一个函数作为参数处理器 +- `got(key, prompt, args_parser)`:直接把函数作为参数传入 + +参数处理函数类型为:`Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]`,即: + +```python +async def parser(bot: Bot, event: Event, state: dict): + state[state["_current_key"]] = str(event.message) +``` + +特别的,`state["_current_key"]` 中存储了当前获取的参数名 + ### 逻辑控制 + +NoneBot 也为事件处理函数提供了一些便捷的逻辑控制函数: + +#### `matcher.send` + +这个函数用于发送一条消息给当前交互的用户。~~其实这并不是一个逻辑控制函数,只是不知道放在哪里……~~ + +#### `matcher.pause` + +这个函数用于结束当前事件处理函数,强制接收一条新的消息再运行**下一个消息处理函数**。 + +#### `matcher.reject` + +这个函数用于结束当前事件处理函数,强制接收一条新的消息再**再次运行当前消息处理函数**。常用于用户输入信息不符合预期。 + +#### `matcher.finish` + +这个函数用于直接结束当前事件处理。 + +以上三个函数都拥有一个参数 `message` / `prompt`,用于向用户发送一条消息。以及 `**kwargs` 直接传递给 `bot.send` 的额外参数。 + +## 常用事件处理结构 + +```python +matcher = on_command("test") + +# 修改默认参数处理 +@matcher.args_parser +async def parse(bot: Bot, event: Event, state: dict): + print(state["_current_key"], ":", str(event.message)) + state[state["_current_key"]] = str(event.message) + +@matcher.handle() +async def first_receive(bot: Bot, event: Event, state: dict): + # 获取用户原始命令,如:/test + print(state["_prefix"]["raw_command"]) + # 处理用户输入参数,如:/test arg1 arg2 + raw_args = str(event.message).strip() + if raw_args: + arg_list = raw_args.split() + # 将参数存入state以阻止后续再向用户询问参数 + state["arg1"] = arg_list[0] + + +@matcher.got("arg1", prompt="参数?") +async def arg_handle(bot: Bot, event: Event, state: dict): + # 在这里对参数进行验证 + if state["arg1"] not in ["allow", "list"]: + await matcher.reject("参数不正确!请重新输入") + # 发送一些信息 + await bot.send("message") + await matcher.send("message") + await matcher.finish("message") +``` diff --git a/docs/guide/creating-a-matcher.md b/docs/guide/creating-a-matcher.md index 1a27246a..027dd009 100644 --- a/docs/guide/creating-a-matcher.md +++ b/docs/guide/creating-a-matcher.md @@ -142,88 +142,4 @@ Rule(async_checker1) & sync_checker & async_checker2 :::danger 警告 `Rule(*checkers)` 只接受 async function,或使用 `nonebot.utils.run_sync` 自行包裹 sync function。在使用 `与 &` 时,NoneBot 会自动包裹 sync function ::: - -### 编写事件处理函数 [Handler](../api/typing.md#handler) - -```python{1,2,8,9} -@weather.handle() -async def handle_first_receive(bot: Bot, event: Event, state: dict): - args = str(event.message).strip() # 首次发送命令时跟随的参数,例:/天气 上海,则args为上海 - if args: - state["city"] = args # 如果用户发送了参数则直接赋值 - - -@weather.got("city", prompt="你想查询哪个城市的天气呢?") -async def handle_city(bot: Bot, event: Event, state: dict): - city = state["city"] - if city not in ["上海", "北京"]: - await weather.reject("你想查询的城市暂不支持,请重新输入!") - city_weather = await get_weather(city) - await weather.finish(city_weather) -``` - -在上面的代码中,我们给 `weather` 事件响应器添加了两个事件处理函数:`handle_first_receive`, `handle_city` - -其中有几个要点,我们一一解释: - -#### 添加一个事件处理函数 - -在事件响应器响应事件时,事件处理函数会依次顺序执行,也就是说,与添加顺序一致。 - -我们可以使用 `@matcher.handle()` 装饰器来简单地为该事件响应器添加一个处理函数。 - -同时,NoneBot 内置了几种添加事件处理函数方式以方便处理: - -- `@matcher.receive()`: 指示 NoneBot 接收一条新的用户消息以继续执行后续处理函数。 -- `@matcher.got(key, [prompt="请输入key"], [args_parser=function])`: 指示 NoneBot 当 `state` 中不存在 `key` 时向用户发送 `prompt` 等待用户回复并赋值给 `state[key]` - -这些装饰器可以套娃使用!例如: - -```python -@matcher.got("key1") -@matcher.got("key2") -async def handle(bot: Bot, event: Event, state: dict): - pass -``` - -#### 事件处理函数参数 - -事件处理函数类型为 `Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` 。 - -参数分别为: - -1. [nonebot.typing.Bot](../api/typing.md#bot): 即事件上报连接对应的 Bot 对象,为 BaseBot 的子类。特别注意,此处的类型注释可以替换为指定的 Bot 类型,例如:`nonebot.adapters.cqhttp.Bot`,只有在上报事件的 Bot 类型与类型注释相符时才会执行该处理函数!可用于多平台进行不同的处理。 -2. [nonebot.typing.Event](../api/typing.md#event): 即上报事件对象,可以获取到上报的所有信息。 -3. `state`: 状态字典,可以存储任意的信息 - -#### 处理事件 - -在事件处理函数中,我们只需要对 `event` 做出相应的处理,存入状态字典 `state` 中,或者向用户发送消息、调用某个机器人 API 等等。 - -在 NoneBot 中,提供了几种特殊的处理函数: - -##### `@matcher.args_parser` - -这是一个装饰器,装饰一个函数来使它成为参数的默认解析函数,当使用 `matcher.got(xxx, [args_parser])` 获取到一条消息时,会运行 `matcher.got` 的 `args_parser` ,如果不存在则运行 `@matcher.args_parser`。 - -##### `matcher.pause` - -这个函数用于结束当前事件处理函数,强制接收一条新的消息再运行**下一个消息处理函数**。 - -##### `matcher.reject` - -这个函数用于结束当前事件处理函数,强制接收一条新的消息再**再次运行当前消息处理函数**。 - -##### `matcher.finish` - -这个函数用于直接结束当前事件处理。 - -以上三个函数都拥有一个参数 `prompt`,用于向用户发送一条消息。 - -## 结语 - -至此,相信你已经能够写出一个基础的插件了,更多的用法将会在 进阶 部分进行介绍,这里给出几个小提示: - -- 请千万注意事件处理器的优先级设定 -- 在匹配规则中请勿使用耗时极长的函数 -- 同一个用户可以跨群(私聊)继续他的事件处理(除非做出权限限制,将在后续介绍) +t \ No newline at end of file diff --git a/docs/guide/end-or-start.md b/docs/guide/end-or-start.md new file mode 100644 index 00000000..f1b0baa9 --- /dev/null +++ b/docs/guide/end-or-start.md @@ -0,0 +1,7 @@ +# 结语 + +至此,相信你已经能够写出一个基础的插件了,更多的用法将会在 进阶 部分进行介绍,这里给出几个小提示: + +- 请千万注意事件处理器的优先级设定 +- 在匹配规则中请勿使用耗时极长的函数 +- 同一个用户可以**跨群**(**私聊**)继续他的事件处理(除非做出权限限制,将在后续介绍) From 5c29abd7245b75fbc108ea3d1559314c21cf8814 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Thu, 12 Nov 2020 13:10:41 +0800 Subject: [PATCH 09/34] :pencil2: fix tip typo --- docs/guide/creating-a-handler.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/guide/creating-a-handler.md b/docs/guide/creating-a-handler.md index 3950dd67..725aaacf 100644 --- a/docs/guide/creating-a-handler.md +++ b/docs/guide/creating-a-handler.md @@ -23,7 +23,7 @@ async def handle_city(bot: Bot, event: Event, state: dict): 在之前的样例中,我们定义了两个函数 `handle_first_receive`, `handle_city`,他们被事件响应器的装饰器装饰从而成为事件响应器的事件处理函数。 -:::tips 提示 +:::tip 提示 在事件响应器中,事件处理函数是**顺序**执行的! ::: @@ -64,7 +64,6 @@ async def handle2(bot: Bot, event: Event, state: dict): `args_parser` 为参数处理函数,在这里传入一个新的函数以覆盖默认的参数处理。详情参照 [args_parser](#参数处理函数-args-parser) - 特别的,这些装饰器都可以套娃使用: ```python @@ -83,8 +82,9 @@ async def handle(bot: Bot, event: Event, state: dict): 1. [nonebot.typing.Bot](../api/typing.md#bot): 即事件上报连接对应的 Bot 对象,为 BaseBot 的子类。特别注意,此处的类型注释可以替换为指定的 Bot 类型,例如:`nonebot.adapters.cqhttp.Bot`,只有在上报事件的 Bot 类型与类型注释相符时才会执行该处理函数!可用于多平台进行不同的处理。 2. [nonebot.typing.Event](../api/typing.md#event): 即上报事件对象,可以获取到上报的所有信息。 3. `state`: 状态字典,可以存储任意的信息,其中还包含一些特殊的值以获取 NoneBot 内部处理时的一些信息,如: - - `state["_current_key"]`: 存储当前 `got` 获取的参数名 - - `state["_prefix"]`, `state["_suffix"]`: 存储当前 TRIE 匹配的前缀/后缀,可以通过该值获取用户命令的原始命令 + +- `state["_current_key"]`: 存储当前 `got` 获取的参数名 +- `state["_prefix"]`, `state["_suffix"]`: 存储当前 TRIE 匹配的前缀/后缀,可以通过该值获取用户命令的原始命令 ### 参数处理函数 args_parser From 28e3177e603b6e60f5d32b25152fc60596fd83a9 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Fri, 13 Nov 2020 01:46:26 +0800 Subject: [PATCH 10/34] :bulb: update source docstring #46 #49 --- docs/api/adapters/README.md | 41 ++++++++++- docs/api/adapters/cqhttp.md | 9 +++ docs/api/drivers/fastapi.md | 27 +------- docs/api/exception.md | 23 +++++- docs_build/drivers/fastapi.rst | 3 +- nonebot/adapters/__init__.py | 15 +++- nonebot/adapters/cqhttp/__init__.py | 4 ++ nonebot/drivers/__init__.py | 4 +- nonebot/drivers/fastapi.py | 7 +- poetry.lock | 104 ++++++++++++++++------------ 10 files changed, 160 insertions(+), 77 deletions(-) diff --git a/docs/api/adapters/README.md b/docs/api/adapters/README.md index f3f937ec..c2d2e399 100644 --- a/docs/api/adapters/README.md +++ b/docs/api/adapters/README.md @@ -69,6 +69,45 @@ Websocket 连接对象 Adapter 类型 +### _abstract async classmethod_ `check_permission(driver, connection_type, headers, body)` + + +* **说明** + + 检查连接请求是否合法的函数,如果合法则返回当前连接 `唯一标识符`,通常为机器人 ID;如果不合法则抛出 `RequestDenied` 异常。 + + + +* **参数** + + + * `driver: Driver`: Driver 对象 + + + * `connection_type: str`: 连接类型 + + + * `headers: dict`: 请求头 + + + * `body: Optional[dict]`: 请求数据,WebSocket 连接该部分为空 + + + +* **返回** + + + * `str`: 连接唯一标识符 + + + +* **异常** + + + * `RequestDenied`: 请求非法 + + + ### _abstract async_ `handle_message(message)` @@ -108,7 +147,7 @@ Adapter 类型 ```python -await bot.call_api("send_msg", data={"message": "hello world"}) +await bot.call_api("send_msg", message="hello world"}) await bot.send_msg(message="hello world") ``` diff --git a/docs/api/adapters/cqhttp.md b/docs/api/adapters/cqhttp.md index 73b4e044..3f609383 100644 --- a/docs/api/adapters/cqhttp.md +++ b/docs/api/adapters/cqhttp.md @@ -169,6 +169,15 @@ CQHTTP 协议 Bot 适配。继承属性参考 [BaseBot](./#class-basebot) 。 * 返回: `"cqhttp"` +### _async classmethod_ `check_permission(driver, connection_type, headers, body)` + + +* **说明** + + CQHTTP (OneBot) 协议鉴权。参考 [鉴权](https://github.com/howmanybots/onebot/blob/master/v11/specs/communication/authorization.md) + + + ### _async_ `handle_message(message)` diff --git a/docs/api/drivers/fastapi.md b/docs/api/drivers/fastapi.md index 523b09b2..e890fb69 100644 --- a/docs/api/drivers/fastapi.md +++ b/docs/api/drivers/fastapi.md @@ -17,19 +17,6 @@ sidebarDepth: 0 FastAPI 驱动框架 -### `__init__(env, config)` - - -* **参数** - - - -* `env: Env`: 包含环境信息的 Env 对象 - - -* `config: Config`: 包含配置信息的 Config 对象 - - ### _property_ `type` 驱动名称: `fastapi` @@ -65,12 +52,12 @@ fastapi 使用的 logger 使用 `uvicorn` 启动 FastAPI -### _async_ `_handle_http(adapter, data=Body(Ellipsis), x_self_id=Header(None), x_signature=Header(None), auth=Depends(get_auth_bearer))` +### _async_ `_handle_http(adapter, request, data=Body(Ellipsis))` 用于处理 HTTP 类型请求的函数 -### _async_ `_handle_ws_reverse(adapter, websocket, x_self_id=Header(None), auth=Depends(get_auth_bearer))` +### _async_ `_handle_ws_reverse(adapter, websocket)` 用于处理 WebSocket 类型请求的函数 @@ -80,16 +67,6 @@ fastapi 使用的 logger 基类:[`nonebot.drivers.BaseWebSocket`](#None) -### `__init__(websocket)` - - -* **参数** - - - -* `websocket: Any`: WebSocket 连接对象 - - ### _property_ `closed` diff --git a/docs/api/exception.md b/docs/api/exception.md index 2c7abad1..5c89a5f8 100644 --- a/docs/api/exception.md +++ b/docs/api/exception.md @@ -117,6 +117,27 @@ sidebarDepth: 0 +## _exception_ `RequestDenied` + +基类:`Exception` + + +* **说明** + + Bot 连接请求不合法。 + + + +* **参数** + + + * `status_code: int`: HTTP 状态码 + + + * `reason: str`: 拒绝原因 + + + ## _exception_ `ApiNotAvailable` 基类:`Exception` @@ -153,4 +174,4 @@ sidebarDepth: 0 * **参数** - * `retcode`: 错误代码 + * `retcode: Optional[int]`: 错误代码 diff --git a/docs_build/drivers/fastapi.rst b/docs_build/drivers/fastapi.rst index bbc67caf..87c7b609 100644 --- a/docs_build/drivers/fastapi.rst +++ b/docs_build/drivers/fastapi.rst @@ -4,10 +4,9 @@ sidebarDepth: 0 --- NoneBot.drivers.fastapi 模块 -===================== +============================= .. automodule:: nonebot.drivers.fastapi :members: :private-members: - :special-members: __init__ :show-inheritance: diff --git a/nonebot/adapters/__init__.py b/nonebot/adapters/__init__.py index ca2ef828..2ca5505f 100644 --- a/nonebot/adapters/__init__.py +++ b/nonebot/adapters/__init__.py @@ -60,6 +60,19 @@ class BaseBot(abc.ABC): async def check_permission(cls, driver: Driver, connection_type: str, headers: dict, body: Optional[dict]) -> Union[str, NoReturn]: + """ + :说明: + 检查连接请求是否合法的函数,如果合法则返回当前连接 ``唯一标识符``,通常为机器人 ID;如果不合法则抛出 ``RequestDenied`` 异常。 + :参数: + * ``driver: Driver``: Driver 对象 + * ``connection_type: str``: 连接类型 + * ``headers: dict``: 请求头 + * ``body: Optional[dict]``: 请求数据,WebSocket 连接该部分为空 + :返回: + - ``str``: 连接唯一标识符 + :异常: + - ``RequestDenied``: 请求非法 + """ raise NotImplementedError @abc.abstractmethod @@ -84,7 +97,7 @@ class BaseBot(abc.ABC): .. code-block:: python - await bot.call_api("send_msg", data={"message": "hello world"}) + await bot.call_api("send_msg", message="hello world"}) await bot.send_msg(message="hello world") """ raise NotImplementedError diff --git a/nonebot/adapters/cqhttp/__init__.py b/nonebot/adapters/cqhttp/__init__.py index aa47e9cf..867c3bad 100644 --- a/nonebot/adapters/cqhttp/__init__.py +++ b/nonebot/adapters/cqhttp/__init__.py @@ -296,6 +296,10 @@ class Bot(BaseBot): async def check_permission(cls, driver: Driver, connection_type: str, headers: dict, body: Optional[dict]) -> Union[str, NoReturn]: + """ + :说明: + CQHTTP (OneBot) 协议鉴权。参考 `鉴权 `_ + """ x_self_id = headers.get("x-self-id") x_signature = headers.get("x-signature") access_token = get_auth_bearer(headers.get("authorization")) diff --git a/nonebot/drivers/__init__.py b/nonebot/drivers/__init__.py index 09c182fe..ce6097ff 100644 --- a/nonebot/drivers/__init__.py +++ b/nonebot/drivers/__init__.py @@ -1,6 +1,6 @@ """ 后端驱动适配基类 -=============== +================= 各驱动请继承以下基类 """ @@ -27,6 +27,7 @@ class BaseDriver(abc.ABC): def __init__(self, env: Env, config: Config): """ :参数: + * ``env: Env``: 包含环境信息的 Env 对象 * ``config: Config``: 包含配置信息的 Config 对象 """ @@ -136,6 +137,7 @@ class BaseWebSocket(object): def __init__(self, websocket): """ :参数: + * ``websocket: Any``: WebSocket 连接对象 """ self._websocket = websocket diff --git a/nonebot/drivers/fastapi.py b/nonebot/drivers/fastapi.py index 6a5585dd..0828e344 100644 --- a/nonebot/drivers/fastapi.py +++ b/nonebot/drivers/fastapi.py @@ -150,7 +150,7 @@ class Driver(BaseDriver): detail=e.reason) from None if x_self_id in self._clients: - logger.warning("There's already a reverse websocket api connection," + logger.warning("There's already a reverse websocket connection," "so the event may be handled twice.") bot = BotClass(self, "http", self.config, x_self_id) @@ -178,6 +178,11 @@ class Driver(BaseDriver): await ws.close(code=status.WS_1008_POLICY_VIOLATION) return + if x_self_id in self._clients: + logger.warning("There's already a reverse websocket connection, " + f"{adapter.upper()} Bot {x_self_id} ignored.") + await ws.close(code=status.WS_1008_POLICY_VIOLATION) + bot = BotClass(self, "websocket", self.config, x_self_id, websocket=ws) await ws.accept() diff --git a/poetry.lock b/poetry.lock index 4cad8a1e..9e3185f4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -92,7 +92,7 @@ reference = "aliyun" [[package]] name = "babel" -version = "2.8.0" +version = "2.9.0" description = "Internationalization utilities" category = "dev" optional = false @@ -158,7 +158,7 @@ reference = "aliyun" [[package]] name = "certifi" -version = "2020.6.20" +version = "2020.11.8" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -433,7 +433,7 @@ reference = "aliyun" [[package]] name = "httpcore" -version = "0.12.0" +version = "0.12.1" description = "A minimal low-level HTTP client." category = "main" optional = false @@ -1026,7 +1026,7 @@ reference = "aliyun" [[package]] name = "regex" -version = "2020.10.28" +version = "2020.11.11" description = "Alternative regular expression module, to replace re." category = "main" optional = true @@ -1039,7 +1039,7 @@ reference = "aliyun" [[package]] name = "requests" -version = "2.24.0" +version = "2.25.0" description = "Python HTTP for Humans." category = "main" optional = false @@ -1049,7 +1049,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" certifi = ">=2017.4.17" chardet = ">=3.0.2,<4" idna = ">=2.5,<3" -urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" +urllib3 = ">=1.21.1,<1.27" [package.extras] security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] @@ -1174,7 +1174,7 @@ yapf = "*" type = "git" url = "https://github.com/nonebot/sphinx-markdown-builder.git" reference = "master" -resolved_reference = "09751bd6c81ee246d91d63baa65d09e3618b7288" +resolved_reference = "fdbc39e1b50aabf8dbcf129895fbbd02cbf54554" [[package]] name = "sphinxcontrib-applehelp" @@ -1366,7 +1366,7 @@ reference = "aliyun" [[package]] name = "urllib3" -version = "1.25.11" +version = "1.26.1" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false @@ -1539,8 +1539,8 @@ attrs = [ {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, ] babel = [ - {file = "Babel-2.8.0-py2.py3-none-any.whl", hash = "sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"}, - {file = "Babel-2.8.0.tar.gz", hash = "sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38"}, + {file = "Babel-2.9.0-py2.py3-none-any.whl", hash = "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5"}, + {file = "Babel-2.9.0.tar.gz", hash = "sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05"}, ] bcrypt = [ {file = "bcrypt-3.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c95d4cbebffafcdd28bd28bb4e25b31c50f6da605c81ffd9ad8a3d1b2ab7b1b6"}, @@ -1560,8 +1560,8 @@ cached-property = [ {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"}, ] certifi = [ - {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"}, - {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"}, + {file = "certifi-2020.11.8-py2.py3-none-any.whl", hash = "sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd"}, + {file = "certifi-2020.11.8.tar.gz", hash = "sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4"}, ] cffi = [ {file = "cffi-1.14.3-2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc"}, @@ -1676,8 +1676,8 @@ html2text = [ {file = "html2text-2020.1.16.tar.gz", hash = "sha256:e296318e16b059ddb97f7a8a1d6a5c1d7af4544049a01e261731d2d5cc277bbb"}, ] httpcore = [ - {file = "httpcore-0.12.0-py3-none-any.whl", hash = "sha256:18c4afcbfe884b635e59739105aed1692e132bc5d31597109f3c1c97e4ec1cac"}, - {file = "httpcore-0.12.0.tar.gz", hash = "sha256:2526a38f31ac5967d38b7f593b5d8c4bd3fa82c21400402f866ba3312946acbf"}, + {file = "httpcore-0.12.1-py3-none-any.whl", hash = "sha256:37660b117ba9055e8d5d19c29684d2204bbd3150020dde0ebd2dd2bcf18dfe50"}, + {file = "httpcore-0.12.1.tar.gz", hash = "sha256:3c5fcd97c52c3f6a1e4d939d776458e6177b5c238b825ed51d72840e582573b5"}, ] httptools = [ {file = "httptools-0.1.1-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:a2719e1d7a84bb131c4f1e0cb79705034b48de6ae486eb5297a139d6a3296dce"}, @@ -1909,37 +1909,51 @@ pyyaml = [ {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] regex = [ - {file = "regex-2020.10.28-cp27-cp27m-win32.whl", hash = "sha256:4b5a9bcb56cc146c3932c648603b24514447eafa6ce9295234767bf92f69b504"}, - {file = "regex-2020.10.28-cp27-cp27m-win_amd64.whl", hash = "sha256:c13d311a4c4a8d671f5860317eb5f09591fbe8259676b86a85769423b544451e"}, - {file = "regex-2020.10.28-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c8a2b7ccff330ae4c460aff36626f911f918555660cc28163417cb84ffb25789"}, - {file = "regex-2020.10.28-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4afa350f162551cf402bfa3cd8302165c8e03e689c897d185f16a167328cc6dd"}, - {file = "regex-2020.10.28-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:b88fa3b8a3469f22b4f13d045d9bd3eda797aa4e406fde0a2644bc92bbdd4bdd"}, - {file = "regex-2020.10.28-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f43109822df2d3faac7aad79613f5f02e4eab0fc8ad7932d2e70e2a83bd49c26"}, - {file = "regex-2020.10.28-cp36-cp36m-win32.whl", hash = "sha256:8092a5a06ad9a7a247f2a76ace121183dc4e1a84c259cf9c2ce3bbb69fac3582"}, - {file = "regex-2020.10.28-cp36-cp36m-win_amd64.whl", hash = "sha256:49461446b783945597c4076aea3f49aee4b4ce922bd241e4fcf62a3e7c61794c"}, - {file = "regex-2020.10.28-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:8ca9dca965bd86ea3631b975d63b0693566d3cc347e55786d5514988b6f5b84c"}, - {file = "regex-2020.10.28-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ea37320877d56a7f0a1e6a625d892cf963aa7f570013499f5b8d5ab8402b5625"}, - {file = "regex-2020.10.28-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:3a5f08039eee9ea195a89e180c5762bfb55258bfb9abb61a20d3abee3b37fd12"}, - {file = "regex-2020.10.28-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:cb905f3d2e290a8b8f1579d3984f2cfa7c3a29cc7cba608540ceeed18513f520"}, - {file = "regex-2020.10.28-cp37-cp37m-win32.whl", hash = "sha256:a62162be05edf64f819925ea88d09d18b09bebf20971b363ce0c24e8b4aa14c0"}, - {file = "regex-2020.10.28-cp37-cp37m-win_amd64.whl", hash = "sha256:03855ee22980c3e4863dc84c42d6d2901133362db5daf4c36b710dd895d78f0a"}, - {file = "regex-2020.10.28-cp38-cp38-manylinux1_i686.whl", hash = "sha256:625116aca6c4b57c56ea3d70369cacc4d62fead4930f8329d242e4fe7a58ce4b"}, - {file = "regex-2020.10.28-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2dc522e25e57e88b4980d2bdd334825dbf6fa55f28a922fc3bfa60cc09e5ef53"}, - {file = "regex-2020.10.28-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:119e0355dbdd4cf593b17f2fc5dbd4aec2b8899d0057e4957ba92f941f704bf5"}, - {file = "regex-2020.10.28-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:cfcf28ed4ce9ced47b9b9670a4f0d3d3c0e4d4779ad4dadb1ad468b097f808aa"}, - {file = "regex-2020.10.28-cp38-cp38-win32.whl", hash = "sha256:06b52815d4ad38d6524666e0d50fe9173533c9cc145a5779b89733284e6f688f"}, - {file = "regex-2020.10.28-cp38-cp38-win_amd64.whl", hash = "sha256:c3466a84fce42c2016113101018a9981804097bacbab029c2d5b4fcb224b89de"}, - {file = "regex-2020.10.28-cp39-cp39-manylinux1_i686.whl", hash = "sha256:c2c6c56ee97485a127555c9595c069201b5161de9d05495fbe2132b5ac104786"}, - {file = "regex-2020.10.28-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1ec66700a10e3c75f1f92cbde36cca0d3aaee4c73dfa26699495a3a30b09093c"}, - {file = "regex-2020.10.28-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:11116d424734fe356d8777f89d625f0df783251ada95d6261b4c36ad27a394bb"}, - {file = "regex-2020.10.28-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f1fce1e4929157b2afeb4bb7069204d4370bab9f4fc03ca1fbec8bd601f8c87d"}, - {file = "regex-2020.10.28-cp39-cp39-win32.whl", hash = "sha256:832339223b9ce56b7b15168e691ae654d345ac1635eeb367ade9ecfe0e66bee0"}, - {file = "regex-2020.10.28-cp39-cp39-win_amd64.whl", hash = "sha256:654c1635f2313d0843028487db2191530bca45af61ca85d0b16555c399625b0e"}, - {file = "regex-2020.10.28.tar.gz", hash = "sha256:dd3e6547ecf842a29cf25123fbf8d2461c53c8d37aa20d87ecee130c89b7079b"}, + {file = "regex-2020.11.11-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:dd7bee615680d940dd44ac0a479f2bc5f73d6ca63a5915cd8d30739c14ca522c"}, + {file = "regex-2020.11.11-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3002ee2d4e8bbe4656237627203d8290a562d1fc1962deee470905ab63570345"}, + {file = "regex-2020.11.11-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:064d2fc83ab4ee0055fcc1ef38ec60e505742850a40061f854ac64cb3d8d6dd3"}, + {file = "regex-2020.11.11-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:83a390a653c13be1ab26287240df1fd9324ca8a0d31b603fa57cd7d9520648fa"}, + {file = "regex-2020.11.11-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:412969d58ecd4f576510ec88bcb7602e9e582bbef78859ed8c9ca4de4f9e891c"}, + {file = "regex-2020.11.11-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:ccfea4911ac28a8f744096bce1559e0bd86b09a53c8a9d5856ca8e1f5f4de1f5"}, + {file = "regex-2020.11.11-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:cefcdb2ac3b67fd9f7244820ce1965c8cf352366199cc1358d67c6cc3c5c8bbc"}, + {file = "regex-2020.11.11-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9e8b3187f6beea8e56cb4b33c35049cbe376cf69aefaee5bc035309d88c98ca5"}, + {file = "regex-2020.11.11-cp36-cp36m-win32.whl", hash = "sha256:787e44e5f4fd027dd90b5ee0240b05dc1752cb43c2903617f25baa495fe551e9"}, + {file = "regex-2020.11.11-cp36-cp36m-win_amd64.whl", hash = "sha256:a9f76d9122359b09e38f27cd9c41729169171cf0fd73ec5b22cc4628f9e486ca"}, + {file = "regex-2020.11.11-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6d128368def4b0cd95c0fc9d99a89ae73c083b25e67f27a410830e30f9df0edc"}, + {file = "regex-2020.11.11-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:df50ba964812606663ca9d23d374036bc5ae3d71e86168409cdd84ca7948d8a3"}, + {file = "regex-2020.11.11-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d1e57c16c4840f1c3543507742e99b8398609474a0e6a6925476914479de3488"}, + {file = "regex-2020.11.11-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6e50b3b417ab2fd67bfa6235f0df4782fe2ff8be83f0c4435e1dc43d25052ee8"}, + {file = "regex-2020.11.11-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bb17a7fe9c47167337009ce18cd6e6b3edf3ca0063bf6bed6ce02515129c016a"}, + {file = "regex-2020.11.11-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:826d0119f14f9a9ce25999a13ed5922c785b50e469800f6e5a6721318650ef49"}, + {file = "regex-2020.11.11-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:8cc3717146ce4040419639cf45455663a002a554806ddac46304acc5bd41dae2"}, + {file = "regex-2020.11.11-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:86ad88c7c2512094a85b0a01ce053bab1e28eafb8f3868bb8c22f4903e33f147"}, + {file = "regex-2020.11.11-cp37-cp37m-win32.whl", hash = "sha256:e03867f3baf64ecab47dfc9ddb58afc67acb6a0f80f6cf8ff9fa82962ec4d1cd"}, + {file = "regex-2020.11.11-cp37-cp37m-win_amd64.whl", hash = "sha256:56d1e298bb6482d0466399a6383181bf2627c37ad414e205b3ce0f85aa140be7"}, + {file = "regex-2020.11.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:19ac2bf0048a2f4d460ee20647e84ca160512a7ee8af844dc9207720778470f1"}, + {file = "regex-2020.11.11-cp38-cp38-manylinux1_i686.whl", hash = "sha256:84ab584dcb5e81815040d86148805a808acb0bee303d19638fe2f9488d704bc1"}, + {file = "regex-2020.11.11-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:4159ecf20dffea07f4a7241b2a236f90eb622c7e8caab9f43caba5f27ca37284"}, + {file = "regex-2020.11.11-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:8060be04baec546fe3afa6975d2998e15d1b655d7255f0e6b0ed3f482cccc218"}, + {file = "regex-2020.11.11-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:cdb98be55db1b94c950822cbc10d3d768f01e184365851ebb42cd377486ced7b"}, + {file = "regex-2020.11.11-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:11d9100bd874ce8b2a037db9150e732cd768359fc25fe5f77973208aa24eb13e"}, + {file = "regex-2020.11.11-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:0951c78fa4cb26d1278a4b3784fcf973fc97ec39c07483328a74b034b0cc569c"}, + {file = "regex-2020.11.11-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:c8b1ad791debd67221fb1266f8d09730ae927acacb32d0dad9fd07a7d341a28f"}, + {file = "regex-2020.11.11-cp38-cp38-win32.whl", hash = "sha256:beae9db1545f8116cfc9301a9601e9c975bb56ca22a38ac0fe06a72c3460f31a"}, + {file = "regex-2020.11.11-cp38-cp38-win_amd64.whl", hash = "sha256:48e94218f06317b6d32feb4ecff8b6025695450009bcb3291fb23daf79689431"}, + {file = "regex-2020.11.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c67fd5f3ad81f8301184354014e8e7510ab77e0c7e450a427d77f28ae8effbef"}, + {file = "regex-2020.11.11-cp39-cp39-manylinux1_i686.whl", hash = "sha256:e7cdd5ee8053c82607432b7ebad37e2ece54548fef2b254f7bce6f7831904586"}, + {file = "regex-2020.11.11-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:394b5be4fa72354a78763b317f82997ad881896dd4a860e429a6fa74afaacb07"}, + {file = "regex-2020.11.11-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:3b46a4c73ec1f25361147a7a0fd86084f3627dc78d09bcbe14e70db12683efec"}, + {file = "regex-2020.11.11-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:267d1b13f863e664150948ce2a9ed4927bf4ac7a068780f1ee8af83352aa17a2"}, + {file = "regex-2020.11.11-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:68267a7a5fb0bd9676b86f967143b6a6ecefb3eed4042ecc9e7f0e014aef8f74"}, + {file = "regex-2020.11.11-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:e899b69dd5d26655cb454835ea2fceb18832c9ee9c4fb45dc4cf8a6089d35312"}, + {file = "regex-2020.11.11-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:396411bb5a7849aeda9c49873b8295919fdc118c50b57122b09cb2097047c118"}, + {file = "regex-2020.11.11-cp39-cp39-win32.whl", hash = "sha256:32f8714c4bcc4b0d2aa259b1647e3c5b6cfe2e923c6c124234a5e03408224227"}, + {file = "regex-2020.11.11-cp39-cp39-win_amd64.whl", hash = "sha256:bf02ab95ff5261ba108725dbd795bf6395eaac1b8468b41472d82d35b12b0295"}, + {file = "regex-2020.11.11.tar.gz", hash = "sha256:0a235841237d4487329bcabcb5b902858f7967f5e684e08e968367f25b2c3d37"}, ] requests = [ - {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"}, - {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"}, + {file = "requests-2.25.0-py2.py3-none-any.whl", hash = "sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998"}, + {file = "requests-2.25.0.tar.gz", hash = "sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8"}, ] rfc3986 = [ {file = "rfc3986-1.4.0-py2.py3-none-any.whl", hash = "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"}, @@ -2009,8 +2023,8 @@ untokenize = [ {file = "untokenize-0.1.1.tar.gz", hash = "md5:50d325dff09208c624cc603fad33bb0d"}, ] urllib3 = [ - {file = "urllib3-1.25.11-py2.py3-none-any.whl", hash = "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e"}, - {file = "urllib3-1.25.11.tar.gz", hash = "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2"}, + {file = "urllib3-1.26.1-py2.py3-none-any.whl", hash = "sha256:61ad24434555a42c0439770462df38b47d05d9e8e353d93ec3742900975e3e65"}, + {file = "urllib3-1.26.1.tar.gz", hash = "sha256:097116a6f16f13482d2a2e56792088b9b2920f4eb6b4f84a2c90555fb673db74"}, ] uvicorn = [ {file = "uvicorn-0.11.8-py3-none-any.whl", hash = "sha256:4b70ddb4c1946e39db9f3082d53e323dfd50634b95fd83625d778729ef1730ef"}, From c79a6df4001f8301f4dbaedf6327e85d8a16ac8c Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Fri, 13 Nov 2020 11:31:24 +0800 Subject: [PATCH 11/34] :pencil2: fix issue template typo --- .github/ISSUE_TEMPLATE/bug-report.md | 2 +- .prettierignore | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 .prettierignore diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 5d454e8f..677925ff 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -26,7 +26,7 @@ A clear and concise description of what you expected to happen. - OS: [e.g. Linux] - Python Version: [e.g. 3.8] - - Nonebot Version [e.g. 2.0.0] + - Nonebot Version: [e.g. 2.0.0] **截图** diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..a87d4fae --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +.github/**/*.md From 781ec1ef671804885cf3821abec144c31d0b4ee5 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Fri, 13 Nov 2020 16:51:14 +0800 Subject: [PATCH 12/34] :ambulance: fix index error in cqhttp check to_me --- nonebot/adapters/cqhttp/__init__.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/nonebot/adapters/cqhttp/__init__.py b/nonebot/adapters/cqhttp/__init__.py index 867c3bad..88b71820 100644 --- a/nonebot/adapters/cqhttp/__init__.py +++ b/nonebot/adapters/cqhttp/__init__.py @@ -115,6 +115,8 @@ async def _check_reply(bot: "Bot", event: "Event"): if event.reply["sender"]["user_id"] == event.self_id: event.to_me = True del event.message[index] + if not event.message: + event.message.append(MessageSegment.text("")) def _check_at_me(bot: "Bot", event: "Event"): @@ -137,8 +139,7 @@ def _check_at_me(bot: "Bot", event: "Event"): at_me_seg = MessageSegment.at(event.self_id) # check the first segment - first_msg_seg = event.message[0] - if first_msg_seg == at_me_seg: + if event.message[0] == at_me_seg: event.to_me = True del event.message[0] if event.message[0].type == "text": @@ -192,13 +193,9 @@ def _check_nickname(bot: "Bot", event: "Event"): first_text = first_msg_seg.data["text"] - if bot.config.nickname: + nicknames = set(filter(lambda n: n, bot.config.nickname)) + if nicknames: # check if the user is calling me with my nickname - if isinstance(bot.config.nickname, str) or \ - not isinstance(bot.config.nickname, Iterable): - nicknames = (bot.config.nickname,) - else: - nicknames = filter(lambda n: n, bot.config.nickname) nickname_regex = "|".join(nicknames) m = re.search(rf"^({nickname_regex})([\s,,]*|$)", first_text, re.IGNORECASE) From bc00c1be82a0646ab703a079eb85f298cbad85ce Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Fri, 13 Nov 2020 17:15:45 +0800 Subject: [PATCH 13/34] :bulb: add message docstring --- docs/.vuepress/config.js | 4 ++++ docs/api/README.md | 3 +++ docs/api/adapters/cqhttp.md | 8 ++++---- docs/api/drivers/fastapi.md | 4 ++-- docs/api/message.md | 8 ++++++++ docs_build/README.rst | 1 + docs_build/message.rst | 11 +++++++++++ nonebot/message.py | 5 +++++ 8 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 docs/api/message.md create mode 100644 docs_build/message.rst diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 528d5101..009bc113 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -128,6 +128,10 @@ module.exports = context => ({ title: "nonebot.plugin 模块", path: "plugin" }, + { + title: "nonebot.message 模块", + path: "message" + }, { title: "nonebot.matcher 模块", path: "matcher" diff --git a/docs/api/README.md b/docs/api/README.md index dcfb548c..75132b72 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -13,6 +13,9 @@ * [nonebot.plugin](plugin.html) + * [nonebot.message](message.html) + + * [nonebot.matcher](matcher.html) diff --git a/docs/api/adapters/cqhttp.md b/docs/api/adapters/cqhttp.md index 3f609383..aa6ad47f 100644 --- a/docs/api/adapters/cqhttp.md +++ b/docs/api/adapters/cqhttp.md @@ -158,7 +158,7 @@ sidebarDepth: 0 ## _class_ `Bot` -基类:[`nonebot.adapters.BaseBot`](#None) +基类:[`nonebot.adapters.BaseBot`](README.md#nonebot.adapters.BaseBot) CQHTTP 协议 Bot 适配。继承属性参考 [BaseBot](./#class-basebot) 。 @@ -270,7 +270,7 @@ CQHTTP 协议 Bot 适配。继承属性参考 [BaseBot](./#class-basebot) 。 ## _class_ `Event` -基类:[`nonebot.adapters.BaseEvent`](#None) +基类:[`nonebot.adapters.BaseEvent`](README.md#nonebot.adapters.BaseEvent) CQHTTP 协议 Event 适配。继承属性参考 [BaseEvent](./#class-baseevent) 。 @@ -412,13 +412,13 @@ CQHTTP 协议 Event 适配。继承属性参考 [BaseEvent](./#class-baseevent) ## _class_ `MessageSegment` -基类:[`nonebot.adapters.BaseMessageSegment`](#None) +基类:[`nonebot.adapters.BaseMessageSegment`](README.md#nonebot.adapters.BaseMessageSegment) CQHTTP 协议 MessageSegment 适配。具体方法参考协议消息段类型或源码。 ## _class_ `Message` -基类:[`nonebot.adapters.BaseMessage`](#None) +基类:[`nonebot.adapters.BaseMessage`](README.md#nonebot.adapters.BaseMessage) CQHTTP 协议 Message 适配。 diff --git a/docs/api/drivers/fastapi.md b/docs/api/drivers/fastapi.md index e890fb69..1f7f96ee 100644 --- a/docs/api/drivers/fastapi.md +++ b/docs/api/drivers/fastapi.md @@ -12,7 +12,7 @@ sidebarDepth: 0 ## _class_ `Driver` -基类:[`nonebot.drivers.BaseDriver`](#None) +基类:[`nonebot.drivers.BaseDriver`](README.md#nonebot.drivers.BaseDriver) FastAPI 驱动框架 @@ -64,7 +64,7 @@ fastapi 使用的 logger ## _class_ `WebSocket` -基类:[`nonebot.drivers.BaseWebSocket`](#None) +基类:[`nonebot.drivers.BaseWebSocket`](README.md#nonebot.drivers.BaseWebSocket) ### _property_ `closed` diff --git a/docs/api/message.md b/docs/api/message.md new file mode 100644 index 00000000..5fe65f56 --- /dev/null +++ b/docs/api/message.md @@ -0,0 +1,8 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.message 模块 + +## 事件处理 diff --git a/docs_build/README.rst b/docs_build/README.rst index 02d097be..7721d3d1 100644 --- a/docs_build/README.rst +++ b/docs_build/README.rst @@ -5,6 +5,7 @@ NoneBot Api Reference - `nonebot `_ - `nonebot.config `_ - `nonebot.plugin `_ + - `nonebot.message `_ - `nonebot.matcher `_ - `nonebot.rule `_ - `nonebot.permission `_ diff --git a/docs_build/message.rst b/docs_build/message.rst new file mode 100644 index 00000000..61c44c35 --- /dev/null +++ b/docs_build/message.rst @@ -0,0 +1,11 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +NoneBot.message 模块 +====================== + +.. automodule:: nonebot.message + :members: + :show-inheritance: diff --git a/nonebot/message.py b/nonebot/message.py index 81a089c5..49cff307 100644 --- a/nonebot/message.py +++ b/nonebot/message.py @@ -1,3 +1,8 @@ +""" +事件处理 +======== +""" + import asyncio from datetime import datetime From bde256608b96804488d7de7bf7453f9cc02caf1a Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Sun, 15 Nov 2020 13:26:20 +0800 Subject: [PATCH 14/34] :pencil2: fix doc typo --- docs/guide/creating-a-matcher.md | 1 - tests/test_plugins/test_delete.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 tests/test_plugins/test_delete.py diff --git a/docs/guide/creating-a-matcher.md b/docs/guide/creating-a-matcher.md index 027dd009..b603449a 100644 --- a/docs/guide/creating-a-matcher.md +++ b/docs/guide/creating-a-matcher.md @@ -142,4 +142,3 @@ Rule(async_checker1) & sync_checker & async_checker2 :::danger 警告 `Rule(*checkers)` 只接受 async function,或使用 `nonebot.utils.run_sync` 自行包裹 sync function。在使用 `与 &` 时,NoneBot 会自动包裹 sync function ::: -t \ No newline at end of file diff --git a/tests/test_plugins/test_delete.py b/tests/test_plugins/test_delete.py new file mode 100644 index 00000000..bd02cfbc --- /dev/null +++ b/tests/test_plugins/test_delete.py @@ -0,0 +1,31 @@ +import asyncio + +from nonebot import on_message +from nonebot.permission import USER +from nonebot.typing import Bot, Event + +a = on_message(priority=0, permission=USER(123123123), temp=True) + + +@a.handle() +async def test_a(bot: Bot, event: Event, state: dict): + print("======== A Received ========") + print("======== A Running Completed ========") + + +b = on_message(priority=0, permission=USER(123456789), temp=True) + + +@b.handle() +async def test_b(bot: Bot, event: Event, state: dict): + print("======== B Received ========") + await asyncio.sleep(10) + print("======== B Running Completed ========") + + +c = on_message(priority=0, permission=USER(1111111111)) + + +@c.handle() +async def test_c(bot: Bot, event: Event, state: dict): + print("======== C Received ========") From 47869ccad520d6e0b4a55e600319eb55836be1cf Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Sun, 15 Nov 2020 15:20:15 +0800 Subject: [PATCH 15/34] :construction_worker: fix import typo --- .github/ISSUE_TEMPLATE/plugin-publish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/plugin-publish.md b/.github/ISSUE_TEMPLATE/plugin-publish.md index efd1bfa7..c78d7109 100644 --- a/.github/ISSUE_TEMPLATE/plugin-publish.md +++ b/.github/ISSUE_TEMPLATE/plugin-publish.md @@ -17,7 +17,7 @@ assignees: "" **插件 import 使用的名称** -例:nonebot-plugin-example +例:nonebot_plugin_example **插件 install 使用的名称** From 218c8ea398ab30dd1c2e7ce2f7d598fce48b2602 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Sun, 15 Nov 2020 15:26:57 +0800 Subject: [PATCH 16/34] :beers: publish nonebot-plugin-status --- docs/.vuepress/public/plugins.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/.vuepress/public/plugins.json b/docs/.vuepress/public/plugins.json index 8f97eaa6..4febd274 100644 --- a/docs/.vuepress/public/plugins.json +++ b/docs/.vuepress/public/plugins.json @@ -1,10 +1,10 @@ [ { - "id": "nonebot-plugin-status", + "id": "nonebot_plugin_status", "link": "nonebot-plugin-status", - "name": "状态监控", + "name": "服务器状态查看", "desc": "通过戳一戳获取服务器状态", - "author": "nonebot", - "repo": "nonebot/nonebot2" + "author": "yanyongyu", + "repo": "cscs181/QQ-GitHub-Bot/tree/master/src/plugins/nonebot_plugin_status" } ] From 730cb82eb7f198ce327da5e0058bdccd6e767744 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Sun, 15 Nov 2020 16:18:23 +0800 Subject: [PATCH 17/34] :egg: add __version__ --- nonebot/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nonebot/__init__.py b/nonebot/__init__.py index eefb5f8f..c704479e 100644 --- a/nonebot/__init__.py +++ b/nonebot/__init__.py @@ -21,8 +21,13 @@ """ import importlib +import pkg_resources from nonebot.typing import Bot, Dict, Type, Union, Driver, Optional, NoReturn +_dist: pkg_resources.Distribution = pkg_resources.get_distribution("nonebot2") +__version__ = _dist.version +VERSION = _dist.parsed_version + _driver: Optional[Driver] = None From 698623229086a9a54d17ea49553c33fccc9c00c6 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Mon, 16 Nov 2020 11:25:42 +0800 Subject: [PATCH 18/34] :art: change temp matcher process #50 --- nonebot/exception.py | 6 --- nonebot/message.py | 104 ++++++++++++++++++++++++------------------- nonebot/typing.py | 6 +-- 3 files changed, 62 insertions(+), 54 deletions(-) diff --git a/nonebot/exception.py b/nonebot/exception.py index 4d862e41..5e27acc4 100644 --- a/nonebot/exception.py +++ b/nonebot/exception.py @@ -9,12 +9,6 @@ from nonebot.typing import List, Type, Optional -class _ExceptionContainer(Exception): - - def __init__(self, exceptions: List[Type[Exception]]) -> None: - self.exceptions = exceptions - - class IgnoredException(Exception): """ :说明: diff --git a/nonebot/message.py b/nonebot/message.py index 49cff307..9b3aab47 100644 --- a/nonebot/message.py +++ b/nonebot/message.py @@ -9,10 +9,9 @@ from datetime import datetime from nonebot.log import logger from nonebot.rule import TrieRule from nonebot.utils import escape_tag -from nonebot.matcher import matchers -from nonebot.exception import IgnoredException, ExpiredException -from nonebot.exception import StopPropagation, _ExceptionContainer -from nonebot.typing import Set, Type, Union, NoReturn, Bot, Event, Matcher +from nonebot.matcher import matchers, Matcher +from nonebot.typing import Set, Type, Union, Iterable, NoReturn, Bot, Event +from nonebot.exception import IgnoredException, ExpiredException, StopPropagation from nonebot.typing import EventPreProcessor, RunPreProcessor, EventPostProcessor, RunPostProcessor _event_preprocessors: Set[EventPreProcessor] = set() @@ -41,20 +40,46 @@ def run_postprocessor(func: RunPostProcessor) -> RunPostProcessor: return func +async def _check_matcher(priority: int, bot: Bot, event: Event, + state: dict) -> Iterable[Type[Matcher]]: + current_matchers = matchers[priority].copy() + + async def _check(Matcher: Type[Matcher], bot: Bot, event: Event, + state: dict) -> Optional[Type[Matcher]]: + try: + if await Matcher.check_perm( + bot, event) and await Matcher.check_rule(bot, event, state): + return Matcher + except Exception as e: + logger.opt(colors=True, exception=e).error( + f"Rule check failed for {Matcher}." + ) + return None + + async def _check_expire(Matcher: Type[Matcher]) -> Optional[Type[Matcher]]: + if Matcher.temp or (Matcher.expire_time and + datetime.now() > Matcher.expire_time): + return Matcher + return None + + checking_tasks = [ + _check(Matcher, bot, event, state) for Matcher in current_matchers + ] + checking_expire_tasks = [ + _check_expire(Matcher) for Matcher in current_matchers + ] + results = await asyncio.gather(*checking_tasks, return_exceptions=True) + expired = await asyncio.gather(*checking_expire_tasks) + for expired_matcher in filter(lambda x: issubclass(Matcher), expired): + try: + matchers[priority].remove(expired_matcher) + except Exception: + pass + return filter(lambda x: issubclass(Matcher), results) + + async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event, state: dict) -> Union[None, NoReturn]: - if Matcher.expire_time and datetime.now() > Matcher.expire_time: - raise _ExceptionContainer([ExpiredException]) - - try: - if not await Matcher.check_perm( - bot, event) or not await Matcher.check_rule(bot, event, state): - return - except Exception as e: - logger.opt(colors=True, exception=e).error( - f"Rule check failed for {Matcher}.") - return - logger.info(f"Event will be handled by {Matcher}") matcher = Matcher() @@ -74,7 +99,7 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event, "Running cancelled!") return - exceptions = [] + exception = None try: logger.debug(f"Running matcher {matcher}") @@ -83,15 +108,10 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event, logger.opt(colors=True, exception=e).error( f"Running matcher {matcher} failed." ) - exceptions.append(e) - - if Matcher.temp: - exceptions.append(ExpiredException) - if Matcher.block: - exceptions.append(StopPropagation) + exception = e coros = list( - map(lambda x: x(matcher, exceptions, bot, event, state), + map(lambda x: x(matcher, exception, bot, event, state), _run_postprocessors)) if coros: try: @@ -101,8 +121,8 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event, "Error when running RunPostProcessors" ) - if exceptions: - raise _ExceptionContainer(exceptions) + if matcher.block: + raise StopPropagation async def handle_event(bot: Bot, event: Event): @@ -153,29 +173,23 @@ async def handle_event(bot: Bot, event: Event): if break_flag: break - pending_tasks = [ - _run_matcher(matcher, bot, event, state.copy()) - for matcher in matchers[priority] - ] - if show_log: logger.debug(f"Checking for matchers in priority {priority}...") + + run_matchers = _check_matcher(priority, bot, event, state) + + pending_tasks = [ + _run_matcher(matcher, bot, event, state.copy()) + for matcher in run_matchers + ] + results = await asyncio.gather(*pending_tasks, return_exceptions=True) - i = 0 - for index, result in enumerate(results): - if isinstance(result, _ExceptionContainer): - e_list = result.exceptions - if StopPropagation in e_list: - if not break_flag: - break_flag = True - logger.debug("Stop event propagation") - if ExpiredException in e_list: - logger.debug( - f"Matcher {matchers[priority][index - i]} will be removed." - ) - del matchers[priority][index - i] - i += 1 + for result in results: + if result is StopPropagation: + if not break_flag: + break_flag = True + logger.debug("Stop event propagation") coros = list(map(lambda x: x(bot, event, state), _event_postprocessors)) if coros: diff --git a/nonebot/typing.py b/nonebot/typing.py index da7923bd..09109b37 100644 --- a/nonebot/typing.py +++ b/nonebot/typing.py @@ -119,14 +119,14 @@ RunPreProcessor = Callable[["Matcher", Bot, Event, dict], 事件响应器运行前预处理函数 RunPreProcessor 类型 """ -RunPostProcessor = Callable[["Matcher", List[Any], Bot, Event, dict], +RunPostProcessor = Callable[["Matcher", Optional[Exception], Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]] """ -:类型: ``Callable[[Matcher, List[Any], Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]`` +:类型: ``Callable[[Matcher, Optional[Exception], Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]`` :说明: - 事件响应器运行前预处理函数 RunPostProcessor 类型,第二个参数包含运行时产生的错误以及 ``ExpiredException``, ``StopPropagation`` (如果存在) + 事件响应器运行前预处理函数 RunPostProcessor 类型,第二个参数为运行时产生的错误(如果存在) """ Matcher = TypeVar("Matcher", bound="MatcherClass") From 829f08534073bc7f74aed9ea0c587bbd3384e699 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Mon, 16 Nov 2020 12:35:04 +0800 Subject: [PATCH 19/34] :bug: fix coding error --- nonebot/message.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nonebot/message.py b/nonebot/message.py index 9b3aab47..942ea3e8 100644 --- a/nonebot/message.py +++ b/nonebot/message.py @@ -10,8 +10,8 @@ from nonebot.log import logger from nonebot.rule import TrieRule from nonebot.utils import escape_tag from nonebot.matcher import matchers, Matcher -from nonebot.typing import Set, Type, Union, Iterable, NoReturn, Bot, Event -from nonebot.exception import IgnoredException, ExpiredException, StopPropagation +from nonebot.typing import Set, Type, Union, Optional, Iterable, NoReturn, Bot, Event +from nonebot.exception import IgnoredException, StopPropagation from nonebot.typing import EventPreProcessor, RunPreProcessor, EventPostProcessor, RunPostProcessor _event_preprocessors: Set[EventPreProcessor] = set() @@ -70,12 +70,12 @@ async def _check_matcher(priority: int, bot: Bot, event: Event, ] results = await asyncio.gather(*checking_tasks, return_exceptions=True) expired = await asyncio.gather(*checking_expire_tasks) - for expired_matcher in filter(lambda x: issubclass(Matcher), expired): + for expired_matcher in filter(lambda x: x and x in results, expired): try: matchers[priority].remove(expired_matcher) except Exception: pass - return filter(lambda x: issubclass(Matcher), results) + return filter(lambda x: x, results) async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event, @@ -176,7 +176,7 @@ async def handle_event(bot: Bot, event: Event): if show_log: logger.debug(f"Checking for matchers in priority {priority}...") - run_matchers = _check_matcher(priority, bot, event, state) + run_matchers = await _check_matcher(priority, bot, event, state) pending_tasks = [ _run_matcher(matcher, bot, event, state.copy()) From b3e6d1c8030ac6f8216b2eaf7286d5f92b4ef24e Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Mon, 16 Nov 2020 15:06:37 +0800 Subject: [PATCH 20/34] :bulb: add message docstring --- nonebot/message.py | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/nonebot/message.py b/nonebot/message.py index 942ea3e8..c000d39a 100644 --- a/nonebot/message.py +++ b/nonebot/message.py @@ -1,6 +1,8 @@ """ 事件处理 ======== + +NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供了多个插槽以进行事件的预处理等。 """ import asyncio @@ -21,21 +23,64 @@ _run_postprocessors: Set[RunPostProcessor] = set() def event_preprocessor(func: EventPreProcessor) -> EventPreProcessor: + """ + :说明: + 事件预处理。装饰一个函数,使它在每次接收到事件并分发给各响应器之前执行。 + :参数: + 事件预处理函数接收三个参数。 + + * ``bot: Bot``: Bot 对象 + * ``event: Event``: Event 对象 + * ``state: dict``: 当前 State + """ _event_preprocessors.add(func) return func def event_postprocessor(func: EventPostProcessor) -> EventPostProcessor: + """ + :说明: + 事件后处理。装饰一个函数,使它在每次接收到事件并分发给各响应器之后执行。 + :参数: + 事件后处理函数接收三个参数。 + + * ``bot: Bot``: Bot 对象 + * ``event: Event``: Event 对象 + * ``state: dict``: 当前事件运行前 State + """ _event_postprocessors.add(func) return func def run_preprocessor(func: RunPreProcessor) -> RunPreProcessor: + """ + :说明: + 运行预处理。装饰一个函数,使它在每次事件响应器运行前执行。 + :参数: + 运行预处理函数接收四个参数。 + + * ``matcher: Matcher``: 当前要运行的事件响应器 + * ``bot: Bot``: Bot 对象 + * ``event: Event``: Event 对象 + * ``state: dict``: 当前 State + """ _run_preprocessors.add(func) return func def run_postprocessor(func: RunPostProcessor) -> RunPostProcessor: + """ + :说明: + 运行后处理。装饰一个函数,使它在每次事件响应器运行后执行。 + :参数: + 运行后处理函数接收五个参数。 + + * ``matcher: Matcher``: 运行完毕的事件响应器 + * ``exception: Optional[Exception]``: 事件响应器运行错误(如果存在) + * ``bot: Bot``: Bot 对象 + * ``event: Event``: Event 对象 + * ``state: dict``: 当前 State + """ _run_postprocessors.add(func) return func @@ -126,6 +171,19 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event, async def handle_event(bot: Bot, event: Event): + """ + :说明: + 处理一个事件。调用该函数以实现分发事件。 + :参数: + * ``bot: Bot``: Bot 对象 + * ``event: Event``: Event 对象 + :示例: + + ..code-block:: python + + import asyncio + asyncio.create_task(handle_event(bot, event)) + """ show_log = True log_msg = f"{bot.type.upper()} | {event.self_id} [{event.name}]: " if event.type == "message": From 2e2f3eee042b616d0d3e156e02cee13d8cb17e8c Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Mon, 16 Nov 2020 15:27:20 +0800 Subject: [PATCH 21/34] :pencil2: fix config typo --- docs/guide/creating-a-plugin.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/creating-a-plugin.md b/docs/guide/creating-a-plugin.md index 17b53496..fe52a25d 100644 --- a/docs/guide/creating-a-plugin.md +++ b/docs/guide/creating-a-plugin.md @@ -94,7 +94,7 @@ class Config(BaseSetting): import nonebot from .config import Config -global_config = nonebot.get_bot().config +global_config = nonebot.get_driver().config plugin_config = Config(**global_config.dict()) ``` From 1c31453ba0e4fe2a46842d6929f29f72416e9417 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Mon, 16 Nov 2020 19:39:51 +0800 Subject: [PATCH 22/34] :fire: remove unused exception --- nonebot/exception.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/nonebot/exception.py b/nonebot/exception.py index 5e27acc4..f16cbcd0 100644 --- a/nonebot/exception.py +++ b/nonebot/exception.py @@ -73,19 +73,6 @@ class FinishedException(Exception): pass -class ExpiredException(Exception): - """ - :说明: - - 指示 NoneBot 当前 ``Matcher`` 已失效。 - - :用法: - - 当 ``Matcher`` 运行前检查时抛出。 - """ - pass - - class StopPropagation(Exception): """ :说明: From 36c0d995afbd5f14c0062bae1f95c99f6f47e6a5 Mon Sep 17 00:00:00 2001 From: nonebot Date: Mon, 16 Nov 2020 11:42:47 +0000 Subject: [PATCH 23/34] :memo: update api docs --- docs/api/exception.md | 17 ------ docs/api/message.md | 135 ++++++++++++++++++++++++++++++++++++++++++ docs/api/typing.md | 4 +- 3 files changed, 137 insertions(+), 19 deletions(-) diff --git a/docs/api/exception.md b/docs/api/exception.md index 5c89a5f8..0a9876a8 100644 --- a/docs/api/exception.md +++ b/docs/api/exception.md @@ -83,23 +83,6 @@ sidebarDepth: 0 -## _exception_ `ExpiredException` - -基类:`Exception` - - -* **说明** - - 指示 NoneBot 当前 `Matcher` 已失效。 - - - -* **用法** - - 当 `Matcher` 运行前检查时抛出。 - - - ## _exception_ `StopPropagation` 基类:`Exception` diff --git a/docs/api/message.md b/docs/api/message.md index 5fe65f56..4dc025e3 100644 --- a/docs/api/message.md +++ b/docs/api/message.md @@ -6,3 +6,138 @@ sidebarDepth: 0 # NoneBot.message 模块 ## 事件处理 + +NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供了多个插槽以进行事件的预处理等。 + + +## `event_preprocessor(func)` + + +* **说明** + + 事件预处理。装饰一个函数,使它在每次接收到事件并分发给各响应器之前执行。 + + + +* **参数** + + 事件预处理函数接收三个参数。 + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + * `state: dict`: 当前 State + + + +## `event_postprocessor(func)` + + +* **说明** + + 事件后处理。装饰一个函数,使它在每次接收到事件并分发给各响应器之后执行。 + + + +* **参数** + + 事件后处理函数接收三个参数。 + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + * `state: dict`: 当前事件运行前 State + + + +## `run_preprocessor(func)` + + +* **说明** + + 运行预处理。装饰一个函数,使它在每次事件响应器运行前执行。 + + + +* **参数** + + 运行预处理函数接收四个参数。 + + + * `matcher: Matcher`: 当前要运行的事件响应器 + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + * `state: dict`: 当前 State + + + +## `run_postprocessor(func)` + + +* **说明** + + 运行后处理。装饰一个函数,使它在每次事件响应器运行后执行。 + + + +* **参数** + + 运行后处理函数接收五个参数。 + + + * `matcher: Matcher`: 运行完毕的事件响应器 + + + * `exception: Optional[Exception]`: 事件响应器运行错误(如果存在) + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + * `state: dict`: 当前 State + + + +## _async_ `handle_event(bot, event)` + + +* **说明** + + 处理一个事件。调用该函数以实现分发事件。 + + + +* **参数** + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + +* **示例** + + +..code-block:: python + +> import asyncio +> asyncio.create_task(handle_event(bot, event)) diff --git a/docs/api/typing.md b/docs/api/typing.md index cdf9f224..8f8a2223 100644 --- a/docs/api/typing.md +++ b/docs/api/typing.md @@ -163,13 +163,13 @@ sidebarDepth: 0 * **类型** - `Callable[[Matcher, List[Any], Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` + `Callable[[Matcher, Optional[Exception], Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` * **说明** - 事件响应器运行前预处理函数 RunPostProcessor 类型,第二个参数包含运行时产生的错误以及 `ExpiredException`, `StopPropagation` (如果存在) + 事件响应器运行前预处理函数 RunPostProcessor 类型,第二个参数为运行时产生的错误(如果存在) From 872df975f5b5972d8f02a21a0dea9b758f3b538a Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Mon, 16 Nov 2020 19:57:49 +0800 Subject: [PATCH 24/34] :pencil2: fix rst typo --- nonebot/message.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nonebot/message.py b/nonebot/message.py index c000d39a..af0849a5 100644 --- a/nonebot/message.py +++ b/nonebot/message.py @@ -178,8 +178,8 @@ async def handle_event(bot: Bot, event: Event): * ``bot: Bot``: Bot 对象 * ``event: Event``: Event 对象 :示例: - - ..code-block:: python + + .. code-block:: python import asyncio asyncio.create_task(handle_event(bot, event)) From 8524c32b93164ff209a30401e456b53c832b9f7d Mon Sep 17 00:00:00 2001 From: nonebot Date: Mon, 16 Nov 2020 11:59:05 +0000 Subject: [PATCH 25/34] :memo: update api docs --- docs/api/message.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/api/message.md b/docs/api/message.md index 4dc025e3..c9d7c158 100644 --- a/docs/api/message.md +++ b/docs/api/message.md @@ -137,7 +137,7 @@ NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供 * **示例** -..code-block:: python - -> import asyncio -> asyncio.create_task(handle_event(bot, event)) +```python +import asyncio +asyncio.create_task(handle_event(bot, event)) +``` From 9fc354af4315c094144992e48eb0df9f814f53f9 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Tue, 17 Nov 2020 16:36:42 +0800 Subject: [PATCH 26/34] :bookmark: bump version to 2.0.0a5 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1b6e1bc3..afb696c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nonebot2" -version = "2.0.0a4" +version = "2.0.0-alpha.5" description = "An asynchronous python bot framework." authors = ["yanyongyu "] license = "MIT" From c71c1828340ed879087f600081d3cd827cce6c66 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Tue, 17 Nov 2020 16:50:37 +0800 Subject: [PATCH 27/34] :package: archive version doc --- archive/2.0.0a5/README.md | 15 + archive/2.0.0a5/api/README.md | 52 ++ archive/2.0.0a5/api/adapters/README.md | 362 +++++++++++ archive/2.0.0a5/api/adapters/cqhttp.md | 424 +++++++++++++ archive/2.0.0a5/api/config.md | 265 ++++++++ archive/2.0.0a5/api/drivers/README.md | 246 ++++++++ archive/2.0.0a5/api/drivers/fastapi.md | 102 +++ archive/2.0.0a5/api/exception.md | 160 +++++ archive/2.0.0a5/api/log.md | 55 ++ archive/2.0.0a5/api/matcher.md | 497 +++++++++++++++ archive/2.0.0a5/api/message.md | 143 +++++ archive/2.0.0a5/api/nonebot.md | 254 ++++++++ archive/2.0.0a5/api/permission.md | 121 ++++ archive/2.0.0a5/api/plugin.md | 629 +++++++++++++++++++ archive/2.0.0a5/api/rule.md | 210 +++++++ archive/2.0.0a5/api/sched.md | 41 ++ archive/2.0.0a5/api/typing.md | 300 +++++++++ archive/2.0.0a5/api/utils.md | 62 ++ archive/2.0.0a5/guide/README.md | 36 ++ archive/2.0.0a5/guide/basic-configuration.md | 68 ++ archive/2.0.0a5/guide/creating-a-handler.md | 159 +++++ archive/2.0.0a5/guide/creating-a-matcher.md | 144 +++++ archive/2.0.0a5/guide/creating-a-plugin.md | 113 ++++ archive/2.0.0a5/guide/creating-a-project.md | 55 ++ archive/2.0.0a5/guide/end-or-start.md | 7 + archive/2.0.0a5/guide/getting-started.md | 146 +++++ archive/2.0.0a5/guide/installation.md | 85 +++ archive/2.0.0a5/guide/loading-a-plugin.md | 95 +++ archive/2.0.0a5/sidebar.config.json | 131 ++++ docs/.vuepress/versions.json | 1 + 30 files changed, 4978 insertions(+) create mode 100644 archive/2.0.0a5/README.md create mode 100644 archive/2.0.0a5/api/README.md create mode 100644 archive/2.0.0a5/api/adapters/README.md create mode 100644 archive/2.0.0a5/api/adapters/cqhttp.md create mode 100644 archive/2.0.0a5/api/config.md create mode 100644 archive/2.0.0a5/api/drivers/README.md create mode 100644 archive/2.0.0a5/api/drivers/fastapi.md create mode 100644 archive/2.0.0a5/api/exception.md create mode 100644 archive/2.0.0a5/api/log.md create mode 100644 archive/2.0.0a5/api/matcher.md create mode 100644 archive/2.0.0a5/api/message.md create mode 100644 archive/2.0.0a5/api/nonebot.md create mode 100644 archive/2.0.0a5/api/permission.md create mode 100644 archive/2.0.0a5/api/plugin.md create mode 100644 archive/2.0.0a5/api/rule.md create mode 100644 archive/2.0.0a5/api/sched.md create mode 100644 archive/2.0.0a5/api/typing.md create mode 100644 archive/2.0.0a5/api/utils.md create mode 100644 archive/2.0.0a5/guide/README.md create mode 100644 archive/2.0.0a5/guide/basic-configuration.md create mode 100644 archive/2.0.0a5/guide/creating-a-handler.md create mode 100644 archive/2.0.0a5/guide/creating-a-matcher.md create mode 100644 archive/2.0.0a5/guide/creating-a-plugin.md create mode 100644 archive/2.0.0a5/guide/creating-a-project.md create mode 100644 archive/2.0.0a5/guide/end-or-start.md create mode 100644 archive/2.0.0a5/guide/getting-started.md create mode 100644 archive/2.0.0a5/guide/installation.md create mode 100644 archive/2.0.0a5/guide/loading-a-plugin.md create mode 100644 archive/2.0.0a5/sidebar.config.json diff --git a/archive/2.0.0a5/README.md b/archive/2.0.0a5/README.md new file mode 100644 index 00000000..a1607904 --- /dev/null +++ b/archive/2.0.0a5/README.md @@ -0,0 +1,15 @@ +--- +home: true +heroImage: /logo.png +tagline: An asynchronous QQ bot framework. +actionText: 开始使用 +actionLink: guide/ +features: + - title: 简洁 + details: 提供极其简洁易懂的 API,使你可以毫无压力地开始验证你的绝佳创意,只需编写最少量的代码,即可实现丰富的功能。 + - title: 易于扩展 + details: 精心设计的消息处理流程使得你可以很方便地将原型扩充为具有大量实用功能的完整聊天机器人,并持续保证扩展性。 + - title: 高性能 + details: 采用异步 I/O,利用 WebSocket 进行通信,以获得极高的性能;同时,支持使用多账号同时接入,减少业务宕机的可能。 +footer: MIT Licensed | Copyright © 2020 NoneBot Team +--- diff --git a/archive/2.0.0a5/api/README.md b/archive/2.0.0a5/api/README.md new file mode 100644 index 00000000..75132b72 --- /dev/null +++ b/archive/2.0.0a5/api/README.md @@ -0,0 +1,52 @@ +# NoneBot Api Reference + + +* **模块索引** + + + * [nonebot](nonebot.html) + + + * [nonebot.config](config.html) + + + * [nonebot.plugin](plugin.html) + + + * [nonebot.message](message.html) + + + * [nonebot.matcher](matcher.html) + + + * [nonebot.rule](rule.html) + + + * [nonebot.permission](permission.html) + + + * [nonebot.sched](sched.html) + + + * [nonebot.log](log.html) + + + * [nonebot.utils](utils.html) + + + * [nonebot.typing](typing.html) + + + * [nonebot.exception](exception.html) + + + * [nonebot.drivers](drivers/) + + + * [nonebot.drivers.fastapi](drivers/fastapi.html) + + + * [nonebot.adapters](adapters/) + + + * [nonebot.adapters.cqhttp](adapters/cqhttp.html) diff --git a/archive/2.0.0a5/api/adapters/README.md b/archive/2.0.0a5/api/adapters/README.md new file mode 100644 index 00000000..c2d2e399 --- /dev/null +++ b/archive/2.0.0a5/api/adapters/README.md @@ -0,0 +1,362 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.adapters 模块 + +## 协议适配基类 + +各协议请继承以下基类,并使用 `driver.register_adapter` 注册适配器 + + +## _class_ `BaseBot` + +基类:`abc.ABC` + +Bot 基类。用于处理上报消息,并提供 API 调用接口。 + + +### _abstract_ `__init__(driver, connection_type, config, self_id, *, websocket=None)` + + +* **参数** + + + * `driver: Driver`: Driver 对象 + + + * `connection_type: str`: http 或者 websocket + + + * `config: Config`: Config 对象 + + + * `self_id: str`: 机器人 ID + + + * `websocket: Optional[WebSocket]`: Websocket 连接对象 + + + +### `driver` + +Driver 对象 + + +### `connection_type` + +连接类型 + + +### `config` + +Config 配置对象 + + +### `self_id` + +机器人 ID + + +### `websocket` + +Websocket 连接对象 + + +### _abstract property_ `type` + +Adapter 类型 + + +### _abstract async classmethod_ `check_permission(driver, connection_type, headers, body)` + + +* **说明** + + 检查连接请求是否合法的函数,如果合法则返回当前连接 `唯一标识符`,通常为机器人 ID;如果不合法则抛出 `RequestDenied` 异常。 + + + +* **参数** + + + * `driver: Driver`: Driver 对象 + + + * `connection_type: str`: 连接类型 + + + * `headers: dict`: 请求头 + + + * `body: Optional[dict]`: 请求数据,WebSocket 连接该部分为空 + + + +* **返回** + + + * `str`: 连接唯一标识符 + + + +* **异常** + + + * `RequestDenied`: 请求非法 + + + +### _abstract async_ `handle_message(message)` + + +* **说明** + + 处理上报消息的函数,转换为 `Event` 事件后调用 `nonebot.message.handle_event` 进一步处理事件。 + + + +* **参数** + + + * `message: dict`: 收到的上报消息 + + + +### _abstract async_ `call_api(api, **data)` + + +* **说明** + + 调用机器人 API 接口,可以通过该函数或直接通过 bot 属性进行调用 + + + +* **参数** + + + * `api: str`: API 名称 + + + * `**data`: API 数据 + + + +* **示例** + + +```python +await bot.call_api("send_msg", message="hello world"}) +await bot.send_msg(message="hello world") +``` + + +### _abstract async_ `send(event, message, **kwargs)` + + +* **说明** + + 调用机器人基础发送消息接口 + + + +* **参数** + + + * `event: Event`: 上报事件 + + + * `message: Union[str, Message, MessageSegment]`: 要发送的消息 + + + * `**kwargs` + + + +## _class_ `BaseEvent` + +基类:`abc.ABC` + +Event 基类。提供上报信息的关键信息,其余信息可从原始上报消息获取。 + + +### `__init__(raw_event)` + + +* **参数** + + + * `raw_event: dict`: 原始上报消息 + + + +### _property_ `raw_event` + +原始上报消息 + + +### _abstract property_ `id` + +事件 ID + + +### _abstract property_ `name` + +事件名称 + + +### _abstract property_ `self_id` + +机器人 ID + + +### _abstract property_ `time` + +事件发生时间 + + +### _abstract property_ `type` + +事件主类型 + + +### _abstract property_ `detail_type` + +事件详细类型 + + +### _abstract property_ `sub_type` + +事件子类型 + + +### _abstract property_ `user_id` + +触发事件的主体 ID + + +### _abstract property_ `group_id` + +触发事件的主体群 ID + + +### _abstract property_ `to_me` + +事件是否为发送给机器人的消息 + + +### _abstract property_ `message` + +消息内容 + + +### _abstract property_ `reply` + +回复的消息 + + +### _abstract property_ `raw_message` + +原始消息 + + +### _abstract property_ `plain_text` + +纯文本消息 + + +### _abstract property_ `sender` + +消息发送者信息 + + +## _class_ `BaseMessageSegment` + +基类:`abc.ABC` + +消息段基类 + + +### `type` + + +* 类型: `str` + + +* 说明: 消息段类型 + + +### `data` + + +* 类型: `Dict[str, Union[str, list]]` + + +* 说明: 消息段数据 + + +## _class_ `BaseMessage` + +基类:`list`, `abc.ABC` + +消息数组 + + +### `__init__(message=None, *args, **kwargs)` + + +* **参数** + + + * `message: Union[str, dict, list, MessageSegment, Message]`: 消息内容 + + + +### `append(obj)` + + +* **说明** + + 添加一个消息段到消息数组末尾 + + + +* **参数** + + + * `obj: Union[str, MessageSegment]`: 要添加的消息段 + + + +### `extend(obj)` + + +* **说明** + + 拼接一个消息数组或多个消息段到消息数组末尾 + + + +* **参数** + + + * `obj: Union[Message, Iterable[MessageSegment]]`: 要添加的消息数组 + + + +### `reduce()` + + +* **说明** + + 缩减消息数组,即拼接相邻纯文本消息段 + + + +### `extract_plain_text()` + + +* **说明** + + 提取消息内纯文本消息 diff --git a/archive/2.0.0a5/api/adapters/cqhttp.md b/archive/2.0.0a5/api/adapters/cqhttp.md new file mode 100644 index 00000000..aa6ad47f --- /dev/null +++ b/archive/2.0.0a5/api/adapters/cqhttp.md @@ -0,0 +1,424 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.adapters.cqhttp 模块 + +## CQHTTP (OneBot) v11 协议适配 + +协议详情请看: [CQHTTP](http://cqhttp.cc/) | [OneBot](https://github.com/howmanybots/onebot) + + +## `log(level, message)` + + +* **说明** + + 用于打印 CQHTTP 日志。 + + + +* **参数** + + + * `level: str`: 日志等级 + + + * `message: str`: 日志信息 + + + +## `escape(s, *, escape_comma=True)` + + +* **说明** + + 对字符串进行 CQ 码转义。 + + + +* **参数** + + + * `s: str`: 需要转义的字符串 + + + * `escape_comma: bool`: 是否转义逗号(`,`)。 + + + +## `unescape(s)` + + +* **说明** + + 对字符串进行 CQ 码去转义。 + + + +* **参数** + + + * `s: str`: 需要转义的字符串 + + + +## `_b2s(b)` + +转换布尔值为字符串。 + + +## _async_ `_check_reply(bot, event)` + + +* **说明** + + 检查消息中存在的回复,去除并赋值 `event.reply`, `event.to_me` + + + +* **参数** + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + +## `_check_at_me(bot, event)` + + +* **说明** + + 检查消息开头或结尾是否存在 @机器人,去除并赋值 `event.to_me` + + + +* **参数** + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + +## `_check_nickname(bot, event)` + + +* **说明** + + 检查消息开头是否存在,去除并赋值 `event.to_me` + + + +* **参数** + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + +## `_handle_api_result(result)` + + +* **说明** + + 处理 API 请求返回值。 + + + +* **参数** + + + * `result: Optional[Dict[str, Any]]`: API 返回数据 + + + +* **返回** + + + * `Any`: API 调用返回数据 + + + +* **异常** + + + * `ActionFailed`: API 调用失败 + + + +## _class_ `Bot` + +基类:[`nonebot.adapters.BaseBot`](README.md#nonebot.adapters.BaseBot) + +CQHTTP 协议 Bot 适配。继承属性参考 [BaseBot](./#class-basebot) 。 + + +### _property_ `type` + + +* 返回: `"cqhttp"` + + +### _async classmethod_ `check_permission(driver, connection_type, headers, body)` + + +* **说明** + + CQHTTP (OneBot) 协议鉴权。参考 [鉴权](https://github.com/howmanybots/onebot/blob/master/v11/specs/communication/authorization.md) + + + +### _async_ `handle_message(message)` + + +* **说明** + + 调用 [_check_reply](#async-check-reply-bot-event), [_check_at_me](#check-at-me-bot-event), [_check_nickname](#check-nickname-bot-event) 处理事件并转换为 [Event](#class-event) + + + +### _async_ `call_api(api, **data)` + + +* **说明** + + 调用 CQHTTP 协议 API + + + +* **参数** + + + * `api: str`: API 名称 + + + * `**data: Any`: API 参数 + + + +* **返回** + + + * `Any`: API 调用返回数据 + + + +* **异常** + + + * `NetworkError`: 网络错误 + + + * `ActionFailed`: API 调用失败 + + + +### _async_ `send(event, message, at_sender=False, **kwargs)` + + +* **说明** + + 根据 `event` 向触发事件的主体发送消息。 + + + +* **参数** + + + * `event: Event`: Event 对象 + + + * `message: Union[str, Message, MessageSegment]`: 要发送的消息 + + + * `at_sender: bool`: 是否 @ 事件主体 + + + * `**kwargs`: 覆盖默认参数 + + + +* **返回** + + + * `Any`: API 调用返回数据 + + + +* **异常** + + + * `ValueError`: 缺少 `user_id`, `group_id` + + + * `NetworkError`: 网络错误 + + + * `ActionFailed`: API 调用失败 + + + +## _class_ `Event` + +基类:[`nonebot.adapters.BaseEvent`](README.md#nonebot.adapters.BaseEvent) + +CQHTTP 协议 Event 适配。继承属性参考 [BaseEvent](./#class-baseevent) 。 + + +### _property_ `id` + + +* 类型: `Optional[int]` + + +* 说明: 事件/消息 ID + + +### _property_ `name` + + +* 类型: `str` + + +* 说明: 事件名称,由类型与 `.` 组合而成 + + +### _property_ `self_id` + + +* 类型: `str` + + +* 说明: 机器人自身 ID + + +### _property_ `time` + + +* 类型: `int` + + +* 说明: 事件发生时间 + + +### _property_ `type` + + +* 类型: `str` + + +* 说明: 事件类型 + + +### _property_ `detail_type` + + +* 类型: `str` + + +* 说明: 事件详细类型 + + +### _property_ `sub_type` + + +* 类型: `Optional[str]` + + +* 说明: 事件子类型 + + +### _property_ `user_id` + + +* 类型: `Optional[int]` + + +* 说明: 事件主体 ID + + +### _property_ `group_id` + + +* 类型: `Optional[int]` + + +* 说明: 事件主体群 ID + + +### _property_ `to_me` + + +* 类型: `Optional[bool]` + + +* 说明: 消息是否与机器人相关 + + +### _property_ `message` + + +* 类型: `Optional[Message]` + + +* 说明: 消息内容 + + +### _property_ `reply` + + +* 类型: `Optional[dict]` + + +* 说明: 回复消息详情 + + +### _property_ `raw_message` + + +* 类型: `Optional[str]` + + +* 说明: 原始消息 + + +### _property_ `plain_text` + + +* 类型: `Optional[str]` + + +* 说明: 纯文本消息内容 + + +### _property_ `sender` + + +* 类型: `Optional[dict]` + + +* 说明: 消息发送者信息 + + +## _class_ `MessageSegment` + +基类:[`nonebot.adapters.BaseMessageSegment`](README.md#nonebot.adapters.BaseMessageSegment) + +CQHTTP 协议 MessageSegment 适配。具体方法参考协议消息段类型或源码。 + + +## _class_ `Message` + +基类:[`nonebot.adapters.BaseMessage`](README.md#nonebot.adapters.BaseMessage) + +CQHTTP 协议 Message 适配。 diff --git a/archive/2.0.0a5/api/config.md b/archive/2.0.0a5/api/config.md new file mode 100644 index 00000000..6943427b --- /dev/null +++ b/archive/2.0.0a5/api/config.md @@ -0,0 +1,265 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.config 模块 + +## 配置 + +NoneBot 使用 [pydantic](https://pydantic-docs.helpmanual.io/) 以及 [python-dotenv](https://saurabh-kumar.com/python-dotenv/) 来读取配置。 + +配置项需符合特殊格式或 json 序列化格式。详情见 [pydantic Field Type](https://pydantic-docs.helpmanual.io/usage/types/) 文档。 + + +## _class_ `Env` + +基类:`pydantic.env_settings.BaseSettings` + +运行环境配置。大小写不敏感。 + +将会从 `nonebot.init 参数` > `环境变量` > `.env 环境配置文件` 的优先级读取配置。 + + +### `environment` + + +* 类型: `str` + + +* 默认值: `"prod"` + + +* 说明: +当前环境名。 NoneBot 将从 `.env.{environment}` 文件中加载配置。 + + +## _class_ `Config` + +基类:`nonebot.config.BaseConfig` + +NoneBot 主要配置。大小写不敏感。 + +除了 NoneBot 的配置项外,还可以自行添加配置项到 `.env.{environment}` 文件中。 +这些配置将会在 json 反序列化后一起带入 `Config` 类中。 + + +### `driver` + + +* 类型: `str` + + +* 默认值: `"nonebot.drivers.fastapi"` + + +* 说明: +NoneBot 运行所使用的 `Driver` 。继承自 `nonebot.driver.BaseDriver` 。 + + +### `host` + + +* 类型: `IPvAnyAddress` + + +* 默认值: `127.0.0.1` + + +* 说明: +NoneBot 的 HTTP 和 WebSocket 服务端监听的 IP/主机名。 + + +### `port` + + +* 类型: `int` + + +* 默认值: `8080` + + +* 说明: +NoneBot 的 HTTP 和 WebSocket 服务端监听的端口。 + + +### `debug` + + +* 类型: `bool` + + +* 默认值: `False` + + +* 说明: +是否以调试模式运行 NoneBot。 + + +### `api_root` + + +* 类型: `Dict[str, str]` + + +* 默认值: `{}` + + +* 说明: +以机器人 ID 为键,上报地址为值的字典,环境变量或文件中应使用 json 序列化。 + + +* 示例: + +```default +API_ROOT={"123456": "http://127.0.0.1:5700"} +``` + + +### `api_timeout` + + +* 类型: `Optional[float]` + + +* 默认值: `30.` + + +* 说明: +API 请求超时时间,单位: 秒。 + + +### `access_token` + + +* 类型: `Optional[str]` + + +* 默认值: `None` + + +* 说明: +API 请求以及上报所需密钥,在请求头中携带。 + + +* 示例: + +```http +POST /cqhttp/ HTTP/1.1 +Authorization: Bearer kSLuTF2GC2Q4q4ugm3 +``` + + +### `secret` + + +* 类型: `Optional[str]` + + +* 默认值: `None` + + +* 说明: +HTTP POST 形式上报所需签名,在请求头中携带。 + + +* 示例: + +```http +POST /cqhttp/ HTTP/1.1 +X-Signature: sha1=f9ddd4863ace61e64f462d41ca311e3d2c1176e2 +``` + + +### `superusers` + + +* 类型: `Set[int]` + + +* 默认值: `set()` + + +* 说明: +机器人超级用户。 + + +* 示例: + +```default +SUPER_USERS=[12345789] +``` + + +### `nickname` + + +* 类型: `Set[str]` + + +* 默认值: `set()` + + +* 说明: +机器人昵称。 + + +### `command_start` + + +* 类型: `Set[str]` + + +* 默认值: `{"/"}` + + +* 说明: +命令的起始标记,用于判断一条消息是不是命令。 + + +### `command_sep` + + +* 类型: `Set[str]` + + +* 默认值: `{"."}` + + +* 说明: +命令的分隔标记,用于将文本形式的命令切分为元组(实际的命令名)。 + + +### `session_expire_timeout` + + +* 类型: `timedelta` + + +* 默认值: `timedelta(minutes=2)` + + +* 说明: +等待用户回复的超时时间。 + + +* 示例: + +```default +SESSION_EXPIRE_TIMEOUT=120 # 单位: 秒 +SESSION_EXPIRE_TIMEOUT=[DD ][HH:MM]SS[.ffffff] +SESSION_EXPIRE_TIMEOUT=P[DD]DT[HH]H[MM]M[SS]S # ISO 8601 +``` + + +### `apscheduler_config` + + +* 类型: `dict` + + +* 默认值: `{"apscheduler.timezone": "Asia/Shanghai"}` + + +* 说明: +APScheduler 的配置对象,见 [Configuring the Scheduler](https://apscheduler.readthedocs.io/en/latest/userguide.html#configuring-the-scheduler) diff --git a/archive/2.0.0a5/api/drivers/README.md b/archive/2.0.0a5/api/drivers/README.md new file mode 100644 index 00000000..624220ba --- /dev/null +++ b/archive/2.0.0a5/api/drivers/README.md @@ -0,0 +1,246 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.drivers 模块 + +## 后端驱动适配基类 + +各驱动请继承以下基类 + + +## _class_ `BaseDriver` + +基类:`abc.ABC` + +Driver 基类。将后端框架封装,以满足适配器使用。 + + +### `_adapters` + + +* **类型** + + `Dict[str, Type[Bot]]` + + + +* **说明** + + 已注册的适配器列表 + + + +### _abstract_ `__init__(env, config)` + + +* **参数** + + + * `env: Env`: 包含环境信息的 Env 对象 + + + * `config: Config`: 包含配置信息的 Config 对象 + + + +### `env` + + +* **类型** + + `str` + + + +* **说明** + + 环境名称 + + + +### `config` + + +* **类型** + + `Config` + + + +* **说明** + + 配置对象 + + + +### `_clients` + + +* **类型** + + `Dict[str, Bot]` + + + +* **说明** + + 已连接的 Bot + + + +### _classmethod_ `register_adapter(name, adapter)` + + +* **说明** + + 注册一个协议适配器 + + + +* **参数** + + + * `name: str`: 适配器名称,用于在连接时进行识别 + + + * `adapter: Type[Bot]`: 适配器 Class + + + +### _abstract property_ `type` + +驱动类型名称 + + +### _abstract property_ `server_app` + +驱动 APP 对象 + + +### _abstract property_ `asgi` + +驱动 ASGI 对象 + + +### _abstract property_ `logger` + +驱动专属 logger 日志记录器 + + +### _property_ `bots` + + +* **类型** + + `Dict[str, Bot]` + + + +* **说明** + + 获取当前所有已连接的 Bot + + + +### _abstract_ `on_startup(func)` + +注册一个在驱动启动时运行的函数 + + +### _abstract_ `on_shutdown(func)` + +注册一个在驱动停止时运行的函数 + + +### _abstract_ `run(host=None, port=None, *args, **kwargs)` + + +* **说明** + + 启动驱动框架 + + + +* **参数** + + + * `host: Optional[str]`: 驱动绑定 IP + + + * `post: Optional[int]`: 驱动绑定端口 + + + * `*args` + + + * `**kwargs` + + + +### _abstract async_ `_handle_http()` + +用于处理 HTTP 类型请求的函数 + + +### _abstract async_ `_handle_ws_reverse()` + +用于处理 WebSocket 类型请求的函数 + + +## _class_ `BaseWebSocket` + +基类:`object` + +WebSocket 连接封装,统一接口方便外部调用。 + + +### _abstract_ `__init__(websocket)` + + +* **参数** + + + * `websocket: Any`: WebSocket 连接对象 + + + +### _property_ `websocket` + +WebSocket 连接对象 + + +### _abstract property_ `closed` + + +* **类型** + + `bool` + + + +* **说明** + + 连接是否已经关闭 + + + +### _abstract async_ `accept()` + +接受 WebSocket 连接请求 + + +### _abstract async_ `close(code)` + +关闭 WebSocket 连接请求 + + +### _abstract async_ `receive()` + +接收一条 WebSocket 信息 + + +### _abstract async_ `send(data)` + +发送一条 WebSocket 信息 diff --git a/archive/2.0.0a5/api/drivers/fastapi.md b/archive/2.0.0a5/api/drivers/fastapi.md new file mode 100644 index 00000000..1f7f96ee --- /dev/null +++ b/archive/2.0.0a5/api/drivers/fastapi.md @@ -0,0 +1,102 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.drivers.fastapi 模块 + +## FastAPI 驱动适配 + +后端使用方法请参考: [FastAPI 文档](https://fastapi.tiangolo.com/) + + +## _class_ `Driver` + +基类:[`nonebot.drivers.BaseDriver`](README.md#nonebot.drivers.BaseDriver) + +FastAPI 驱动框架 + + +### _property_ `type` + +驱动名称: `fastapi` + + +### _property_ `server_app` + +`FastAPI APP` 对象 + + +### _property_ `asgi` + +`FastAPI APP` 对象 + + +### _property_ `logger` + +fastapi 使用的 logger + + +### `on_startup(func)` + +参考文档: [Events](https://fastapi.tiangolo.com/advanced/events/#startup-event) + + +### `on_shutdown(func)` + +参考文档: [Events](https://fastapi.tiangolo.com/advanced/events/#startup-event) + + +### `run(host=None, port=None, *, app=None, **kwargs)` + +使用 `uvicorn` 启动 FastAPI + + +### _async_ `_handle_http(adapter, request, data=Body(Ellipsis))` + +用于处理 HTTP 类型请求的函数 + + +### _async_ `_handle_ws_reverse(adapter, websocket)` + +用于处理 WebSocket 类型请求的函数 + + +## _class_ `WebSocket` + +基类:[`nonebot.drivers.BaseWebSocket`](README.md#nonebot.drivers.BaseWebSocket) + + +### _property_ `closed` + + +* **类型** + + `bool` + + + +* **说明** + + 连接是否已经关闭 + + + +### _async_ `accept()` + +接受 WebSocket 连接请求 + + +### _async_ `close(code=1000)` + +关闭 WebSocket 连接请求 + + +### _async_ `receive()` + +接收一条 WebSocket 信息 + + +### _async_ `send(data)` + +发送一条 WebSocket 信息 diff --git a/archive/2.0.0a5/api/exception.md b/archive/2.0.0a5/api/exception.md new file mode 100644 index 00000000..0a9876a8 --- /dev/null +++ b/archive/2.0.0a5/api/exception.md @@ -0,0 +1,160 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.exception 模块 + +## 异常 + +下列文档中的异常是所有 NoneBot 运行时可能会抛出的。 +这些异常并非所有需要用户处理,在 NoneBot 内部运行时被捕获,并进行对应操作。 + + +## _exception_ `IgnoredException` + +基类:`Exception` + + +* **说明** + + 指示 NoneBot 应该忽略该事件。可由 PreProcessor 抛出。 + + + +* **参数** + + + * `reason`: 忽略事件的原因 + + + +## _exception_ `PausedException` + +基类:`Exception` + + +* **说明** + + 指示 NoneBot 结束当前 `Handler` 并等待下一条消息后继续下一个 `Handler`。 + 可用于用户输入新信息。 + + + +* **用法** + + 可以在 `Handler` 中通过 `Matcher.pause()` 抛出。 + + + +## _exception_ `RejectedException` + +基类:`Exception` + + +* **说明** + + 指示 NoneBot 结束当前 `Handler` 并等待下一条消息后重新运行当前 `Handler`。 + 可用于用户重新输入。 + + + +* **用法** + + 可以在 `Handler` 中通过 `Matcher.reject()` 抛出。 + + + +## _exception_ `FinishedException` + +基类:`Exception` + + +* **说明** + + 指示 NoneBot 结束当前 `Handler` 且后续 `Handler` 不再被运行。 + 可用于结束用户会话。 + + + +* **用法** + + 可以在 `Handler` 中通过 `Matcher.finish()` 抛出。 + + + +## _exception_ `StopPropagation` + +基类:`Exception` + + +* **说明** + + 指示 NoneBot 终止事件向下层传播。 + + + +* **用法** + + 在 `Matcher.block == True` 时抛出。 + + + +## _exception_ `RequestDenied` + +基类:`Exception` + + +* **说明** + + Bot 连接请求不合法。 + + + +* **参数** + + + * `status_code: int`: HTTP 状态码 + + + * `reason: str`: 拒绝原因 + + + +## _exception_ `ApiNotAvailable` + +基类:`Exception` + + +* **说明** + + 在 API 连接不可用时抛出。 + + + +## _exception_ `NetworkError` + +基类:`Exception` + + +* **说明** + + 在网络出现问题时抛出,如: API 请求地址不正确, API 请求无返回或返回状态非正常等。 + + + +## _exception_ `ActionFailed` + +基类:`Exception` + + +* **说明** + + API 请求成功返回数据,但 API 操作失败。 + + + +* **参数** + + + * `retcode: Optional[int]`: 错误代码 diff --git a/archive/2.0.0a5/api/log.md b/archive/2.0.0a5/api/log.md new file mode 100644 index 00000000..77ce3609 --- /dev/null +++ b/archive/2.0.0a5/api/log.md @@ -0,0 +1,55 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.log 模块 + +## 日志 + +NoneBot 使用 [loguru](https://github.com/Delgan/loguru) 来记录日志信息。 + +自定义 logger 请参考 [loguru](https://github.com/Delgan/loguru) 文档。 + + +## `logger` + + +* **说明** + + NoneBot 日志记录器对象。 + + + +* **默认信息** + + + * 格式: `[%(asctime)s %(name)s] %(levelname)s: %(message)s` + + + * 等级: `DEBUG` / `INFO` ,根据 config 配置改变 + + + * 输出: 输出至 stdout + + + +* **用法** + + +```python +from nonebot.log import logger +``` + + +## _class_ `LoguruHandler` + +基类:`logging.Handler` + + +### `emit(record)` + +Do whatever it takes to actually log the specified logging record. + +This version is intended to be implemented by subclasses and so +raises a NotImplementedError. diff --git a/archive/2.0.0a5/api/matcher.md b/archive/2.0.0a5/api/matcher.md new file mode 100644 index 00000000..b061cd64 --- /dev/null +++ b/archive/2.0.0a5/api/matcher.md @@ -0,0 +1,497 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.matcher 模块 + +## 事件响应器 + +该模块实现事件响应器的创建与运行,并提供一些快捷方法来帮助用户更好的与机器人进行 对话 。 + + +## `matchers` + + +* **类型** + + `Dict[int, List[Type[Matcher]]]` + + + +* **说明** + + 用于存储当前所有的事件响应器 + + + +## _class_ `Matcher` + +基类:`object` + +事件响应器类 + + +### `module` + + +* **类型** + + `Optional[str]` + + + +* **说明** + + 事件响应器所在模块名称 + + + +### `type` + + +* **类型** + + `str` + + + +* **说明** + + 事件响应器类型 + + + +### `rule` + + +* **类型** + + `Rule` + + + +* **说明** + + 事件响应器匹配规则 + + + +### `permission` + + +* **类型** + + `Permission` + + + +* **说明** + + 事件响应器触发权限 + + + +### `priority` + + +* **类型** + + `int` + + + +* **说明** + + 事件响应器优先级 + + + +### `block` + + +* **类型** + + `bool` + + + +* **说明** + + 事件响应器是否阻止事件传播 + + + +### `temp` + + +* **类型** + + `bool` + + + +* **说明** + + 事件响应器是否为临时 + + + +### `expire_time` + + +* **类型** + + `Optional[datetime]` + + + +* **说明** + + 事件响应器过期时间点 + + + +### `_default_state` + + +* **类型** + + `dict` + + + +* **说明** + + 事件响应器默认状态 + + + +### `_default_parser` + + +* **类型** + + `Optional[ArgsParser]` + + + +* **说明** + + 事件响应器默认参数解析函数 + + + +### `__init__()` + +实例化 Matcher 以便运行 + + +### `handlers` + + +* **类型** + + `List[Handler]` + + + +* **说明** + + 事件响应器拥有的事件处理函数列表 + + + +### _classmethod_ `new(type_='', rule=None, permission=None, handlers=None, temp=False, priority=1, block=False, *, module=None, default_state=None, expire_time=None)` + + +* **说明** + + 创建一个新的事件响应器,并存储至 [matchers](#matchers) + + + +* **参数** + + + * `type_: str`: 事件响应器类型,与 `event.type` 一致时触发,空字符串表示任意 + + + * `rule: Optional[Rule]`: 匹配规则 + + + * `permission: Optional[Permission]`: 权限 + + + * `handlers: Optional[List[Handler]]`: 事件处理函数列表 + + + * `temp: bool`: 是否为临时事件响应器,即触发一次后删除 + + + * `priority: int`: 响应优先级 + + + * `block: bool`: 是否阻止事件向更低优先级的响应器传播 + + + * `module: Optional[str]`: 事件响应器所在模块名称 + + + * `default_state: Optional[dict]`: 默认状态 `state` + + + * `expire_time: Optional[datetime]`: 事件响应器最终有效时间点,过时即被删除 + + + +* **返回** + + + * `Type[Matcher]`: 新的事件响应器类 + + + +### _async classmethod_ `check_perm(bot, event)` + + +* **说明** + + 检查是否满足触发权限 + + + +* **参数** + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: 上报事件 + + + +* **返回** + + + * `bool`: 是否满足权限 + + + +### _async classmethod_ `check_rule(bot, event, state)` + + +* **说明** + + 检查是否满足匹配规则 + + + +* **参数** + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: 上报事件 + + + * `state: dict`: 当前状态 + + + +* **返回** + + + * `bool`: 是否满足匹配规则 + + + +### _classmethod_ `args_parser(func)` + + +* **说明** + + 装饰一个函数来更改当前事件响应器的默认参数解析函数 + + + +* **参数** + + + * `func: ArgsParser`: 参数解析函数 + + + +### _classmethod_ `handle()` + + +* **说明** + + 装饰一个函数来向事件响应器直接添加一个处理函数 + + + +* **参数** + + + * 无 + + + +### _classmethod_ `receive()` + + +* **说明** + + 装饰一个函数来指示 NoneBot 在接收用户新的一条消息后继续运行该函数 + + + +* **参数** + + + * 无 + + + +### _classmethod_ `got(key, prompt=None, args_parser=None)` + + +* **说明** + + 装饰一个函数来指示 NoneBot 当要获取的 `key` 不存在时接收用户新的一条消息并经过 `ArgsParser` 处理后再运行该函数,如果 `key` 已存在则直接继续运行 + + + +* **参数** + + + * `key: str`: 参数名 + + + * `prompt: Optional[Union[str, Message, MessageSegment]]`: 在参数不存在时向用户发送的消息 + + + * `args_parser: Optional[ArgsParser]`: 可选参数解析函数,空则使用默认解析函数 + + + +### _async classmethod_ `send(message, **kwargs)` + + +* **说明** + + 发送一条消息给当前交互用户 + + + +* **参数** + + + * `message: Union[str, Message, MessageSegment]`: 消息内容 + + + * `**kwargs`: 其他传递给 `bot.send` 的参数,请参考对应 adapter 的 bot 对象 api + + + +### _async classmethod_ `finish(message=None, **kwargs)` + + +* **说明** + + 发送一条消息给当前交互用户并结束当前事件响应器 + + + +* **参数** + + + * `message: Union[str, Message, MessageSegment]`: 消息内容 + + + * `**kwargs`: 其他传递给 `bot.send` 的参数,请参考对应 adapter 的 bot 对象 api + + + +### _async classmethod_ `pause(prompt=None, **kwargs)` + + +* **说明** + + 发送一条消息给当前交互用户并暂停事件响应器,在接收用户新的一条消息后继续下一个处理函数 + + + +* **参数** + + + * `prompt: Union[str, Message, MessageSegment]`: 消息内容 + + + * `**kwargs`: 其他传递给 `bot.send` 的参数,请参考对应 adapter 的 bot 对象 api + + + +### _async classmethod_ `reject(prompt=None, **kwargs)` + + +* **说明** + + 发送一条消息给当前交互用户并暂停事件响应器,在接收用户新的一条消息后重新运行当前处理函数 + + + +* **参数** + + + * `prompt: Union[str, Message, MessageSegment]`: 消息内容 + + + * `**kwargs`: 其他传递给 `bot.send` 的参数,请参考对应 adapter 的 bot 对象 api + + + +## _class_ `MatcherGroup` + +基类:`object` + +事件响应器组合,统一管理。用法同 `Matcher` + + +### `__init__(type_='', rule=None, permission=None, handlers=None, temp=False, priority=1, block=False, *, module=None, default_state=None, expire_time=None)` + + +* **说明** + + 创建一个事件响应器组合,参数为默认值,与 `Matcher.new` 一致 + + + +### `matchers` + + +* **类型** + + `List[Type[Matcher]]` + + + +* **说明** + + 组内事件响应器列表 + + + +### `new(type_='', rule=None, permission=None, handlers=None, temp=False, priority=1, block=False, *, module=None, default_state=None, expire_time=None)` + + +* **说明** + + 在组中创建一个新的事件响应器,参数留空则使用组合默认值 + + +:::danger 警告 +如果使用 handlers 参数覆盖组合默认值则该事件响应器不会随组合一起添加新的事件处理函数 +::: diff --git a/archive/2.0.0a5/api/message.md b/archive/2.0.0a5/api/message.md new file mode 100644 index 00000000..c9d7c158 --- /dev/null +++ b/archive/2.0.0a5/api/message.md @@ -0,0 +1,143 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.message 模块 + +## 事件处理 + +NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供了多个插槽以进行事件的预处理等。 + + +## `event_preprocessor(func)` + + +* **说明** + + 事件预处理。装饰一个函数,使它在每次接收到事件并分发给各响应器之前执行。 + + + +* **参数** + + 事件预处理函数接收三个参数。 + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + * `state: dict`: 当前 State + + + +## `event_postprocessor(func)` + + +* **说明** + + 事件后处理。装饰一个函数,使它在每次接收到事件并分发给各响应器之后执行。 + + + +* **参数** + + 事件后处理函数接收三个参数。 + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + * `state: dict`: 当前事件运行前 State + + + +## `run_preprocessor(func)` + + +* **说明** + + 运行预处理。装饰一个函数,使它在每次事件响应器运行前执行。 + + + +* **参数** + + 运行预处理函数接收四个参数。 + + + * `matcher: Matcher`: 当前要运行的事件响应器 + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + * `state: dict`: 当前 State + + + +## `run_postprocessor(func)` + + +* **说明** + + 运行后处理。装饰一个函数,使它在每次事件响应器运行后执行。 + + + +* **参数** + + 运行后处理函数接收五个参数。 + + + * `matcher: Matcher`: 运行完毕的事件响应器 + + + * `exception: Optional[Exception]`: 事件响应器运行错误(如果存在) + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + * `state: dict`: 当前 State + + + +## _async_ `handle_event(bot, event)` + + +* **说明** + + 处理一个事件。调用该函数以实现分发事件。 + + + +* **参数** + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + +* **示例** + + +```python +import asyncio +asyncio.create_task(handle_event(bot, event)) +``` diff --git a/archive/2.0.0a5/api/nonebot.md b/archive/2.0.0a5/api/nonebot.md new file mode 100644 index 00000000..cdb72bdb --- /dev/null +++ b/archive/2.0.0a5/api/nonebot.md @@ -0,0 +1,254 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot 模块 + +## 快捷导入 + +为方便使用,`nonebot` 模块从子模块导入了部分内容 + + +* `on_message` => `nonebot.plugin.on_message` + + +* `on_notice` => `nonebot.plugin.on_notice` + + +* `on_request` => `nonebot.plugin.on_request` + + +* `on_metaevent` => `nonebot.plugin.on_metaevent` + + +* `on_startswith` => `nonebot.plugin.on_startswith` + + +* `on_endswith` => `nonebot.plugin.on_endswith` + + +* `on_keyword` => `nonebot.plugin.on_keyword` + + +* `on_command` => `nonebot.plugin.on_command` + + +* `on_regex` => `nonebot.plugin.on_regex` + + +* `CommandGroup` => `nonebot.plugin.CommandGroup` + + +* `load_plugin` => `nonebot.plugin.load_plugin` + + +* `load_plugins` => `nonebot.plugin.load_plugins` + + +* `load_builtin_plugins` => `nonebot.plugin.load_builtin_plugins` + + +* `get_loaded_plugins` => `nonebot.plugin.get_loaded_plugins` + + +## `get_driver()` + + +* **说明** + + 获取全局 Driver 对象。可用于在计划任务的回调中获取当前 Driver 对象。 + + + +* **返回** + + + * `Driver`: 全局 Driver 对象 + + + +* **异常** + + + * `ValueError`: 全局 Driver 对象尚未初始化 (nonebot.init 尚未调用) + + + +* **用法** + + +```python +driver = nonebot.get_driver() +``` + + +## `get_app()` + + +* **说明** + + 获取全局 Driver 对应 Server App 对象。 + + + +* **返回** + + + * `Any`: Server App 对象 + + + +* **异常** + + + * `ValueError`: 全局 Driver 对象尚未初始化 (nonebot.init 尚未调用) + + + +* **用法** + + +```python +app = nonebot.get_app() +``` + + +## `get_asgi()` + + +* **说明** + + 获取全局 Driver 对应 Asgi 对象。 + + + +* **返回** + + + * `Any`: Asgi 对象 + + + +* **异常** + + + * `ValueError`: 全局 Driver 对象尚未初始化 (nonebot.init 尚未调用) + + + +* **用法** + + +```python +asgi = nonebot.get_asgi() +``` + + +## `get_bots()` + + +* **说明** + + 获取所有通过 ws 连接 NoneBot 的 Bot 对象。 + + + +* **返回** + + + * `Dict[str, Bot]`: 一个以字符串 ID 为键,Bot 对象为值的字典 + + + +* **异常** + + + * `ValueError`: 全局 Driver 对象尚未初始化 (nonebot.init 尚未调用) + + + +* **用法** + + +```python +bots = nonebot.get_bots() +``` + + +## `init(*, _env_file=None, **kwargs)` + + +* **说明** + + 初始化 NoneBot 以及 全局 Driver 对象。 + + NoneBot 将会从 .env 文件中读取环境信息,并使用相应的 env 文件配置。 + + 你也可以传入自定义的 _env_file 来指定 NoneBot 从该文件读取配置。 + + + +* **参数** + + + * `_env_file: Optional[str]`: 配置文件名,默认从 .env.{env_name} 中读取配置 + + + * `**kwargs`: 任意变量,将会存储到 Config 对象里 + + + +* **返回** + + + * `None` + + + +* **用法** + + +```python +nonebot.init(database=Database(...)) +``` + + +## `run(host=None, port=None, *args, **kwargs)` + + +* **说明** + + 启动 NoneBot,即运行全局 Driver 对象。 + + + +* **参数** + + + * `host: Optional[str]`: 主机名/IP,若不传入则使用配置文件中指定的值 + + + * `port: Optional[int]`: 端口,若不传入则使用配置文件中指定的值 + + + * `*args`: 传入 Driver.run 的位置参数 + + + * `**kwargs`: 传入 Driver.run 的命名参数 + + + +* **返回** + + + * `None` + + + +* **用法** + + +```python +nonebot.run(host="127.0.0.1", port=8080) +``` diff --git a/archive/2.0.0a5/api/permission.md b/archive/2.0.0a5/api/permission.md new file mode 100644 index 00000000..26d3cd34 --- /dev/null +++ b/archive/2.0.0a5/api/permission.md @@ -0,0 +1,121 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.permission 模块 + +## 权限 + +每个 `Matcher` 拥有一个 `Permission` ,其中是 **异步** `PermissionChecker` 的集合,只要有一个 `PermissionChecker` 检查结果为 `True` 时就会继续运行。 + +:::tip 提示 +`PermissionChecker` 既可以是 async function 也可以是 sync function +::: + + +## `MESSAGE` + + +* **说明**: 匹配任意 `message` 类型事件,仅在需要同时捕获不同类型事件时使用。优先使用 message type 的 Matcher。 + + +## `NOTICE` + + +* **说明**: 匹配任意 `notice` 类型事件,仅在需要同时捕获不同类型事件时使用。优先使用 notice type 的 Matcher。 + + +## `REQUEST` + + +* **说明**: 匹配任意 `request` 类型事件,仅在需要同时捕获不同类型事件时使用。优先使用 request type 的 Matcher。 + + +## `METAEVENT` + + +* **说明**: 匹配任意 `meta_event` 类型事件,仅在需要同时捕获不同类型事件时使用。优先使用 meta_event type 的 Matcher。 + + +## `USER(*user, perm=)` + + +* **说明** + + 在白名单内且满足 perm + + + +* **参数** + + + * `*user: int`: 白名单 + + + * `perm: Permission`: 需要同时满足的权限 + + + +## `PRIVATE` + + +* **说明**: 匹配任意私聊消息类型事件 + + +## `PRIVATE_FRIEND` + + +* **说明**: 匹配任意好友私聊消息类型事件 + + +## `PRIVATE_GROUP` + + +* **说明**: 匹配任意群临时私聊消息类型事件 + + +## `PRIVATE_OTHER` + + +* **说明**: 匹配任意其他私聊消息类型事件 + + +## `GROUP` + + +* **说明**: 匹配任意群聊消息类型事件 + + +## `GROUP_MEMBER` + + +* **说明**: 匹配任意群员群聊消息类型事件 + +:::warning 警告 +该权限通过 event.sender 进行判断且不包含管理员以及群主! +::: + + +## `GROUP_ADMIN` + + +* **说明**: 匹配任意群管理员群聊消息类型事件 + + +## `GROUP_OWNER` + + +* **说明**: 匹配任意群主群聊消息类型事件 + + +## `SUPERUSER` + + +* **说明**: 匹配任意超级用户消息类型事件 + + +## `EVERYBODY` + + +* **说明**: 匹配任意消息类型事件 diff --git a/archive/2.0.0a5/api/plugin.md b/archive/2.0.0a5/api/plugin.md new file mode 100644 index 00000000..42cdf84f --- /dev/null +++ b/archive/2.0.0a5/api/plugin.md @@ -0,0 +1,629 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.plugin 模块 + +## 插件 + +为 NoneBot 插件开发提供便携的定义函数。 + + +## `plugins` + + +* **类型** + + `Dict[str, Plugin]` + + + +* **说明** + + 已加载的插件 + + + +## _class_ `Plugin` + +基类:`object` + +存储插件信息 + + +### `name` + + +* **类型**: `str` + + +* **说明**: 插件名称,使用 文件/文件夹 名称作为插件名 + + +### `module` + + +* **类型**: `ModuleType` + + +* **说明**: 插件模块对象 + + +### `matcher` + + +* **类型**: `Set[Type[Matcher]]` + + +* **说明**: 插件内定义的 `Matcher` + + +## `on(type='', rule=None, permission=None, *, handlers=None, temp=False, priority=1, block=False, state=None)` + + +* **说明** + + 注册一个基础事件响应器,可自定义类型。 + + + +* **参数** + + + * `type: str`: 事件响应器类型 + + + * `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则 + + + * `permission: Optional[Permission]`: 事件响应权限 + + + * `handlers: Optional[List[Handler]]`: 事件处理函数列表 + + + * `temp: bool`: 是否为临时事件响应器(仅执行一次) + + + * `priority: int`: 事件响应器优先级 + + + * `block: bool`: 是否阻止事件向更低优先级传递 + + + * `state: Optional[dict]`: 默认的 state + + + +* **返回** + + + * `Type[Matcher]` + + + +## `on_metaevent(rule=None, *, handlers=None, temp=False, priority=1, block=False, state=None)` + + +* **说明** + + 注册一个元事件响应器。 + + + +* **参数** + + + * `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则 + + + * `handlers: Optional[List[Handler]]`: 事件处理函数列表 + + + * `temp: bool`: 是否为临时事件响应器(仅执行一次) + + + * `priority: int`: 事件响应器优先级 + + + * `block: bool`: 是否阻止事件向更低优先级传递 + + + * `state: Optional[dict]`: 默认的 state + + + +* **返回** + + + * `Type[Matcher]` + + + +## `on_message(rule=None, permission=None, *, handlers=None, temp=False, priority=1, block=True, state=None)` + + +* **说明** + + 注册一个消息事件响应器。 + + + +* **参数** + + + * `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则 + + + * `permission: Optional[Permission]`: 事件响应权限 + + + * `handlers: Optional[List[Handler]]`: 事件处理函数列表 + + + * `temp: bool`: 是否为临时事件响应器(仅执行一次) + + + * `priority: int`: 事件响应器优先级 + + + * `block: bool`: 是否阻止事件向更低优先级传递 + + + * `state: Optional[dict]`: 默认的 state + + + +* **返回** + + + * `Type[Matcher]` + + + +## `on_notice(rule=None, *, handlers=None, temp=False, priority=1, block=False, state=None)` + + +* **说明** + + 注册一个通知事件响应器。 + + + +* **参数** + + + * `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则 + + + * `handlers: Optional[List[Handler]]`: 事件处理函数列表 + + + * `temp: bool`: 是否为临时事件响应器(仅执行一次) + + + * `priority: int`: 事件响应器优先级 + + + * `block: bool`: 是否阻止事件向更低优先级传递 + + + * `state: Optional[dict]`: 默认的 state + + + +* **返回** + + + * `Type[Matcher]` + + + +## `on_request(rule=None, *, handlers=None, temp=False, priority=1, block=False, state=None)` + + +* **说明** + + 注册一个请求事件响应器。 + + + +* **参数** + + + * `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则 + + + * `handlers: Optional[List[Handler]]`: 事件处理函数列表 + + + * `temp: bool`: 是否为临时事件响应器(仅执行一次) + + + * `priority: int`: 事件响应器优先级 + + + * `block: bool`: 是否阻止事件向更低优先级传递 + + + * `state: Optional[dict]`: 默认的 state + + + +* **返回** + + + * `Type[Matcher]` + + + +## `on_startswith(msg, rule=None, **kwargs)` + + +* **说明** + + 注册一个消息事件响应器,并且当消息的\*\*文本部分\*\*以指定内容开头时响应。 + + + +* **参数** + + + * `msg: str`: 指定消息开头内容 + + + * `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则 + + + * `permission: Optional[Permission]`: 事件响应权限 + + + * `handlers: Optional[List[Handler]]`: 事件处理函数列表 + + + * `temp: bool`: 是否为临时事件响应器(仅执行一次) + + + * `priority: int`: 事件响应器优先级 + + + * `block: bool`: 是否阻止事件向更低优先级传递 + + + * `state: Optional[dict]`: 默认的 state + + + +* **返回** + + + * `Type[Matcher]` + + + +## `on_endswith(msg, rule=None, **kwargs)` + + +* **说明** + + 注册一个消息事件响应器,并且当消息的\*\*文本部分\*\*以指定内容结尾时响应。 + + + +* **参数** + + + * `msg: str`: 指定消息结尾内容 + + + * `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则 + + + * `permission: Optional[Permission]`: 事件响应权限 + + + * `handlers: Optional[List[Handler]]`: 事件处理函数列表 + + + * `temp: bool`: 是否为临时事件响应器(仅执行一次) + + + * `priority: int`: 事件响应器优先级 + + + * `block: bool`: 是否阻止事件向更低优先级传递 + + + * `state: Optional[dict]`: 默认的 state + + + +* **返回** + + + * `Type[Matcher]` + + + +## `on_keyword(keywords, rule=None, **kwargs)` + + +* **说明** + + 注册一个消息事件响应器,并且当消息纯文本部分包含关键词时响应。 + + + +* **参数** + + + * `keywords: Set[str]`: 关键词列表 + + + * `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则 + + + * `permission: Optional[Permission]`: 事件响应权限 + + + * `handlers: Optional[List[Handler]]`: 事件处理函数列表 + + + * `temp: bool`: 是否为临时事件响应器(仅执行一次) + + + * `priority: int`: 事件响应器优先级 + + + * `block: bool`: 是否阻止事件向更低优先级传递 + + + * `state: Optional[dict]`: 默认的 state + + + +* **返回** + + + * `Type[Matcher]` + + + +## `on_command(cmd, rule=None, aliases=None, **kwargs)` + + +* **说明** + + 注册一个消息事件响应器,并且当消息以指定命令开头时响应。 + + 命令匹配规则参考: [命令形式匹配](rule.html#command-command) + + + +* **参数** + + + * `cmd: Union[str, Tuple[str, ...]]`: 指定命令内容 + + + * `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则 + + + * `aliases: Optional[Set[Union[str, Tuple[str, ...]]]]`: 命令别名 + + + * `permission: Optional[Permission]`: 事件响应权限 + + + * `handlers: Optional[List[Handler]]`: 事件处理函数列表 + + + * `temp: bool`: 是否为临时事件响应器(仅执行一次) + + + * `priority: int`: 事件响应器优先级 + + + * `block: bool`: 是否阻止事件向更低优先级传递 + + + * `state: Optional[dict]`: 默认的 state + + + +* **返回** + + + * `Type[Matcher]` + + + +## `on_regex(pattern, flags=0, rule=None, **kwargs)` + + +* **说明** + + 注册一个消息事件响应器,并且当消息匹配正则表达式时响应。 + + 命令匹配规则参考: [正则匹配](rule.html#regex-regex-flags-0) + + + +* **参数** + + + * `pattern: str`: 正则表达式 + + + * `flags: Union[int, re.RegexFlag]`: 正则匹配标志 + + + * `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则 + + + * `permission: Optional[Permission]`: 事件响应权限 + + + * `handlers: Optional[List[Handler]]`: 事件处理函数列表 + + + * `temp: bool`: 是否为临时事件响应器(仅执行一次) + + + * `priority: int`: 事件响应器优先级 + + + * `block: bool`: 是否阻止事件向更低优先级传递 + + + * `state: Optional[dict]`: 默认的 state + + + +* **返回** + + + * `Type[Matcher]` + + + +## _class_ `CommandGroup` + +基类:`object` + +命令组,用于声明一组有相同名称前缀的命令。 + + +### `__init__(cmd, **kwargs)` + + +* **参数** + + + * `cmd: Union[str, Tuple[str, ...]]`: 命令前缀 + + + * `**kwargs`: 其他传递给 `on_command` 的参数默认值,参考 [on_command](#on-command-cmd-rule-none-aliases-none-kwargs) + + + +### `basecmd` + + +* **类型**: `Tuple[str, ...]` + + +* **说明**: 命令前缀 + + +### `base_kwargs` + + +* **类型**: `Dict[str, Any]` + + +* **说明**: 其他传递给 `on_command` 的参数默认值 + + +### `command(cmd, **kwargs)` + + +* **说明** + + 注册一个新的命令。 + + + +* **参数** + + + * `cmd: Union[str, Tuple[str, ...]]`: 命令前缀 + + + * `**kwargs`: 其他传递给 `on_command` 的参数,将会覆盖命令组默认值 + + + +* **返回** + + + * `Type[Matcher]` + + + +## `load_plugin(module_path)` + + +* **说明** + + 使用 `importlib` 加载单个插件,可以是本地插件或是通过 `pip` 安装的插件。 + + + +* **参数** + + + * `module_path: str`: 插件名称 `path.to.your.plugin` + + + +* **返回** + + + * `Optional[Plugin]` + + + +## `load_plugins(*plugin_dir)` + + +* **说明** + + 导入目录下多个插件,以 `_` 开头的插件不会被导入! + + + +* **参数** + + + * `*plugin_dir: str`: 插件路径 + + + +* **返回** + + + * `Set[Plugin]` + + + +## `load_builtin_plugins()` + + +* **说明** + + 导入 NoneBot 内置插件 + + + +* **返回** + + + * `Plugin` + + + +## `get_loaded_plugins()` + + +* **说明** + + 获取当前已导入的插件。 + + + +* **返回** + + + * `Set[Plugin]` diff --git a/archive/2.0.0a5/api/rule.md b/archive/2.0.0a5/api/rule.md new file mode 100644 index 00000000..dade2a5b --- /dev/null +++ b/archive/2.0.0a5/api/rule.md @@ -0,0 +1,210 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.rule 模块 + +## 规则 + +每个事件响应器 `Matcher` 拥有一个匹配规则 `Rule` ,其中是 **异步** `RuleChecker` 的集合,只有当所有 `RuleChecker` 检查结果为 `True` 时继续运行。 + +:::tip 提示 +`RuleChecker` 既可以是 async function 也可以是 sync function,但在最终会被 `nonebot.utils.run_sync` 转换为 async function +::: + + +## _class_ `Rule` + +基类:`object` + + +* **说明** + + `Matcher` 规则类,当事件传递时,在 `Matcher` 运行前进行检查。 + + + +* **示例** + + +```python +Rule(async_function) & sync_function +# 等价于 +from nonebot.utils import run_sync +Rule(async_function, run_sync(sync_function)) +``` + + +### `__init__(*checkers)` + + +* **参数** + + + * `*checkers: Callable[[Bot, Event, dict], Awaitable[bool]]`: **异步** RuleChecker + + + +### `checkers` + + +* **说明** + + 存储 `RuleChecker` + + + +* **类型** + + + * `Set[Callable[[Bot, Event, dict], Awaitable[bool]]]` + + + +### _async_ `__call__(bot, event, state)` + + +* **说明** + + 检查是否符合所有规则 + + + +* **参数** + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + * `state: dict`: 当前 State + + + +* **返回** + + + * `bool` + + + +## `startswith(msg)` + + +* **说明** + + 匹配消息开头 + + + +* **参数** + + + * `msg: str`: 消息开头字符串 + + + +## `endswith(msg)` + + +* **说明** + + 匹配消息结尾 + + + +* **参数** + + + * `msg: str`: 消息结尾字符串 + + + +## `keyword(*keywords)` + + +* **说明** + + 匹配消息关键词 + + + +* **参数** + + + * `*keywords: str`: 关键词 + + + +## `command(*cmds)` + + +* **说明** + + 命令形式匹配,根据配置里提供的 `command_start`, `command_sep` 判断消息是否为命令。 + + 可以通过 `state["_prefix"]["command"]` 获取匹配成功的命令(例:`("test",)`),通过 `state["_prefix"]["raw_command"]` 获取匹配成功的原始命令文本(例:`"/test"`)。 + + + +* **参数** + + + * `*cmds: Union[str, Tuple[str, ...]]`: 命令内容 + + + +* **示例** + + 使用默认 `command_start`, `command_sep` 配置 + + 命令 `("test",)` 可以匹配:`/test` 开头的消息 + 命令 `("test", "sub")` 可以匹配”`/test.sub` 开头的消息 + + +:::tip 提示 +命令内容与后续消息间无需空格! +::: + + +## `regex(regex, flags=0)` + + +* **说明** + + 根据正则表达式进行匹配。 + + 可以通过 `state["_matched"]` 获取正则表达式匹配成功的文本。 + + + +* **参数** + + + * `regex: str`: 正则表达式 + + + * `flags: Union[int, re.RegexFlag]`: 正则标志 + + +:::tip 提示 +正则表达式匹配使用 search 而非 match,如需从头匹配请使用 `r"^xxx"` 来确保匹配开头 +::: + + +## `to_me()` + + +* **说明** + + 通过 `event.to_me` 判断消息是否是发送给机器人 + + + +* **参数** + + + * 无 diff --git a/archive/2.0.0a5/api/sched.md b/archive/2.0.0a5/api/sched.md new file mode 100644 index 00000000..450fd7d0 --- /dev/null +++ b/archive/2.0.0a5/api/sched.md @@ -0,0 +1,41 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.sched 模块 + +## 计划任务 + +计划任务使用第三方库 [APScheduler](https://github.com/agronholm/apscheduler) ,使用文档请参考 [APScheduler使用文档](https://apscheduler.readthedocs.io/en/latest/) 。 + + +## `scheduler` + + +* **类型** + + `Optional[apscheduler.schedulers.asyncio.AsyncIOScheduler]` + + + +* **说明** + + 当可选依赖 `APScheduler` 未安装时,`scheduler` 为 None + + 使用 `pip install nonebot[scheduler]` 安装可选依赖 + + + +* **常用示例** + + +```python +from nonebot import scheduler + +@scheduler.scheduled_job("cron", hour="*/2", id="xxx", args=[1], kwargs={arg2: 2}) +async def run_every_2_hour(arg1, arg2): + pass + +scheduler.add_job(run_every_day_from_program_start, "interval", days=1, id="xxx") +``` diff --git a/archive/2.0.0a5/api/typing.md b/archive/2.0.0a5/api/typing.md new file mode 100644 index 00000000..8f8a2223 --- /dev/null +++ b/archive/2.0.0a5/api/typing.md @@ -0,0 +1,300 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.typing 模块 + +## 类型 + +下面的文档中,「类型」部分使用 Python 的 Type Hint 语法,见 [PEP 484](https://www.python.org/dev/peps/pep-0484/)、[PEP 526](https://www.python.org/dev/peps/pep-0526/) 和 [typing](https://docs.python.org/3/library/typing.html)。 + +除了 Python 内置的类型,下面还出现了如下 NoneBot 自定类型,实际上它们是 Python 内置类型的别名。 + +以下类型均可从 nonebot.typing 模块导入。 + + +## `Driver` + + +* **类型** + + `BaseDriver` + + + +* **说明** + + 所有 Driver 的基类。 + + + + +## `WebSocket` + + +* **类型** + + `BaseWebSocket` + + + +* **说明** + + 所有 WebSocket 的基类。 + + + + +## `Bot` + + +* **类型** + + `BaseBot` + + + +* **说明** + + 所有 Bot 的基类。 + + + + +## `Event` + + +* **类型** + + `BaseEvent` + + + +* **说明** + + 所有 Event 的基类。 + + + + +## `Message` + + +* **类型** + + `BaseMessage` + + + +* **说明** + + 所有 Message 的基类。 + + + + +## `MessageSegment` + + +* **类型** + + `BaseMessageSegment` + + + +* **说明** + + 所有 MessageSegment 的基类。 + + + + +## `EventPreProcessor` + + +* **类型** + + `Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` + + + +* **说明** + + 事件预处理函数 EventPreProcessor 类型 + + + + +## `EventPostProcessor` + + +* **类型** + + `Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` + + + +* **说明** + + 事件预处理函数 EventPostProcessor 类型 + + + + +## `RunPreProcessor` + + +* **类型** + + `Callable[[Matcher, Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` + + + +* **说明** + + 事件响应器运行前预处理函数 RunPreProcessor 类型 + + + + +## `RunPostProcessor` + + +* **类型** + + `Callable[[Matcher, Optional[Exception], Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` + + + +* **说明** + + 事件响应器运行前预处理函数 RunPostProcessor 类型,第二个参数为运行时产生的错误(如果存在) + + + + +## `Matcher` + + +* **类型** + + `Matcher` + + + +* **说明** + + Matcher 即响应事件的处理类。通过 Rule 判断是否响应事件,运行 Handler。 + + + + +## `MatcherGroup` + + +* **类型** + + `MatcherGroup` + + + +* **说明** + + MatcherGroup 为 Matcher 的集合。可以共享 Handler。 + + + + +## `Rule` + + +* **类型** + + `Rule` + + + +* **说明** + + Rule 即判断是否响应事件的处理类。内部存储 RuleChecker ,返回全为 True 则响应事件。 + + + + +## `RuleChecker` + + +* **类型** + + `Callable[[Bot, Event, dict], Union[bool, Awaitable[bool]]]` + + + +* **说明** + + RuleChecker 即判断是否响应事件的处理函数。 + + + + +## `Permission` + + +* **类型** + + `Permission` + + + +* **说明** + + Permission 即判断是否响应消息的处理类。内部存储 PermissionChecker ,返回只要有一个 True 则响应消息。 + + + + +## `PermissionChecker` + + +* **类型** + + `Callable[[Bot, Event], Union[bool, Awaitable[bool]]]` + + + +* **说明** + + RuleChecker 即判断是否响应消息的处理函数。 + + + + +## `Handler` + + +* **类型** + + `Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` + + + +* **说明** + + Handler 即事件的处理函数。 + + + + +## `ArgsParser` + + +* **类型** + + `Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` + + + +* **说明** + + ArgsParser 即消息参数解析函数,在 Matcher.got 获取参数时被运行。 diff --git a/archive/2.0.0a5/api/utils.md b/archive/2.0.0a5/api/utils.md new file mode 100644 index 00000000..ed98fab9 --- /dev/null +++ b/archive/2.0.0a5/api/utils.md @@ -0,0 +1,62 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.utils 模块 + + +## `escape_tag(s)` + + +* **说明** + + 用于记录带颜色日志时转义 `` 类型特殊标签 + + + +* **参数** + + + * `s: str`: 需要转义的字符串 + + + +* **返回** + + + * `str` + + + +## `run_sync(func)` + + +* **说明** + + 一个用于包装 sync function 为 async function 的装饰器 + + + +* **参数** + + + * `func: Callable[..., Any]`: 被装饰的同步函数 + + + +* **返回** + + + * `Callable[..., Awaitable[Any]]` + + + +## _class_ `DataclassEncoder` + +基类:`json.encoder.JSONEncoder` + + +* **说明** + + 在JSON序列化 `Message` (List[Dataclass]) 时使用的 `JSONEncoder` diff --git a/archive/2.0.0a5/guide/README.md b/archive/2.0.0a5/guide/README.md new file mode 100644 index 00000000..43b7f325 --- /dev/null +++ b/archive/2.0.0a5/guide/README.md @@ -0,0 +1,36 @@ +# 概览 + +:::tip 提示 +如果在阅读本文档时遇到难以理解的词汇,请随时查阅 [术语表](../glossary.md) 或使用 [Google 搜索](https://www.google.com/)。 +::: + +:::tip 提示 +初次使用时可能会觉得这里的概览过于枯燥,可以先简单略读之后直接前往 [安装](./installation.md) 查看安装方法,并进行后续的基础使用教程。 +::: + +NoneBot2 是一个可扩展的 Python 异步机器人框架,它会对机器人收到的消息进行解析和处理,并以插件化的形式,分发给消息所对应的命令处理器和自然语言处理器,来完成具体的功能。 + +除了起到解析消息的作用,NoneBot 还为插件提供了大量实用的预设操作和权限控制机制,尤其对于命令处理器,它更是提供了完善且易用的会话机制和内部调用机制,以分别适应命令的连续交互和插件内部功能复用等需求。 + +目前 NoneBot2 在 [FastAPI](https://fastapi.tiangolo.com/) 的基础上封装了与 [CQHTTP(OneBot) 协议](http://cqhttp.cc/)插件的网络交互。 + +得益于 Python 的 [asyncio](https://docs.python.org/3/library/asyncio.html) 机制,NoneBot 处理消息的吞吐量有了很大的保障,再配合 WebSocket 通信方式(也是最建议的通信方式),NoneBot 的性能可以达到 HTTP 通信方式的两倍以上,相较于传统同步 I/O 的 HTTP 通信,更是有质的飞跃。 + +需要注意的是,NoneBot 仅支持 Python 3.7+ 及 CQHTTP(OneBot) 插件 v11+。 + +## 它如何工作? + + + +~~未填坑~~ + +## 特色 + +- 提供直观的测试前端 +- 提供使用简易的脚手架 +- 基于异步 I/O +- 同时支持 HTTP 和反向 WebSocket 通信方式 +- 支持多个机器人账号负载均衡 +- 提供直观的交互式会话接口 +- 提供可自定义的权限控制机制 +- 多种方式渲染要发送的消息内容,使对话足够自然 diff --git a/archive/2.0.0a5/guide/basic-configuration.md b/archive/2.0.0a5/guide/basic-configuration.md new file mode 100644 index 00000000..e4c4449f --- /dev/null +++ b/archive/2.0.0a5/guide/basic-configuration.md @@ -0,0 +1,68 @@ +# 基本配置 + +到目前为止我们还在使用 NoneBot 的默认行为,在开始编写自己的插件之前,我们先尝试在配置文件上动动手脚,让 NoneBot 表现出不同的行为。 + +在上一章节中,我们创建了默认的项目结构,其中 `.env`, `.env.*` 均为项目的配置文件,下面将介绍几种 NoneBot 配置方式。 + +:::danger 警告 +请勿将敏感信息写入配置文件并提交至开源仓库! +::: + +## .env 文件 + +NoneBot 在启动时将会从系统环境变量或者 `.env` 文件中寻找变量 `ENVIRONMENT` (大小写不敏感),默认值为 `prod`。 +这将引导 NoneBot 从系统环境变量或者 `.env.{ENVIRONMENT}` 文件中进一步加载具体配置。 + +现在,我们在 `.env` 文件中写入当前环境信息 + +```bash +# .env +ENVIRONMENT=dev +``` + +## .env.\* 文件 + +详细配置文件,使用 [pydantic](https://pydantic-docs.helpmanual.io/) 加载配置。在 NoneBot 初始化时可以指定忽略 `.env` 中的环境信息转而加载某个配置文件: `nonebot.init(_env_file=".env.dev")`。 + +:::warning 提示 +由于 `pydantic` 使用 JSON 加载配置项,请确保配置项值为 JSON 能够解析的数据。如果 JSON 解析失败将作为字符串处理。 +::: + +示例及说明: + +```bash +HOST=0.0.0.0 # 配置 NoneBot 监听的 IP/主机名 +PORT=8080 # 配置 NoneBot 监听的端口 +DEBUG=true # 开启 debug 模式 **请勿在生产环境开启** +SUPERUSERS=["123456789", "987654321"] # 配置 NoneBot 超级用户 +NICKNAME=["awesome", "bot"] # 配置机器人的昵称 +COMMAND_START=["/", ""] # 配置命令起始字符 +COMMAND_SEP=["."] # 配置命令分割字符 + +# Custom Configs +CUSTOM_CONFIG1="config in env file" +CUSTOM_CONFIG2= # 留空则从系统环境变量读取,如不存在则为空字符串 +``` + +详细的配置项参考 [Config Reference](../api/config.md) 。 + +## bot.py 文件 + +配置项也可以在 NoneBot 初始化时传入。此处可以传入任意合法 Python 变量。当然也可以在初始化完成后修改或新增。 + +示例: + +```python +# bot.py +import nonebot + +nonebot.init(custom_config3="config on init") + +config = nonebot.get_driver().config +config.custom_config3 = "changed after init" +config.custom_config4 = "new config after init" +``` + +## 优先级 + +`bot.py init` > `env file` > `system env` diff --git a/archive/2.0.0a5/guide/creating-a-handler.md b/archive/2.0.0a5/guide/creating-a-handler.md new file mode 100644 index 00000000..725aaacf --- /dev/null +++ b/archive/2.0.0a5/guide/creating-a-handler.md @@ -0,0 +1,159 @@ +# 事件处理 + +在上一章中,我们已经注册了事件响应器,现在我们可以正式编写事件处理逻辑了! + +## [事件处理函数](../api/typing.md#handler) + +```python{1,2,8,9} +@weather.handle() +async def handle_first_receive(bot: Bot, event: Event, state: dict): + args = str(event.message).strip() # 首次发送命令时跟随的参数,例:/天气 上海,则args为上海 + if args: + state["city"] = args # 如果用户发送了参数则直接赋值 + + +@weather.got("city", prompt="你想查询哪个城市的天气呢?") +async def handle_city(bot: Bot, event: Event, state: dict): + city = state["city"] + if city not in ["上海", "北京"]: + await weather.reject("你想查询的城市暂不支持,请重新输入!") + city_weather = await get_weather(city) + await weather.finish(city_weather) +``` + +在之前的样例中,我们定义了两个函数 `handle_first_receive`, `handle_city`,他们被事件响应器的装饰器装饰从而成为事件响应器的事件处理函数。 + +:::tip 提示 +在事件响应器中,事件处理函数是**顺序**执行的! +::: + +### 添加一个事件处理函数 + +事件响应器提供了三种装饰事件处理函数的装饰器,分别是: + +1. [handle()](../api/matcher.md#classmethod-handle) +2. [receive()](../api/matcher.md#classmethod-receive) +3. [got(key, prompt, args_parser)](../api/matcher.md#classmethod-got-key-prompt-none-args-parser-none) + +#### handle() + +简单的为事件响应器添加一个事件处理函数,这个函数将会在上一个处理函数正常返回执行完毕后立即执行。 + +#### receive() + +指示 NoneBot 接收一条新的用户消息后继续执行该处理函数。此时函数将会接收到新的消息而非前一条消息,之前相关信息可以存储在 state 中。 + +特别的,当装饰的函数前没有其他事件处理函数,那么 `receive()` 不会接收一条新的消息而是直接使用第一条接收到的消息。 + +#### got(key, prompt, args_parser) + +指示 NoneBot 当 `state` 中不存在 `key` 时向用户发送 `prompt` 等待用户回复并赋值给 `state[key]`。 + +`prompt` 可以为 `str`, `Message`, `MessageSegment`,若为空则不会向用户发送,若不为空则会在 format 之后发送,即 `prompt.format(**state)`,注意对 `{}` 进行转义。示例: + +```python +@matcher.receive() +async def handle(bot: Bot, event: Event, state: dict): + state["key"] = "hello" + + +@matcher.got("key2", prompt="{key}!") +async def handle2(bot: Bot, event: Event, state: dict): + pass +``` + +`args_parser` 为参数处理函数,在这里传入一个新的函数以覆盖默认的参数处理。详情参照 [args_parser](#参数处理函数-args-parser) + +特别的,这些装饰器都可以套娃使用: + +```python +@matcher.got("key1") +@matcher.got("key2") +async def handle(bot: Bot, event: Event, state: dict): + pass +``` + +### 事件处理函数参数 + +事件处理函数类型为 `Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` 。 + +参数分别为: + +1. [nonebot.typing.Bot](../api/typing.md#bot): 即事件上报连接对应的 Bot 对象,为 BaseBot 的子类。特别注意,此处的类型注释可以替换为指定的 Bot 类型,例如:`nonebot.adapters.cqhttp.Bot`,只有在上报事件的 Bot 类型与类型注释相符时才会执行该处理函数!可用于多平台进行不同的处理。 +2. [nonebot.typing.Event](../api/typing.md#event): 即上报事件对象,可以获取到上报的所有信息。 +3. `state`: 状态字典,可以存储任意的信息,其中还包含一些特殊的值以获取 NoneBot 内部处理时的一些信息,如: + +- `state["_current_key"]`: 存储当前 `got` 获取的参数名 +- `state["_prefix"]`, `state["_suffix"]`: 存储当前 TRIE 匹配的前缀/后缀,可以通过该值获取用户命令的原始命令 + +### 参数处理函数 args_parser + +在使用 `got` 获取用户输入参数时,需要对用户的消息进行处理以转换为我们所需要的信息。在默认情况下,NoneBot 会把用户的消息字符串原封不动的赋值给 `state[key]` 。可以通过以下两种方式修改默认处理逻辑: + +- `@matcher.args_parser` 装饰器:直接装饰一个函数作为参数处理器 +- `got(key, prompt, args_parser)`:直接把函数作为参数传入 + +参数处理函数类型为:`Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]`,即: + +```python +async def parser(bot: Bot, event: Event, state: dict): + state[state["_current_key"]] = str(event.message) +``` + +特别的,`state["_current_key"]` 中存储了当前获取的参数名 + +### 逻辑控制 + +NoneBot 也为事件处理函数提供了一些便捷的逻辑控制函数: + +#### `matcher.send` + +这个函数用于发送一条消息给当前交互的用户。~~其实这并不是一个逻辑控制函数,只是不知道放在哪里……~~ + +#### `matcher.pause` + +这个函数用于结束当前事件处理函数,强制接收一条新的消息再运行**下一个消息处理函数**。 + +#### `matcher.reject` + +这个函数用于结束当前事件处理函数,强制接收一条新的消息再**再次运行当前消息处理函数**。常用于用户输入信息不符合预期。 + +#### `matcher.finish` + +这个函数用于直接结束当前事件处理。 + +以上三个函数都拥有一个参数 `message` / `prompt`,用于向用户发送一条消息。以及 `**kwargs` 直接传递给 `bot.send` 的额外参数。 + +## 常用事件处理结构 + +```python +matcher = on_command("test") + +# 修改默认参数处理 +@matcher.args_parser +async def parse(bot: Bot, event: Event, state: dict): + print(state["_current_key"], ":", str(event.message)) + state[state["_current_key"]] = str(event.message) + +@matcher.handle() +async def first_receive(bot: Bot, event: Event, state: dict): + # 获取用户原始命令,如:/test + print(state["_prefix"]["raw_command"]) + # 处理用户输入参数,如:/test arg1 arg2 + raw_args = str(event.message).strip() + if raw_args: + arg_list = raw_args.split() + # 将参数存入state以阻止后续再向用户询问参数 + state["arg1"] = arg_list[0] + + +@matcher.got("arg1", prompt="参数?") +async def arg_handle(bot: Bot, event: Event, state: dict): + # 在这里对参数进行验证 + if state["arg1"] not in ["allow", "list"]: + await matcher.reject("参数不正确!请重新输入") + # 发送一些信息 + await bot.send("message") + await matcher.send("message") + await matcher.finish("message") +``` diff --git a/archive/2.0.0a5/guide/creating-a-matcher.md b/archive/2.0.0a5/guide/creating-a-matcher.md new file mode 100644 index 00000000..b603449a --- /dev/null +++ b/archive/2.0.0a5/guide/creating-a-matcher.md @@ -0,0 +1,144 @@ +# 注册事件响应器 + +好了,现在插件已经创建完毕,我们可以开始编写实际代码了,下面将以一个简易单文件天气查询插件为例。 + +在插件目录下 `weather.py` 中添加如下代码: + +```python +from nonebot import on_command +from nonebot.rule import to_me +from nonebot.adapters.cqhttp import Bot, Event + +weather = on_command("天气", rule=to_me(), priority=5) + + +@weather.handle() +async def handle_first_receive(bot: Bot, event: Event, state: dict): + args = str(event.message).strip() # 首次发送命令时跟随的参数,例:/天气 上海,则args为上海 + if args: + state["city"] = args # 如果用户发送了参数则直接赋值 + + +@weather.got("city", prompt="你想查询哪个城市的天气呢?") +async def handle_city(bot: Bot, event: Event, state: dict): + city = state["city"] + if city not in ["上海", "北京"]: + await weather.reject("你想查询的城市暂不支持,请重新输入!") + city_weather = await get_weather(city) + await weather.finish(city_weather) + + +async def get_weather(city: str): + return f"{city}的天气是..." +``` + +为了简单起见,我们在这里的例子中没有接入真实的天气数据,但要接入也非常简单,你可以使用中国天气网、和风天气等网站提供的 API。 + +接下来我们来说明这段代码是如何工作的。 + +:::tip 提示 +从这里开始,你需要对 Python 的 asyncio 编程有所了解,因为 NoneBot 是完全基于 asyncio 的,具体可以参考 [廖雪峰的 Python 教程](https://www.liaoxuefeng.com/wiki/1016959663602400/1017959540289152) +::: + +## [事件响应器](../api/matcher.md) + +```python{4} +from nonebot import on_command +from nonebot.rule import to_me +from nonebot.permission import Permission + +weather = on_command("天气", rule=to_me(), permission=Permission(), priority=5) +``` + +在上方代码中,我们注册了一个事件响应器 `Matcher`,它由几个部分组成: + +1. `on_command` 注册一个消息类型的命令处理器 +2. `"天气"` 指定 command 参数 - 命令名 +3. `rule` 补充事件响应器的匹配规则 +4. `priority` 事件响应器优先级 +5. `block` 是否阻止事件传递 + +其他详细配置可以参考 API 文档,下面我们详细说明各个部分: + +### 事件响应器类型 type + +事件响应器类型其实就是对应事件的类型 `Event.type` ,NoneBot 提供了一个基础类型事件响应器 `on()` 以及一些其他内置的事件响应器。 + +以下所有类型的事件响应器都是由 `on(type, rule)` 的形式进行了简化封装。 + +- `on("事件类型")`: 基础事件响应器,第一个参数为事件类型,空字符串表示不限 +- `on_metaevent()` ~ `on("meta_event")`: 元事件响应器 +- `on_message()` ~ `on("message")`: 消息事件响应器 +- `on_request()` ~ `on("request")`: 请求事件响应器 +- `on_notice()` ~ `on("notice")`: 通知事件响应器 +- `on_startswith(str)` ~ `on("message", startswith(str))`: 消息开头匹配响应器,参考 [startswith](../api/rule.md#startswith-msg) +- `on_endswith(str)` ~ `on("message", endswith(str))`: 消息结尾匹配响应器,参考 [endswith](../api/rule.md#endswith-msg) +- `on_keyword(set)` ~ `on("message", keyword(str))`: 消息关键词匹配响应器,参考 [keyword](../api/rule.md#keyword-keywords) +- `on_command(str|tuple)` ~ `on("message", command(str|tuple))`: 命令响应器,参考 [command](../api/rule.md#command-cmds) +- `on_regex(pattern_str)` ~ `on("message", regex(pattern_str))`: 正则匹配处理器,参考 [regex](../api/rule.md#regex-regex-flags-0) + +### 匹配规则 rule + +事件响应器的匹配规则即 `Rule`,详细内容在下方介绍。[直达](#自定义-rule) + +### 优先级 priority + +事件响应器的优先级代表事件响应器的执行顺序,同一优先级的事件响应器会 **同时执行!**,优先级数字**越小**越先响应!优先级请从 `1` 开始排序! + +:::tip 提示 +使用 `nonebot-test` 可以看到当前所有事件响应器的执行流程,有助理解事件响应流程! + +```bash +pip install nonebot2[test] +``` + +::: + +### 阻断 block + +当有任意事件响应器发出了阻止事件传递信号时,该事件将不再会传递给下一优先级,直接结束处理。 + +NoneBot 内置的事件响应器中,所有 `message` 类的事件响应器默认会阻断事件传递,其他则不会。 + +## 自定义 rule + +rule 的出现使得 nonebot 对事件的响应可以非常自由,nonebot 内置了一些规则: + +- [startswith(msg)](../api/rule.md#startswith-msg) +- [endswith(msg)](../api/rule.md#endswith-msg) +- [keyword(\*keywords)](../api/rule.md#keyword-keywords) +- [command(\*cmds)](../api/rule.md#command-cmds) +- [regex(regex, flag)](../api/rule.md#regex-regex-flags-0) + +以上规则都是返回类型为 `Rule` 的函数,`Rule` 由非负个 `RuleChecker` 组成,当所有 `RuleChecker` 返回 `True` 时匹配成功。这些 `Rule`, `RuleChecker` 的形式如下: + +```python +from nonebot.rule import Rule + +async def async_checker(bot: Bot, event: Event, state: dict) -> bool: + return True + +def sync_checker(bot: Bot, event: Event, state: dict) -> bool: + return True + +def check(arg1, args2): + + async def _checker(bot: Bot, event: Event, state: dict) -> bool: + return bool(arg1 + arg2) + + return Rule(_check) +``` + +`Rule` 和 `RuleChecker` 之间可以使用 `与 &` 互相组合: + +```python +from nonebot.rule import Rule + +Rule(async_checker1) & sync_checker & async_checker2 +``` + +**_请勿将事件处理的逻辑写入 `rule` 中,这会使得事件处理返回奇怪的响应。_** + +:::danger 警告 +`Rule(*checkers)` 只接受 async function,或使用 `nonebot.utils.run_sync` 自行包裹 sync function。在使用 `与 &` 时,NoneBot 会自动包裹 sync function +::: diff --git a/archive/2.0.0a5/guide/creating-a-plugin.md b/archive/2.0.0a5/guide/creating-a-plugin.md new file mode 100644 index 00000000..fe52a25d --- /dev/null +++ b/archive/2.0.0a5/guide/creating-a-plugin.md @@ -0,0 +1,113 @@ +# 创建插件 + +如果之前使用 `nb-cli` 生成了项目结构,那我们已经有了一个空的插件目录 `Awesome-Bot/awesome_bot/plugins`,并且它已在 `bot.py` 中被加载,我们现在可以开始创建插件了! + +插件通常有两种形式,下面分别介绍 + +## 单文件形式 + +在插件目录下创建名为 `foo.py` 的 Python 文件,暂时留空,此时目录结构如下: + + +:::vue +AweSome-Bot +├── awesome_bot +│ └── plugins +│ └── `foo.py` +├── .env +├── .env.dev +├── .env.prod +├── .gitignore +├── bot.py +├── docker-compose.yml +├── Dockerfile +├── pyproject.toml +└── README.md +::: + + +这个时候它已经可以被称为一个插件了,尽管它还什么都没做。 + +## 包形式(推荐) + +在插件目录下创建文件夹 `foo`,并在该文件夹下创建文件 `__init__.py`,此时目录结构如下: + + +:::vue +AweSome-Bot +├── awesome_bot +│ └── plugins +│ └── `foo` +│ └── `__init__.py` +├── .env +├── .env.dev +├── .env.prod +├── .gitignore +├── bot.py +├── docker-compose.yml +├── Dockerfile +├── pyproject.toml +└── README.md +::: + + +这个时候 `foo` 就是一个合法的 Python 包了,同时也是合法的 NoneBot 插件,插件内容可以在 `__init__.py` 中编写。 + +### 推荐结构(仅供参考) + + +:::vue +foo +├── `__init__.py` +├── `config.py` +├── `data_source.py` +└── `model.py` +::: + + +#### \_\_init\_\_.py + +在该文件中编写各类事件响应及处理逻辑。 + +#### config.py + +在该文件中使用 `pydantic` 定义插件所需要的配置项以及类型。 + +示例: + +```python +from pydantic import BaseSetting + + +class Config(BaseSetting): + + # plugin custom config + plugin_setting: str = "default" + + class Config: + extra = "ignore" +``` + +并在 `__init__.py` 文件中添加以下行 + +```python +import nonebot +from .config import Config + +global_config = nonebot.get_driver().config +plugin_config = Config(**global_config.dict()) +``` + +此时就可以通过 `plugin_config.plugin_setting` 获取到插件所需要的配置项了。 + +#### data_source.py + +在该文件中编写数据获取函数。 + +:::warning 警告 +数据获取应尽量使用**异步**处理!例如使用 [httpx](https://www.python-httpx.org/) 而非 [requests](https://requests.readthedocs.io/en/master/) +::: + +#### model.py + +在该文件中编写数据库模型。 diff --git a/archive/2.0.0a5/guide/creating-a-project.md b/archive/2.0.0a5/guide/creating-a-project.md new file mode 100644 index 00000000..74cdb24a --- /dev/null +++ b/archive/2.0.0a5/guide/creating-a-project.md @@ -0,0 +1,55 @@ +# 创建一个完整的项目 + +上一章中我们已经运行了一个最小的 NoneBot 实例,在这一章,我们将从零开始一个完整的项目。 + +## 目录结构 + +首先,我们可以使用 `nb-cli` 或者自行创建项目目录: + +```bash +pip install nonebot2[cli] +# pip install nb-cli +nb create +``` + +这将创建默认的目录结构 + + +:::vue +AweSome-Bot +├── `awesome_bot` _(**或是 src**)_ +│ └── `plugins` +├── `.env` _(**可选的**)_ +├── `.env.dev` _(**可选的**)_ +├── `.env.prod` _(**可选的**)_ +├── .gitignore +├── `bot.py` +├── docker-compose.yml +├── Dockerfile +├── `pyproject.toml` +└── README.md +::: + + +- `awesome_bot/plugins` 或 `src/plugins`: 用于存放编写的 bot 插件 +- `.env`, `.env.dev`, `.env.prod`: 各环境配置文件 +- `bot.py`: bot 入口文件 +- `pyproject.toml`: 项目依赖管理文件,默认使用 [poetry](https://python-poetry.org/) + +## 启动 Bot + +如果你使用 `nb-cli` + +```bash +nb run [--file=bot.py] [--app=app] +``` + +或者使用 + +```bash +python bot.py +``` + +:::tip 提示 +如果在 bot 入口文件内定义了 asgi server, `nb-cli` 将会为你启动**冷重载模式** +::: diff --git a/archive/2.0.0a5/guide/end-or-start.md b/archive/2.0.0a5/guide/end-or-start.md new file mode 100644 index 00000000..f1b0baa9 --- /dev/null +++ b/archive/2.0.0a5/guide/end-or-start.md @@ -0,0 +1,7 @@ +# 结语 + +至此,相信你已经能够写出一个基础的插件了,更多的用法将会在 进阶 部分进行介绍,这里给出几个小提示: + +- 请千万注意事件处理器的优先级设定 +- 在匹配规则中请勿使用耗时极长的函数 +- 同一个用户可以**跨群**(**私聊**)继续他的事件处理(除非做出权限限制,将在后续介绍) diff --git a/archive/2.0.0a5/guide/getting-started.md b/archive/2.0.0a5/guide/getting-started.md new file mode 100644 index 00000000..9c358ead --- /dev/null +++ b/archive/2.0.0a5/guide/getting-started.md @@ -0,0 +1,146 @@ +# 开始使用 + +一切都安装成功后,你就已经做好了进行简单配置以运行一个最小的 NoneBot 实例的准备。 + +## 最小实例 + +使用你最熟悉的编辑器或 IDE,创建一个名为 `bot.py` 的文件,内容如下: + +```python{3,4,7} +import nonebot + +nonebot.init() +nonebot.load_builtin_plugins() + +if __name__ == "__main__": + nonebot.run() +``` + +这几行高亮代码将依次: + +1. 使用默认配置初始化 NoneBot 包 +2. 加载 NoneBot 内置的插件 +3. 在地址 `127.0.0.1:8080` 运行 NoneBot + +在命令行使用如下命令即可运行这个 NoneBot 实例: + +```bash +python bot.py +``` + +运行后会产生如下日志: + +```default +09-14 21:02:00 [INFO] nonebot | Succeeded to import "nonebot.plugins.base" +09-14 21:02:00 [INFO] nonebot | Running NoneBot... +09-14 21:02:00 [INFO] uvicorn | Started server process [1234] +09-14 21:02:00 [INFO] uvicorn | Waiting for application startup. +09-14 21:02:00 [INFO] nonebot | Scheduler Started +09-14 21:02:00 [INFO] uvicorn | Application startup complete. +09-14 21:02:00 [INFO] uvicorn | Uvicorn running on http://127.0.0.1:8080 (Press CTRL+C to quit) +``` + +## 配置 QQ 协议端 + +单纯运行 NoneBot 实例并不会产生任何效果,因为此刻 QQ 这边还不知道 NoneBot 的存在,也就无法把消息发送给它,因此现在需要使用一个无头 QQ 来把消息等事件上报给 NoneBot。 + +目前支持的协议有: + +- [OneBot(CQHTTP)](https://github.com/howmanybots/onebot) + +QQ 协议端举例: + +- [Mirai](https://github.com/mamoe/mirai) + [cqhttp-mirai](https://github.com/yyuueexxiinngg/cqhttp-mirai) +- [cqhttp-mirai-embedded](https://github.com/yyuueexxiinngg/cqhttp-mirai/tree/embedded) +- [Mirai](https://github.com/mamoe/mirai) + [Mirai Native](https://github.com/iTXTech/mirai-native) + [CQHTTP](https://github.com/richardchien/coolq-http-api) +- [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) (基于 [MiraiGo](https://github.com/Mrs4s/MiraiGo)) +- [OICQ-http-api](https://github.com/takayama-lily/onebot) (基于 [OICQ](https://github.com/takayama-lily/oicq)) + +这里以 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 为例 + +1. 下载 go-cqhttp 对应平台的 release 文件 +2. 双击 exe 文件或者使用 `./go-cqhttp` 启动 +3. 生成默认配置文件并修改默认配置 + +```json{2,3,30-31} +{ + "uin": 你的QQ号, + "password": "你的密码", + "encrypt_password": false, + "password_encrypted": "", + "enable_db": true, + "access_token": "", + "relogin": { + "enabled": true, + "relogin_delay": 3, + "max_relogin_times": 0 + }, + "ignore_invalid_cqcode": false, + "force_fragmented": true, + "heartbeat_interval": 0, + "http_config": { + "enabled": false, + "host": "0.0.0.0", + "port": 5700, + "timeout": 0, + "post_urls": {} + }, + "ws_config": { + "enabled": false, + "host": "0.0.0.0", + "port": 6700 + }, + "ws_reverse_servers": [ + { + "enabled": true, + "reverse_url": "ws://127.0.0.1:8080/cqhttp/ws", + "reverse_api_url": "", + "reverse_event_url": "", + "reverse_reconnect_interval": 3000 + } + ], + "post_message_format": "string", + "debug": false, + "log_level": "" +} +``` + +其中 `ws://127.0.0.1:8080/cqhttp/ws` 中的 `127.0.0.1` 和 `8080` 应分别对应 nonebot 配置的 HOST 和 PORT + +## 历史性的第一次对话 + +一旦新的配置文件正确生效之后,NoneBot 所在的控制台(如果正在运行的话)应该会输出类似下面的内容(两条访问日志): + +```default +09-14 21:31:16 [INFO] uvicorn | ('127.0.0.1', 12345) - "WebSocket /cqhttp/ws" [accepted] +09-14 21:31:16 [INFO] nonebot | WebSocket Connection from CQHTTP Bot 你的QQ号 Accepted! +``` + +这表示 QQ 协议端已经成功地使用 CQHTTP 协议连接上了 NoneBot。 + +:::warning 注意 +如果到这一步你没有看到上面这样的成功日志,CQHTTP 的日志中在不断地重连或无反应,请注意检查配置中的 IP 和端口是否确实可以访问。比较常见的出错点包括: + +- NoneBot 监听 `0.0.0.0`,然后在 CQHTTP 配置中填了 `ws://0.0.0.0:8080/cqhttp/ws` +- 在 Docker 容器内运行 CQHTTP,并通过 `127.0.0.1` 访问宿主机上的 NoneBot +- 想从公网访问,但没有修改云服务商的安全组策略或系统防火墙 +- NoneBot 所监听的端口存在冲突,已被其它程序占用 +- 弄混了 NoneBot 的 `host`、`port` 参数与 CQHTTP 配置中的 `host`、`port` 参数 +- 使用了 `ws_reverse_api_url` 和 `ws_reverse_event_url` 而非 universal client +- `ws://` 错填为 `http://` +- CQHTTP 或 NoneBot 启动时遭到外星武器干扰 + +请尝试重启 CQHTTP、重启 NoneBot、更换端口、修改防火墙、重启系统、仔细阅读前面的文档及提示、更新 CQHTTP 和 NoneBot 到最新版本等方式来解决。 +::: + +现在,尝试向你的 QQ 机器人账号发送如下内容: + +```default +/echo 你好,世界 +``` + +到这里如果一切 OK,你应该会收到机器人给你回复了 `你好,世界`。这一历史性的对话标志着你已经成功地运行了一个 NoneBot 的最小实例,开始了编写更强大的 QQ 机器人的创意之旅! + + + + diff --git a/archive/2.0.0a5/guide/installation.md b/archive/2.0.0a5/guide/installation.md new file mode 100644 index 00000000..907630c9 --- /dev/null +++ b/archive/2.0.0a5/guide/installation.md @@ -0,0 +1,85 @@ +# 安装 + +## NoneBot + +:::warning 注意 +请确保你的 Python 版本 >= 3.7。 +::: + +请在安装 nonebot2 之前卸载 nonebot 1.x + +```bash +pip uninstall nonebot +pip install nonebot2 +``` + +如果你需要使用最新的(可能尚未发布的)特性,可以直接从 GitHub 仓库安装: + +```bash +# master +poetry add git+https://github.com/nonebot/nonebot2.git#master +# dev +poetry add git+https://github.com/nonebot/nonebot2.git#dev +``` + +或者克隆 Git 仓库后手动安装: + +```bash +git clone https://github.com/nonebot/nonebot2.git +cd nonebot2 +poetry install --no-dev # 推荐 +pip install . # 不推荐 +``` + +## 额外依赖 + +### APScheduler + +A task scheduling library for Python. + +可用于计划任务,后台执行任务等 + +```bash +pip install nonebot2[scheduler] +poetry add nonebot2[scheduler] +``` + +[View On GitHub](https://github.com/agronholm/apscheduler) + +### NoneBot-Test + +A test frontend for nonebot2. + +通过前端展示 nonebot 已加载的插件以及运行状态,同时可以用于模拟发送事件测试机器人 + +```bash +pip install nonebot2[test] +poetry add nonebot2[test] +``` + +[View On GitHub](https://github.com/nonebot/nonebot-test) + +### CLI + +CLI for nonebot2. + +一个多功能脚手架 + +```bash +pip install nonebot2[cli] +poetry add nonebot2[cli] +``` + +[View On GitHub](https://github.com/yanyongyu/nb-cli) + +### 我全都要 + +```bash +pip install nonebot2[full] +poetry add nonebot2[full] +``` + +```bash +pip install nonebot2[cli,scheduler] +poetry add nonebot2[cli,scheduler] +``` diff --git a/archive/2.0.0a5/guide/loading-a-plugin.md b/archive/2.0.0a5/guide/loading-a-plugin.md new file mode 100644 index 00000000..bb7db02d --- /dev/null +++ b/archive/2.0.0a5/guide/loading-a-plugin.md @@ -0,0 +1,95 @@ +# 加载插件 + +在 [创建一个完整的项目](creating-a-project) 一章节中,我们已经创建了插件目录 `awesome_bot/plugins`,现在我们在机器人入口文件中加载它。当然,你也可以单独加载一个插件。 + +## 加载内置插件 + +在 `bot.py` 文件中添加以下行: + +```python{5} +import nonebot + +nonebot.init() +# 加载 nonebot 内置插件 +nonebot.load_bulitin_plugins() + +app = nonebot.get_asgi() + +if __name__ == "__main__": + nonebot.run() +``` + +这将会加载 nonebot 内置的插件,它包含: + +- 命令 `say`:可由**superuser**使用,可以将消息内容由特殊纯文本转为富文本 +- 命令 `echo`:可由任何人使用,将消息原样返回 + +以上命令均需要指定机器人,即私聊、群聊内@机器人、群聊内称呼机器人昵称。参考 [Rule: to_me](../api/rule.md#to-me) + +## 加载插件目录 + +在 `bot.py` 文件中添加以下行: + +```python{5} +import nonebot + +nonebot.init() +# 加载插件目录,该目录下为各插件,以下划线开头的插件将不会被加载 +nonebot.load_plugins("awesome_bot/plugins") + +app = nonebot.get_asgi() + +if __name__ == "__main__": + nonebot.run() +``` + +:::tip 提示 +加载插件目录时,目录下以 `_` 下划线开头的插件将不会被加载! +::: + +:::warning 提示 +**插件不能存在相同名称!** +::: + +:::danger 警告 +插件间不应该存在过多的耦合,如果确实需要导入某个插件内的数据,可以使用如下两种方法: + +1. (推荐) `from plugin_name import xxx` 而非 `from awesome_bot.plugins.plugin_name import xxx` +2. 在需要导入其他插件的文件中添加 `__package__ = "plugins"; from .plugin_name import xxx` (将共同的上层目录设定为父包后使用相对导入) + +具体可以参考:[nonebot/nonebot2#32](https://github.com/nonebot/nonebot2/issues/32) +::: + +## 加载单个插件 + +在 `bot.py` 文件中添加以下行: + +```python{5,7} +import nonebot + +nonebot.init() +# 加载一个 pip 安装的插件 +nonebot.load_plugin("nonebot_plugin_status") +# 加载本地的单独插件 +nonebot.load_plugin("awesome_bot.plugins.xxx") + +app = nonebot.get_asgi() + +if __name__ == "__main__": + nonebot.run() +``` + +## 子插件(嵌套插件) + + + +~~待填坑~~ + +## 运行结果 + +尝试运行 `nb run` 或者 `python bot.py`,可以看到日志输出了类似如下内容: + +```plain +09-19 21:51:59 [INFO] nonebot | Succeeded to import "nonebot.plugins.base" +09-19 21:51:59 [INFO] nonebot | Succeeded to import "plugin_in_folder" +``` diff --git a/archive/2.0.0a5/sidebar.config.json b/archive/2.0.0a5/sidebar.config.json new file mode 100644 index 00000000..94ec57ab --- /dev/null +++ b/archive/2.0.0a5/sidebar.config.json @@ -0,0 +1,131 @@ +{ + "sidebar": {}, + "locales": { + "/": { + "label": "简体中文", + "selectText": "Languages", + "editLinkText": "在 GitHub 上编辑此页", + "lastUpdated": "上次更新", + "nav": [ + { + "text": "主页", + "link": "/" + }, + { + "text": "指南", + "link": "/guide/" + }, + { + "text": "API", + "link": "/api/" + }, + { + "text": "插件广场", + "link": "/plugin-store" + } + ], + "sidebarDepth": 2, + "sidebar": { + "/guide/": [ + { + "title": "开始", + "collapsable": false, + "sidebar": "auto", + "children": [ + "", + "installation", + "getting-started", + "creating-a-project", + "basic-configuration" + ] + }, + { + "title": "编写插件", + "collapsable": false, + "sidebar": "auto", + "children": [ + "loading-a-plugin", + "creating-a-plugin", + "creating-a-matcher", + "creating-a-handler", + "end-or-start" + ] + } + ], + "/api/": [ + { + "title": "NoneBot Api Reference", + "path": "", + "collapsable": false, + "children": [ + { + "title": "nonebot 模块", + "path": "nonebot" + }, + { + "title": "nonebot.config 模块", + "path": "config" + }, + { + "title": "nonebot.plugin 模块", + "path": "plugin" + }, + { + "title": "nonebot.message 模块", + "path": "message" + }, + { + "title": "nonebot.matcher 模块", + "path": "matcher" + }, + { + "title": "nonebot.rule 模块", + "path": "rule" + }, + { + "title": "nonebot.permission 模块", + "path": "permission" + }, + { + "title": "nonebot.sched 模块", + "path": "sched" + }, + { + "title": "nonebot.log 模块", + "path": "log" + }, + { + "title": "nonebot.utils 模块", + "path": "utils" + }, + { + "title": "nonebot.typing 模块", + "path": "typing" + }, + { + "title": "nonebot.exception 模块", + "path": "exception" + }, + { + "title": "nonebot.drivers 模块", + "path": "drivers/" + }, + { + "title": "nonebot.drivers.fastapi 模块", + "path": "drivers/fastapi" + }, + { + "title": "nonebot.adapters 模块", + "path": "adapters/" + }, + { + "title": "nonebot.adapters.cqhttp 模块", + "path": "adapters/cqhttp" + } + ] + } + ] + } + } + } +} \ No newline at end of file diff --git a/docs/.vuepress/versions.json b/docs/.vuepress/versions.json index 56a1527a..aa00d565 100644 --- a/docs/.vuepress/versions.json +++ b/docs/.vuepress/versions.json @@ -1,3 +1,4 @@ [ + "2.0.0a5", "2.0.0a4" ] \ No newline at end of file From 42e714ee7d4e800792f34b5d1a6aa9739860144b Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Tue, 17 Nov 2020 17:35:53 +0800 Subject: [PATCH 28/34] :memo: add sub plugin guide --- docs/guide/loading-a-plugin.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/docs/guide/loading-a-plugin.md b/docs/guide/loading-a-plugin.md index bb7db02d..b648b4b6 100644 --- a/docs/guide/loading-a-plugin.md +++ b/docs/guide/loading-a-plugin.md @@ -83,7 +83,39 @@ if __name__ == "__main__": -~~待填坑~~ +在插件中同样可以加载子插件,例如如下插件目录结构: + + +:::vue +foo_plugin +├── `plugins` +│ ├── `sub_plugin1` +│ │ └── \_\_init\_\_.py +│ └── `sub_plugin2.py` +├── `__init__.py` +└── config.py +::: + + +在插件目录下的 `__init__.py` 中添加如下代码: + +```python +from pathlib import Path + +import nonebot + +# store all subplugins +_sub_plugins = set() +# load sub plugins +_sub_plugins |= nonebot.load_plugins( + str((Path(__file__).parent / "plugins").resolve())) +``` + +插件将会被加载并存储于 `_sub_plugins` 中。 + +:::tip 提示 +如果在父插件中需要定义事件响应器,应在**子插件被加载后**进行定义 +::: ## 运行结果 From e6268f319d02ece05498b350d5cd4e97e456ab4d Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Wed, 18 Nov 2020 08:48:23 +0800 Subject: [PATCH 29/34] :memo: add advanced and changelog --- docs/.vuepress/config.js | 17 ++++++++++++++++- docs/advanced/README.md | 7 +++++++ docs/advanced/permission.md | 1 + docs/advanced/runtime-hook.md | 1 + docs/advanced/scheduler.md | 1 + docs/guide/README.md | 6 ------ docs/guide/end-or-start.md | 2 ++ pages/changelog.md | 12 ++++++++++++ 8 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 docs/advanced/README.md create mode 100644 docs/advanced/permission.md create mode 100644 docs/advanced/runtime-hook.md create mode 100644 docs/advanced/scheduler.md create mode 100644 pages/changelog.md diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 009bc113..8faed7a5 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -79,8 +79,10 @@ module.exports = context => ({ nav: [ { text: "主页", link: "/" }, { text: "指南", link: "/guide/" }, + { text: "进阶", link: "/advanced/" }, { text: "API", link: "/api/" }, - { text: "插件广场", link: "/plugin-store" } + { text: "插件广场", link: "/plugin-store" }, + { text: "更新日志", link: "/changelog" } ], sidebarDepth: 2, sidebar: { @@ -110,6 +112,19 @@ module.exports = context => ({ ] } ], + "/advanced/": [ + { + title: "进阶", + collapsable: false, + sidebar: "auto", + children: [ + "", + "scheduler", + "permission", + "runtime-hook" + ] + } + ], "/api/": [ { title: "NoneBot Api Reference", diff --git a/docs/advanced/README.md b/docs/advanced/README.md new file mode 100644 index 00000000..92c6af3e --- /dev/null +++ b/docs/advanced/README.md @@ -0,0 +1,7 @@ +# 深入 + +## 它如何工作? + + + +~~未填坑~~ diff --git a/docs/advanced/permission.md b/docs/advanced/permission.md new file mode 100644 index 00000000..30e20002 --- /dev/null +++ b/docs/advanced/permission.md @@ -0,0 +1 @@ +# 权限控制 \ No newline at end of file diff --git a/docs/advanced/runtime-hook.md b/docs/advanced/runtime-hook.md new file mode 100644 index 00000000..ed3968a4 --- /dev/null +++ b/docs/advanced/runtime-hook.md @@ -0,0 +1 @@ +# 运行时插槽 \ No newline at end of file diff --git a/docs/advanced/scheduler.md b/docs/advanced/scheduler.md new file mode 100644 index 00000000..99d906d3 --- /dev/null +++ b/docs/advanced/scheduler.md @@ -0,0 +1 @@ +# 定时任务 \ No newline at end of file diff --git a/docs/guide/README.md b/docs/guide/README.md index 43b7f325..cacb58ac 100644 --- a/docs/guide/README.md +++ b/docs/guide/README.md @@ -18,12 +18,6 @@ NoneBot2 是一个可扩展的 Python 异步机器人框架,它会对机器人 需要注意的是,NoneBot 仅支持 Python 3.7+ 及 CQHTTP(OneBot) 插件 v11+。 -## 它如何工作? - - - -~~未填坑~~ - ## 特色 - 提供直观的测试前端 diff --git a/docs/guide/end-or-start.md b/docs/guide/end-or-start.md index f1b0baa9..11780114 100644 --- a/docs/guide/end-or-start.md +++ b/docs/guide/end-or-start.md @@ -5,3 +5,5 @@ - 请千万注意事件处理器的优先级设定 - 在匹配规则中请勿使用耗时极长的函数 - 同一个用户可以**跨群**(**私聊**)继续他的事件处理(除非做出权限限制,将在后续介绍) + +如果你还不能满足,前往 [进阶](../advanced/README.md) 获得更多的功能信息。 diff --git a/pages/changelog.md b/pages/changelog.md new file mode 100644 index 00000000..1d8a9cac --- /dev/null +++ b/pages/changelog.md @@ -0,0 +1,12 @@ +--- +sidebar: auto +--- + +# 更新日志 + +## v2.0.0a6 + +## v2.0.0a5 + +- 更新插件指南文档 +- 修复临时事件响应器运行后删除造成的多次响应问题 From d0c19d09a3ebb4a2f3d32adb2a0d16a9a149fbe4 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Wed, 18 Nov 2020 11:24:11 +0800 Subject: [PATCH 30/34] :beers: publish Haruka Bot --- docs/.vuepress/public/plugins.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/.vuepress/public/plugins.json b/docs/.vuepress/public/plugins.json index 4febd274..007aec4c 100644 --- a/docs/.vuepress/public/plugins.json +++ b/docs/.vuepress/public/plugins.json @@ -6,5 +6,13 @@ "desc": "通过戳一戳获取服务器状态", "author": "yanyongyu", "repo": "cscs181/QQ-GitHub-Bot/tree/master/src/plugins/nonebot_plugin_status" + }, + { + "id": "haruka_bot", + "link": "haruka-bot", + "name": "HarukaBot", + "desc": "将B站UP主的动态和直播信息推送至QQ", + "author": "SK-415", + "repo": "SK-415/HarukaBot" } ] From aaa860de334521375962d28deec8dc813251d32a Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Wed, 18 Nov 2020 12:02:26 +0800 Subject: [PATCH 31/34] :ambulance: fix full extra apscheduler error --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index afb696c4..1b240ee4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,7 @@ sphinx-markdown-builder = { git = "https://github.com/nonebot/sphinx-markdown-bu cli = ["nb-cli"] test = ["nonebot-test"] scheduler = ["apscheduler"] -full = ["nb-cli", "nonebot-test", "scheduler"] +full = ["nb-cli", "nonebot-test", "apscheduler"] [[tool.poetry.source]] name = "aliyun" From 31123b05730d07a4e9f7231a0c27a20befca593e Mon Sep 17 00:00:00 2001 From: hemengyang Date: Wed, 18 Nov 2020 14:43:12 +0800 Subject: [PATCH 32/34] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=98=BB=E6=AD=A2?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E4=BC=A0=E9=80=92=E4=BF=A1=E5=8F=B7=E6=97=A0?= =?UTF-8?q?=E6=95=88=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot/message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nonebot/message.py b/nonebot/message.py index af0849a5..90af9a96 100644 --- a/nonebot/message.py +++ b/nonebot/message.py @@ -244,7 +244,7 @@ async def handle_event(bot: Bot, event: Event): results = await asyncio.gather(*pending_tasks, return_exceptions=True) for result in results: - if result is StopPropagation: + if isinstance(result, StopPropagation): if not break_flag: break_flag = True logger.debug("Stop event propagation") From 0a6008ddc0a2007b8194c6c5228ad75f00c3bc9a Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Wed, 18 Nov 2020 15:05:03 +0800 Subject: [PATCH 33/34] :rewind: update changelog --- pages/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pages/changelog.md b/pages/changelog.md index 1d8a9cac..ef8bad8f 100644 --- a/pages/changelog.md +++ b/pages/changelog.md @@ -6,6 +6,8 @@ sidebar: auto ## v2.0.0a6 +- 修复 block 失效问题 (hotfix) + ## v2.0.0a5 - 更新插件指南文档 From 39f508bbdf1640402e2b775eb07449a2a91bb881 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Wed, 18 Nov 2020 15:12:48 +0800 Subject: [PATCH 34/34] :bookmark: Pre Release 2.0.0a6 --- archive/{2.0.0a5 => 2.0.0a6}/README.md | 0 archive/2.0.0a6/advanced/README.md | 7 ++ archive/2.0.0a6/advanced/permission.md | 1 + archive/2.0.0a6/advanced/runtime-hook.md | 1 + archive/2.0.0a6/advanced/scheduler.md | 1 + archive/{2.0.0a5 => 2.0.0a6}/api/README.md | 0 .../api/adapters/README.md | 0 .../api/adapters/cqhttp.md | 0 archive/{2.0.0a5 => 2.0.0a6}/api/config.md | 0 .../api/drivers/README.md | 0 .../api/drivers/fastapi.md | 0 archive/{2.0.0a5 => 2.0.0a6}/api/exception.md | 0 archive/{2.0.0a5 => 2.0.0a6}/api/log.md | 0 archive/{2.0.0a5 => 2.0.0a6}/api/matcher.md | 0 archive/{2.0.0a5 => 2.0.0a6}/api/message.md | 0 archive/{2.0.0a5 => 2.0.0a6}/api/nonebot.md | 0 .../{2.0.0a5 => 2.0.0a6}/api/permission.md | 0 archive/{2.0.0a5 => 2.0.0a6}/api/plugin.md | 0 archive/{2.0.0a5 => 2.0.0a6}/api/rule.md | 0 archive/{2.0.0a5 => 2.0.0a6}/api/sched.md | 0 archive/{2.0.0a5 => 2.0.0a6}/api/typing.md | 0 archive/{2.0.0a5 => 2.0.0a6}/api/utils.md | 0 archive/{2.0.0a5 => 2.0.0a6}/guide/README.md | 6 -- .../guide/basic-configuration.md | 0 .../guide/creating-a-handler.md | 0 .../guide/creating-a-matcher.md | 0 .../guide/creating-a-plugin.md | 0 .../guide/creating-a-project.md | 0 .../guide/end-or-start.md | 2 + .../guide/getting-started.md | 0 .../guide/installation.md | 0 .../guide/loading-a-plugin.md | 34 +++++- .../{2.0.0a5 => 2.0.0a6}/sidebar.config.json | 21 ++++ docs/.vuepress/versions.json | 2 +- poetry.lock | 100 +++++++++--------- pyproject.toml | 6 +- 36 files changed, 120 insertions(+), 61 deletions(-) rename archive/{2.0.0a5 => 2.0.0a6}/README.md (100%) create mode 100644 archive/2.0.0a6/advanced/README.md create mode 100644 archive/2.0.0a6/advanced/permission.md create mode 100644 archive/2.0.0a6/advanced/runtime-hook.md create mode 100644 archive/2.0.0a6/advanced/scheduler.md rename archive/{2.0.0a5 => 2.0.0a6}/api/README.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/api/adapters/README.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/api/adapters/cqhttp.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/api/config.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/api/drivers/README.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/api/drivers/fastapi.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/api/exception.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/api/log.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/api/matcher.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/api/message.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/api/nonebot.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/api/permission.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/api/plugin.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/api/rule.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/api/sched.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/api/typing.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/api/utils.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/guide/README.md (96%) rename archive/{2.0.0a5 => 2.0.0a6}/guide/basic-configuration.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/guide/creating-a-handler.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/guide/creating-a-matcher.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/guide/creating-a-plugin.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/guide/creating-a-project.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/guide/end-or-start.md (79%) rename archive/{2.0.0a5 => 2.0.0a6}/guide/getting-started.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/guide/installation.md (100%) rename archive/{2.0.0a5 => 2.0.0a6}/guide/loading-a-plugin.md (76%) rename archive/{2.0.0a5 => 2.0.0a6}/sidebar.config.json (87%) diff --git a/archive/2.0.0a5/README.md b/archive/2.0.0a6/README.md similarity index 100% rename from archive/2.0.0a5/README.md rename to archive/2.0.0a6/README.md diff --git a/archive/2.0.0a6/advanced/README.md b/archive/2.0.0a6/advanced/README.md new file mode 100644 index 00000000..92c6af3e --- /dev/null +++ b/archive/2.0.0a6/advanced/README.md @@ -0,0 +1,7 @@ +# 深入 + +## 它如何工作? + + + +~~未填坑~~ diff --git a/archive/2.0.0a6/advanced/permission.md b/archive/2.0.0a6/advanced/permission.md new file mode 100644 index 00000000..30e20002 --- /dev/null +++ b/archive/2.0.0a6/advanced/permission.md @@ -0,0 +1 @@ +# 权限控制 \ No newline at end of file diff --git a/archive/2.0.0a6/advanced/runtime-hook.md b/archive/2.0.0a6/advanced/runtime-hook.md new file mode 100644 index 00000000..ed3968a4 --- /dev/null +++ b/archive/2.0.0a6/advanced/runtime-hook.md @@ -0,0 +1 @@ +# 运行时插槽 \ No newline at end of file diff --git a/archive/2.0.0a6/advanced/scheduler.md b/archive/2.0.0a6/advanced/scheduler.md new file mode 100644 index 00000000..99d906d3 --- /dev/null +++ b/archive/2.0.0a6/advanced/scheduler.md @@ -0,0 +1 @@ +# 定时任务 \ No newline at end of file diff --git a/archive/2.0.0a5/api/README.md b/archive/2.0.0a6/api/README.md similarity index 100% rename from archive/2.0.0a5/api/README.md rename to archive/2.0.0a6/api/README.md diff --git a/archive/2.0.0a5/api/adapters/README.md b/archive/2.0.0a6/api/adapters/README.md similarity index 100% rename from archive/2.0.0a5/api/adapters/README.md rename to archive/2.0.0a6/api/adapters/README.md diff --git a/archive/2.0.0a5/api/adapters/cqhttp.md b/archive/2.0.0a6/api/adapters/cqhttp.md similarity index 100% rename from archive/2.0.0a5/api/adapters/cqhttp.md rename to archive/2.0.0a6/api/adapters/cqhttp.md diff --git a/archive/2.0.0a5/api/config.md b/archive/2.0.0a6/api/config.md similarity index 100% rename from archive/2.0.0a5/api/config.md rename to archive/2.0.0a6/api/config.md diff --git a/archive/2.0.0a5/api/drivers/README.md b/archive/2.0.0a6/api/drivers/README.md similarity index 100% rename from archive/2.0.0a5/api/drivers/README.md rename to archive/2.0.0a6/api/drivers/README.md diff --git a/archive/2.0.0a5/api/drivers/fastapi.md b/archive/2.0.0a6/api/drivers/fastapi.md similarity index 100% rename from archive/2.0.0a5/api/drivers/fastapi.md rename to archive/2.0.0a6/api/drivers/fastapi.md diff --git a/archive/2.0.0a5/api/exception.md b/archive/2.0.0a6/api/exception.md similarity index 100% rename from archive/2.0.0a5/api/exception.md rename to archive/2.0.0a6/api/exception.md diff --git a/archive/2.0.0a5/api/log.md b/archive/2.0.0a6/api/log.md similarity index 100% rename from archive/2.0.0a5/api/log.md rename to archive/2.0.0a6/api/log.md diff --git a/archive/2.0.0a5/api/matcher.md b/archive/2.0.0a6/api/matcher.md similarity index 100% rename from archive/2.0.0a5/api/matcher.md rename to archive/2.0.0a6/api/matcher.md diff --git a/archive/2.0.0a5/api/message.md b/archive/2.0.0a6/api/message.md similarity index 100% rename from archive/2.0.0a5/api/message.md rename to archive/2.0.0a6/api/message.md diff --git a/archive/2.0.0a5/api/nonebot.md b/archive/2.0.0a6/api/nonebot.md similarity index 100% rename from archive/2.0.0a5/api/nonebot.md rename to archive/2.0.0a6/api/nonebot.md diff --git a/archive/2.0.0a5/api/permission.md b/archive/2.0.0a6/api/permission.md similarity index 100% rename from archive/2.0.0a5/api/permission.md rename to archive/2.0.0a6/api/permission.md diff --git a/archive/2.0.0a5/api/plugin.md b/archive/2.0.0a6/api/plugin.md similarity index 100% rename from archive/2.0.0a5/api/plugin.md rename to archive/2.0.0a6/api/plugin.md diff --git a/archive/2.0.0a5/api/rule.md b/archive/2.0.0a6/api/rule.md similarity index 100% rename from archive/2.0.0a5/api/rule.md rename to archive/2.0.0a6/api/rule.md diff --git a/archive/2.0.0a5/api/sched.md b/archive/2.0.0a6/api/sched.md similarity index 100% rename from archive/2.0.0a5/api/sched.md rename to archive/2.0.0a6/api/sched.md diff --git a/archive/2.0.0a5/api/typing.md b/archive/2.0.0a6/api/typing.md similarity index 100% rename from archive/2.0.0a5/api/typing.md rename to archive/2.0.0a6/api/typing.md diff --git a/archive/2.0.0a5/api/utils.md b/archive/2.0.0a6/api/utils.md similarity index 100% rename from archive/2.0.0a5/api/utils.md rename to archive/2.0.0a6/api/utils.md diff --git a/archive/2.0.0a5/guide/README.md b/archive/2.0.0a6/guide/README.md similarity index 96% rename from archive/2.0.0a5/guide/README.md rename to archive/2.0.0a6/guide/README.md index 43b7f325..cacb58ac 100644 --- a/archive/2.0.0a5/guide/README.md +++ b/archive/2.0.0a6/guide/README.md @@ -18,12 +18,6 @@ NoneBot2 是一个可扩展的 Python 异步机器人框架,它会对机器人 需要注意的是,NoneBot 仅支持 Python 3.7+ 及 CQHTTP(OneBot) 插件 v11+。 -## 它如何工作? - - - -~~未填坑~~ - ## 特色 - 提供直观的测试前端 diff --git a/archive/2.0.0a5/guide/basic-configuration.md b/archive/2.0.0a6/guide/basic-configuration.md similarity index 100% rename from archive/2.0.0a5/guide/basic-configuration.md rename to archive/2.0.0a6/guide/basic-configuration.md diff --git a/archive/2.0.0a5/guide/creating-a-handler.md b/archive/2.0.0a6/guide/creating-a-handler.md similarity index 100% rename from archive/2.0.0a5/guide/creating-a-handler.md rename to archive/2.0.0a6/guide/creating-a-handler.md diff --git a/archive/2.0.0a5/guide/creating-a-matcher.md b/archive/2.0.0a6/guide/creating-a-matcher.md similarity index 100% rename from archive/2.0.0a5/guide/creating-a-matcher.md rename to archive/2.0.0a6/guide/creating-a-matcher.md diff --git a/archive/2.0.0a5/guide/creating-a-plugin.md b/archive/2.0.0a6/guide/creating-a-plugin.md similarity index 100% rename from archive/2.0.0a5/guide/creating-a-plugin.md rename to archive/2.0.0a6/guide/creating-a-plugin.md diff --git a/archive/2.0.0a5/guide/creating-a-project.md b/archive/2.0.0a6/guide/creating-a-project.md similarity index 100% rename from archive/2.0.0a5/guide/creating-a-project.md rename to archive/2.0.0a6/guide/creating-a-project.md diff --git a/archive/2.0.0a5/guide/end-or-start.md b/archive/2.0.0a6/guide/end-or-start.md similarity index 79% rename from archive/2.0.0a5/guide/end-or-start.md rename to archive/2.0.0a6/guide/end-or-start.md index f1b0baa9..11780114 100644 --- a/archive/2.0.0a5/guide/end-or-start.md +++ b/archive/2.0.0a6/guide/end-or-start.md @@ -5,3 +5,5 @@ - 请千万注意事件处理器的优先级设定 - 在匹配规则中请勿使用耗时极长的函数 - 同一个用户可以**跨群**(**私聊**)继续他的事件处理(除非做出权限限制,将在后续介绍) + +如果你还不能满足,前往 [进阶](../advanced/README.md) 获得更多的功能信息。 diff --git a/archive/2.0.0a5/guide/getting-started.md b/archive/2.0.0a6/guide/getting-started.md similarity index 100% rename from archive/2.0.0a5/guide/getting-started.md rename to archive/2.0.0a6/guide/getting-started.md diff --git a/archive/2.0.0a5/guide/installation.md b/archive/2.0.0a6/guide/installation.md similarity index 100% rename from archive/2.0.0a5/guide/installation.md rename to archive/2.0.0a6/guide/installation.md diff --git a/archive/2.0.0a5/guide/loading-a-plugin.md b/archive/2.0.0a6/guide/loading-a-plugin.md similarity index 76% rename from archive/2.0.0a5/guide/loading-a-plugin.md rename to archive/2.0.0a6/guide/loading-a-plugin.md index bb7db02d..b648b4b6 100644 --- a/archive/2.0.0a5/guide/loading-a-plugin.md +++ b/archive/2.0.0a6/guide/loading-a-plugin.md @@ -83,7 +83,39 @@ if __name__ == "__main__": -~~待填坑~~ +在插件中同样可以加载子插件,例如如下插件目录结构: + + +:::vue +foo_plugin +├── `plugins` +│ ├── `sub_plugin1` +│ │ └── \_\_init\_\_.py +│ └── `sub_plugin2.py` +├── `__init__.py` +└── config.py +::: + + +在插件目录下的 `__init__.py` 中添加如下代码: + +```python +from pathlib import Path + +import nonebot + +# store all subplugins +_sub_plugins = set() +# load sub plugins +_sub_plugins |= nonebot.load_plugins( + str((Path(__file__).parent / "plugins").resolve())) +``` + +插件将会被加载并存储于 `_sub_plugins` 中。 + +:::tip 提示 +如果在父插件中需要定义事件响应器,应在**子插件被加载后**进行定义 +::: ## 运行结果 diff --git a/archive/2.0.0a5/sidebar.config.json b/archive/2.0.0a6/sidebar.config.json similarity index 87% rename from archive/2.0.0a5/sidebar.config.json rename to archive/2.0.0a6/sidebar.config.json index 94ec57ab..bee7b291 100644 --- a/archive/2.0.0a5/sidebar.config.json +++ b/archive/2.0.0a6/sidebar.config.json @@ -15,6 +15,10 @@ "text": "指南", "link": "/guide/" }, + { + "text": "进阶", + "link": "/advanced/" + }, { "text": "API", "link": "/api/" @@ -22,6 +26,10 @@ { "text": "插件广场", "link": "/plugin-store" + }, + { + "text": "更新日志", + "link": "/changelog" } ], "sidebarDepth": 2, @@ -52,6 +60,19 @@ ] } ], + "/advanced/": [ + { + "title": "进阶", + "collapsable": false, + "sidebar": "auto", + "children": [ + "", + "scheduler", + "permission", + "runtime-hook" + ] + } + ], "/api/": [ { "title": "NoneBot Api Reference", diff --git a/docs/.vuepress/versions.json b/docs/.vuepress/versions.json index aa00d565..b2845794 100644 --- a/docs/.vuepress/versions.json +++ b/docs/.vuepress/versions.json @@ -1,4 +1,4 @@ [ - "2.0.0a5", + "2.0.0a6", "2.0.0a4" ] \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 9e3185f4..a4d8902d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1026,7 +1026,7 @@ reference = "aliyun" [[package]] name = "regex" -version = "2020.11.11" +version = "2020.11.13" description = "Alternative regular expression module, to replace re." category = "main" optional = true @@ -1120,7 +1120,7 @@ reference = "aliyun" [[package]] name = "sphinx" -version = "3.3.0" +version = "3.3.1" description = "Python documentation generator" category = "dev" optional = false @@ -1366,7 +1366,7 @@ reference = "aliyun" [[package]] name = "urllib3" -version = "1.26.1" +version = "1.26.2" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false @@ -1508,14 +1508,14 @@ reference = "aliyun" [extras] cli = ["nb-cli"] -full = ["nb-cli", "nonebot-test"] +full = ["nb-cli", "nonebot-test", "apscheduler"] scheduler = ["apscheduler"] test = ["nonebot-test"] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "3760d7d6c8119c6fa29e171cabbecc5e705d2bb3faff82f7211a19e27925abfe" +content-hash = "1d1ddae66f1495be07658ec5a004c4819445f785317c556030c9edb05612917e" [metadata.files] aiofiles = [ @@ -1909,47 +1909,47 @@ pyyaml = [ {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] regex = [ - {file = "regex-2020.11.11-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:dd7bee615680d940dd44ac0a479f2bc5f73d6ca63a5915cd8d30739c14ca522c"}, - {file = "regex-2020.11.11-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3002ee2d4e8bbe4656237627203d8290a562d1fc1962deee470905ab63570345"}, - {file = "regex-2020.11.11-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:064d2fc83ab4ee0055fcc1ef38ec60e505742850a40061f854ac64cb3d8d6dd3"}, - {file = "regex-2020.11.11-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:83a390a653c13be1ab26287240df1fd9324ca8a0d31b603fa57cd7d9520648fa"}, - {file = "regex-2020.11.11-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:412969d58ecd4f576510ec88bcb7602e9e582bbef78859ed8c9ca4de4f9e891c"}, - {file = "regex-2020.11.11-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:ccfea4911ac28a8f744096bce1559e0bd86b09a53c8a9d5856ca8e1f5f4de1f5"}, - {file = "regex-2020.11.11-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:cefcdb2ac3b67fd9f7244820ce1965c8cf352366199cc1358d67c6cc3c5c8bbc"}, - {file = "regex-2020.11.11-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9e8b3187f6beea8e56cb4b33c35049cbe376cf69aefaee5bc035309d88c98ca5"}, - {file = "regex-2020.11.11-cp36-cp36m-win32.whl", hash = "sha256:787e44e5f4fd027dd90b5ee0240b05dc1752cb43c2903617f25baa495fe551e9"}, - {file = "regex-2020.11.11-cp36-cp36m-win_amd64.whl", hash = "sha256:a9f76d9122359b09e38f27cd9c41729169171cf0fd73ec5b22cc4628f9e486ca"}, - {file = "regex-2020.11.11-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6d128368def4b0cd95c0fc9d99a89ae73c083b25e67f27a410830e30f9df0edc"}, - {file = "regex-2020.11.11-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:df50ba964812606663ca9d23d374036bc5ae3d71e86168409cdd84ca7948d8a3"}, - {file = "regex-2020.11.11-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d1e57c16c4840f1c3543507742e99b8398609474a0e6a6925476914479de3488"}, - {file = "regex-2020.11.11-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6e50b3b417ab2fd67bfa6235f0df4782fe2ff8be83f0c4435e1dc43d25052ee8"}, - {file = "regex-2020.11.11-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bb17a7fe9c47167337009ce18cd6e6b3edf3ca0063bf6bed6ce02515129c016a"}, - {file = "regex-2020.11.11-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:826d0119f14f9a9ce25999a13ed5922c785b50e469800f6e5a6721318650ef49"}, - {file = "regex-2020.11.11-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:8cc3717146ce4040419639cf45455663a002a554806ddac46304acc5bd41dae2"}, - {file = "regex-2020.11.11-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:86ad88c7c2512094a85b0a01ce053bab1e28eafb8f3868bb8c22f4903e33f147"}, - {file = "regex-2020.11.11-cp37-cp37m-win32.whl", hash = "sha256:e03867f3baf64ecab47dfc9ddb58afc67acb6a0f80f6cf8ff9fa82962ec4d1cd"}, - {file = "regex-2020.11.11-cp37-cp37m-win_amd64.whl", hash = "sha256:56d1e298bb6482d0466399a6383181bf2627c37ad414e205b3ce0f85aa140be7"}, - {file = "regex-2020.11.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:19ac2bf0048a2f4d460ee20647e84ca160512a7ee8af844dc9207720778470f1"}, - {file = "regex-2020.11.11-cp38-cp38-manylinux1_i686.whl", hash = "sha256:84ab584dcb5e81815040d86148805a808acb0bee303d19638fe2f9488d704bc1"}, - {file = "regex-2020.11.11-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:4159ecf20dffea07f4a7241b2a236f90eb622c7e8caab9f43caba5f27ca37284"}, - {file = "regex-2020.11.11-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:8060be04baec546fe3afa6975d2998e15d1b655d7255f0e6b0ed3f482cccc218"}, - {file = "regex-2020.11.11-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:cdb98be55db1b94c950822cbc10d3d768f01e184365851ebb42cd377486ced7b"}, - {file = "regex-2020.11.11-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:11d9100bd874ce8b2a037db9150e732cd768359fc25fe5f77973208aa24eb13e"}, - {file = "regex-2020.11.11-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:0951c78fa4cb26d1278a4b3784fcf973fc97ec39c07483328a74b034b0cc569c"}, - {file = "regex-2020.11.11-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:c8b1ad791debd67221fb1266f8d09730ae927acacb32d0dad9fd07a7d341a28f"}, - {file = "regex-2020.11.11-cp38-cp38-win32.whl", hash = "sha256:beae9db1545f8116cfc9301a9601e9c975bb56ca22a38ac0fe06a72c3460f31a"}, - {file = "regex-2020.11.11-cp38-cp38-win_amd64.whl", hash = "sha256:48e94218f06317b6d32feb4ecff8b6025695450009bcb3291fb23daf79689431"}, - {file = "regex-2020.11.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c67fd5f3ad81f8301184354014e8e7510ab77e0c7e450a427d77f28ae8effbef"}, - {file = "regex-2020.11.11-cp39-cp39-manylinux1_i686.whl", hash = "sha256:e7cdd5ee8053c82607432b7ebad37e2ece54548fef2b254f7bce6f7831904586"}, - {file = "regex-2020.11.11-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:394b5be4fa72354a78763b317f82997ad881896dd4a860e429a6fa74afaacb07"}, - {file = "regex-2020.11.11-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:3b46a4c73ec1f25361147a7a0fd86084f3627dc78d09bcbe14e70db12683efec"}, - {file = "regex-2020.11.11-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:267d1b13f863e664150948ce2a9ed4927bf4ac7a068780f1ee8af83352aa17a2"}, - {file = "regex-2020.11.11-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:68267a7a5fb0bd9676b86f967143b6a6ecefb3eed4042ecc9e7f0e014aef8f74"}, - {file = "regex-2020.11.11-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:e899b69dd5d26655cb454835ea2fceb18832c9ee9c4fb45dc4cf8a6089d35312"}, - {file = "regex-2020.11.11-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:396411bb5a7849aeda9c49873b8295919fdc118c50b57122b09cb2097047c118"}, - {file = "regex-2020.11.11-cp39-cp39-win32.whl", hash = "sha256:32f8714c4bcc4b0d2aa259b1647e3c5b6cfe2e923c6c124234a5e03408224227"}, - {file = "regex-2020.11.11-cp39-cp39-win_amd64.whl", hash = "sha256:bf02ab95ff5261ba108725dbd795bf6395eaac1b8468b41472d82d35b12b0295"}, - {file = "regex-2020.11.11.tar.gz", hash = "sha256:0a235841237d4487329bcabcb5b902858f7967f5e684e08e968367f25b2c3d37"}, + {file = "regex-2020.11.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6"}, + {file = "regex-2020.11.13-cp36-cp36m-win32.whl", hash = "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e"}, + {file = "regex-2020.11.13-cp36-cp36m-win_amd64.whl", hash = "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884"}, + {file = "regex-2020.11.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538"}, + {file = "regex-2020.11.13-cp37-cp37m-win32.whl", hash = "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4"}, + {file = "regex-2020.11.13-cp37-cp37m-win_amd64.whl", hash = "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444"}, + {file = "regex-2020.11.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b"}, + {file = "regex-2020.11.13-cp38-cp38-win32.whl", hash = "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c"}, + {file = "regex-2020.11.13-cp38-cp38-win_amd64.whl", hash = "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683"}, + {file = "regex-2020.11.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c"}, + {file = "regex-2020.11.13-cp39-cp39-win32.whl", hash = "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"}, + {file = "regex-2020.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d"}, + {file = "regex-2020.11.13.tar.gz", hash = "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562"}, ] requests = [ {file = "requests-2.25.0-py2.py3-none-any.whl", hash = "sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998"}, @@ -1972,8 +1972,8 @@ snowballstemmer = [ {file = "snowballstemmer-2.0.0.tar.gz", hash = "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"}, ] sphinx = [ - {file = "Sphinx-3.3.0-py3-none-any.whl", hash = "sha256:3abdb2c57a65afaaa4f8573cbabd5465078eb6fd282c1e4f87f006875a7ec0c7"}, - {file = "Sphinx-3.3.0.tar.gz", hash = "sha256:1c21e7c5481a31b531e6cbf59c3292852ccde175b504b00ce2ff0b8f4adc3649"}, + {file = "Sphinx-3.3.1-py3-none-any.whl", hash = "sha256:d4e59ad4ea55efbb3c05cde3bfc83bfc14f0c95aa95c3d75346fcce186a47960"}, + {file = "Sphinx-3.3.1.tar.gz", hash = "sha256:1e8d592225447104d1172be415bc2972bd1357e3e12fdc76edf2261105db4300"}, ] sphinx-markdown-builder = [] sphinxcontrib-applehelp = [ @@ -2023,8 +2023,8 @@ untokenize = [ {file = "untokenize-0.1.1.tar.gz", hash = "md5:50d325dff09208c624cc603fad33bb0d"}, ] urllib3 = [ - {file = "urllib3-1.26.1-py2.py3-none-any.whl", hash = "sha256:61ad24434555a42c0439770462df38b47d05d9e8e353d93ec3742900975e3e65"}, - {file = "urllib3-1.26.1.tar.gz", hash = "sha256:097116a6f16f13482d2a2e56792088b9b2920f4eb6b4f84a2c90555fb673db74"}, + {file = "urllib3-1.26.2-py2.py3-none-any.whl", hash = "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"}, + {file = "urllib3-1.26.2.tar.gz", hash = "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08"}, ] uvicorn = [ {file = "uvicorn-0.11.8-py3-none-any.whl", hash = "sha256:4b70ddb4c1946e39db9f3082d53e323dfd50634b95fd83625d778729ef1730ef"}, diff --git a/pyproject.toml b/pyproject.toml index 1b240ee4..c9f38794 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,13 +1,13 @@ [tool.poetry] name = "nonebot2" -version = "2.0.0-alpha.5" +version = "2.0.0-alpha.6" description = "An asynchronous python bot framework." authors = ["yanyongyu "] license = "MIT" readme = "README.md" -homepage = "https://docs.nonebot.dev/" +homepage = "https://v2.nonebot.dev/" repository = "https://github.com/nonebot/nonebot2" -documentation = "https://docs.nonebot.dev/" +documentation = "https://v2.nonebot.dev/" keywords = ["bot", "qq", "qqbot", "mirai", "coolq"] classifiers = [ "Development Status :: 5 - Production/Stable",