diff --git a/.github/workflows/api_docs.yml b/.github/workflows/api_docs.yml
index 18d3085e..ac01aa18 100644
--- a/.github/workflows/api_docs.yml
+++ b/.github/workflows/api_docs.yml
@@ -12,7 +12,7 @@ jobs:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.sha }}
-
+
- name: Set up Python
uses: actions/setup-python@v2
with:
@@ -27,10 +27,10 @@ jobs:
- name: Build Doc
run: poetry run sphinx-build -M markdown ./docs_build ./build
-
+
- name: Copy Files
run: cp -r ./build/markdown/* ./docs/api/
-
+
- run: |
git config user.name nonebot
git config user.email nonebot@nonebot.dev
diff --git a/README.md b/README.md
index 78caed43..31fd5ac3 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
-

+

# NoneBot
-[](LICENSE)
-[](https://pypi.python.org/pypi/nonebot)
+[](LICENSE)
+[](https://pypi.python.org/pypi/nonebot2)


[](https://jq.qq.com/?_wv=1027&k=5OFifDh)
@@ -16,8 +16,6 @@
## 简介
-**NoneBot2 尚在开发中**
-
NoneBot2 是一个可扩展的 Python 异步机器人框架,它会对机器人收到的消息进行解析和处理,并以插件化的形式,分发给消息所对应的命令处理器和自然语言处理器,来完成具体的功能。
除了起到解析消息的作用,NoneBot 还为插件提供了大量实用的预设操作和权限控制机制,尤其对于命令处理器,它更是提供了完善且易用的会话机制和内部调用机制,以分别适应命令的连续交互和插件内部功能复用等需求。
diff --git a/archive/2.0.0a1/README.md b/archive/2.0.0a2/README.md
similarity index 100%
rename from archive/2.0.0a1/README.md
rename to archive/2.0.0a2/README.md
diff --git a/archive/2.0.0a1/api/README.md b/archive/2.0.0a2/api/README.md
similarity index 87%
rename from archive/2.0.0a1/api/README.md
rename to archive/2.0.0a2/api/README.md
index 2fa1066a..1c8acf1b 100644
--- a/archive/2.0.0a1/api/README.md
+++ b/archive/2.0.0a2/api/README.md
@@ -29,3 +29,6 @@
* [nonebot.exception](exception.html)
+
+
+ * [nonebot.adapters.cqhttp](adapters/cqhttp.html)
diff --git a/archive/2.0.0a2/api/adapters/README.md b/archive/2.0.0a2/api/adapters/README.md
new file mode 100644
index 00000000..b6529990
--- /dev/null
+++ b/archive/2.0.0a2/api/adapters/README.md
@@ -0,0 +1,41 @@
+---
+contentSidebar: true
+sidebarDepth: 0
+---
+
+# NoneBot.adapters 模块
+
+
+## _class_ `BaseBot`
+
+基类:`abc.ABC`
+
+
+## _class_ `BaseEvent`
+
+基类:`abc.ABC`
+
+
+### `_raw_event`
+
+原始 event
+
+
+## _class_ `BaseMessageSegment`
+
+基类:`abc.ABC`
+
+
+## _class_ `BaseMessage`
+
+基类:`list`, `abc.ABC`
+
+
+### `append(obj)`
+
+Append object to the end of the list.
+
+
+### `extend(obj)`
+
+Extend list by appending elements from the iterable.
diff --git a/archive/2.0.0a2/api/adapters/cqhttp.md b/archive/2.0.0a2/api/adapters/cqhttp.md
new file mode 100644
index 00000000..53fa04f9
--- /dev/null
+++ b/archive/2.0.0a2/api/adapters/cqhttp.md
@@ -0,0 +1,411 @@
+---
+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`](#None)
+
+CQHTTP 协议 Bot 适配。继承属性参考 [BaseBot](./#class-basebot) 。
+
+
+### _property_ `type`
+
+
+* 返回: `"cqhttp"`
+
+
+### _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`](#None)
+
+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`
+
+
+* 类型: `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`](#None)
+
+
+## _class_ `Message`
+
+基类:[`nonebot.adapters.BaseMessage`](#None)
diff --git a/archive/2.0.0a1/api/config.md b/archive/2.0.0a2/api/config.md
similarity index 100%
rename from archive/2.0.0a1/api/config.md
rename to archive/2.0.0a2/api/config.md
diff --git a/archive/2.0.0a1/api/exception.md b/archive/2.0.0a2/api/exception.md
similarity index 100%
rename from archive/2.0.0a1/api/exception.md
rename to archive/2.0.0a2/api/exception.md
diff --git a/archive/2.0.0a1/api/log.md b/archive/2.0.0a2/api/log.md
similarity index 100%
rename from archive/2.0.0a1/api/log.md
rename to archive/2.0.0a2/api/log.md
diff --git a/archive/2.0.0a1/api/nonebot.md b/archive/2.0.0a2/api/nonebot.md
similarity index 100%
rename from archive/2.0.0a1/api/nonebot.md
rename to archive/2.0.0a2/api/nonebot.md
diff --git a/archive/2.0.0a1/api/permission.md b/archive/2.0.0a2/api/permission.md
similarity index 100%
rename from archive/2.0.0a1/api/permission.md
rename to archive/2.0.0a2/api/permission.md
diff --git a/archive/2.0.0a1/api/rule.md b/archive/2.0.0a2/api/rule.md
similarity index 100%
rename from archive/2.0.0a1/api/rule.md
rename to archive/2.0.0a2/api/rule.md
diff --git a/archive/2.0.0a1/api/sched.md b/archive/2.0.0a2/api/sched.md
similarity index 100%
rename from archive/2.0.0a1/api/sched.md
rename to archive/2.0.0a2/api/sched.md
diff --git a/archive/2.0.0a1/api/typing.md b/archive/2.0.0a2/api/typing.md
similarity index 94%
rename from archive/2.0.0a1/api/typing.md
rename to archive/2.0.0a2/api/typing.md
index f68bdfcb..72d66f23 100644
--- a/archive/2.0.0a1/api/typing.md
+++ b/archive/2.0.0a2/api/typing.md
@@ -142,6 +142,22 @@ sidebarDepth: 0
+## `MatcherGroup`
+
+
+* **类型**
+
+ `MatcherGroup`
+
+
+
+* **说明**
+
+ MatcherGroup 为 Matcher 的集合。可以共享 Handler。
+
+
+
+
## `Rule`
diff --git a/archive/2.0.0a1/api/utils.md b/archive/2.0.0a2/api/utils.md
similarity index 70%
rename from archive/2.0.0a1/api/utils.md
rename to archive/2.0.0a2/api/utils.md
index 7de3ba3f..ed98fab9 100644
--- a/archive/2.0.0a1/api/utils.md
+++ b/archive/2.0.0a2/api/utils.md
@@ -6,6 +6,29 @@ sidebarDepth: 0
# NoneBot.utils 模块
+## `escape_tag(s)`
+
+
+* **说明**
+
+ 用于记录带颜色日志时转义 `
` 类型特殊标签
+
+
+
+* **参数**
+
+
+ * `s: str`: 需要转义的字符串
+
+
+
+* **返回**
+
+
+ * `str`
+
+
+
## `run_sync(func)`
diff --git a/archive/2.0.0a1/guide/README.md b/archive/2.0.0a2/guide/README.md
similarity index 100%
rename from archive/2.0.0a1/guide/README.md
rename to archive/2.0.0a2/guide/README.md
diff --git a/archive/2.0.0a1/guide/basic-configuration.md b/archive/2.0.0a2/guide/basic-configuration.md
similarity index 100%
rename from archive/2.0.0a1/guide/basic-configuration.md
rename to archive/2.0.0a2/guide/basic-configuration.md
diff --git a/archive/2.0.0a1/guide/creating-a-project.md b/archive/2.0.0a2/guide/creating-a-project.md
similarity index 100%
rename from archive/2.0.0a1/guide/creating-a-project.md
rename to archive/2.0.0a2/guide/creating-a-project.md
diff --git a/archive/2.0.0a1/guide/getting-started.md b/archive/2.0.0a2/guide/getting-started.md
similarity index 100%
rename from archive/2.0.0a1/guide/getting-started.md
rename to archive/2.0.0a2/guide/getting-started.md
diff --git a/archive/2.0.0a1/guide/installation.md b/archive/2.0.0a2/guide/installation.md
similarity index 100%
rename from archive/2.0.0a1/guide/installation.md
rename to archive/2.0.0a2/guide/installation.md
diff --git a/archive/2.0.0a1/guide/writing-a-plugin.md b/archive/2.0.0a2/guide/writing-a-plugin.md
similarity index 99%
rename from archive/2.0.0a1/guide/writing-a-plugin.md
rename to archive/2.0.0a2/guide/writing-a-plugin.md
index bd176750..42657d9d 100644
--- a/archive/2.0.0a1/guide/writing-a-plugin.md
+++ b/archive/2.0.0a2/guide/writing-a-plugin.md
@@ -159,7 +159,7 @@ weather = on_command("天气", rule=to_me(), permission=Permission(), priority=5
- `on_startswith(str)` ~ `on("message", startswith(str))`: 消息开头匹配处理器
- `on_endswith(str)` ~ `on("message", endswith(str))`: 消息结尾匹配处理器
- `on_command(str|tuple)` ~ `on("message", command(str|tuple))`: 命令处理器
-- `on_regax(pattern_str)` ~ `on("message", regax(pattern_str))`: 正则匹配处理器
+- `on_regex(pattern_str)` ~ `on("message", regex(pattern_str))`: 正则匹配处理器
#### 匹配规则 rule
diff --git a/archive/2.0.0a1/sidebar.config.json b/archive/2.0.0a2/sidebar.config.json
similarity index 89%
rename from archive/2.0.0a1/sidebar.config.json
rename to archive/2.0.0a2/sidebar.config.json
index 9dd289f1..bec088a6 100644
--- a/archive/2.0.0a1/sidebar.config.json
+++ b/archive/2.0.0a2/sidebar.config.json
@@ -78,6 +78,14 @@
{
"title": "nonebot.exception 模块",
"path": "exception"
+ },
+ {
+ "title": "nonebot.adapters 模块",
+ "path": "adapters/"
+ },
+ {
+ "title": "nonebot.adapters.cqhttp 模块",
+ "path": "adapters/cqhttp"
}
]
}
diff --git a/docs/.vuepress/components/Messenger.vue b/docs/.vuepress/components/Messenger.vue
index 7c12d979..030e2188 100644
--- a/docs/.vuepress/components/Messenger.vue
+++ b/docs/.vuepress/components/Messenger.vue
@@ -1,7 +1,7 @@
-
+
+
@@ -139,19 +139,15 @@ export default {
default: () => []
}
},
- data: () => ({
- wow: null
- }),
methods: {
initWOW: function() {
- this.wow = new WOW({
+ new WOW({
noxClass: "wow",
animateClass: "animate__animated",
offset: 0,
mobile: true,
live: true
- });
- this.wow.init();
+ }).init();
}
},
mounted() {
@@ -167,6 +163,7 @@ export default {
.chat {
min-height: 150px;
+ overflow-x: hidden;
}
.chat-bg {
background-color: #f3f6f9;
@@ -214,3 +211,9 @@ export default {
font-size: 12px;
}
+
+
diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js
index aa2e8ea1..da72ca0c 100644
--- a/docs/.vuepress/config.js
+++ b/docs/.vuepress/config.js
@@ -119,6 +119,14 @@ module.exports = context => ({
{
title: "nonebot.exception 模块",
path: "exception"
+ },
+ {
+ title: "nonebot.adapters 模块",
+ path: "adapters/"
+ },
+ {
+ title: "nonebot.adapters.cqhttp 模块",
+ path: "adapters/cqhttp"
}
]
}
diff --git a/docs/.vuepress/versions.json b/docs/.vuepress/versions.json
index 76e10341..386fcf77 100644
--- a/docs/.vuepress/versions.json
+++ b/docs/.vuepress/versions.json
@@ -1,3 +1,3 @@
[
- "2.0.0a1"
+ "2.0.0a2"
]
\ No newline at end of file
diff --git a/docs/api/README.md b/docs/api/README.md
index 2fa1066a..1c8acf1b 100644
--- a/docs/api/README.md
+++ b/docs/api/README.md
@@ -29,3 +29,6 @@
* [nonebot.exception](exception.html)
+
+
+ * [nonebot.adapters.cqhttp](adapters/cqhttp.html)
diff --git a/docs/api/adapters/README.md b/docs/api/adapters/README.md
new file mode 100644
index 00000000..b6529990
--- /dev/null
+++ b/docs/api/adapters/README.md
@@ -0,0 +1,41 @@
+---
+contentSidebar: true
+sidebarDepth: 0
+---
+
+# NoneBot.adapters 模块
+
+
+## _class_ `BaseBot`
+
+基类:`abc.ABC`
+
+
+## _class_ `BaseEvent`
+
+基类:`abc.ABC`
+
+
+### `_raw_event`
+
+原始 event
+
+
+## _class_ `BaseMessageSegment`
+
+基类:`abc.ABC`
+
+
+## _class_ `BaseMessage`
+
+基类:`list`, `abc.ABC`
+
+
+### `append(obj)`
+
+Append object to the end of the list.
+
+
+### `extend(obj)`
+
+Extend list by appending elements from the iterable.
diff --git a/docs/api/adapters/cqhttp.md b/docs/api/adapters/cqhttp.md
new file mode 100644
index 00000000..53fa04f9
--- /dev/null
+++ b/docs/api/adapters/cqhttp.md
@@ -0,0 +1,411 @@
+---
+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`](#None)
+
+CQHTTP 协议 Bot 适配。继承属性参考 [BaseBot](./#class-basebot) 。
+
+
+### _property_ `type`
+
+
+* 返回: `"cqhttp"`
+
+
+### _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`](#None)
+
+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`
+
+
+* 类型: `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`](#None)
+
+
+## _class_ `Message`
+
+基类:[`nonebot.adapters.BaseMessage`](#None)
diff --git a/docs/api/typing.md b/docs/api/typing.md
index f68bdfcb..72d66f23 100644
--- a/docs/api/typing.md
+++ b/docs/api/typing.md
@@ -142,6 +142,22 @@ sidebarDepth: 0
+## `MatcherGroup`
+
+
+* **类型**
+
+ `MatcherGroup`
+
+
+
+* **说明**
+
+ MatcherGroup 为 Matcher 的集合。可以共享 Handler。
+
+
+
+
## `Rule`
diff --git a/docs/api/utils.md b/docs/api/utils.md
index 7de3ba3f..ed98fab9 100644
--- a/docs/api/utils.md
+++ b/docs/api/utils.md
@@ -6,6 +6,29 @@ sidebarDepth: 0
# NoneBot.utils 模块
+## `escape_tag(s)`
+
+
+* **说明**
+
+ 用于记录带颜色日志时转义 `` 类型特殊标签
+
+
+
+* **参数**
+
+
+ * `s: str`: 需要转义的字符串
+
+
+
+* **返回**
+
+
+ * `str`
+
+
+
## `run_sync(func)`
diff --git a/docs/guide/writing-a-plugin.md b/docs/guide/writing-a-plugin.md
index bd176750..42657d9d 100644
--- a/docs/guide/writing-a-plugin.md
+++ b/docs/guide/writing-a-plugin.md
@@ -159,7 +159,7 @@ weather = on_command("天气", rule=to_me(), permission=Permission(), priority=5
- `on_startswith(str)` ~ `on("message", startswith(str))`: 消息开头匹配处理器
- `on_endswith(str)` ~ `on("message", endswith(str))`: 消息结尾匹配处理器
- `on_command(str|tuple)` ~ `on("message", command(str|tuple))`: 命令处理器
-- `on_regax(pattern_str)` ~ `on("message", regax(pattern_str))`: 正则匹配处理器
+- `on_regex(pattern_str)` ~ `on("message", regex(pattern_str))`: 正则匹配处理器
#### 匹配规则 rule
diff --git a/docs_build/README.rst b/docs_build/README.rst
index cb726c38..97f45a20 100644
--- a/docs_build/README.rst
+++ b/docs_build/README.rst
@@ -11,3 +11,4 @@ NoneBot Api Reference
- `nonebot.permission `_
- `nonebot.utils `_
- `nonebot.exception `_
+ - `nonebot.adapters.cqhttp `_
diff --git a/docs_build/adapters/README.rst b/docs_build/adapters/README.rst
new file mode 100644
index 00000000..25dbf4e9
--- /dev/null
+++ b/docs_build/adapters/README.rst
@@ -0,0 +1,12 @@
+---
+contentSidebar: true
+sidebarDepth: 0
+---
+
+NoneBot.adapters 模块
+=================
+
+.. automodule:: nonebot.adapters
+ :members:
+ :private-members:
+ :show-inheritance:
diff --git a/docs_build/adapters/cqhttp.rst b/docs_build/adapters/cqhttp.rst
new file mode 100644
index 00000000..ddf78105
--- /dev/null
+++ b/docs_build/adapters/cqhttp.rst
@@ -0,0 +1,12 @@
+---
+contentSidebar: true
+sidebarDepth: 0
+---
+
+NoneBot.adapters.cqhttp 模块
+=================
+
+.. automodule:: nonebot.adapters.cqhttp
+ :members:
+ :private-members:
+ :show-inheritance:
diff --git a/docs_build/utils.rst b/docs_build/utils.rst
index 776bbfd0..b7609fdc 100644
--- a/docs_build/utils.rst
+++ b/docs_build/utils.rst
@@ -7,6 +7,7 @@ NoneBot.utils 模块
==================
+.. autofunction:: nonebot.utils.escape_tag
.. autodecorator:: nonebot.utils.run_sync
.. autoclass:: nonebot.utils.DataclassEncoder
:show-inheritance:
diff --git a/nonebot/__init__.py b/nonebot/__init__.py
index bd0ee80b..213b63d2 100644
--- a/nonebot/__init__.py
+++ b/nonebot/__init__.py
@@ -109,6 +109,7 @@ def get_bots() -> Union[NoReturn, Dict[str, Bot]]:
from nonebot.sched import scheduler
+from nonebot.utils import escape_tag
from nonebot.config import Env, Config
from nonebot.log import logger, default_filter
from nonebot.adapters.cqhttp import Bot as CQBot
@@ -155,8 +156,8 @@ def init(*, _env_file: Optional[str] = None, **kwargs):
_env_file=_env_file or f".env.{env.environment}")
default_filter.level = "DEBUG" if config.debug else "INFO"
- logger.opt(
- colors=True).debug(f"Loaded Config: {config.dict()}")
+ logger.opt(colors=True).debug(
+ f"Loaded Config: {escape_tag(str(config.dict()))}")
DriverClass: Type[Driver] = getattr(
importlib.import_module(config.driver), "Driver")
@@ -213,5 +214,5 @@ async def _start_scheduler():
from nonebot.plugin import on_message, on_notice, on_request, on_metaevent
-from nonebot.plugin import on_startswith, on_endswith, on_command, on_regex
+from nonebot.plugin import on_startswith, on_endswith, on_command, on_regex, CommandGroup
from nonebot.plugin import load_plugin, load_plugins, load_builtin_plugins, get_loaded_plugins
diff --git a/nonebot/adapters/__init__.py b/nonebot/adapters/__init__.py
index a4d10c2d..fb1fdbec 100644
--- a/nonebot/adapters/__init__.py
+++ b/nonebot/adapters/__init__.py
@@ -52,6 +52,9 @@ class BaseEvent(abc.ABC):
def __init__(self, raw_event: dict):
self._raw_event = raw_event
+ """
+ 原始 event
+ """
def __repr__(self) -> str:
return f""
diff --git a/nonebot/adapters/cqhttp.py b/nonebot/adapters/cqhttp.py
index dc7ad38a..96777759 100644
--- a/nonebot/adapters/cqhttp.py
+++ b/nonebot/adapters/cqhttp.py
@@ -28,14 +28,29 @@ from nonebot.adapters import BaseBot, BaseEvent, BaseMessage, BaseMessageSegment
def log(level: str, message: str):
+ """
+ :说明:
+
+ 用于打印 CQHTTP 日志。
+
+ :参数:
+
+ * ``level: str``: 日志等级
+ * ``message: str``: 日志信息
+ """
return logger.opt(colors=True).log(level, "CQHTTP | " + message)
def escape(s: str, *, escape_comma: bool = True) -> str:
"""
- 对字符串进行 CQ 码转义。
+ :说明:
- ``escape_comma`` 参数控制是否转义逗号(``,``)。
+ 对字符串进行 CQ 码转义。
+
+ :参数:
+
+ * ``s: str``: 需要转义的字符串
+ * ``escape_comma: bool``: 是否转义逗号(``,``)。
"""
s = s.replace("&", "&") \
.replace("[", "[") \
@@ -46,7 +61,15 @@ def escape(s: str, *, escape_comma: bool = True) -> str:
def unescape(s: str) -> str:
- """对字符串进行 CQ 码去转义。"""
+ """
+ :说明:
+
+ 对字符串进行 CQ 码去转义。
+
+ :参数:
+
+ * ``s: str``: 需要转义的字符串
+ """
return s.replace(",", ",") \
.replace("[", "[") \
.replace("]", "]") \
@@ -54,10 +77,21 @@ def unescape(s: str) -> str:
def _b2s(b: Optional[bool]) -> Optional[str]:
+ """转换布尔值为字符串。"""
return b if b is None else str(b).lower()
async def _check_reply(bot: "Bot", event: "Event"):
+ """
+ :说明:
+
+ 检查消息中存在的回复,去除并赋值 ``event.reply``, ``event.to_me``
+
+ :参数:
+
+ * ``bot: Bot``: Bot 对象
+ * ``event: Event``: Event 对象
+ """
if event.type != "message":
return
@@ -74,6 +108,16 @@ async def _check_reply(bot: "Bot", event: "Event"):
def _check_at_me(bot: "Bot", event: "Event"):
+ """
+ :说明:
+
+ 检查消息开头或结尾是否存在 @机器人,去除并赋值 ``event.to_me``
+
+ :参数:
+
+ * ``bot: Bot``: Bot 对象
+ * ``event: Event``: Event 对象
+ """
if event.type != "message":
return
@@ -119,6 +163,16 @@ def _check_at_me(bot: "Bot", event: "Event"):
def _check_nickname(bot: "Bot", event: "Event"):
+ """
+ :说明:
+
+ 检查消息开头是否存在,去除并赋值 ``event.to_me``
+
+ :参数:
+
+ * ``bot: Bot``: Bot 对象
+ * ``event: Event``: Event 对象
+ """
if event.type != "message":
return
@@ -145,7 +199,25 @@ def _check_nickname(bot: "Bot", event: "Event"):
first_msg_seg.data["text"] = first_text[m.end():]
-def _handle_api_result(result: Optional[Dict[str, Any]]) -> Any:
+def _handle_api_result(
+ result: Optional[Dict[str, Any]]) -> Union[Any, NoReturn]:
+ """
+ :说明:
+
+ 处理 API 请求返回值。
+
+ :参数:
+
+ * ``result: Optional[Dict[str, Any]]``: API 返回数据
+
+ :返回:
+
+ - ``Any``: API 调用返回数据
+
+ :异常:
+
+ - ``ActionFailed``: API 调用失败
+ """
if isinstance(result, dict):
if result.get("status") == "failed":
raise ActionFailed(retcode=result.get("retcode"))
@@ -183,6 +255,9 @@ class ResultStore:
class Bot(BaseBot):
+ """
+ CQHTTP 协议 Bot 适配。继承属性参考 `BaseBot <./#class-basebot>`_ 。
+ """
def __init__(self,
driver: Driver,
@@ -203,10 +278,18 @@ class Bot(BaseBot):
@property
@overrides(BaseBot)
def type(self) -> str:
+ """
+ - 返回: ``"cqhttp"``
+ """
return "cqhttp"
@overrides(BaseBot)
async def handle_message(self, message: dict):
+ """
+ :说明:
+
+ 调用 `_check_reply <#async-check-reply-bot-event>`_, `_check_at_me <#check-at-me-bot-event>`_, `_check_nickname <#check-nickname-bot-event>`_ 处理事件并转换为 `Event <#class-event>`_
+ """
if not message:
return
@@ -230,6 +313,25 @@ class Bot(BaseBot):
@overrides(BaseBot)
async def call_api(self, api: str, **data) -> Union[Any, NoReturn]:
+ """
+ :说明:
+
+ 调用 CQHTTP 协议 API
+
+ :参数:
+
+ * ``api: str``: API 名称
+ * ``**data: Any``: API 参数
+
+ :返回:
+
+ - ``Any``: API 调用返回数据
+
+ :异常:
+
+ - ``NetworkError``: 网络错误
+ - ``ActionFailed``: API 调用失败
+ """
if "self_id" in data:
self_id = data.pop("self_id")
if self_id:
@@ -278,12 +380,36 @@ class Bot(BaseBot):
raise NetworkError("HTTP request failed")
@overrides(BaseBot)
- async def send(self, event: "Event", message: Union[str, "Message",
- "MessageSegment"],
+ async def send(self,
+ event: "Event",
+ message: Union[str, "Message", "MessageSegment"],
+ at_sender: bool = False,
**kwargs) -> Union[Any, NoReturn]:
+ """
+ :说明:
+
+ 根据 ``event`` 向触发事件的主体发送消息。
+
+ :参数:
+
+ * ``event: Event``: Event 对象
+ * ``message: Union[str, Message, MessageSegment]``: 要发送的消息
+ * ``at_sender: bool``: 是否 @ 事件主体
+ * ``**kwargs``: 覆盖默认参数
+
+ :返回:
+
+ - ``Any``: API 调用返回数据
+
+ :异常:
+
+ - ``ValueError``: 缺少 ``user_id``, ``group_id``
+ - ``NetworkError``: 网络错误
+ - ``ActionFailed``: API 调用失败
+ """
msg = message if isinstance(message, Message) else Message(message)
- at_sender = kwargs.pop("at_sender", False) and bool(event.user_id)
+ at_sender = at_sender and bool(event.user_id)
params = {}
if event.user_id:
@@ -309,6 +435,9 @@ class Bot(BaseBot):
class Event(BaseEvent):
+ """
+ CQHTTP 协议 Event 适配。继承属性参考 `BaseEvent <./#class-baseevent>`_ 。
+ """
def __init__(self, raw_event: dict):
if "message" in raw_event:
@@ -319,11 +448,19 @@ class Event(BaseEvent):
@property
@overrides(BaseEvent)
def id(self) -> Optional[int]:
+ """
+ - 类型: ``Optional[int]``
+ - 说明: 事件/消息 ID
+ """
return self._raw_event.get("message_id") or self._raw_event.get("flag")
@property
@overrides(BaseEvent)
def name(self) -> str:
+ """
+ - 类型: ``str``
+ - 说明: 事件名称,由类型与 ``.`` 组合而成
+ """
n = self.type + "." + self.detail_type
if self.sub_type:
n += "." + self.sub_type
@@ -332,16 +469,28 @@ class Event(BaseEvent):
@property
@overrides(BaseEvent)
def self_id(self) -> str:
+ """
+ - 类型: ``str``
+ - 说明: 机器人自身 ID
+ """
return str(self._raw_event["self_id"])
@property
@overrides(BaseEvent)
def time(self) -> int:
+ """
+ - 类型: ``int``
+ - 说明: 事件发生时间
+ """
return self._raw_event["time"]
@property
@overrides(BaseEvent)
def type(self) -> str:
+ """
+ - 类型: ``str``
+ - 说明: 事件类型
+ """
return self._raw_event["post_type"]
@type.setter
@@ -352,6 +501,10 @@ class Event(BaseEvent):
@property
@overrides(BaseEvent)
def detail_type(self) -> str:
+ """
+ - 类型: ``str``
+ - 说明: 事件详细类型
+ """
return self._raw_event[f"{self.type}_type"]
@detail_type.setter
@@ -362,6 +515,10 @@ class Event(BaseEvent):
@property
@overrides(BaseEvent)
def sub_type(self) -> Optional[str]:
+ """
+ - 类型: ``Optional[str]``
+ - 说明: 事件子类型
+ """
return self._raw_event.get("sub_type")
@type.setter
@@ -372,6 +529,10 @@ class Event(BaseEvent):
@property
@overrides(BaseEvent)
def user_id(self) -> Optional[int]:
+ """
+ - 类型: ``Optional[int]``
+ - 说明: 事件主体 ID
+ """
return self._raw_event.get("user_id")
@user_id.setter
@@ -382,6 +543,10 @@ class Event(BaseEvent):
@property
@overrides(BaseEvent)
def group_id(self) -> Optional[int]:
+ """
+ - 类型: ``Optional[int]``
+ - 说明: 事件主体群 ID
+ """
return self._raw_event.get("group_id")
@group_id.setter
@@ -392,6 +557,10 @@ class Event(BaseEvent):
@property
@overrides(BaseEvent)
def to_me(self) -> Optional[bool]:
+ """
+ - 类型: ``Optional[bool]``
+ - 说明: 消息是否与机器人相关
+ """
return self._raw_event.get("to_me")
@to_me.setter
@@ -402,6 +571,10 @@ class Event(BaseEvent):
@property
@overrides(BaseEvent)
def message(self) -> Optional["Message"]:
+ """
+ - 类型: ``Optional[Message]``
+ - 说明: 消息内容
+ """
return self._raw_event.get("message")
@message.setter
@@ -412,6 +585,10 @@ class Event(BaseEvent):
@property
@overrides(BaseEvent)
def reply(self) -> Optional[dict]:
+ """
+ - 类型: ``Optional[dict]``
+ - 说明: 回复消息详情
+ """
return self._raw_event.get("reply")
@reply.setter
@@ -422,6 +599,10 @@ class Event(BaseEvent):
@property
@overrides(BaseEvent)
def raw_message(self) -> Optional[str]:
+ """
+ - 类型: ``Optional[str]``
+ - 说明: 原始消息
+ """
return self._raw_event.get("raw_message")
@raw_message.setter
@@ -432,11 +613,19 @@ class Event(BaseEvent):
@property
@overrides(BaseEvent)
def plain_text(self) -> Optional[str]:
+ """
+ - 类型: ``Optional[str]``
+ - 说明: 纯文本消息内容
+ """
return self.message and self.message.extract_plain_text()
@property
@overrides(BaseEvent)
def sender(self) -> Optional[dict]:
+ """
+ - 类型: ``Optional[dict]``
+ - 说明: 消息发送者信息
+ """
return self._raw_event.get("sender")
@sender.setter
diff --git a/nonebot/adapters/cqhttp.pyi b/nonebot/adapters/cqhttp.pyi
index d6ddd6d9..08cdb2cd 100644
--- a/nonebot/adapters/cqhttp.pyi
+++ b/nonebot/adapters/cqhttp.pyi
@@ -1,15 +1,91 @@
+import asyncio
+
+from nonebot.config import Config
from nonebot.adapters import BaseBot
-from nonebot.typing import Any, Dict, List, Union, Message, Optional
+from nonebot.typing import Any, Dict, List, Union, Driver, Optional, NoReturn, WebSocket, Iterable
+
+
+def log(level: str, message: str):
+ ...
+
+
+def escape(s: str, *, escape_comma: bool = ...) -> str:
+ ...
+
+
+def unescape(s: str) -> str:
+ ...
+
+
+def _b2s(b: Optional[bool]) -> Optional[str]:
+ ...
+
+
+async def _check_reply(bot: "Bot", event: "Event"):
+ ...
+
+
+def _check_at_me(bot: "Bot", event: "Event"):
+ ...
+
+
+def _check_nickname(bot: "Bot", event: "Event"):
+ ...
+
+
+def _handle_api_result(
+ result: Optional[Dict[str, Any]]) -> Union[Any, NoReturn]:
+ ...
+
+
+class ResultStore:
+ _seq: int = ...
+ _futures: Dict[int, asyncio.Future] = ...
+
+ @classmethod
+ def get_seq(cls) -> int:
+ ...
+
+ @classmethod
+ def add_result(cls, result: Dict[str, Any]):
+ ...
+
+ @classmethod
+ async def fetch(cls, seq: int, timeout: Optional[float]) -> Dict[str, Any]:
+ ...
class Bot(BaseBot):
+ def __init__(self,
+ driver: Driver,
+ connection_type: str,
+ config: Config,
+ self_id: str,
+ *,
+ websocket: WebSocket = None):
+ ...
+
+ def type(self) -> str:
+ ...
+
+ async def handle_message(self, message: dict):
+ ...
+
+ async def call_api(self, api: str, **data) -> Union[Any, NoReturn]:
+ ...
+
+ async def send(self, event: "Event", message: Union[str, "Message",
+ "MessageSegment"],
+ **kwargs) -> Union[Any, NoReturn]:
+ ...
+
async def send_private_msg(self,
*,
user_id: int,
message: Union[str, Message],
- auto_escape: bool = False,
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ auto_escape: bool = ...,
+ self_id: Optional[int] = ...) -> Dict[str, Any]:
"""
:说明:
@@ -28,8 +104,8 @@ class Bot(BaseBot):
*,
group_id: int,
message: Union[str, Message],
- auto_escape: bool = False,
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ auto_escape: bool = ...,
+ self_id: Optional[int] = ...) -> Dict[str, Any]:
"""
:说明:
@@ -46,12 +122,12 @@ class Bot(BaseBot):
async def send_msg(self,
*,
- message_type: Optional[str] = None,
- user_id: Optional[int] = None,
- group_id: Optional[int] = None,
+ message_type: Optional[str] = ...,
+ user_id: Optional[int] = ...,
+ group_id: Optional[int] = ...,
message: Union[str, Message],
- auto_escape: bool = False,
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ auto_escape: bool = ...,
+ self_id: Optional[int] = ...) -> Dict[str, Any]:
"""
:说明:
@@ -71,7 +147,7 @@ class Bot(BaseBot):
async def delete_msg(self,
*,
message_id: int,
- self_id: Optional[int] = None) -> None:
+ self_id: Optional[int] = ...) -> None:
"""
:说明:
@@ -87,7 +163,7 @@ class Bot(BaseBot):
async def get_msg(self,
*,
message_id: int,
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ self_id: Optional[int] = ...) -> Dict[str, Any]:
"""
:说明:
@@ -103,7 +179,7 @@ class Bot(BaseBot):
async def get_forward_msg(self,
*,
id: int,
- self_id: Optional[int] = None) -> None:
+ self_id: Optional[int] = ...) -> None:
"""
:说明:
@@ -119,8 +195,8 @@ class Bot(BaseBot):
async def send_like(self,
*,
user_id: int,
- times: int = 1,
- self_id: Optional[int] = None) -> None:
+ times: int = ...,
+ self_id: Optional[int] = ...) -> None:
"""
:说明:
@@ -138,8 +214,8 @@ class Bot(BaseBot):
*,
group_id: int,
user_id: int,
- reject_add_request: bool = False,
- self_id: Optional[int] = None) -> None:
+ reject_add_request: bool = ...,
+ self_id: Optional[int] = ...) -> None:
"""
:说明:
@@ -158,8 +234,8 @@ class Bot(BaseBot):
*,
group_id: int,
user_id: int,
- duration: int = 30 * 60,
- self_id: Optional[int] = None) -> None:
+ duration: int = ...,
+ self_id: Optional[int] = ...) -> None:
"""
:说明:
@@ -177,11 +253,10 @@ class Bot(BaseBot):
async def set_group_anonymous_ban(self,
*,
group_id: int,
- anonymous: Optional[Dict[str,
- Any]] = None,
- anonymous_flag: Optional[str] = None,
- duration: int = 30 * 60,
- self_id: Optional[int] = None) -> None:
+ anonymous: Optional[Dict[str, Any]] = ...,
+ anonymous_flag: Optional[str] = ...,
+ duration: int = ...,
+ self_id: Optional[int] = ...) -> None:
"""
:说明:
@@ -200,8 +275,8 @@ class Bot(BaseBot):
async def set_group_whole_ban(self,
*,
group_id: int,
- enable: bool = True,
- self_id: Optional[int] = None) -> None:
+ enable: bool = ...,
+ self_id: Optional[int] = ...) -> None:
"""
:说明:
@@ -219,8 +294,8 @@ class Bot(BaseBot):
*,
group_id: int,
user_id: int,
- enable: bool = True,
- self_id: Optional[int] = None) -> None:
+ enable: bool = ...,
+ self_id: Optional[int] = ...) -> None:
"""
:说明:
@@ -238,8 +313,8 @@ class Bot(BaseBot):
async def set_group_anonymous(self,
*,
group_id: int,
- enable: bool = True,
- self_id: Optional[int] = None) -> None:
+ enable: bool = ...,
+ self_id: Optional[int] = ...) -> None:
"""
:说明:
@@ -257,8 +332,8 @@ class Bot(BaseBot):
*,
group_id: int,
user_id: int,
- card: str = "",
- self_id: Optional[int] = None) -> None:
+ card: str = ...,
+ self_id: Optional[int] = ...) -> None:
"""
:说明:
@@ -277,7 +352,7 @@ class Bot(BaseBot):
*,
group_id: int,
group_name: str,
- self_id: Optional[int] = None) -> None:
+ self_id: Optional[int] = ...) -> None:
"""
:说明:
@@ -294,8 +369,8 @@ class Bot(BaseBot):
async def set_group_leave(self,
*,
group_id: int,
- is_dismiss: bool = False,
- self_id: Optional[int] = None) -> None:
+ is_dismiss: bool = ...,
+ self_id: Optional[int] = ...) -> None:
"""
:说明:
@@ -313,9 +388,9 @@ class Bot(BaseBot):
*,
group_id: int,
user_id: int,
- special_title: str = "",
- duration: int = -1,
- self_id: Optional[int] = None) -> None:
+ special_title: str = ...,
+ duration: int = ...,
+ self_id: Optional[int] = ...) -> None:
"""
:说明:
@@ -334,9 +409,9 @@ class Bot(BaseBot):
async def set_friend_add_request(self,
*,
flag: str,
- approve: bool = True,
- remark: str = "",
- self_id: Optional[int] = None) -> None:
+ approve: bool = ...,
+ remark: str = ...,
+ self_id: Optional[int] = ...) -> None:
"""
:说明:
@@ -355,9 +430,9 @@ class Bot(BaseBot):
*,
flag: str,
sub_type: str,
- approve: bool = True,
- reason: str = "",
- self_id: Optional[int] = None) -> None:
+ approve: bool = ...,
+ reason: str = ...,
+ self_id: Optional[int] = ...) -> None:
"""
:说明:
@@ -375,7 +450,7 @@ class Bot(BaseBot):
async def get_login_info(self,
*,
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ self_id: Optional[int] = ...) -> Dict[str, Any]:
"""
:说明:
@@ -387,12 +462,11 @@ class Bot(BaseBot):
"""
...
- async def get_stranger_info(
- self,
- *,
- user_id: int,
- no_cache: bool = False,
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ async def get_stranger_info(self,
+ *,
+ user_id: int,
+ no_cache: bool = ...,
+ self_id: Optional[int] = ...) -> Dict[str, Any]:
"""
:说明:
@@ -408,7 +482,7 @@ class Bot(BaseBot):
async def get_friend_list(self,
*,
- self_id: Optional[int] = None
+ self_id: Optional[int] = ...
) -> List[Dict[str, Any]]:
"""
:说明:
@@ -424,8 +498,8 @@ class Bot(BaseBot):
async def get_group_info(self,
*,
group_id: int,
- no_cache: bool = False,
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ no_cache: bool = ...,
+ self_id: Optional[int] = ...) -> Dict[str, Any]:
"""
:说明:
@@ -441,7 +515,7 @@ class Bot(BaseBot):
async def get_group_list(self,
*,
- self_id: Optional[int] = None
+ self_id: Optional[int] = ...
) -> List[Dict[str, Any]]:
"""
:说明:
@@ -459,8 +533,8 @@ class Bot(BaseBot):
*,
group_id: int,
user_id: int,
- no_cache: bool = False,
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ no_cache: bool = ...,
+ self_id: Optional[int] = ...) -> Dict[str, Any]:
"""
:说明:
@@ -479,7 +553,7 @@ class Bot(BaseBot):
self,
*,
group_id: int,
- self_id: Optional[int] = None) -> List[Dict[str, Any]]:
+ self_id: Optional[int] = ...) -> List[Dict[str, Any]]:
"""
:说明:
@@ -492,12 +566,12 @@ class Bot(BaseBot):
"""
...
- async def get_group_honor_info(
- self,
- *,
- group_id: int,
- type: str = "all",
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ async def get_group_honor_info(self,
+ *,
+ group_id: int,
+ type: str = ...,
+ self_id: Optional[int] = ...
+ ) -> Dict[str, Any]:
"""
:说明:
@@ -513,8 +587,8 @@ class Bot(BaseBot):
async def get_cookies(self,
*,
- domain: str = "",
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ domain: str = ...,
+ self_id: Optional[int] = ...) -> Dict[str, Any]:
"""
:说明:
@@ -529,7 +603,7 @@ class Bot(BaseBot):
async def get_csrf_token(self,
*,
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ self_id: Optional[int] = ...) -> Dict[str, Any]:
"""
:说明:
@@ -543,8 +617,8 @@ class Bot(BaseBot):
async def get_credentials(self,
*,
- domain: str = "",
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ domain: str = ...,
+ self_id: Optional[int] = ...) -> Dict[str, Any]:
"""
:说明:
@@ -561,7 +635,7 @@ class Bot(BaseBot):
*,
file: str,
out_format: str,
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ self_id: Optional[int] = ...) -> Dict[str, Any]:
"""
:说明:
@@ -578,7 +652,7 @@ class Bot(BaseBot):
async def get_image(self,
*,
file: str,
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ self_id: Optional[int] = ...) -> Dict[str, Any]:
"""
:说明:
@@ -593,7 +667,7 @@ class Bot(BaseBot):
async def can_send_image(self,
*,
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ self_id: Optional[int] = ...) -> Dict[str, Any]:
"""
:说明:
@@ -607,7 +681,7 @@ class Bot(BaseBot):
async def can_send_record(self,
*,
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ self_id: Optional[int] = ...) -> Dict[str, Any]:
"""
:说明:
@@ -621,7 +695,7 @@ class Bot(BaseBot):
async def get_status(self,
*,
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ self_id: Optional[int] = ...) -> Dict[str, Any]:
"""
:说明:
@@ -635,7 +709,7 @@ class Bot(BaseBot):
async def get_version_info(self,
*,
- self_id: Optional[int] = None) -> Dict[str, Any]:
+ self_id: Optional[int] = ...) -> Dict[str, Any]:
"""
:说明:
@@ -649,8 +723,8 @@ class Bot(BaseBot):
async def set_restart(self,
*,
- delay: int = 0,
- self_id: Optional[int] = None) -> None:
+ delay: int = ...,
+ self_id: Optional[int] = ...) -> None:
"""
:说明:
@@ -663,7 +737,7 @@ class Bot(BaseBot):
"""
...
- async def clean_cache(self, *, self_id: Optional[int] = None) -> None:
+ async def clean_cache(self, *, self_id: Optional[int] = ...) -> None:
"""
:说明:
@@ -674,3 +748,242 @@ class Bot(BaseBot):
* ``self_id``: 机器人 QQ 号
"""
...
+
+
+class Event:
+
+ def __init__(self, raw_event: dict):
+ ...
+
+ @property
+ def id(self) -> Optional[int]:
+ ...
+
+ @property
+ def name(self) -> str:
+ ...
+
+ @property
+ def self_id(self) -> str:
+ ...
+
+ @property
+ def time(self) -> int:
+ ...
+
+ @property
+ def type(self) -> str:
+ ...
+
+ @type.setter
+ def type(self, value) -> None:
+ ...
+
+ @property
+ def detail_type(self) -> str:
+ ...
+
+ @detail_type.setter
+ def detail_type(self, value) -> None:
+ ...
+
+ @property
+ def sub_type(self) -> Optional[str]:
+ ...
+
+ @type.setter
+ def sub_type(self, value) -> None:
+ ...
+
+ @property
+ def user_id(self) -> Optional[int]:
+ ...
+
+ @user_id.setter
+ def user_id(self, value) -> None:
+ ...
+
+ @property
+ def group_id(self) -> Optional[int]:
+ ...
+
+ @group_id.setter
+ def group_id(self, value) -> None:
+ ...
+
+ @property
+ def to_me(self) -> Optional[bool]:
+ ...
+
+ @to_me.setter
+ def to_me(self, value) -> None:
+ ...
+
+ @property
+ def message(self) -> Optional["Message"]:
+ ...
+
+ @message.setter
+ def message(self, value) -> None:
+ ...
+
+ @property
+ def reply(self) -> Optional[dict]:
+ ...
+
+ @reply.setter
+ def reply(self, value) -> None:
+ ...
+
+ @property
+ def raw_message(self) -> Optional[str]:
+ ...
+
+ @raw_message.setter
+ def raw_message(self, value) -> None:
+ ...
+
+ @property
+ def plain_text(self) -> Optional[str]:
+ ...
+
+ @property
+ def sender(self) -> Optional[dict]:
+ ...
+
+ @sender.setter
+ def sender(self, value) -> None:
+ ...
+
+
+class MessageSegment:
+
+ def __init__(self, type: str, data: Dict[str, Union[str, list]]) -> None:
+ ...
+
+ def __str__(self):
+ ...
+
+ def __add__(self, other) -> "Message":
+ ...
+
+ @staticmethod
+ def anonymous(ignore_failure: Optional[bool] = ...) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def at(user_id: Union[int, str]) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def contact_group(group_id: int) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def contact_user(user_id: int) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def dice() -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def face(id_: int) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def forward(id_: str) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def image(file: str,
+ type_: Optional[str] = ...,
+ cache: bool = ...,
+ proxy: bool = ...,
+ timeout: Optional[int] = ...) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def json(data: str) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def location(latitude: float,
+ longitude: float,
+ title: Optional[str] = ...,
+ content: Optional[str] = ...) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def music(type_: str, id_: int) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def music_custom(url: str,
+ audio: str,
+ title: str,
+ content: Optional[str] = ...,
+ img_url: Optional[str] = ...) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def node(id_: int) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def node_custom(user_id: int, nickname: str,
+ content: Union[str, "Message"]) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def poke(type_: str, id_: str) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def record(file: str,
+ magic: Optional[bool] = ...,
+ cache: Optional[bool] = ...,
+ proxy: Optional[bool] = ...,
+ timeout: Optional[int] = ...) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def reply(id_: int) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def rps() -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def shake() -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def share(url: str = ...,
+ title: str = ...,
+ content: Optional[str] = ...,
+ img_url: Optional[str] = ...) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def text(text: str) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def video(file: str,
+ cache: Optional[bool] = ...,
+ proxy: Optional[bool] = ...,
+ timeout: Optional[int] = ...) -> "MessageSegment":
+ ...
+
+ @staticmethod
+ def xml(data: str) -> "MessageSegment":
+ ...
+
+
+class Message:
+
+ @staticmethod
+ def _construct(msg: Union[str, dict, list]) -> Iterable[MessageSegment]:
+ ...
diff --git a/nonebot/drivers/fastapi.py b/nonebot/drivers/fastapi.py
index 9b95a462..8a52b3df 100644
--- a/nonebot/drivers/fastapi.py
+++ b/nonebot/drivers/fastapi.py
@@ -200,8 +200,8 @@ class Driver(BaseDriver):
websocket=ws)
else:
logger.warning("Unknown adapter")
- raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
- detail="adapter not found")
+ await ws.close(code=status.WS_1008_POLICY_VIOLATION)
+ return
await ws.accept()
self._clients[x_self_id] = bot
diff --git a/nonebot/matcher.py b/nonebot/matcher.py
index a2a5ad66..76465b0b 100644
--- a/nonebot/matcher.py
+++ b/nonebot/matcher.py
@@ -159,15 +159,18 @@ class Matcher(metaclass=MatcherMeta):
) -> Callable[[Handler], Handler]:
async def _key_getter(bot: Bot, event: Event, state: dict):
+ state["_current_key"] = key
if key not in state:
- state["_current_key"] = key
if prompt:
await bot.send(event=event, message=prompt)
raise PausedException
+ else:
+ state["_skip_key"] = True
async def _key_parser(bot: Bot, event: Event, state: dict):
- # if key in state:
- # return
+ if key in state and state.get("_skip_key"):
+ del state["_skip_key"]
+ return
parser = args_parser or cls._default_parser
if parser:
await parser(bot, event, state)
@@ -185,6 +188,8 @@ class Matcher(metaclass=MatcherMeta):
async def wrapper(bot: Bot, event: Event, state: dict):
await parser(bot, event, state)
await func(bot, event, state)
+ if "_current_key" in state:
+ del state["_current_key"]
cls.handlers.append(wrapper)
@@ -273,3 +278,172 @@ class Matcher(metaclass=MatcherMeta):
logger.info(f"Matcher {self} running complete")
current_bot.reset(b_t)
current_event.reset(e_t)
+
+
+class MatcherGroup:
+
+ def __init__(self,
+ type_: str = "",
+ rule: Rule = Rule(),
+ permission: Permission = Permission(),
+ handlers: Optional[list] = None,
+ temp: bool = False,
+ priority: int = 1,
+ block: bool = False,
+ *,
+ module: Optional[str] = None,
+ default_state: Optional[dict] = None,
+ expire_time: Optional[datetime] = None):
+ self.matchers: List[Type[Matcher]] = []
+
+ self.type = type_
+ self.rule = rule
+ self.permission = permission
+ self.handlers = handlers
+ self.temp = temp
+ self.priority = priority
+ self.block = block
+ self.module = module
+ self.default_state = default_state
+ self.expire_time = expire_time
+
+ def __repr__(self) -> str:
+ return (
+ f"")
+
+ def __str__(self) -> str:
+ return self.__repr__()
+
+ def new(self,
+ type_: str = "",
+ rule: Rule = Rule(),
+ permission: Permission = Permission(),
+ handlers: Optional[list] = None,
+ temp: bool = False,
+ priority: int = 1,
+ block: bool = False,
+ *,
+ module: Optional[str] = None,
+ default_state: Optional[dict] = None,
+ expire_time: Optional[datetime] = None) -> Type[Matcher]:
+ matcher = Matcher.new(type_=type_ or self.type,
+ rule=self.rule & rule,
+ permission=permission or self.permission,
+ handlers=handlers or self.handlers,
+ temp=temp or self.temp,
+ priority=priority or self.priority,
+ block=block or self.block,
+ module=module or self.module,
+ default_state=default_state or self.default_state,
+ expire_time=expire_time or self.expire_time)
+ self.matchers.append(matcher)
+ return matcher
+
+ def args_parser(self, func: ArgsParser) -> ArgsParser:
+ for matcher in self.matchers:
+ matcher.args_parser(func)
+ return func
+
+ def handle(self) -> Callable[[Handler], Handler]:
+ """直接处理消息事件"""
+
+ def _decorator(func: Handler) -> Handler:
+ self.handlers.append(func)
+ return func
+
+ return _decorator
+
+ def receive(self) -> Callable[[Handler], Handler]:
+ """接收一条新消息并处理"""
+
+ async def _receive(bot: Bot, event: Event, state: dict) -> NoReturn:
+ raise PausedException
+
+ if self.handlers:
+ # 已有前置handlers则接受一条新的消息,否则视为接收初始消息
+ self.handlers.append(_receive)
+
+ def _decorator(func: Handler) -> Handler:
+ if not self.handlers or self.handlers[-1] is not func:
+ self.handlers.append(func)
+
+ return func
+
+ return _decorator
+
+ def got(
+ self,
+ key: str,
+ prompt: Optional[str] = None,
+ args_parser: Optional[ArgsParser] = None
+ ) -> Callable[[Handler], Handler]:
+
+ async def _key_getter(bot: Bot, event: Event, state: dict):
+ state["_current_key"] = key
+ if key not in state:
+ if prompt:
+ await bot.send(event=event, message=prompt)
+ raise PausedException
+ else:
+ state["_skip_key"] = True
+
+ async def _key_parser(bot: Bot, event: Event, state: dict):
+ if key in state and state.get("_skip_key"):
+ del state["_skip_key"]
+ return
+ parser = args_parser or self._default_parser
+ if parser:
+ await parser(bot, event, state)
+ else:
+ state[state["_current_key"]] = str(event.message)
+
+ self.handlers.append(_key_getter)
+ self.handlers.append(_key_parser)
+
+ def _decorator(func: Handler) -> Handler:
+ if not hasattr(self.handlers[-1], "__wrapped__"):
+ parser = self.handlers.pop()
+
+ @wraps(func)
+ async def wrapper(bot: Bot, event: Event, state: dict):
+ await parser(bot, event, state)
+ await func(bot, event, state)
+ if "_current_key" in state:
+ del state["_current_key"]
+
+ self.handlers.append(wrapper)
+
+ return func
+
+ return _decorator
+
+ async def finish(
+ self,
+ prompt: Optional[Union[str, Message,
+ MessageSegment]] = None) -> NoReturn:
+ bot: Bot = current_bot.get()
+ event: Event = current_event.get()
+ if prompt:
+ await bot.send(event=event, message=prompt)
+ raise FinishedException
+
+ async def pause(
+ self,
+ prompt: Optional[Union[str, Message,
+ MessageSegment]] = None) -> NoReturn:
+ bot: Bot = current_bot.get()
+ event: Event = current_event.get()
+ if prompt:
+ await bot.send(event=event, message=prompt)
+ raise PausedException
+
+ async def reject(
+ self,
+ prompt: Optional[Union[str, Message,
+ MessageSegment]] = None) -> NoReturn:
+ bot: Bot = current_bot.get()
+ event: Event = current_event.get()
+ if prompt:
+ await bot.send(event=event, message=prompt)
+ raise RejectedException
diff --git a/nonebot/message.py b/nonebot/message.py
index b6c3d888..f3a69e3a 100644
--- a/nonebot/message.py
+++ b/nonebot/message.py
@@ -6,6 +6,7 @@ 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.typing import Set, Type, Union, NoReturn
from nonebot.typing import Bot, Event, Matcher, PreProcessor
@@ -64,7 +65,9 @@ async def handle_event(bot: Bot, event: Event):
log_msg += f"@[群:{event.group_id}]:"
log_msg += ' "' + "".join(
- map(lambda x: str(x) if x.type == "text" else f"{x!s}",
+ map(
+ lambda x: escape_tag(str(x))
+ if x.type == "text" else f"{escape_tag(str(x))}",
event.message)) + '"' # type: ignore
elif event.type == "notice":
log_msg += f"Notice {event.raw_event}"
diff --git a/nonebot/permission.py b/nonebot/permission.py
index 1f43cf38..e194f453 100644
--- a/nonebot/permission.py
+++ b/nonebot/permission.py
@@ -14,7 +14,7 @@
import asyncio
from nonebot.utils import run_sync
-from nonebot.typing import Bot, Event, Union, NoReturn, Callable, Awaitable, PermissionChecker
+from nonebot.typing import Bot, Event, Union, NoReturn, Optional, Callable, Awaitable, PermissionChecker
class Permission:
@@ -53,10 +53,13 @@ class Permission:
def __and__(self, other) -> NoReturn:
raise RuntimeError("And operation between Permissions is not allowed.")
- def __or__(self, other: Union["Permission",
- PermissionChecker]) -> "Permission":
+ def __or__(
+ self, other: Optional[Union["Permission",
+ PermissionChecker]]) -> "Permission":
checkers = self.checkers.copy()
- if isinstance(other, Permission):
+ if other is None:
+ return self
+ elif isinstance(other, Permission):
checkers |= other.checkers
elif asyncio.iscoroutinefunction(other):
checkers.add(other) # type: ignore
diff --git a/nonebot/plugin.py b/nonebot/plugin.py
index ba526fcf..82439f1a 100644
--- a/nonebot/plugin.py
+++ b/nonebot/plugin.py
@@ -9,9 +9,9 @@ from dataclasses import dataclass
from importlib._bootstrap import _load
from nonebot.log import logger
-from nonebot.matcher import Matcher
from nonebot.permission import Permission
from nonebot.typing import Handler, RuleChecker
+from nonebot.matcher import Matcher, MatcherGroup
from nonebot.rule import Rule, startswith, endswith, command, regex
from nonebot.typing import Set, List, Dict, Type, Tuple, Union, Optional, ModuleType
@@ -27,8 +27,8 @@ class Plugin(object):
matcher: Set[Type[Matcher]]
-def on(rule: Union[Rule, RuleChecker] = Rule(),
- permission: Permission = Permission(),
+def on(rule: Optional[Union[Rule, RuleChecker]] = None,
+ permission: Optional[Permission] = None,
*,
handlers: Optional[List[Handler]] = None,
temp: bool = False,
@@ -37,7 +37,7 @@ def on(rule: Union[Rule, RuleChecker] = Rule(),
state: Optional[dict] = None) -> Type[Matcher]:
matcher = Matcher.new("",
Rule() & rule,
- permission,
+ permission or Permission(),
temp=temp,
priority=priority,
block=block,
@@ -47,7 +47,7 @@ def on(rule: Union[Rule, RuleChecker] = Rule(),
return matcher
-def on_metaevent(rule: Union[Rule, RuleChecker] = Rule(),
+def on_metaevent(rule: Optional[Union[Rule, RuleChecker]] = None,
*,
handlers: Optional[List[Handler]] = None,
temp: bool = False,
@@ -66,8 +66,8 @@ def on_metaevent(rule: Union[Rule, RuleChecker] = Rule(),
return matcher
-def on_message(rule: Union[Rule, RuleChecker] = Rule(),
- permission: Permission = Permission(),
+def on_message(rule: Optional[Union[Rule, RuleChecker]] = None,
+ permission: Optional[Permission] = None,
*,
handlers: Optional[List[Handler]] = None,
temp: bool = False,
@@ -76,7 +76,7 @@ def on_message(rule: Union[Rule, RuleChecker] = Rule(),
state: Optional[dict] = None) -> Type[Matcher]:
matcher = Matcher.new("message",
Rule() & rule,
- permission,
+ permission or Permission(),
temp=temp,
priority=priority,
block=block,
@@ -86,7 +86,7 @@ def on_message(rule: Union[Rule, RuleChecker] = Rule(),
return matcher
-def on_notice(rule: Union[Rule, RuleChecker] = Rule(),
+def on_notice(rule: Optional[Union[Rule, RuleChecker]] = None,
*,
handlers: Optional[List[Handler]] = None,
temp: bool = False,
@@ -105,7 +105,7 @@ def on_notice(rule: Union[Rule, RuleChecker] = Rule(),
return matcher
-def on_request(rule: Union[Rule, RuleChecker] = Rule(),
+def on_request(rule: Optional[Union[Rule, RuleChecker]] = None,
*,
handlers: Optional[List[Handler]] = None,
temp: bool = False,
@@ -125,27 +125,23 @@ def on_request(rule: Union[Rule, RuleChecker] = Rule(),
def on_startswith(msg: str,
- rule: Optional[Union[Rule, RuleChecker]] = None,
- permission: Permission = Permission(),
+ rule: Optional[Optional[Union[Rule, RuleChecker]]] = None,
**kwargs) -> Type[Matcher]:
- return on_message(startswith(msg) &
- rule, permission, **kwargs) if rule else on_message(
- startswith(msg), permission, **kwargs)
+ return on_message(startswith(msg) & rule, **kwargs) if rule else on_message(
+ startswith(msg), **kwargs)
def on_endswith(msg: str,
- rule: Optional[Union[Rule, RuleChecker]] = None,
- permission: Permission = Permission(),
+ rule: Optional[Optional[Union[Rule, RuleChecker]]] = None,
**kwargs) -> Type[Matcher]:
- return on_message(endswith(msg) &
- rule, permission, **kwargs) if rule else on_message(
- startswith(msg), permission, **kwargs)
+ return on_message(endswith(msg) & rule, **kwargs) if rule else on_message(
+ startswith(msg), **kwargs)
def on_command(cmd: Union[str, Tuple[str, ...]],
rule: Optional[Union[Rule, RuleChecker]] = None,
- permission: Permission = Permission(),
- **kwargs) -> Type[Matcher]:
+ aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = None,
+ **kwargs) -> Union[Type[Matcher], MatcherGroup]:
if isinstance(cmd, str):
cmd = (cmd,)
@@ -157,20 +153,28 @@ def on_command(cmd: Union[str, Tuple[str, ...]],
handlers = kwargs.pop("handlers", [])
handlers.insert(0, _strip_cmd)
- return on_message(
- command(cmd) &
- rule, permission, handlers=handlers, **kwargs) if rule else on_message(
- command(cmd), permission, handlers=handlers, **kwargs)
+ if aliases:
+ aliases = set(map(lambda x: (x,) if isinstance(x, str) else x, aliases))
+ group = MatcherGroup("message",
+ Rule() & rule,
+ handlers=handlers,
+ **kwargs)
+ for cmd_ in [cmd, *aliases]:
+ _tmp_matchers.add(group.new(rule=command(cmd_)))
+ return group
+ else:
+ return on_message(command(cmd) & rule, handlers=handlers, **
+ kwargs) if rule else on_message(
+ command(cmd), handlers=handlers, **kwargs)
def on_regex(pattern: str,
flags: Union[int, re.RegexFlag] = 0,
rule: Optional[Rule] = None,
- permission: Permission = Permission(),
**kwargs) -> Type[Matcher]:
return on_message(regex(pattern, flags) &
- rule, permission, **kwargs) if rule else on_message(
- regex(pattern, flags), permission, **kwargs)
+ rule, **kwargs) if rule else on_message(
+ regex(pattern, flags), **kwargs)
def load_plugin(module_path: str) -> Optional[Plugin]:
@@ -234,3 +238,21 @@ def load_builtin_plugins():
def get_loaded_plugins() -> Set[Plugin]:
return set(plugins.values())
+
+
+class CommandGroup:
+
+ def __init__(self, cmd: Union[str, Tuple[str, ...]], **kwargs):
+ self.basecmd = (cmd,) if isinstance(cmd, str) else cmd
+ if "aliases" in kwargs:
+ del kwargs["aliases"]
+ self.base_kwargs = kwargs
+
+ def command(self, cmd: Union[str, Tuple[str, ...]],
+ **kwargs) -> Union[Type[Matcher], MatcherGroup]:
+ sub_cmd = (cmd,) if isinstance(cmd, str) else cmd
+ cmd = self.basecmd + sub_cmd
+
+ final_kwargs = self.base_kwargs.copy()
+ final_kwargs.update(kwargs)
+ return on_command(cmd, **final_kwargs)
diff --git a/nonebot/plugin.pyi b/nonebot/plugin.pyi
new file mode 100644
index 00000000..f75a2ee4
--- /dev/null
+++ b/nonebot/plugin.pyi
@@ -0,0 +1,180 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+import re
+from typing import overload
+
+from nonebot.typing import Rule, Matcher, Handler, Permission, RuleChecker, MatcherGroup
+from nonebot.typing import Set, List, Dict, Type, Tuple, Union, Optional, ModuleType
+
+plugins: Dict[str, "Plugin"] = ...
+
+_tmp_matchers: Set[Type[Matcher]] = ...
+
+
+class Plugin(object):
+ name: str
+ module: ModuleType
+ matcher: Set[Type[Matcher]]
+
+
+def on(rule: Optional[Union[Rule, RuleChecker]] = ...,
+ permission: Optional[Permission] = ...,
+ *,
+ handlers: Optional[List[Handler]] = ...,
+ temp: bool = ...,
+ priority: int = ...,
+ block: bool = ...,
+ state: Optional[dict] = ...) -> Type[Matcher]:
+ ...
+
+
+def on_metaevent(rule: Optional[Union[Rule, RuleChecker]] = ...,
+ *,
+ handlers: Optional[List[Handler]] = ...,
+ temp: bool = ...,
+ priority: int = ...,
+ block: bool = ...,
+ state: Optional[dict] = ...) -> Type[Matcher]:
+ ...
+
+
+def on_message(rule: Optional[Union[Rule, RuleChecker]] = ...,
+ permission: Optional[Permission] = ...,
+ *,
+ handlers: Optional[List[Handler]] = ...,
+ temp: bool = ...,
+ priority: int = ...,
+ block: bool = ...,
+ state: Optional[dict] = ...) -> Type[Matcher]:
+ ...
+
+
+def on_notice(rule: Optional[Union[Rule, RuleChecker]] = ...,
+ *,
+ handlers: Optional[List[Handler]] = ...,
+ temp: bool = ...,
+ priority: int = ...,
+ block: bool = ...,
+ state: Optional[dict] = ...) -> Type[Matcher]:
+ ...
+
+
+def on_request(rule: Optional[Union[Rule, RuleChecker]] = ...,
+ *,
+ handlers: Optional[List[Handler]] = ...,
+ temp: bool = ...,
+ priority: int = ...,
+ block: bool = ...,
+ state: Optional[dict] = ...) -> Type[Matcher]:
+ ...
+
+
+def on_startswith(msg: str,
+ rule: Optional[Optional[Union[Rule, RuleChecker]]] = ...,
+ permission: Optional[Permission] = ...,
+ *,
+ handlers: Optional[List[Handler]] = ...,
+ temp: bool = ...,
+ priority: int = ...,
+ block: bool = ...,
+ state: Optional[dict] = ...) -> Type[Matcher]:
+ ...
+
+
+def on_endswith(msg: str,
+ rule: Optional[Optional[Union[Rule, RuleChecker]]] = ...,
+ permission: Optional[Permission] = ...,
+ *,
+ handlers: Optional[List[Handler]] = ...,
+ temp: bool = ...,
+ priority: int = ...,
+ block: bool = ...,
+ state: Optional[dict] = ...) -> Type[Matcher]:
+ ...
+
+
+@overload
+def on_command(cmd: Union[str, Tuple[str, ...]],
+ rule: Optional[Union[Rule, RuleChecker]] = ...,
+ aliases: None = ...,
+ permission: Optional[Permission] = ...,
+ *,
+ handlers: Optional[List[Handler]] = ...,
+ temp: bool = ...,
+ priority: int = ...,
+ block: bool = ...,
+ state: Optional[dict] = ...) -> Type[Matcher]:
+ ...
+
+
+@overload
+def on_command(cmd: Union[str, Tuple[str, ...]],
+ rule: Optional[Union[Rule, RuleChecker]] = ...,
+ aliases: Set[Union[str, Tuple[str, ...]]] = ...,
+ permission: Optional[Permission] = ...,
+ *,
+ handlers: Optional[List[Handler]] = ...,
+ temp: bool = ...,
+ priority: int = ...,
+ block: bool = ...,
+ state: Optional[dict] = ...) -> MatcherGroup:
+ ...
+
+
+def on_regex(pattern: str,
+ flags: Union[int, re.RegexFlag] = 0,
+ rule: Optional[Rule] = ...,
+ permission: Optional[Permission] = ...,
+ *,
+ handlers: Optional[List[Handler]] = ...,
+ temp: bool = ...,
+ priority: int = ...,
+ block: bool = ...,
+ state: Optional[dict] = ...) -> Type[Matcher]:
+ ...
+
+
+def load_plugin(module_path: str) -> Optional[Plugin]:
+ ...
+
+
+def load_plugins(*plugin_dir: str) -> Set[Plugin]:
+ ...
+
+
+def load_builtin_plugins():
+ ...
+
+
+def get_loaded_plugins() -> Set[Plugin]:
+ ...
+
+
+class CommandGroup:
+
+ def __init__(self,
+ cmd: Union[str, Tuple[str, ...]],
+ rule: Optional[Union[Rule, RuleChecker]] = ...,
+ permission: Optional[Permission] = ...,
+ *,
+ handlers: Optional[List[Handler]] = ...,
+ temp: bool = ...,
+ priority: int = ...,
+ block: bool = ...,
+ state: Optional[dict] = ...):
+ ...
+
+ def command(
+ self,
+ cmd: Union[str, Tuple[str, ...]],
+ rule: Optional[Union[Rule, RuleChecker]] = ...,
+ aliases: Set[Union[str, Tuple[str, ...]]] = ...,
+ permission: Optional[Permission] = ...,
+ *,
+ handlers: Optional[List[Handler]] = ...,
+ temp: bool = ...,
+ priority: int = ...,
+ block: bool = ...,
+ state: Optional[dict] = ...) -> Union[Type[Matcher], MatcherGroup]:
+ ...
diff --git a/nonebot/rule.py b/nonebot/rule.py
index 0b1b5cc0..7d57b33e 100644
--- a/nonebot/rule.py
+++ b/nonebot/rule.py
@@ -20,7 +20,7 @@ from pygtrie import CharTrie
from nonebot import get_driver
from nonebot.log import logger
from nonebot.utils import run_sync
-from nonebot.typing import Bot, Any, Dict, Event, Union, Tuple, NoReturn, Callable, Awaitable, RuleChecker
+from nonebot.typing import Bot, Any, Dict, Event, Union, Tuple, NoReturn, Optional, Callable, Awaitable, RuleChecker
class Rule:
@@ -68,9 +68,11 @@ class Rule:
*map(lambda c: c(bot, event, state), self.checkers))
return all(results)
- def __and__(self, other: Union["Rule", RuleChecker]) -> "Rule":
+ def __and__(self, other: Optional[Union["Rule", RuleChecker]]) -> "Rule":
checkers = self.checkers.copy()
- if isinstance(other, Rule):
+ if other is None:
+ return self
+ elif isinstance(other, Rule):
checkers |= other.checkers
elif asyncio.iscoroutinefunction(other):
checkers.add(other) # type: ignore
diff --git a/nonebot/typing.py b/nonebot/typing.py
index 51834a08..5b722655 100644
--- a/nonebot/typing.py
+++ b/nonebot/typing.py
@@ -28,10 +28,10 @@ from typing import Union, TypeVar, Optional, Iterable, Callable, Awaitable
# import some modules needed when checking types
if TYPE_CHECKING:
from nonebot.rule import Rule as RuleClass
- from nonebot.matcher import Matcher as MatcherClass
from nonebot.drivers import BaseDriver, BaseWebSocket
from nonebot.permission import Permission as PermissionClass
from nonebot.adapters import BaseBot, BaseEvent, BaseMessage, BaseMessageSegment
+ from nonebot.matcher import Matcher as MatcherClass, MatcherGroup as MatcherGroupClass
def overrides(InterfaceClass: object):
@@ -112,6 +112,14 @@ Matcher = TypeVar("Matcher", bound="MatcherClass")
Matcher 即响应事件的处理类。通过 Rule 判断是否响应事件,运行 Handler。
"""
+MatcherGroup = TypeVar("MatcherGroup", bound="MatcherGroupClass")
+"""
+:类型: ``MatcherGroup``
+
+:说明:
+
+ MatcherGroup 为 Matcher 的集合。可以共享 Handler。
+"""
Rule = TypeVar("Rule", bound="RuleClass")
"""
:类型: ``Rule``
diff --git a/nonebot/utils.py b/nonebot/utils.py
index 06bdaaa4..9efeb8ca 100644
--- a/nonebot/utils.py
+++ b/nonebot/utils.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
+import re
import json
import asyncio
import dataclasses
@@ -9,6 +10,18 @@ from functools import wraps, partial
from nonebot.typing import Any, Callable, Awaitable, overrides
+def escape_tag(s: str) -> str:
+ """
+ :说明:
+ 用于记录带颜色日志时转义 ```` 类型特殊标签
+ :参数:
+ * ``s: str``: 需要转义的字符串
+ :返回:
+ - ``str``
+ """
+ return re.sub(r"?((?:[fb]g\s)?[^<>\s]*)>", r"\\\g<0>", s)
+
+
def run_sync(func: Callable[..., Any]) -> Callable[..., Awaitable[Any]]:
"""
:说明:
diff --git a/poetry.lock b/poetry.lock
index 10ae5b4c..f4d0c251 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -43,6 +43,41 @@ reference = "aliyun"
type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
+[[package]]
+category = "main"
+description = "Better dates & times for Python"
+name = "arrow"
+optional = true
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "0.16.0"
+
+[package.dependencies]
+python-dateutil = ">=2.7.0"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "Classes Without Boilerplate"
+name = "attrs"
+optional = true
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "20.2.0"
+
+[package.extras]
+dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"]
+docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
+tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"]
+tests_no_zope = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"]
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
[[package]]
category = "dev"
description = "Internationalization utilities"
@@ -59,6 +94,56 @@ reference = "aliyun"
type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
+[[package]]
+category = "main"
+description = "Modern password hashing for your software and your servers"
+name = "bcrypt"
+optional = true
+python-versions = ">=3.6"
+version = "3.2.0"
+
+[package.dependencies]
+cffi = ">=1.1"
+six = ">=1.4.1"
+
+[package.extras]
+tests = ["pytest (>=3.2.1,<3.3.0 || >3.3.0)"]
+typecheck = ["mypy"]
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "Ultra-lightweight pure Python package to check if a file is binary or text."
+name = "binaryornot"
+optional = true
+python-versions = "*"
+version = "0.4.4"
+
+[package.dependencies]
+chardet = ">=3.0.2"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "A decorator for caching properties in classes."
+name = "cached-property"
+optional = true
+python-versions = "*"
+version = "1.5.2"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
[[package]]
category = "main"
description = "Python package for providing Mozilla's CA Bundle."
@@ -72,6 +157,22 @@ reference = "aliyun"
type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
+[[package]]
+category = "main"
+description = "Foreign Function Interface for Python calling C code."
+name = "cffi"
+optional = true
+python-versions = "*"
+version = "1.14.3"
+
+[package.dependencies]
+pycparser = "*"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
[[package]]
category = "main"
description = "Universal encoding detector for Python 2 and 3"
@@ -101,7 +202,6 @@ url = "https://mirrors.aliyun.com/pypi/simple"
[[package]]
category = "main"
description = "Cross-platform colored terminal text."
-marker = "sys_platform == \"win32\""
name = "colorama"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
@@ -112,6 +212,157 @@ reference = "aliyun"
type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
+[[package]]
+category = "main"
+description = "A command-line utility that creates projects from project templates, e.g. creating a Python package project from a Python package project template."
+name = "cookiecutter"
+optional = true
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "1.7.2"
+
+[package.dependencies]
+Jinja2 = "<3.0.0"
+MarkupSafe = "<2.0.0"
+binaryornot = ">=0.4.4"
+click = ">=7.0"
+jinja2-time = ">=0.2.0"
+poyo = ">=0.5.0"
+python-slugify = ">=4.0.0"
+requests = ">=2.23.0"
+six = ">=1.10"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
+name = "cryptography"
+optional = true
+python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
+version = "3.1.1"
+
+[package.dependencies]
+cffi = ">=1.8,<1.11.3 || >1.11.3"
+six = ">=1.4.1"
+
+[package.extras]
+docs = ["sphinx (>=1.6.5,<1.8.0 || >1.8.0,<3.1.0 || >3.1.0,<3.1.1 || >3.1.1)", "sphinx-rtd-theme"]
+docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"]
+pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"]
+ssh = ["bcrypt (>=3.1.5)"]
+test = ["pytest (>=3.6.0,<3.9.0 || >3.9.0,<3.9.1 || >3.9.1,<3.9.2 || >3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,<3.79.2 || >3.79.2)"]
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "Distro - an OS platform information API"
+name = "distro"
+optional = true
+python-versions = "*"
+version = "1.5.0"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "A Python library for the Docker Engine API."
+name = "docker"
+optional = true
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "4.3.1"
+
+[package.dependencies]
+pywin32 = "227"
+requests = ">=2.14.2,<2.18.0 || >2.18.0"
+six = ">=1.4.0"
+websocket-client = ">=0.32.0"
+
+[package.dependencies.paramiko]
+optional = true
+version = ">=2.4.2"
+
+[package.extras]
+ssh = ["paramiko (>=2.4.2)"]
+tls = ["pyOpenSSL (>=17.5.0)", "cryptography (>=1.3.4)", "idna (>=2.0.0)"]
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "Multi-container orchestration for Docker"
+name = "docker-compose"
+optional = true
+python-versions = ">=3.4"
+version = "1.27.4"
+
+[package.dependencies]
+PyYAML = ">=3.10,<6"
+cached-property = ">=1.2.0,<2"
+colorama = ">=0.4,<1"
+distro = ">=1.5.0,<2"
+dockerpty = ">=0.4.1,<1"
+docopt = ">=0.6.1,<1"
+jsonschema = ">=2.5.1,<4"
+python-dotenv = ">=0.13.0,<1"
+requests = ">=2.20.0,<3"
+texttable = ">=0.9.0,<2"
+websocket-client = ">=0.32.0,<1"
+
+[package.dependencies.docker]
+extras = ["ssh"]
+version = ">=4.3.1,<5"
+
+[package.extras]
+socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2)"]
+tests = ["ddt (>=1.2.2,<2)", "pytest (<6)"]
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "Python library to use the pseudo-tty of a docker container"
+name = "dockerpty"
+optional = true
+python-versions = "*"
+version = "0.4.1"
+
+[package.dependencies]
+six = ">=1.3.0"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "Pythonic argument parser, that will make you smile"
+name = "docopt"
+optional = true
+python-versions = "*"
+version = "0.6.2"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
[[package]]
category = "dev"
description = "Docutils -- Python Documentation Utilities"
@@ -197,7 +448,7 @@ description = "Chromium HSTS Preload list as a Python package and updated daily"
name = "hstspreload"
optional = false
python-versions = ">=3.6"
-version = "2020.9.15"
+version = "2020.9.29"
[package.source]
reference = "aliyun"
@@ -314,7 +565,28 @@ type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
[[package]]
-category = "dev"
+category = "main"
+description = "Read metadata from Python packages"
+marker = "python_version < \"3.8\""
+name = "importlib-metadata"
+optional = true
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+version = "2.0.0"
+
+[package.dependencies]
+zipp = ">=0.5"
+
+[package.extras]
+docs = ["sphinx", "rst.linker"]
+testing = ["packaging", "pep517", "importlib-resources (>=1.3)"]
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
description = "A very fast and expressive template engine."
name = "jinja2"
optional = false
@@ -332,13 +604,57 @@ reference = "aliyun"
type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
+[[package]]
+category = "main"
+description = "Jinja2 Extension for Dates and Times"
+name = "jinja2-time"
+optional = true
+python-versions = "*"
+version = "0.2.0"
+
+[package.dependencies]
+arrow = "*"
+jinja2 = "*"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "An implementation of JSON Schema validation for Python"
+name = "jsonschema"
+optional = true
+python-versions = "*"
+version = "3.2.0"
+
+[package.dependencies]
+attrs = ">=17.4.0"
+pyrsistent = ">=0.14.0"
+setuptools = "*"
+six = ">=1.11.0"
+
+[package.dependencies.importlib-metadata]
+python = "<3.8"
+version = "*"
+
+[package.extras]
+format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"]
+format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"]
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
[[package]]
category = "main"
description = "Python logging made (stupidly) simple"
name = "loguru"
optional = false
python-versions = ">=3.5"
-version = "0.5.2"
+version = "0.5.3"
[package.dependencies]
colorama = ">=0.3.4"
@@ -353,7 +669,7 @@ type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
[[package]]
-category = "dev"
+category = "main"
description = "Safely add untrusted strings to HTML/XML markup."
name = "markupsafe"
optional = false
@@ -365,6 +681,28 @@ reference = "aliyun"
type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
+[[package]]
+category = "main"
+description = "CLI for nonebot2"
+name = "nb-cli"
+optional = true
+python-versions = ">=3.7,<4.0"
+version = "0.1.0"
+
+[package.dependencies]
+click = ">=7.1.2,<8.0.0"
+colorama = ">=0.4.3,<0.5.0"
+cookiecutter = ">=1.7.2,<2.0.0"
+docker-compose = ">=1.27.2,<2.0.0"
+nonebot2 = ">=2.0.0-alpha.1,<3.0.0"
+pyfiglet = ">=0.8.post1,<0.9"
+pyinquirer = "1.0.3"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
[[package]]
category = "dev"
description = "Core utilities for Python packages"
@@ -382,6 +720,73 @@ reference = "aliyun"
type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
+[[package]]
+category = "main"
+description = "SSH2 protocol library"
+name = "paramiko"
+optional = true
+python-versions = "*"
+version = "2.7.2"
+
+[package.dependencies]
+bcrypt = ">=3.1.3"
+cryptography = ">=2.5"
+pynacl = ">=1.0.1"
+
+[package.extras]
+all = ["pyasn1 (>=0.1.7)", "pynacl (>=1.0.1)", "bcrypt (>=3.1.3)", "invoke (>=1.3)", "gssapi (>=1.4.1)", "pywin32 (>=2.1.8)"]
+ed25519 = ["pynacl (>=1.0.1)", "bcrypt (>=3.1.3)"]
+gssapi = ["pyasn1 (>=0.1.7)", "gssapi (>=1.4.1)", "pywin32 (>=2.1.8)"]
+invoke = ["invoke (>=1.3)"]
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "A lightweight YAML Parser for Python. 🐓"
+name = "poyo"
+optional = true
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "0.5.0"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "Library for building powerful interactive command lines in Python"
+name = "prompt-toolkit"
+optional = true
+python-versions = "*"
+version = "1.0.14"
+
+[package.dependencies]
+six = ">=1.9.0"
+wcwidth = "*"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "C parser in Python"
+name = "pycparser"
+optional = true
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "2.20"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
[[package]]
category = "main"
description = "Data validation and settings management using python 3.6 type hinting"
@@ -422,7 +827,20 @@ type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
[[package]]
-category = "dev"
+category = "main"
+description = "Pure-python FIGlet implementation"
+name = "pyfiglet"
+optional = true
+python-versions = "*"
+version = "0.8.post1"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
description = "Pygments is a syntax highlighting package written in Python."
name = "pygments"
optional = false
@@ -447,6 +865,45 @@ reference = "aliyun"
type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
+[[package]]
+category = "main"
+description = "A Python module for collection of common interactive command line user interfaces, based on Inquirer.js"
+name = "pyinquirer"
+optional = true
+python-versions = "*"
+version = "1.0.3"
+
+[package.dependencies]
+Pygments = ">=2.2.0"
+prompt_toolkit = "1.0.14"
+regex = ">=2016.11.21"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "Python binding to the Networking and Cryptography (NaCl) library"
+name = "pynacl"
+optional = true
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "1.4.0"
+
+[package.dependencies]
+cffi = ">=1.4.1"
+six = "*"
+
+[package.extras]
+docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"]
+tests = ["pytest (>=3.2.1,<3.3.0 || >3.3.0)", "hypothesis (>=3.27.0)"]
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
[[package]]
category = "dev"
description = "Python parsing module"
@@ -460,6 +917,35 @@ reference = "aliyun"
type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
+[[package]]
+category = "main"
+description = "Persistent/Functional/Immutable data structures"
+name = "pyrsistent"
+optional = true
+python-versions = ">=3.5"
+version = "0.17.3"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "Extensions to the standard Python datetime module"
+name = "python-dateutil"
+optional = true
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+version = "2.8.1"
+
+[package.dependencies]
+six = ">=1.5"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
[[package]]
category = "main"
description = "Add .env support to your django/flask apps in development and deployments"
@@ -476,6 +962,25 @@ reference = "aliyun"
type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
+[[package]]
+category = "main"
+description = "A Python Slugify application that handles Unicode"
+name = "python-slugify"
+optional = true
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "4.0.1"
+
+[package.dependencies]
+text-unidecode = ">=1.3"
+
+[package.extras]
+unidecode = ["Unidecode (>=1.1.1)"]
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
[[package]]
category = "main"
description = "World timezone definitions, modern and historical"
@@ -490,7 +995,47 @@ type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
[[package]]
-category = "dev"
+category = "main"
+description = "Python for Window Extensions"
+marker = "sys_platform == \"win32\""
+name = "pywin32"
+optional = true
+python-versions = "*"
+version = "227"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "YAML parser and emitter for Python"
+name = "pyyaml"
+optional = true
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "5.3.1"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "Alternative regular expression module, to replace re."
+name = "regex"
+optional = true
+python-versions = "*"
+version = "2020.9.27"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
description = "Python HTTP for Humans."
name = "requests"
optional = false
@@ -620,7 +1165,7 @@ unify = "*"
yapf = "*"
[package.source]
-reference = "792133d3bb15b956e5150c158371eb71f5670844"
+reference = "09751bd6c81ee246d91d63baa65d09e3618b7288"
type = "git"
url = "https://github.com/nonebot/sphinx-markdown-builder.git"
[[package]]
@@ -740,6 +1285,32 @@ reference = "aliyun"
type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
+[[package]]
+category = "main"
+description = "The most basic Text::Unidecode port"
+name = "text-unidecode"
+optional = true
+python-versions = "*"
+version = "1.3"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "module for creating simple ASCII tables"
+name = "texttable"
+optional = true
+python-versions = "*"
+version = "1.6.3"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
[[package]]
category = "main"
description = "tzinfo object for the local timezone"
@@ -786,7 +1357,7 @@ type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
[[package]]
-category = "dev"
+category = "main"
description = "HTTP library with thread-safe connection pooling, file post, and more."
name = "urllib3"
optional = false
@@ -840,6 +1411,35 @@ reference = "aliyun"
type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
+[[package]]
+category = "main"
+description = "Measures the displayed width of unicode strings in a terminal"
+name = "wcwidth"
+optional = true
+python-versions = "*"
+version = "0.2.5"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
+[[package]]
+category = "main"
+description = "WebSocket client for Python. hybi13 is supported."
+name = "websocket-client"
+optional = true
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "0.57.0"
+
+[package.dependencies]
+six = "*"
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
[[package]]
category = "main"
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
@@ -883,12 +1483,31 @@ reference = "aliyun"
type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
+[[package]]
+category = "main"
+description = "Backport of pathlib-compatible object wrapper for zip files"
+marker = "python_version < \"3.8\""
+name = "zipp"
+optional = true
+python-versions = ">=3.6"
+version = "3.2.0"
+
+[package.extras]
+docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
+
+[package.source]
+reference = "aliyun"
+type = "legacy"
+url = "https://mirrors.aliyun.com/pypi/simple"
+
[extras]
-full = []
+cli = ["nb-cli"]
+full = ["nb-cli"]
scheduler = ["apscheduler"]
[metadata]
-content-hash = "3a0f821e8ecef5548fa95e88cee11ff12910d08bed1633d8d03280f65d0bc1bf"
+content-hash = "b8e6ee3b0726717b1cfa1978693f1c4c25cf6fe044784ef5661b9bee50c34d9a"
lock-version = "1.0"
python-versions = "^3.7"
@@ -901,14 +1520,77 @@ apscheduler = [
{file = "APScheduler-3.6.3-py2.py3-none-any.whl", hash = "sha256:e8b1ecdb4c7cb2818913f766d5898183c7cb8936680710a4d3a966e02262e526"},
{file = "APScheduler-3.6.3.tar.gz", hash = "sha256:3bb5229eed6fbbdafc13ce962712ae66e175aa214c69bed35a06bffcf0c5e244"},
]
+arrow = [
+ {file = "arrow-0.16.0-py2.py3-none-any.whl", hash = "sha256:98184d8dd3e5d30b96c2df4596526f7de679ccb467f358b82b0f686436f3a6b8"},
+ {file = "arrow-0.16.0.tar.gz", hash = "sha256:92aac856ea5175c804f7ccb96aca4d714d936f1c867ba59d747a8096ec30e90a"},
+]
+attrs = [
+ {file = "attrs-20.2.0-py2.py3-none-any.whl", hash = "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"},
+ {file = "attrs-20.2.0.tar.gz", hash = "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594"},
+]
babel = [
{file = "Babel-2.8.0-py2.py3-none-any.whl", hash = "sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"},
{file = "Babel-2.8.0.tar.gz", hash = "sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38"},
]
+bcrypt = [
+ {file = "bcrypt-3.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c95d4cbebffafcdd28bd28bb4e25b31c50f6da605c81ffd9ad8a3d1b2ab7b1b6"},
+ {file = "bcrypt-3.2.0-cp36-abi3-manylinux1_x86_64.whl", hash = "sha256:63d4e3ff96188e5898779b6057878fecf3f11cfe6ec3b313ea09955d587ec7a7"},
+ {file = "bcrypt-3.2.0-cp36-abi3-manylinux2010_x86_64.whl", hash = "sha256:cd1ea2ff3038509ea95f687256c46b79f5fc382ad0aa3664d200047546d511d1"},
+ {file = "bcrypt-3.2.0-cp36-abi3-manylinux2014_aarch64.whl", hash = "sha256:cdcdcb3972027f83fe24a48b1e90ea4b584d35f1cc279d76de6fc4b13376239d"},
+ {file = "bcrypt-3.2.0-cp36-abi3-win32.whl", hash = "sha256:a67fb841b35c28a59cebed05fbd3e80eea26e6d75851f0574a9273c80f3e9b55"},
+ {file = "bcrypt-3.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:81fec756feff5b6818ea7ab031205e1d323d8943d237303baca2c5f9c7846f34"},
+ {file = "bcrypt-3.2.0.tar.gz", hash = "sha256:5b93c1726e50a93a033c36e5ca7fdcd29a5c7395af50a6892f5d9e7c6cfbfb29"},
+]
+binaryornot = [
+ {file = "binaryornot-0.4.4-py2.py3-none-any.whl", hash = "md5:7a69d00f3c384693c9616c3381c411a3"},
+ {file = "binaryornot-0.4.4.tar.gz", hash = "md5:09c0b7f5f3f7c881e2f306780eac7128"},
+]
+cached-property = [
+ {file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"},
+ {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"},
]
+cffi = [
+ {file = "cffi-1.14.3-2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc"},
+ {file = "cffi-1.14.3-2-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:cb763ceceae04803adcc4e2d80d611ef201c73da32d8f2722e9d0ab0c7f10768"},
+ {file = "cffi-1.14.3-2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f60519595eaca110f248e5017363d751b12782a6f2bd6a7041cba275215f5d"},
+ {file = "cffi-1.14.3-2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c53af463f4a40de78c58b8b2710ade243c81cbca641e34debf3396a9640d6ec1"},
+ {file = "cffi-1.14.3-2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:33c6cdc071ba5cd6d96769c8969a0531be2d08c2628a0143a10a7dcffa9719ca"},
+ {file = "cffi-1.14.3-2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c11579638288e53fc94ad60022ff1b67865363e730ee41ad5e6f0a17188b327a"},
+ {file = "cffi-1.14.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3cb3e1b9ec43256c4e0f8d2837267a70b0e1ca8c4f456685508ae6106b1f504c"},
+ {file = "cffi-1.14.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f0620511387790860b249b9241c2f13c3a80e21a73e0b861a2df24e9d6f56730"},
+ {file = "cffi-1.14.3-cp27-cp27m-win32.whl", hash = "sha256:005f2bfe11b6745d726dbb07ace4d53f057de66e336ff92d61b8c7e9c8f4777d"},
+ {file = "cffi-1.14.3-cp27-cp27m-win_amd64.whl", hash = "sha256:2f9674623ca39c9ebe38afa3da402e9326c245f0f5ceff0623dccdac15023e05"},
+ {file = "cffi-1.14.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:09e96138280241bd355cd585148dec04dbbedb4f46128f340d696eaafc82dd7b"},
+ {file = "cffi-1.14.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:3363e77a6176afb8823b6e06db78c46dbc4c7813b00a41300a4873b6ba63b171"},
+ {file = "cffi-1.14.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0ef488305fdce2580c8b2708f22d7785ae222d9825d3094ab073e22e93dfe51f"},
+ {file = "cffi-1.14.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:0b1ad452cc824665ddc682400b62c9e4f5b64736a2ba99110712fdee5f2505c4"},
+ {file = "cffi-1.14.3-cp35-cp35m-win32.whl", hash = "sha256:85ba797e1de5b48aa5a8427b6ba62cf69607c18c5d4eb747604b7302f1ec382d"},
+ {file = "cffi-1.14.3-cp35-cp35m-win_amd64.whl", hash = "sha256:e66399cf0fc07de4dce4f588fc25bfe84a6d1285cc544e67987d22663393926d"},
+ {file = "cffi-1.14.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:15f351bed09897fbda218e4db5a3d5c06328862f6198d4fb385f3e14e19decb3"},
+ {file = "cffi-1.14.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4d7c26bfc1ea9f92084a1d75e11999e97b62d63128bcc90c3624d07813c52808"},
+ {file = "cffi-1.14.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:23e5d2040367322824605bc29ae8ee9175200b92cb5483ac7d466927a9b3d537"},
+ {file = "cffi-1.14.3-cp36-cp36m-win32.whl", hash = "sha256:a624fae282e81ad2e4871bdb767e2c914d0539708c0f078b5b355258293c98b0"},
+ {file = "cffi-1.14.3-cp36-cp36m-win_amd64.whl", hash = "sha256:de31b5164d44ef4943db155b3e8e17929707cac1e5bd2f363e67a56e3af4af6e"},
+ {file = "cffi-1.14.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f92cdecb618e5fa4658aeb97d5eb3d2f47aa94ac6477c6daf0f306c5a3b9e6b1"},
+ {file = "cffi-1.14.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:22399ff4870fb4c7ef19fff6eeb20a8bbf15571913c181c78cb361024d574579"},
+ {file = "cffi-1.14.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:f4eae045e6ab2bb54ca279733fe4eb85f1effda392666308250714e01907f394"},
+ {file = "cffi-1.14.3-cp37-cp37m-win32.whl", hash = "sha256:b0358e6fefc74a16f745afa366acc89f979040e0cbc4eec55ab26ad1f6a9bfbc"},
+ {file = "cffi-1.14.3-cp37-cp37m-win_amd64.whl", hash = "sha256:6642f15ad963b5092d65aed022d033c77763515fdc07095208f15d3563003869"},
+ {file = "cffi-1.14.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:2791f68edc5749024b4722500e86303a10d342527e1e3bcac47f35fbd25b764e"},
+ {file = "cffi-1.14.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:529c4ed2e10437c205f38f3691a68be66c39197d01062618c55f74294a4a4828"},
+ {file = "cffi-1.14.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f0f1e499e4000c4c347a124fa6a27d37608ced4fe9f7d45070563b7c4c370c9"},
+ {file = "cffi-1.14.3-cp38-cp38-win32.whl", hash = "sha256:3b8eaf915ddc0709779889c472e553f0d3e8b7bdf62dab764c8921b09bf94522"},
+ {file = "cffi-1.14.3-cp38-cp38-win_amd64.whl", hash = "sha256:bbd2f4dfee1079f76943767fce837ade3087b578aeb9f69aec7857d5bf25db15"},
+ {file = "cffi-1.14.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:cc75f58cdaf043fe6a7a6c04b3b5a0e694c6a9e24050967747251fb80d7bce0d"},
+ {file = "cffi-1.14.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:bf39a9e19ce7298f1bd6a9758fa99707e9e5b1ebe5e90f2c3913a47bc548747c"},
+ {file = "cffi-1.14.3-cp39-cp39-win32.whl", hash = "sha256:d80998ed59176e8cba74028762fbd9b9153b9afc71ea118e63bbf5d4d0f9552b"},
+ {file = "cffi-1.14.3-cp39-cp39-win_amd64.whl", hash = "sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3"},
+ {file = "cffi-1.14.3.tar.gz", hash = "sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591"},
+]
chardet = [
{file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "md5:0004b00caff7bb543a1d0d0bd0185a03"},
{file = "chardet-3.0.4.tar.gz", hash = "md5:7dd1ba7f9c77e32351b0a0cfacf4055c"},
@@ -921,6 +1603,52 @@ colorama = [
{file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
{file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"},
]
+cookiecutter = [
+ {file = "cookiecutter-1.7.2-py2.py3-none-any.whl", hash = "sha256:430eb882d028afb6102c084bab6cf41f6559a77ce9b18dc6802e3bc0cc5f4a30"},
+ {file = "cookiecutter-1.7.2.tar.gz", hash = "sha256:efb6b2d4780feda8908a873e38f0e61778c23f6a2ea58215723bcceb5b515dac"},
+]
+cryptography = [
+ {file = "cryptography-3.1.1-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:65beb15e7f9c16e15934569d29fb4def74ea1469d8781f6b3507ab896d6d8719"},
+ {file = "cryptography-3.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:983c0c3de4cb9fcba68fd3f45ed846eb86a2a8b8d8bc5bb18364c4d00b3c61fe"},
+ {file = "cryptography-3.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:e97a3b627e3cb63c415a16245d6cef2139cca18bb1183d1b9375a1c14e83f3b3"},
+ {file = "cryptography-3.1.1-cp27-cp27m-win32.whl", hash = "sha256:cb179acdd4ae1e4a5a160d80b87841b3d0e0be84af46c7bb2cd7ece57a39c4ba"},
+ {file = "cryptography-3.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:b372026ebf32fe2523159f27d9f0e9f485092e43b00a5adacf732192a70ba118"},
+ {file = "cryptography-3.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:680da076cad81cdf5ffcac50c477b6790be81768d30f9da9e01960c4b18a66db"},
+ {file = "cryptography-3.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:5d52c72449bb02dd45a773a203196e6d4fae34e158769c896012401f33064396"},
+ {file = "cryptography-3.1.1-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:f0e099fc4cc697450c3dd4031791559692dd941a95254cb9aeded66a7aa8b9bc"},
+ {file = "cryptography-3.1.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:a7597ffc67987b37b12e09c029bd1dc43965f75d328076ae85721b84046e9ca7"},
+ {file = "cryptography-3.1.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:4549b137d8cbe3c2eadfa56c0c858b78acbeff956bd461e40000b2164d9167c6"},
+ {file = "cryptography-3.1.1-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:89aceb31cd5f9fc2449fe8cf3810797ca52b65f1489002d58fe190bfb265c536"},
+ {file = "cryptography-3.1.1-cp35-cp35m-win32.whl", hash = "sha256:559d622aef2a2dff98a892eef321433ba5bc55b2485220a8ca289c1ecc2bd54f"},
+ {file = "cryptography-3.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:451cdf60be4dafb6a3b78802006a020e6cd709c22d240f94f7a0696240a17154"},
+ {file = "cryptography-3.1.1-cp36-abi3-win32.whl", hash = "sha256:762bc5a0df03c51ee3f09c621e1cee64e3a079a2b5020de82f1613873d79ee70"},
+ {file = "cryptography-3.1.1-cp36-abi3-win_amd64.whl", hash = "sha256:b12e715c10a13ca1bd27fbceed9adc8c5ff640f8e1f7ea76416352de703523c8"},
+ {file = "cryptography-3.1.1-cp36-cp36m-win32.whl", hash = "sha256:21b47c59fcb1c36f1113f3709d37935368e34815ea1d7073862e92f810dc7499"},
+ {file = "cryptography-3.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:48ee615a779ffa749d7d50c291761dc921d93d7cf203dca2db663b4f193f0e49"},
+ {file = "cryptography-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:b2bded09c578d19e08bd2c5bb8fed7f103e089752c9cf7ca7ca7de522326e921"},
+ {file = "cryptography-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f99317a0fa2e49917689b8cf977510addcfaaab769b3f899b9c481bbd76730c2"},
+ {file = "cryptography-3.1.1-cp38-cp38-win32.whl", hash = "sha256:ab010e461bb6b444eaf7f8c813bb716be2d78ab786103f9608ffd37a4bd7d490"},
+ {file = "cryptography-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:99d4984aabd4c7182050bca76176ce2dbc9fa9748afe583a7865c12954d714ba"},
+ {file = "cryptography-3.1.1.tar.gz", hash = "sha256:9d9fc6a16357965d282dd4ab6531013935425d0dc4950df2e0cf2a1b1ac1017d"},
+]
+distro = [
+ {file = "distro-1.5.0-py2.py3-none-any.whl", hash = "sha256:df74eed763e18d10d0da624258524ae80486432cd17392d9c3d96f5e83cd2799"},
+ {file = "distro-1.5.0.tar.gz", hash = "sha256:0e58756ae38fbd8fc3020d54badb8eae17c5b9dcbed388b17bb55b8a5928df92"},
+]
+docker = [
+ {file = "docker-4.3.1-py2.py3-none-any.whl", hash = "sha256:13966471e8bc23b36bfb3a6fb4ab75043a5ef1dac86516274777576bed3b9828"},
+ {file = "docker-4.3.1.tar.gz", hash = "sha256:bad94b8dd001a8a4af19ce4becc17f41b09f228173ffe6a4e0355389eef142f2"},
+]
+docker-compose = [
+ {file = "docker-compose-1.27.4.tar.gz", hash = "sha256:5a5690f24c27d4b43dcbe6b3fae91ba680713208e99ee863352b3bae37bcaa83"},
+ {file = "docker_compose-1.27.4-py2.py3-none-any.whl", hash = "sha256:84ca2edad226435e3a378ea24ca2ca4e1a77cc7c8de057e2812124c6dcb55147"},
+]
+dockerpty = [
+ {file = "dockerpty-0.4.1.tar.gz", hash = "md5:028bacb34536f3ee6a2ccd668c27e8e4"},
+]
+docopt = [
+ {file = "docopt-0.6.2.tar.gz", hash = "md5:4bc74561b37fad5d3e7d037f82a4c3b1"},
+]
docutils = [
{file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"},
{file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"},
@@ -942,8 +1670,8 @@ hpack = [
{file = "hpack-3.0.0.tar.gz", hash = "sha256:8eec9c1f4bfae3408a3f30500261f7e6a65912dc138526ea054f9ad98892e9d2"},
]
hstspreload = [
- {file = "hstspreload-2020.9.15-py3-none-any.whl", hash = "sha256:c09f02dd4b7e3953a8353836aea964b868b22905bdb6d8932ca4b273df0f820f"},
- {file = "hstspreload-2020.9.15.tar.gz", hash = "sha256:059fc2ead7bb83ea9e85d06c1afc7112413f1d2a81e6448bd276af6bced035ac"},
+ {file = "hstspreload-2020.9.29-py3-none-any.whl", hash = "sha256:b6bdbe6e36d8acea5bb97b53ae50552293b6b0425785918da7b63e48f08af4a8"},
+ {file = "hstspreload-2020.9.29.tar.gz", hash = "sha256:349c59cd2889a2cd94f49b3872a195164cc50754f1bef148b7d2d0d6c0efe0de"},
]
html2text = [
{file = "html2text-2020.1.16-py3-none-any.whl", hash = "sha256:c7c629882da0cf377d66f073329ccf34a12ed2adf0169b9285ae4e63ef54c82b"},
@@ -983,13 +1711,25 @@ imagesize = [
{file = "imagesize-1.2.0-py2.py3-none-any.whl", hash = "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1"},
{file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"},
]
+importlib-metadata = [
+ {file = "importlib_metadata-2.0.0-py2.py3-none-any.whl", hash = "sha256:cefa1a2f919b866c5beb7c9f7b0ebb4061f30a8a9bf16d609b000e2dfaceb9c3"},
+ {file = "importlib_metadata-2.0.0.tar.gz", hash = "sha256:77a540690e24b0305878c37ffd421785a6f7e53c8b5720d211b211de8d0e95da"},
+]
jinja2 = [
{file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"},
{file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"},
]
+jinja2-time = [
+ {file = "jinja2-time-0.2.0.tar.gz", hash = "md5:b6ebc4ecac395a18982532f4c2869c06"},
+ {file = "jinja2_time-0.2.0-py2.py3-none-any.whl", hash = "md5:d0d553ae5c3e9e7abb79c044acd165c8"},
+]
+jsonschema = [
+ {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"},
+ {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"},
+]
loguru = [
- {file = "loguru-0.5.2-py3-none-any.whl", hash = "sha256:a5e5e196b9958feaf534ac2050171d16576bae633074ce3e73af7dda7e9a58ae"},
- {file = "loguru-0.5.2.tar.gz", hash = "sha256:5aecbf13bc8e2f6e5a5d0475460a345b44e2885464095ea7de44e8795857ad33"},
+ {file = "loguru-0.5.3-py3-none-any.whl", hash = "sha256:f8087ac396b5ee5f67c963b495d615ebbceac2796379599820e324419d53667c"},
+ {file = "loguru-0.5.3.tar.gz", hash = "sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319"},
]
markupsafe = [
{file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"},
@@ -1026,10 +1766,31 @@ markupsafe = [
{file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"},
{file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"},
]
+nb-cli = [
+ {file = "nb-cli-0.1.0.tar.gz", hash = "sha256:5106212dd0bae270fc547a59f8c75e20951b9fbd87af263d647f8474677a2e26"},
+ {file = "nb_cli-0.1.0-py3-none-any.whl", hash = "sha256:a5b5f72bd68b48da446d56623e16b08a3483c84840b3a191771cd9b3e6fde8e9"},
+]
packaging = [
{file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"},
{file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"},
]
+paramiko = [
+ {file = "paramiko-2.7.2-py2.py3-none-any.whl", hash = "sha256:4f3e316fef2ac628b05097a637af35685183111d4bc1b5979bd397c2ab7b5898"},
+ {file = "paramiko-2.7.2.tar.gz", hash = "sha256:7f36f4ba2c0d81d219f4595e35f70d56cc94f9ac40a6acdf51d6ca210ce65035"},
+]
+poyo = [
+ {file = "poyo-0.5.0-py2.py3-none-any.whl", hash = "sha256:3e2ca8e33fdc3c411cd101ca395668395dd5dc7ac775b8e809e3def9f9fe041a"},
+ {file = "poyo-0.5.0.tar.gz", hash = "sha256:e26956aa780c45f011ca9886f044590e2d8fd8b61db7b1c1cf4e0869f48ed4dd"},
+]
+prompt-toolkit = [
+ {file = "prompt_toolkit-1.0.14-py2-none-any.whl", hash = "sha256:82c7f8e07d7a0411ff5367a5a8ff520f0112b9179f3e599ee8ad2ad9b943d911"},
+ {file = "prompt_toolkit-1.0.14-py3-none-any.whl", hash = "sha256:7281b5199235adaef6980942840c43753e4ab20dfe41338da634fb41c194f9d8"},
+ {file = "prompt_toolkit-1.0.14.tar.gz", hash = "sha256:cc66413b1b4b17021675d9f2d15d57e640b06ddfd99bb724c73484126d22622f"},
+]
+pycparser = [
+ {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
+ {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
+]
pydantic = [
{file = "pydantic-1.6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:418b84654b60e44c0cdd5384294b0e4bc1ebf42d6e873819424f3b78b8690614"},
{file = "pydantic-1.6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4900b8820b687c9a3ed753684337979574df20e6ebe4227381d04b3c3c628f99"},
@@ -1053,6 +1814,10 @@ pydash = [
{file = "pydash-4.8.0-py2.py3-none-any.whl", hash = "sha256:611ad40e3b11fb57396cca4a55ea04641200a1dd632c3c7e2c14849bee386625"},
{file = "pydash-4.8.0.tar.gz", hash = "sha256:546afa043ed1defa3122383bebe8b7072f43554ccc5f0c4360638f99e5ed7327"},
]
+pyfiglet = [
+ {file = "pyfiglet-0.8.post1-py2.py3-none-any.whl", hash = "sha256:d555bcea17fbeaf70eaefa48bb119352487e629c9b56f30f383e2c62dd67a01c"},
+ {file = "pyfiglet-0.8.post1.tar.gz", hash = "sha256:c6c2321755d09267b438ec7b936825a4910fec696292139e664ca8670e103639"},
+]
pygments = [
{file = "Pygments-2.7.1-py3-none-any.whl", hash = "sha256:307543fe65c0947b126e83dd5a61bd8acbd84abec11f43caebaf5534cbc17998"},
{file = "Pygments-2.7.1.tar.gz", hash = "sha256:926c3f319eda178d1bd90851e4317e6d8cdb5e292a3386aac9bd75eca29cf9c7"},
@@ -1060,18 +1825,99 @@ pygments = [
pygtrie = [
{file = "pygtrie-2.3.3.tar.gz", hash = "sha256:2204dbd95584f67821da5b3771c4305ac5585552b3230b210f1f05322608db2c"},
]
+pyinquirer = [
+ {file = "PyInquirer-1.0.3.tar.gz", hash = "sha256:c9a92d68d7727fbd886a7908c08fd9e9773e5dc211bf5cbf836ba90d366dee51"},
+]
+pynacl = [
+ {file = "PyNaCl-1.4.0-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff"},
+ {file = "PyNaCl-1.4.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:d452a6746f0a7e11121e64625109bc4468fc3100452817001dbe018bb8b08514"},
+ {file = "PyNaCl-1.4.0-cp27-cp27m-win32.whl", hash = "sha256:2fe0fc5a2480361dcaf4e6e7cea00e078fcda07ba45f811b167e3f99e8cff574"},
+ {file = "PyNaCl-1.4.0-cp27-cp27m-win_amd64.whl", hash = "sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80"},
+ {file = "PyNaCl-1.4.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7757ae33dae81c300487591c68790dfb5145c7d03324000433d9a2c141f82af7"},
+ {file = "PyNaCl-1.4.0-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:757250ddb3bff1eecd7e41e65f7f833a8405fede0194319f87899690624f2122"},
+ {file = "PyNaCl-1.4.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:30f9b96db44e09b3304f9ea95079b1b7316b2b4f3744fe3aaecccd95d547063d"},
+ {file = "PyNaCl-1.4.0-cp35-cp35m-win32.whl", hash = "sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4"},
+ {file = "PyNaCl-1.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:511d269ee845037b95c9781aa702f90ccc36036f95d0f31373a6a79bd8242e25"},
+ {file = "PyNaCl-1.4.0-cp36-cp36m-win32.whl", hash = "sha256:11335f09060af52c97137d4ac54285bcb7df0cef29014a1a4efe64ac065434c4"},
+ {file = "PyNaCl-1.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:cd401ccbc2a249a47a3a1724c2918fcd04be1f7b54eb2a5a71ff915db0ac51c6"},
+ {file = "PyNaCl-1.4.0-cp37-cp37m-win32.whl", hash = "sha256:8122ba5f2a2169ca5da936b2e5a511740ffb73979381b4229d9188f6dcb22f1f"},
+ {file = "PyNaCl-1.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:537a7ccbea22905a0ab36ea58577b39d1fa9b1884869d173b5cf111f006f689f"},
+ {file = "PyNaCl-1.4.0-cp38-cp38-win32.whl", hash = "sha256:9c4a7ea4fb81536c1b1f5cc44d54a296f96ae78c1ebd2311bd0b60be45a48d96"},
+ {file = "PyNaCl-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:7c6092102219f59ff29788860ccb021e80fffd953920c4a8653889c029b2d420"},
+ {file = "PyNaCl-1.4.0.tar.gz", hash = "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505"},
+]
pyparsing = [
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
]
+pyrsistent = [
+ {file = "pyrsistent-0.17.3.tar.gz", hash = "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e"},
+]
+python-dateutil = [
+ {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"},
+ {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"},
+]
python-dotenv = [
{file = "python-dotenv-0.14.0.tar.gz", hash = "sha256:8c10c99a1b25d9a68058a1ad6f90381a62ba68230ca93966882a4dbc3bc9c33d"},
{file = "python_dotenv-0.14.0-py2.py3-none-any.whl", hash = "sha256:c10863aee750ad720f4f43436565e4c1698798d763b63234fb5021b6c616e423"},
]
+python-slugify = [
+ {file = "python-slugify-4.0.1.tar.gz", hash = "sha256:69a517766e00c1268e5bbfc0d010a0a8508de0b18d30ad5a1ff357f8ae724270"},
+]
pytz = [
{file = "pytz-2020.1-py2.py3-none-any.whl", hash = "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed"},
{file = "pytz-2020.1.tar.gz", hash = "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"},
]
+pywin32 = [
+ {file = "pywin32-227-cp27-cp27m-win32.whl", hash = "sha256:371fcc39416d736401f0274dd64c2302728c9e034808e37381b5e1b22be4a6b0"},
+ {file = "pywin32-227-cp27-cp27m-win_amd64.whl", hash = "sha256:4cdad3e84191194ea6d0dd1b1b9bdda574ff563177d2adf2b4efec2a244fa116"},
+ {file = "pywin32-227-cp35-cp35m-win32.whl", hash = "sha256:f4c5be1a293bae0076d93c88f37ee8da68136744588bc5e2be2f299a34ceb7aa"},
+ {file = "pywin32-227-cp35-cp35m-win_amd64.whl", hash = "sha256:a929a4af626e530383a579431b70e512e736e9588106715215bf685a3ea508d4"},
+ {file = "pywin32-227-cp36-cp36m-win32.whl", hash = "sha256:300a2db938e98c3e7e2093e4491439e62287d0d493fe07cce110db070b54c0be"},
+ {file = "pywin32-227-cp36-cp36m-win_amd64.whl", hash = "sha256:9b31e009564fb95db160f154e2aa195ed66bcc4c058ed72850d047141b36f3a2"},
+ {file = "pywin32-227-cp37-cp37m-win32.whl", hash = "sha256:47a3c7551376a865dd8d095a98deba954a98f326c6fe3c72d8726ca6e6b15507"},
+ {file = "pywin32-227-cp37-cp37m-win_amd64.whl", hash = "sha256:31f88a89139cb2adc40f8f0e65ee56a8c585f629974f9e07622ba80199057511"},
+ {file = "pywin32-227-cp38-cp38-win32.whl", hash = "sha256:7f18199fbf29ca99dff10e1f09451582ae9e372a892ff03a28528a24d55875bc"},
+ {file = "pywin32-227-cp38-cp38-win_amd64.whl", hash = "sha256:7c1ae32c489dc012930787f06244426f8356e129184a02c25aef163917ce158e"},
+ {file = "pywin32-227-cp39-cp39-win32.whl", hash = "sha256:c054c52ba46e7eb6b7d7dfae4dbd987a1bb48ee86debe3f245a2884ece46e295"},
+ {file = "pywin32-227-cp39-cp39-win_amd64.whl", hash = "sha256:f27cec5e7f588c3d1051651830ecc00294f90728d19c3bf6916e6dba93ea357c"},
+]
+pyyaml = [
+ {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"},
+ {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"},
+ {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"},
+ {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"},
+ {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"},
+ {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"},
+ {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"},
+ {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"},
+ {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"},
+ {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"},
+ {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"},
+]
+regex = [
+ {file = "regex-2020.9.27-cp27-cp27m-win32.whl", hash = "sha256:d23a18037313714fb3bb5a94434d3151ee4300bae631894b1ac08111abeaa4a3"},
+ {file = "regex-2020.9.27-cp27-cp27m-win_amd64.whl", hash = "sha256:84e9407db1b2eb368b7ecc283121b5e592c9aaedbe8c78b1a2f1102eb2e21d19"},
+ {file = "regex-2020.9.27-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5f18875ac23d9aa2f060838e8b79093e8bb2313dbaaa9f54c6d8e52a5df097be"},
+ {file = "regex-2020.9.27-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ae91972f8ac958039920ef6e8769277c084971a142ce2b660691793ae44aae6b"},
+ {file = "regex-2020.9.27-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9a02d0ae31d35e1ec12a4ea4d4cca990800f66a917d0fb997b20fbc13f5321fc"},
+ {file = "regex-2020.9.27-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:ebbe29186a3d9b0c591e71b7393f1ae08c83cb2d8e517d2a822b8f7ec99dfd8b"},
+ {file = "regex-2020.9.27-cp36-cp36m-win32.whl", hash = "sha256:4707f3695b34335afdfb09be3802c87fa0bc27030471dbc082f815f23688bc63"},
+ {file = "regex-2020.9.27-cp36-cp36m-win_amd64.whl", hash = "sha256:9bc13e0d20b97ffb07821aa3e113f9998e84994fe4d159ffa3d3a9d1b805043b"},
+ {file = "regex-2020.9.27-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f1b3afc574a3db3b25c89161059d857bd4909a1269b0b3cb3c904677c8c4a3f7"},
+ {file = "regex-2020.9.27-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5533a959a1748a5c042a6da71fe9267a908e21eded7a4f373efd23a2cbdb0ecc"},
+ {file = "regex-2020.9.27-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:1fe0a41437bbd06063aa184c34804efa886bcc128222e9916310c92cd54c3b4c"},
+ {file = "regex-2020.9.27-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:c570f6fa14b9c4c8a4924aaad354652366577b4f98213cf76305067144f7b100"},
+ {file = "regex-2020.9.27-cp37-cp37m-win32.whl", hash = "sha256:eda4771e0ace7f67f58bc5b560e27fb20f32a148cbc993b0c3835970935c2707"},
+ {file = "regex-2020.9.27-cp37-cp37m-win_amd64.whl", hash = "sha256:60b0e9e6dc45683e569ec37c55ac20c582973841927a85f2d8a7d20ee80216ab"},
+ {file = "regex-2020.9.27-cp38-cp38-manylinux1_i686.whl", hash = "sha256:088afc8c63e7bd187a3c70a94b9e50ab3f17e1d3f52a32750b5b77dbe99ef5ef"},
+ {file = "regex-2020.9.27-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:eaf548d117b6737df379fdd53bdde4f08870e66d7ea653e230477f071f861121"},
+ {file = "regex-2020.9.27-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:41bb65f54bba392643557e617316d0d899ed5b4946dccee1cb6696152b29844b"},
+ {file = "regex-2020.9.27-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:8d69cef61fa50c8133382e61fd97439de1ae623fe943578e477e76a9d9471637"},
+ {file = "regex-2020.9.27-cp38-cp38-win32.whl", hash = "sha256:f2388013e68e750eaa16ccbea62d4130180c26abb1d8e5d584b9baf69672b30f"},
+ {file = "regex-2020.9.27-cp38-cp38-win_amd64.whl", hash = "sha256:4318d56bccfe7d43e5addb272406ade7a2274da4b70eb15922a071c58ab0108c"},
+ {file = "regex-2020.9.27.tar.gz", hash = "sha256:a6f32aea4260dfe0e55dc9733ea162ea38f0ea86aa7d0f77b15beac5bf7b369d"},
+]
requests = [
{file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"},
{file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"},
@@ -1125,6 +1971,14 @@ starlette = [
{file = "starlette-0.13.4-py3-none-any.whl", hash = "sha256:0fb4b38d22945b46acb880fedee7ee143fd6c0542992501be8c45c0ed737dd1a"},
{file = "starlette-0.13.4.tar.gz", hash = "sha256:04fe51d86fd9a594d9b71356ed322ccde5c9b448fc716ac74155e5821a922f8d"},
]
+text-unidecode = [
+ {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"},
+ {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"},
+]
+texttable = [
+ {file = "texttable-1.6.3-py2.py3-none-any.whl", hash = "sha256:f802f2ef8459058736264210f716c757cbf85007a30886d8541aa8c3404f1dda"},
+ {file = "texttable-1.6.3.tar.gz", hash = "sha256:ce0faf21aa77d806bbff22b107cc22cce68dc9438f97a2df32c93e9afa4ce436"},
+]
tzlocal = [
{file = "tzlocal-2.1-py2.py3-none-any.whl", hash = "sha256:e2cb6c6b5b604af38597403e9852872d7f534962ae2954c7f35efcb1ccacf4a4"},
{file = "tzlocal-2.1.tar.gz", hash = "sha256:643c97c5294aedc737780a49d9df30889321cbe1204eac2c2ec6134035a92e44"},
@@ -1154,6 +2008,14 @@ uvloop = [
{file = "uvloop-0.14.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4315d2ec3ca393dd5bc0b0089d23101276778c304d42faff5dc4579cb6caef09"},
{file = "uvloop-0.14.0.tar.gz", hash = "sha256:123ac9c0c7dd71464f58f1b4ee0bbd81285d96cdda8bc3519281b8973e3a461e"},
]
+wcwidth = [
+ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
+ {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
+]
+websocket-client = [
+ {file = "websocket_client-0.57.0-py2.py3-none-any.whl", hash = "sha256:0fc45c961324d79c781bab301359d5a1b00b13ad1b10415a4780229ef71a5549"},
+ {file = "websocket_client-0.57.0.tar.gz", hash = "sha256:d735b91d6d1692a6a181f2a8c9e0238e5f6373356f561bb9dc4c7af36f452010"},
+]
websockets = [
{file = "websockets-8.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:3762791ab8b38948f0c4d281c8b2ddfa99b7e510e46bd8dfa942a5fff621068c"},
{file = "websockets-8.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3db87421956f1b0779a7564915875ba774295cc86e81bc671631379371af1170"},
@@ -1186,3 +2048,7 @@ yapf = [
{file = "yapf-0.30.0-py2.py3-none-any.whl", hash = "sha256:3abf61ba67cf603069710d30acbc88cfe565d907e16ad81429ae90ce9651e0c9"},
{file = "yapf-0.30.0.tar.gz", hash = "sha256:3000abee4c28daebad55da6c85f3cd07b8062ce48e2e9943c8da1b9667d48427"},
]
+zipp = [
+ {file = "zipp-3.2.0-py3-none-any.whl", hash = "sha256:43f4fa8d8bb313e65d8323a3952ef8756bf40f9a5c3ea7334be23ee4ec8278b6"},
+ {file = "zipp-3.2.0.tar.gz", hash = "sha256:b52f22895f4cfce194bc8172f3819ee8de7540aa6d873535a8668b730b8b411f"},
+]
diff --git a/pyproject.toml b/pyproject.toml
index b6c78064..f0a3b83f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "nonebot2"
-version = "2.0.0a1"
+version = "2.0.0a2"
description = "An asynchronous python bot framework."
authors = ["yanyongyu "]
license = "MIT"
@@ -32,7 +32,7 @@ uvicorn = "^0.11.5"
pydantic = { extras = ["dotenv"], version = "^1.6.1" }
apscheduler = { version = "^3.6.3", optional = true }
# nonebot-test = { version = "^0.1.0", optional = true }
-# nb-cli = { version="^0.1.0", optional = true }
+nb-cli = { version="^0.1.0", optional = true }
[tool.poetry.dev-dependencies]
yapf = "^0.30.0"
@@ -40,7 +40,7 @@ sphinx = "^3.1.1"
sphinx-markdown-builder = { git = "https://github.com/nonebot/sphinx-markdown-builder.git" }
[tool.poetry.extras]
-# cli = ["nb-cli"]
+cli = ["nb-cli"]
# test = ["nonebot-test"]
scheduler = ["apscheduler"]
full = ["nb-cli", "nonebot-test", "scheduler"]
diff --git a/tests/test_plugins/test_group/__init__.py b/tests/test_plugins/test_group/__init__.py
new file mode 100644
index 00000000..1f47bd5c
--- /dev/null
+++ b/tests/test_plugins/test_group/__init__.py
@@ -0,0 +1,6 @@
+from nonebot.rule import to_me
+from nonebot import CommandGroup
+
+test = CommandGroup("test", rule=to_me())
+
+from . import commands
diff --git a/tests/test_plugins/test_group/commands.py b/tests/test_plugins/test_group/commands.py
new file mode 100644
index 00000000..e825329d
--- /dev/null
+++ b/tests/test_plugins/test_group/commands.py
@@ -0,0 +1,11 @@
+from nonebot.typing import Bot, Event
+from nonebot.permission import GROUP_OWNER
+
+from . import test
+
+test_1 = test.command("1", aliases={"test"}, permission=GROUP_OWNER)
+
+
+@test_1.handle()
+async def test1(bot: Bot, event: Event, state: dict):
+ await test_1.finish(event.raw_message)
diff --git a/tests/test_plugins/test_message.py b/tests/test_plugins/test_message.py
deleted file mode 100644
index e683b630..00000000
--- a/tests/test_plugins/test_message.py
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-
-from nonebot.rule import to_me
-from nonebot.typing import Event
-from nonebot.plugin import on_message
-from nonebot.adapters.cqhttp import Bot
-
-test_message = on_message(to_me(), state={"default": 1})
-
-
-@test_message.handle()
-async def test_handler(bot: Bot, event: Event, state: dict):
- print("[*] Test Matcher Received:", event)
- state["event"] = event
- await bot.send(message="Received", event=event)
-
-
-@test_message.receive()
-async def test_receive(bot: Bot, event: Event, state: dict):
- print("[*] Test Matcher Received next time:", event)
- print("[*] Current State:", state)