Compare commits

...

82 Commits

Author SHA1 Message Date
Ju4tCode
70c400da7f 🔀 Merge pull request #43
Pre Release 2.0.0a4
2020-11-04 13:46:05 +08:00
yanyongyu
2a5cfe712f 📝 update doc version 2020-11-04 13:41:52 +08:00
yanyongyu
533a045622 ⬆️ bump version and update dependency 2020-11-04 13:27:49 +08:00
Ju4tCode
93aa1791c9 🔀 Merge pull request #42
Fix: Plugin publish template
2020-11-02 20:25:28 +08:00
yanyongyu
06c76cc096 👷 fix plugin template 2020-11-02 20:20:41 +08:00
Ju4tCode
04df0e5f51 🔀 Merge pull request #41 2020-11-02 19:39:55 +08:00
yanyongyu
f99e029567 📌 update poetry lock file 2020-11-02 19:33:45 +08:00
yanyongyu
71ee9aee21 🚧 update plugin store 2020-11-02 19:07:53 +08:00
yanyongyu
d9ea95c67e 🚑 fix message segment get 2020-11-01 19:20:18 +08:00
yanyongyu
af7f42ac60 📝 update plugin store page 2020-11-01 18:21:31 +08:00
yanyongyu
15e59a1778 👷 update plugin template 2020-11-01 18:21:10 +08:00
yanyongyu
22962d55e1 👷 create plugin issue template 2020-11-01 16:18:57 +08:00
yanyongyu
70c7927006 💡 add docstring for cqhttp message 2020-10-30 16:49:31 +08:00
yanyongyu
89c3aa38a6 🎨 allow multi value for keyword rule 2020-10-30 16:26:04 +08:00
Ju4tCode
2eb330b8a8 🔀 Merge pull request #39 2020-10-30 15:57:31 +08:00
yanyongyu
383c0031a5 📝 update plugin 2020-10-30 01:59:45 +08:00
yanyongyu
06f20281a5 🔇 remove meta event logs #21 2020-10-29 17:06:07 +08:00
yanyongyu
a40f1a453c 📝 replace say with echo due to superuser perm 2020-10-29 16:27:20 +08:00
Ju4tCode
5d9f354209 🔀 Merge pull request #37 2020-10-28 13:49:53 +08:00
Ju4tCode
c3bd8ebf57 🚧 change result store 2020-10-28 13:45:54 +08:00
yanyongyu
c9c615c8cb 👷 try pull request target 2020-10-28 13:42:20 +08:00
Ju4tCode
47f491039c 💡 update regex docstring 2020-10-28 13:23:48 +08:00
yanyongyu
dc5c35a9ed 💡 update command docstrinrg 2020-10-28 13:17:41 +08:00
yanyongyu
c636186321 👷 update api doc action 2020-10-28 13:05:51 +08:00
Ju4tCode
aafc3dd060 🔀 Merge pull request #38 2020-10-28 12:52:27 +08:00
ayayaneru
38469611a4 Update README 2020-10-28 10:01:33 +08:00
rkroom
59a8bd8c97 add matched_string when rule regex
当使用正则匹配消息成功时,向state添加matched_string以保存匹配到的内容供接下来使用。
2020-10-26 17:18:26 +08:00
Ju4tCode
50cfd3c9b5 🔀 Merge pull request #36
Issue35:修改鉴权限制
2020-10-25 14:48:00 +08:00
yanyongyu
5d08d53c27 👽 update auth header 2020-10-25 14:42:46 +08:00
Ju4tCode
4f6f99146c 🔀 Merge pull request #33
修改 Command 实现逻辑
2020-10-22 22:36:23 +08:00
yanyongyu
32388d070d 🎨 improve command implementation 2020-10-22 22:08:19 +08:00
yanyongyu
d9f8bf38c6 🐛 fix matcher group send 2020-10-22 00:00:29 +08:00
yanyongyu
4e2b74af75 🚧 add plugin store page 2020-10-21 00:55:23 +08:00
yanyongyu
c2c28cebf5 👽 update nonebot theme 2020-10-21 00:54:09 +08:00
yanyongyu
24141b1a4b 🎨 remove shebang line #31 2020-10-20 22:40:16 +08:00
Ju4tCode
c85e50d73d 👷 Add release draft config 2020-10-20 17:28:51 +08:00
Ju4tCode
444156766a 👷 Update release drafter 2020-10-20 17:27:41 +08:00
yanyongyu
308824d0b1 🐛 fix matcher group args parser 2020-10-20 00:01:26 +08:00
yanyongyu
c3ed962837 add site cache to improve performance #29 2020-10-19 17:49:04 +08:00
Ju4tCode
2c13303d89 👷 Update issue templates 2020-10-19 00:00:11 +08:00
Ju4tCode
1304b94b61 🔀 Merge pull request #28
Update vuepress theme
2020-10-18 03:05:42 -05:00
yanyongyu
8ca677c271 👽 fix home link 2020-10-18 15:34:14 +08:00
yanyongyu
7c17412106 💡 add plugin docstring 2020-10-18 15:04:45 +08:00
yanyongyu
26207f762b 🏷️ fix class inherit in pyi 2020-10-18 15:02:56 +08:00
yanyongyu
8db70dbb0c 👽 fix theme smooth scroll 2020-10-18 02:30:01 +08:00
yanyongyu
1b6924a104 👽 update lock file 2020-10-18 02:04:32 +08:00
yanyongyu
c5a30a8a79 use custom theme 2020-10-18 01:39:34 +08:00
yanyongyu
56c9c24dc6 add kwargs support for matcher send/finish/pause/reject 2020-10-17 19:50:25 +08:00
yanyongyu
9ad629841b 📝 add uninstall nonebot 1.x tip 2020-10-16 17:15:40 +08:00
yanyongyu
78433bdae9 🚑 restore markdown 2020-10-16 16:08:51 +08:00
yanyongyu
5f44212faa 🚑 restore div 2020-10-16 16:07:04 +08:00
yanyongyu
df517b6b36 📝 use p instead of div 2020-10-16 16:03:49 +08:00
yanyongyu
6afe9b6f4f 📝 Update Readme (fix #27) 2020-10-16 15:59:34 +08:00
yanyongyu
1d79ac232f 🐛 fix missing param for on event 2020-10-16 15:12:15 +08:00
yanyongyu
0a64959973 💡 add driver docstring 2020-10-16 01:10:46 +08:00
yanyongyu
1e4b058681 👽 update repo link 2020-10-11 14:51:37 +08:00
yanyongyu
3c5d06a2de 📝 add todo tag 2020-10-11 13:49:58 +08:00
Ju4tCode
1ee7c792e8 🔀 Merge pull request #26
Pre Release 2.0.0a3
2020-10-11 13:46:01 +08:00
yanyongyu
39a950ea80 📌 update lock file 2020-10-11 13:39:45 +08:00
yanyongyu
5bb41395d8 🔖 Pre Release 2.0.0a3 2020-10-11 13:19:20 +08:00
yanyongyu
a68ba09910 📝 Update docs 2020-10-11 13:17:40 +08:00
yanyongyu
b4e0034876 💡 add driver docstring 2020-10-10 23:40:01 +08:00
yanyongyu
e58174f6ec 📝 remove outdated docs 2020-10-09 20:26:39 +08:00
yanyongyu
1377f7337d 🚑 fix message segment setitem 2020-10-09 00:57:30 +08:00
yanyongyu
96ce29fd52 add optional dependence nonebot-test 2020-10-09 00:48:50 +08:00
yanyongyu
f164d85c5c 🐛 fix message segment getitem 2020-10-09 00:10:50 +08:00
yanyongyu
ce758a2231 📝 add nonebot quick import doc 2020-10-08 21:36:57 +08:00
yanyongyu
05c3a4b84f 💡 fix nickname docstring 2020-10-08 21:36:32 +08:00
yanyongyu
1d31e646ad 🔇 reduce log for meta event #21 2020-10-08 21:25:13 +08:00
yanyongyu
927a963d4f 🐛 fix nickname 2020-10-06 22:29:05 +08:00
yanyongyu
727eef9a34 💡 add matcher docstring 2020-10-06 17:03:05 +08:00
yanyongyu
9f8d009309 add matcher.send method 2020-10-06 16:24:04 +08:00
yanyongyu
734d3cd333 💡 update docstring for several modules 2020-10-06 02:08:48 +08:00
yanyongyu
2e989b5fe1 💡 add matcher docstring 2020-10-05 23:10:20 +08:00
yanyongyu
781d0cf654 ⚗️ rewrite echo and say builtin plugin 2020-10-04 18:10:01 +08:00
yanyongyu
0a11bd3e8e ✏️ fix cqhttp sub_type typo 2020-10-04 16:27:58 +08:00
yanyongyu
2574ef3e7a 💡 add message docstring 2020-10-03 18:18:43 +08:00
Ju4tCode
b80f0bf202 🔀 Merge pull request #19 2020-10-03 15:06:46 +08:00
yanyongyu
c7f1859d99 🐛 fix white space before plain text #18 2020-10-03 14:56:38 +08:00
yanyongyu
d71260ed68 🐛 fix group attrs 2020-10-02 00:28:53 +08:00
yanyongyu
3e4dc1a123 💡 add adapter docstring 2020-10-01 23:52:56 +08:00
Ju4tCode
a308f4d4ee 🔀 Merge pull request #16 from nonebot/dev
Pre Release 2.0.0a2
2020-10-01 01:31:40 +08:00
97 changed files with 7087 additions and 1837 deletions

33
.github/ISSUE_TEMPLATE/bug-report.md vendored Normal file
View File

@@ -0,0 +1,33 @@
---
name: Bug report
about: Create a bug report to help us improve
title: 'Bug: Something went wrong'
labels: bug
assignees: ''
---
**描述问题:**
A clear and concise description of what the bug is.
**如何复现?**
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**期望的结果**
A clear and concise description of what you expected to happen.
**环境信息:**
- OS: [e.g. Linux]
- Python Version: [e.g. 3.8]
- Nonebot Version [e.g. 2.0.0]
**截图**
If applicable, add screenshots to help explain your problem.

View File

@@ -0,0 +1,16 @@
---
name: Feature request
about: Suggest an idea for this project
title: 'Feature: Something you want'
labels: enhancement
assignees: ''
---
**是否在使用中遇到某些问题而需要新的特性?请描述:**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**描述你所需要的特性:**
A clear and concise description of what you want to happen.

View File

@@ -0,0 +1,36 @@
---
name: Plugin Publish
about: Publish your plugin to nonebot homepage and nb-cli
title: "Plugin: blabla 的插件"
labels: Plugin
assignees: ""
---
**你的插件名称:**
例:复读机
**简短描述插件功能:**
例:复读群友的消息
**插件 import 使用的名称**
nonebot-plugin-example
**插件 install 使用的名称**
例 1nonebot-plugin-example
通过 pypi 安装
> 请事先发布插件到[pypi](https://pypi.org/)
例 2git+https://github.com/nonebot/nonebot-plugin-example
从 github 仓库安装
**插件项目仓库/主页链接**
nonebot/nonebot2默认 github )或其他链接

31
.github/release-drafter.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name-template: 'v$RESOLVED_VERSION 🌈'
tag-template: 'v$RESOLVED_VERSION'
categories:
- title: '🚀 Features'
labels:
- 'feature'
- 'enhancement'
- title: '🐛 Bug Fixes'
labels:
- 'fix'
- 'bugfix'
- 'bug'
- title: '🧰 Maintenance'
label: 'chore'
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
version-resolver:
major:
labels:
- 'major'
minor:
labels:
- 'minor'
patch:
labels:
- 'patch'
default: patch
template: |
## Changes
$CHANGES

View File

@@ -1,7 +1,7 @@
name: Build API Doc
on:
pull_request:
pull_request_target:
types: [ opened, synchronize, reopened ]
jobs:
@@ -12,6 +12,7 @@ jobs:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.sha }}
token: ${{ secrets.GH_TOKEN }}
- name: Set up Python
uses: actions/setup-python@v2

14
.github/workflows/release-draft.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
name: Release Drafter
on:
push:
branches:
- master
jobs:
update_release_draft:
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,19 +1,46 @@
<div align=center>
<img src="./docs/.vuepress/public/logo.png" width="200" height="200">
<p align="center">
<a href="https://v2.nonebot.dev/"><img src="https://raw.githubusercontent.com/nonebot/nonebot2/master/docs/.vuepress/public/logo.png" width="200" height="200" alt="nonebot"></a>
</p>
<div align="center">
# NoneBot
[![License](https://img.shields.io/github/license/nonebot/nonebot2.svg)](LICENSE)
[![PyPI](https://img.shields.io/pypi/v/nonebot2.svg)](https://pypi.python.org/pypi/nonebot2)
![Python Version](https://img.shields.io/badge/python-3.7+-blue.svg)
![CQHTTP Version](https://img.shields.io/badge/cqhttp-11+-black.svg)
[![QQ 群](https://img.shields.io/badge/qq%E7%BE%A4-768887710-orange.svg)](https://jq.qq.com/?_wv=1027&k=5OFifDh)
[![Telegram](https://img.shields.io/badge/telegram-chat-blue.svg)](https://t.me/cqhttp)
[![QQ 版本发布群](https://img.shields.io/badge/%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83%E7%BE%A4-218529254-green.svg)](https://jq.qq.com/?_wv=1027&k=5Nl0zhE)
[![Telegram 版本发布频道](https://img.shields.io/badge/%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83%E9%A2%91%E9%81%93-join-green.svg)](https://t.me/cqhttp_release)
_✨ Python 异步机器人框架 ✨_
</div>
<p align="center">
<a href="https://raw.githubusercontent.com/nonebot/nonebot2/master/LICENSE">
<img src="https://img.shields.io/github/license/nonebot/nonebot2.svg" alt="license">
</a>
<a href="https://pypi.python.org/pypi/nonebot2">
<img src="https://img.shields.io/pypi/v/nonebot2.svg" alt="pypi">
</a>
<img src="https://img.shields.io/badge/python-3.7+-blue.svg" alt="python">
<img src="https://img.shields.io/badge/cqhttp-11+-black.svg" alt="cqhttp"><br />
<a href="https://jq.qq.com/?_wv=1027&k=5OFifDh">
<img src="https://img.shields.io/badge/qq%E7%BE%A4-768887710-orange.svg" alt="QQ Chat">
</a>
<a href="https://t.me/cqhttp">
<img src="https://img.shields.io/badge/telegram-chat-blue.svg" alt="Telegram Chat">
</a>
<a href="https://jq.qq.com/?_wv=1027&k=5Nl0zhE">
<img src="https://img.shields.io/badge/%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83%E7%BE%A4-218529254-green.svg" alt="QQ Release">
</a>
<a href="https://t.me/cqhttp_release">
<img src="https://img.shields.io/badge/版本发布频道-join-green.svg" alt="Telegram Release">
</a>
</p>
<p align="center">
<a href="https://v2.nonebot.dev/">文档</a>
·
<a href="https://v2.nonebot.dev/guide/installation.html">安装</a>
·
<a href="https://v2.nonebot.dev/guide/getting-started.html">开始使用</a>
</p>
## 简介
NoneBot2 是一个可扩展的 Python 异步机器人框架,它会对机器人收到的消息进行解析和处理,并以插件化的形式,分发给消息所对应的命令处理器和自然语言处理器,来完成具体的功能。
@@ -26,6 +53,11 @@ NoneBot2 是一个可扩展的 Python 异步机器人框架,它会对机器人
需要注意的是NoneBot 仅支持 Python 3.7+ 及 CQHTTP(OneBot) 插件 v11+。
此外NoneBot2 还有可配套使用的额外脚手架/框架:
- [NB-CLI](https://github.com/nonebot/nb-cli)
- [NoneBot-Test](https://github.com/nonebot/nonebot-test)
## 文档
文档目前尚未完成「API」部分由 sphinx 自动生成,你可以在 [这里](https://v2.nonebot.dev/) 查看。

View File

@@ -1,41 +0,0 @@
---
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.

View File

@@ -1,122 +0,0 @@
---
contentSidebar: true
sidebarDepth: 0
---
# NoneBot.rule 模块
## 规则
每个 `Matcher` 拥有一个 `Rule` ,其中是 **异步** `RuleChecker` 的集合,只有当所有 `RuleChecker` 检查结果为 `True` 时继续运行。
:::tip 提示
`RuleChecker` 既可以是 async function 也可以是 sync function
:::
## _class_ `Rule`
基类:`object`
* **说明**
`Matcher` 规则类,当事件传递时,在 `Matcher` 运行前进行检查。
* **示例**
```python
Rule(async_function) & sync_function
# 等价于
from nonebot.utils import run_sync
Rule(async_function, run_sync(sync_function))
```
### `__init__(*checkers)`
* **参数**
* `*checkers: Callable[[Bot, Event, dict], Awaitable[bool]]`: **异步** RuleChecker
### `checkers`
* **说明**
存储 `RuleChecker`
* **类型**
* `Set[Callable[[Bot, Event, dict], Awaitable[bool]]]`
### _async_ `__call__(bot, event, state)`
* **说明**
检查是否符合所有规则
* **参数**
* `bot: Bot`: Bot 对象
* `event: Event`: Event 对象
* `state: dict`: 当前 State
* **返回**
* `bool`
## `startswith(msg)`
* **说明**
匹配消息开头
* **参数**
* `msg: str`: 消息开头字符串
## `endswith(msg)`
* **说明**
匹配消息结尾
* **参数**
* `msg: str`: 消息结尾字符串

View File

@@ -7,16 +7,13 @@
* [nonebot](nonebot.html)
* [nonebot.typing](typing.html)
* [nonebot.config](config.html)
* [nonebot.sched](sched.html)
* [nonebot.plugin](plugin.html)
* [nonebot.log](log.html)
* [nonebot.matcher](matcher.html)
* [nonebot.rule](rule.html)
@@ -25,10 +22,28 @@
* [nonebot.permission](permission.html)
* [nonebot.sched](sched.html)
* [nonebot.log](log.html)
* [nonebot.utils](utils.html)
* [nonebot.typing](typing.html)
* [nonebot.exception](exception.html)
* [nonebot.drivers](drivers/)
* [nonebot.drivers.fastapi](drivers/fastapi.html)
* [nonebot.adapters](adapters/)
* [nonebot.adapters.cqhttp](adapters/cqhttp.html)

View File

@@ -0,0 +1,323 @@
---
contentSidebar: true
sidebarDepth: 0
---
# NoneBot.adapters 模块
## 协议适配基类
各协议请继承以下基类,并使用 `driver.register_adapter` 注册适配器
## _class_ `BaseBot`
基类:`abc.ABC`
Bot 基类。用于处理上报消息,并提供 API 调用接口。
### _abstract_ `__init__(driver, connection_type, config, self_id, *, websocket=None)`
* **参数**
* `driver: Driver`: Driver 对象
* `connection_type: str`: http 或者 websocket
* `config: Config`: Config 对象
* `self_id: str`: 机器人 ID
* `websocket: Optional[WebSocket]`: Websocket 连接对象
### `driver`
Driver 对象
### `connection_type`
连接类型
### `config`
Config 配置对象
### `self_id`
机器人 ID
### `websocket`
Websocket 连接对象
### _abstract property_ `type`
Adapter 类型
### _abstract async_ `handle_message(message)`
* **说明**
处理上报消息的函数,转换为 `Event` 事件后调用 `nonebot.message.handle_event` 进一步处理事件。
* **参数**
* `message: dict`: 收到的上报消息
### _abstract async_ `call_api(api, **data)`
* **说明**
调用机器人 API 接口,可以通过该函数或直接通过 bot 属性进行调用
* **参数**
* `api: str`: API 名称
* `**data`: API 数据
* **示例**
```python
await bot.call_api("send_msg", data={"message": "hello world"})
await bot.send_msg(message="hello world")
```
### _abstract async_ `send(event, message, **kwargs)`
* **说明**
调用机器人基础发送消息接口
* **参数**
* `event: Event`: 上报事件
* `message: Union[str, Message, MessageSegment]`: 要发送的消息
* `**kwargs`
## _class_ `BaseEvent`
基类:`abc.ABC`
Event 基类。提供上报信息的关键信息,其余信息可从原始上报消息获取。
### `__init__(raw_event)`
* **参数**
* `raw_event: dict`: 原始上报消息
### _property_ `raw_event`
原始上报消息
### _abstract property_ `id`
事件 ID
### _abstract property_ `name`
事件名称
### _abstract property_ `self_id`
机器人 ID
### _abstract property_ `time`
事件发生时间
### _abstract property_ `type`
事件主类型
### _abstract property_ `detail_type`
事件详细类型
### _abstract property_ `sub_type`
事件子类型
### _abstract property_ `user_id`
触发事件的主体 ID
### _abstract property_ `group_id`
触发事件的主体群 ID
### _abstract property_ `to_me`
事件是否为发送给机器人的消息
### _abstract property_ `message`
消息内容
### _abstract property_ `reply`
回复的消息
### _abstract property_ `raw_message`
原始消息
### _abstract property_ `plain_text`
纯文本消息
### _abstract property_ `sender`
消息发送者信息
## _class_ `BaseMessageSegment`
基类:`abc.ABC`
消息段基类
### `type`
* 类型: `str`
* 说明: 消息段类型
### `data`
* 类型: `Dict[str, Union[str, list]]`
* 说明: 消息段数据
## _class_ `BaseMessage`
基类:`list`, `abc.ABC`
消息数组
### `__init__(message=None, *args, **kwargs)`
* **参数**
* `message: Union[str, dict, list, MessageSegment, Message]`: 消息内容
### `append(obj)`
* **说明**
添加一个消息段到消息数组末尾
* **参数**
* `obj: Union[str, MessageSegment]`: 要添加的消息段
### `extend(obj)`
* **说明**
拼接一个消息数组或多个消息段到消息数组末尾
* **参数**
* `obj: Union[Message, Iterable[MessageSegment]]`: 要添加的消息数组
### `reduce()`
* **说明**
缩减消息数组,即拼接相邻纯文本消息段
### `extract_plain_text()`
* **说明**
提取消息内纯文本消息

View File

@@ -323,10 +323,10 @@ CQHTTP 协议 Event 适配。继承属性参考 [BaseEvent](./#class-baseevent)
### _property_ `sub_type`
* 类型: `str`
* 类型: `Optional[str]`
* 说明: 事件类型
* 说明: 事件类型
### _property_ `user_id`
@@ -405,7 +405,11 @@ CQHTTP 协议 Event 适配。继承属性参考 [BaseEvent](./#class-baseevent)
基类:[`nonebot.adapters.BaseMessageSegment`](#None)
CQHTTP 协议 MessageSegment 适配。具体方法参考协议消息段类型或源码。
## _class_ `Message`
基类:[`nonebot.adapters.BaseMessage`](#None)
CQHTTP 协议 Message 适配。

View File

@@ -194,10 +194,10 @@ SUPER_USERS=[12345789]
### `nickname`
* 类型: `Union[str, Set[str]]`
* 类型: `Set[str]`
* 默认值: `""`
* 默认值: `set()`
* 说明:

View File

@@ -0,0 +1,246 @@
---
contentSidebar: true
sidebarDepth: 0
---
# NoneBot.drivers 模块
## 后端驱动适配基类
各驱动请继承以下基类
## _class_ `BaseDriver`
基类:`abc.ABC`
Driver 基类。将后端框架封装,以满足适配器使用。
### `_adapters`
* **类型**
`Dict[str, Type[Bot]]`
* **说明**
已注册的适配器列表
### _abstract_ `__init__(env, config)`
* **参数**
* `env: Env`: 包含环境信息的 Env 对象
* `config: Config`: 包含配置信息的 Config 对象
### `env`
* **类型**
`str`
* **说明**
环境名称
### `config`
* **类型**
`Config`
* **说明**
配置对象
### `_clients`
* **类型**
`Dict[str, Bot]`
* **说明**
已连接的 Bot
### _classmethod_ `register_adapter(name, adapter)`
* **说明**
注册一个协议适配器
* **参数**
* `name: str`: 适配器名称,用于在连接时进行识别
* `adapter: Type[Bot]`: 适配器 Class
### _abstract property_ `type`
驱动类型名称
### _abstract property_ `server_app`
驱动 APP 对象
### _abstract property_ `asgi`
驱动 ASGI 对象
### _abstract property_ `logger`
驱动专属 logger 日志记录器
### _property_ `bots`
* **类型**
`Dict[str, Bot]`
* **说明**
获取当前所有已连接的 Bot
### _abstract_ `on_startup(func)`
注册一个在驱动启动时运行的函数
### _abstract_ `on_shutdown(func)`
注册一个在驱动停止时运行的函数
### _abstract_ `run(host=None, port=None, *args, **kwargs)`
* **说明**
启动驱动框架
* **参数**
* `host: Optional[str]`: 驱动绑定 IP
* `post: Optional[int]`: 驱动绑定端口
* `*args`
* `**kwargs`
### _abstract async_ `_handle_http()`
用于处理 HTTP 类型请求的函数
### _abstract async_ `_handle_ws_reverse()`
用于处理 WebSocket 类型请求的函数
## _class_ `BaseWebSocket`
基类:`object`
WebSocket 连接封装,统一接口方便外部调用。
### _abstract_ `__init__(websocket)`
* **参数**
* `websocket: Any`: WebSocket 连接对象
### _property_ `websocket`
WebSocket 连接对象
### _abstract property_ `closed`
* **类型**
`bool`
* **说明**
连接是否已经关闭
### _abstract async_ `accept()`
接受 WebSocket 连接请求
### _abstract async_ `close(code)`
关闭 WebSocket 连接请求
### _abstract async_ `receive()`
接收一条 WebSocket 信息
### _abstract async_ `send(data)`
发送一条 WebSocket 信息

View File

@@ -0,0 +1,125 @@
---
contentSidebar: true
sidebarDepth: 0
---
# NoneBot.drivers.fastapi 模块
## FastAPI 驱动适配
后端使用方法请参考: [FastAPI 文档](https://fastapi.tiangolo.com/)
## _class_ `Driver`
基类:[`nonebot.drivers.BaseDriver`](#None)
FastAPI 驱动框架
### `__init__(env, config)`
* **参数**
* `env: Env`: 包含环境信息的 Env 对象
* `config: Config`: 包含配置信息的 Config 对象
### _property_ `type`
驱动名称: `fastapi`
### _property_ `server_app`
`FastAPI APP` 对象
### _property_ `asgi`
`FastAPI APP` 对象
### _property_ `logger`
fastapi 使用的 logger
### `on_startup(func)`
参考文档: [Events](https://fastapi.tiangolo.com/advanced/events/#startup-event)
### `on_shutdown(func)`
参考文档: [Events](https://fastapi.tiangolo.com/advanced/events/#startup-event)
### `run(host=None, port=None, *, app=None, **kwargs)`
使用 `uvicorn` 启动 FastAPI
### _async_ `_handle_http(adapter, data=Body(Ellipsis), x_self_id=Header(None), x_signature=Header(None), auth=Depends(get_auth_bearer))`
用于处理 HTTP 类型请求的函数
### _async_ `_handle_ws_reverse(adapter, websocket, x_self_id=Header(None), auth=Depends(get_auth_bearer))`
用于处理 WebSocket 类型请求的函数
## _class_ `WebSocket`
基类:[`nonebot.drivers.BaseWebSocket`](#None)
### `__init__(websocket)`
* **参数**
* `websocket: Any`: WebSocket 连接对象
### _property_ `closed`
* **类型**
`bool`
* **说明**
连接是否已经关闭
### _async_ `accept()`
接受 WebSocket 连接请求
### _async_ `close(code=1000)`
关闭 WebSocket 连接请求
### _async_ `receive()`
接收一条 WebSocket 信息
### _async_ `send(data)`
发送一条 WebSocket 信息

View File

@@ -0,0 +1,497 @@
---
contentSidebar: true
sidebarDepth: 0
---
# NoneBot.matcher 模块
## 事件响应器
该模块实现事件响应器的创建与运行,并提供一些快捷方法来帮助用户更好的与机器人进行 对话 。
## `matchers`
* **类型**
`Dict[int, List[Type[Matcher]]]`
* **说明**
用于存储当前所有的事件响应器
## _class_ `Matcher`
基类:`object`
事件响应器类
### `module`
* **类型**
`Optional[str]`
* **说明**
事件响应器所在模块名称
### `type`
* **类型**
`str`
* **说明**
事件响应器类型
### `rule`
* **类型**
`Rule`
* **说明**
事件响应器匹配规则
### `permission`
* **类型**
`Permission`
* **说明**
事件响应器触发权限
### `priority`
* **类型**
`int`
* **说明**
事件响应器优先级
### `block`
* **类型**
`bool`
* **说明**
事件响应器是否阻止事件传播
### `temp`
* **类型**
`bool`
* **说明**
事件响应器是否为临时
### `expire_time`
* **类型**
`Optional[datetime]`
* **说明**
事件响应器过期时间点
### `_default_state`
* **类型**
`dict`
* **说明**
事件响应器默认状态
### `_default_parser`
* **类型**
`Optional[ArgsParser]`
* **说明**
事件响应器默认参数解析函数
### `__init__()`
实例化 Matcher 以便运行
### `handlers`
* **类型**
`List[Handler]`
* **说明**
事件响应器拥有的事件处理函数列表
### _classmethod_ `new(type_='', rule=None, permission=None, handlers=None, temp=False, priority=1, block=False, *, module=None, default_state=None, expire_time=None)`
* **说明**
创建一个新的事件响应器,并存储至 [matchers](#matchers)
* **参数**
* `type_: str`: 事件响应器类型,与 `event.type` 一致时触发,空字符串表示任意
* `rule: Optional[Rule]`: 匹配规则
* `permission: Optional[Permission]`: 权限
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器,即触发一次后删除
* `priority: int`: 响应优先级
* `block: bool`: 是否阻止事件向更低优先级的响应器传播
* `module: Optional[str]`: 事件响应器所在模块名称
* `default_state: Optional[dict]`: 默认状态 `state`
* `expire_time: Optional[datetime]`: 事件响应器最终有效时间点,过时即被删除
* **返回**
* `Type[Matcher]`: 新的事件响应器类
### _async classmethod_ `check_perm(bot, event)`
* **说明**
检查是否满足触发权限
* **参数**
* `bot: Bot`: Bot 对象
* `event: Event`: 上报事件
* **返回**
* `bool`: 是否满足权限
### _async classmethod_ `check_rule(bot, event, state)`
* **说明**
检查是否满足匹配规则
* **参数**
* `bot: Bot`: Bot 对象
* `event: Event`: 上报事件
* `state: dict`: 当前状态
* **返回**
* `bool`: 是否满足匹配规则
### _classmethod_ `args_parser(func)`
* **说明**
装饰一个函数来更改当前事件响应器的默认参数解析函数
* **参数**
* `func: ArgsParser`: 参数解析函数
### _classmethod_ `handle()`
* **说明**
装饰一个函数来向事件响应器直接添加一个处理函数
* **参数**
*
### _classmethod_ `receive()`
* **说明**
装饰一个函数来指示 NoneBot 在接收用户新的一条消息后继续运行该函数
* **参数**
*
### _classmethod_ `got(key, prompt=None, args_parser=None)`
* **说明**
装饰一个函数来指示 NoneBot 当要获取的 `key` 不存在时接收用户新的一条消息并经过 `ArgsParser` 处理后再运行该函数,如果 `key` 已存在则直接继续运行
* **参数**
* `key: str`: 参数名
* `prompt: Optional[Union[str, Message, MessageSegment]]`: 在参数不存在时向用户发送的消息
* `args_parser: Optional[ArgsParser]`: 可选参数解析函数,空则使用默认解析函数
### _async classmethod_ `send(message, **kwargs)`
* **说明**
发送一条消息给当前交互用户
* **参数**
* `message: Union[str, Message, MessageSegment]`: 消息内容
* `**kwargs`: 其他传递给 `bot.send` 的参数,请参考对应 adapter 的 bot 对象 api
### _async classmethod_ `finish(message=None, **kwargs)`
* **说明**
发送一条消息给当前交互用户并结束当前事件响应器
* **参数**
* `message: Union[str, Message, MessageSegment]`: 消息内容
* `**kwargs`: 其他传递给 `bot.send` 的参数,请参考对应 adapter 的 bot 对象 api
### _async classmethod_ `pause(prompt=None, **kwargs)`
* **说明**
发送一条消息给当前交互用户并暂停事件响应器,在接收用户新的一条消息后继续下一个处理函数
* **参数**
* `prompt: Union[str, Message, MessageSegment]`: 消息内容
* `**kwargs`: 其他传递给 `bot.send` 的参数,请参考对应 adapter 的 bot 对象 api
### _async classmethod_ `reject(prompt=None, **kwargs)`
* **说明**
发送一条消息给当前交互用户并暂停事件响应器,在接收用户新的一条消息后重新运行当前处理函数
* **参数**
* `prompt: Union[str, Message, MessageSegment]`: 消息内容
* `**kwargs`: 其他传递给 `bot.send` 的参数,请参考对应 adapter 的 bot 对象 api
## _class_ `MatcherGroup`
基类:`object`
事件响应器组合,统一管理。用法同 `Matcher`
### `__init__(type_='', rule=None, permission=None, handlers=None, temp=False, priority=1, block=False, *, module=None, default_state=None, expire_time=None)`
* **说明**
创建一个事件响应器组合,参数为默认值,与 `Matcher.new` 一致
### `matchers`
* **类型**
`List[Type[Matcher]]`
* **说明**
组内事件响应器列表
### `new(type_='', rule=None, permission=None, handlers=None, temp=False, priority=1, block=False, *, module=None, default_state=None, expire_time=None)`
* **说明**
在组中创建一个新的事件响应器,参数留空则使用组合默认值
:::danger 警告
如果使用 handlers 参数覆盖组合默认值则该事件响应器不会随组合一起添加新的事件处理函数
:::

View File

@@ -5,6 +5,55 @@ sidebarDepth: 0
# NoneBot 模块
## 快捷导入
为方便使用,`nonebot` 模块从子模块导入了部分内容
* `on_message` => `nonebot.plugin.on_message`
* `on_notice` => `nonebot.plugin.on_notice`
* `on_request` => `nonebot.plugin.on_request`
* `on_metaevent` => `nonebot.plugin.on_metaevent`
* `on_startswith` => `nonebot.plugin.on_startswith`
* `on_endswith` => `nonebot.plugin.on_endswith`
* `on_command` => `nonebot.plugin.on_command`
* `on_regex` => `nonebot.plugin.on_regex`
* `on_regex` => `nonebot.plugin.on_regex`
* `on_regex` => `nonebot.plugin.on_regex`
* `CommandGroup` => `nonebot.plugin.CommandGroup`
* `load_plugin` => `nonebot.plugin.load_plugin`
* `load_plugins` => `nonebot.plugin.load_plugins`
* `load_builtin_plugins` => `nonebot.plugin.load_builtin_plugins`
* `get_loaded_plugins` => `nonebot.plugin.get_loaded_plugins`
## `get_driver()`

View File

@@ -0,0 +1,629 @@
---
contentSidebar: true
sidebarDepth: 0
---
# NoneBot.plugin 模块
## 插件
为 NoneBot 插件开发提供便携的定义函数。
## `plugins`
* **类型**
`Dict[str, Plugin]`
* **说明**
已加载的插件
## _class_ `Plugin`
基类:`object`
存储插件信息
### `name`
* **类型**: `str`
* **说明**: 插件名称,使用 文件/文件夹 名称作为插件名
### `module`
* **类型**: `ModuleType`
* **说明**: 插件模块对象
### `matcher`
* **类型**: `Set[Type[Matcher]]`
* **说明**: 插件内定义的 `Matcher`
## `on(type='', rule=None, permission=None, *, handlers=None, temp=False, priority=1, block=False, state=None)`
* **说明**
注册一个基础事件响应器,可自定义类型。
* **参数**
* `type: str`: 事件响应器类型
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `permission: Optional[Permission]`: 事件响应权限
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_metaevent(rule=None, *, handlers=None, temp=False, priority=1, block=False, state=None)`
* **说明**
注册一个元事件响应器。
* **参数**
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_message(rule=None, permission=None, *, handlers=None, temp=False, priority=1, block=True, state=None)`
* **说明**
注册一个消息事件响应器。
* **参数**
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `permission: Optional[Permission]`: 事件响应权限
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_notice(rule=None, *, handlers=None, temp=False, priority=1, block=False, state=None)`
* **说明**
注册一个通知事件响应器。
* **参数**
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_request(rule=None, *, handlers=None, temp=False, priority=1, block=False, state=None)`
* **说明**
注册一个请求事件响应器。
* **参数**
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_startswith(msg, rule=None, **kwargs)`
* **说明**
注册一个消息事件响应器,并且当消息的\*\*文本部分\*\*以指定内容开头时响应。
* **参数**
* `msg: str`: 指定消息开头内容
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `permission: Optional[Permission]`: 事件响应权限
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_endswith(msg, rule=None, **kwargs)`
* **说明**
注册一个消息事件响应器,并且当消息的\*\*文本部分\*\*以指定内容结尾时响应。
* **参数**
* `msg: str`: 指定消息结尾内容
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `permission: Optional[Permission]`: 事件响应权限
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_keyword(keywords, rule=None, **kwargs)`
* **说明**
注册一个消息事件响应器,并且当消息纯文本部分包含关键词时响应。
* **参数**
* `keywords: Set[str]`: 关键词列表
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `permission: Optional[Permission]`: 事件响应权限
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_command(cmd, rule=None, aliases=None, **kwargs)`
* **说明**
注册一个消息事件响应器,并且当消息以指定命令开头时响应。
命令匹配规则参考: [命令形式匹配](rule.html#command-command)
* **参数**
* `cmd: Union[str, Tuple[str, ...]]`: 指定命令内容
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `aliases: Optional[Set[Union[str, Tuple[str, ...]]]]`: 命令别名
* `permission: Optional[Permission]`: 事件响应权限
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_regex(pattern, flags=0, rule=None, **kwargs)`
* **说明**
注册一个消息事件响应器,并且当消息匹配正则表达式时响应。
命令匹配规则参考: [正则匹配](rule.html#regex-regex-flags-0)
* **参数**
* `pattern: str`: 正则表达式
* `flags: Union[int, re.RegexFlag]`: 正则匹配标志
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `permission: Optional[Permission]`: 事件响应权限
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## _class_ `CommandGroup`
基类:`object`
命令组,用于声明一组有相同名称前缀的命令。
### `__init__(cmd, **kwargs)`
* **参数**
* `cmd: Union[str, Tuple[str, ...]]`: 命令前缀
* `**kwargs`: 其他传递给 `on_command` 的参数默认值,参考 [on_command](#on-command-cmd-rule-none-aliases-none-kwargs)
### `basecmd`
* **类型**: `Tuple[str, ...]`
* **说明**: 命令前缀
### `base_kwargs`
* **类型**: `Dict[str, Any]`
* **说明**: 其他传递给 `on_command` 的参数默认值
### `command(cmd, **kwargs)`
* **说明**
注册一个新的命令。
* **参数**
* `cmd: Union[str, Tuple[str, ...]]`: 命令前缀
* `**kwargs`: 其他传递给 `on_command` 的参数,将会覆盖命令组默认值
* **返回**
* `Type[Matcher]`
## `load_plugin(module_path)`
* **说明**
使用 `importlib` 加载单个插件,可以是本地插件或是通过 `pip` 安装的插件。
* **参数**
* `module_path: str`: 插件名称 `path.to.your.plugin`
* **返回**
* `Optional[Plugin]`
## `load_plugins(*plugin_dir)`
* **说明**
导入目录下多个插件,以 `_` 开头的插件不会被导入!
* **参数**
* `*plugin_dir: str`: 插件路径
* **返回**
* `Set[Plugin]`
## `load_builtin_plugins()`
* **说明**
导入 NoneBot 内置插件
* **返回**
* `Plugin`
## `get_loaded_plugins()`
* **说明**
获取当前已导入的插件。
* **返回**
* `Set[Plugin]`

210
archive/2.0.0a4/api/rule.md Normal file
View File

@@ -0,0 +1,210 @@
---
contentSidebar: true
sidebarDepth: 0
---
# NoneBot.rule 模块
## 规则
每个事件响应器 `Matcher` 拥有一个匹配规则 `Rule` ,其中是 **异步** `RuleChecker` 的集合,只有当所有 `RuleChecker` 检查结果为 `True` 时继续运行。
:::tip 提示
`RuleChecker` 既可以是 async function 也可以是 sync function但在最终会被 `nonebot.utils.run_sync` 转换为 async function
:::
## _class_ `Rule`
基类:`object`
* **说明**
`Matcher` 规则类,当事件传递时,在 `Matcher` 运行前进行检查。
* **示例**
```python
Rule(async_function) & sync_function
# 等价于
from nonebot.utils import run_sync
Rule(async_function, run_sync(sync_function))
```
### `__init__(*checkers)`
* **参数**
* `*checkers: Callable[[Bot, Event, dict], Awaitable[bool]]`: **异步** RuleChecker
### `checkers`
* **说明**
存储 `RuleChecker`
* **类型**
* `Set[Callable[[Bot, Event, dict], Awaitable[bool]]]`
### _async_ `__call__(bot, event, state)`
* **说明**
检查是否符合所有规则
* **参数**
* `bot: Bot`: Bot 对象
* `event: Event`: Event 对象
* `state: dict`: 当前 State
* **返回**
* `bool`
## `startswith(msg)`
* **说明**
匹配消息开头
* **参数**
* `msg: str`: 消息开头字符串
## `endswith(msg)`
* **说明**
匹配消息结尾
* **参数**
* `msg: str`: 消息结尾字符串
## `keyword(*keywords)`
* **说明**
匹配消息关键词
* **参数**
* `*keywords: str`: 关键词
## `command(*cmds)`
* **说明**
命令形式匹配,根据配置里提供的 `command_start`, `command_sep` 判断消息是否为命令。
可以通过 `state["_prefix"]["command"]` 获取匹配成功的命令(例:`("test",)`),通过 `state["_prefix"]["raw_command"]` 获取匹配成功的原始命令文本(例:`"/test"`)。
* **参数**
* `*cmds: Union[str, Tuple[str, ...]]`: 命令内容
* **示例**
使用默认 `command_start`, `command_sep` 配置
命令 `("test",)` 可以匹配:`/test` 开头的消息
命令 `("test", "sub")` 可以匹配”`/test.sub` 开头的消息
:::tip 提示
命令内容与后续消息间无需空格!
:::
## `regex(regex, flags=0)`
* **说明**
根据正则表达式进行匹配。
可以通过 `state["_matched"]` 获取正则表达式匹配成功的文本。
* **参数**
* `regex: str`: 正则表达式
* `flags: Union[int, re.RegexFlag]`: 正则标志
:::tip 提示
正则表达式匹配使用 search 而非 match如需从头匹配请使用 `r"^xxx"` 来确保匹配开头
:::
## `to_me()`
* **说明**
通过 `event.to_me` 判断消息是否是发送给机器人
* **参数**
*

View File

@@ -20,11 +20,9 @@ NoneBot2 是一个可扩展的 Python 异步机器人框架,它会对机器人
## 它如何工作?
NoneBot 的运行离不开 酷 Q 和 CQHTTP 插件。酷 Q 扮演着「无头 QQ 客户端」的角色,它进行实际的消息、通知、请求的接收和发送,当 酷 Q 收到消息时,它将这个消息包装为一个事件(通知和请求同理),并通过它自己的插件机制将事件传送给 CQHTTP 插件,后者再根据其配置中的 `post_url``ws_reverse_url` 等项来将事件发送至 NoneBot。
<!-- TODO: how to work -->
在 NoneBot 收到事件前,它底层的 aiocqhttp 实际已经先看到了事件aiocqhttp 根据事件的类型信息,通知到 NoneBot 的相应函数。特别地,对于消息类型的事件,还将消息内容转换成了 `aiocqhttp.message.Message` 类型,以便处理。
NoneBot 的事件处理函数收到通知后对于不同类型的事件再做相应的预处理和解析然后调用对应的插件并向其提供适合此类事件的会话Session对象。NoneBot 插件的编写者要做的,就是利用 Session 对象中提供的数据,在插件的处理函数中实现所需的功能。
~~未填坑~~
## 特色

View File

@@ -136,11 +136,11 @@ QQ 协议端举例:
现在,尝试向你的 QQ 机器人账号发送如下内容:
```default
/say 你好,世界
/echo 你好,世界
```
到这里如果一切 OK你应该会收到机器人给你回复了 `你好,世界`。这一历史性的对话标志着你已经成功地运行了一个 NoneBot 的最小实例,开始了编写更强大的 QQ 机器人的创意之旅!
<ClientOnly>
<Messenger :messages="[{ position: 'right', msg: '/say 你好,世界' }, { position: 'left', msg: '你好,世界' }]"/>
<Messenger :messages="[{ position: 'right', msg: '/echo 你好,世界' }, { position: 'left', msg: '你好,世界' }]"/>
</ClientOnly>

View File

@@ -6,7 +6,10 @@
请确保你的 Python 版本 >= 3.7。
:::
请在安装 nonebot2 之前卸载 nonebot 1.x
```bash
pip uninstall nonebot
pip install nonebot2
```

View File

@@ -1,4 +1,5 @@
{
"sidebar": {},
"locales": {
"/": {
"label": "简体中文",
@@ -17,6 +18,10 @@
{
"text": "API",
"link": "/api/"
},
{
"text": "插件广场",
"link": "/plugin-store"
}
],
"sidebarDepth": 2,
@@ -47,21 +52,17 @@
"title": "nonebot 模块",
"path": "nonebot"
},
{
"title": "nonebot.typing 模块",
"path": "typing"
},
{
"title": "nonebot.config 模块",
"path": "config"
},
{
"title": "nonebot.sched 模块",
"path": "sched"
"title": "nonebot.plugin 模块",
"path": "plugin"
},
{
"title": "nonebot.log 模块",
"path": "log"
"title": "nonebot.matcher 模块",
"path": "matcher"
},
{
"title": "nonebot.rule 模块",
@@ -71,14 +72,34 @@
"title": "nonebot.permission 模块",
"path": "permission"
},
{
"title": "nonebot.sched 模块",
"path": "sched"
},
{
"title": "nonebot.log 模块",
"path": "log"
},
{
"title": "nonebot.utils 模块",
"path": "utils"
},
{
"title": "nonebot.typing 模块",
"path": "typing"
},
{
"title": "nonebot.exception 模块",
"path": "exception"
},
{
"title": "nonebot.drivers 模块",
"path": "drivers/"
},
{
"title": "nonebot.drivers.fastapi 模块",
"path": "drivers/fastapi"
},
{
"title": "nonebot.adapters 模块",
"path": "adapters/"

View File

@@ -0,0 +1,162 @@
<template>
<div class="plugins">
<v-app>
<v-main>
<v-row>
<v-col cols="12" sm="4">
<v-text-field
v-model="filterText"
dense
rounded
outlined
clearable
hide-details
label="Filter Plugin"
>
<template v-slot:prepend-inner>
<div class="v-input__icon v-input__icon--prepend-inner">
<v-icon small>fa-filter</v-icon>
</div>
</template>
</v-text-field>
</v-col>
<v-col cols="12" sm="4">
<v-btn
block
color="primary"
target="_blank"
rel="noopener noreferrer"
href="https://github.com/nonebot/nonebot2/issues/new?template=plugin-publish.md"
>Publish Your Plugin
</v-btn>
</v-col>
<v-col cols="12" sm="4">
<v-pagination
v-model="page"
:length="pageNum"
prev-icon="fa-caret-left"
next-icon="fa-caret-right"
></v-pagination>
</v-col>
</v-row>
<hr />
<v-row>
<v-col
cols="12"
sm="6"
v-for="(plugin, index) in filteredPlugins"
:key="index"
>
<v-card>
<v-card-title>
{{ plugin.name }}
<v-spacer></v-spacer>
<a
class="repo-link"
v-if="repoLink(plugin.repo)"
rel="noopener noreferrer"
target="_blank"
:title="plugin.repo"
:href="repoLink(plugin.repo)"
>
<v-icon>fab fa-github</v-icon>
</a>
</v-card-title>
<v-card-text>{{ plugin.desc }}</v-card-text>
<v-card-text>
<v-icon x-small>fa-fingerprint</v-icon>
{{ plugin.id }}
<v-icon x-small class="ml-2">fa-user</v-icon>
{{ plugin.author }}
</v-card-text>
<v-card-actions>
<v-btn
block
depressed
class="btn-copy"
@click="copyCommand(plugin)"
>
copy nb-cli command
<v-icon right small>fa-copy</v-icon>
</v-btn>
<v-snackbar v-model="snackbar">Copied!</v-snackbar>
</v-card-actions>
</v-card>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-pagination
v-model="page"
:length="pageNum"
prev-icon="fa-caret-left"
next-icon="fa-caret-right"
></v-pagination>
</v-col>
</v-row>
</v-main>
</v-app>
</div>
</template>
<script>
import copy from "copy-to-clipboard";
import plugins from "../public/plugins.json";
export default {
name: "Plugins",
data() {
return {
plugins: plugins,
snackbar: false,
filterText: "",
page: 1
};
},
computed: {
pageNum() {
return Math.ceil(this.filteredPlugins.length / 10);
},
filteredPlugins() {
return this.plugins.filter(plugin => {
return (
plugin.id.indexOf(this.filterText) != -1 ||
plugin.name.indexOf(this.filterText) != -1 ||
plugin.desc.indexOf(this.filterText) != -1 ||
plugin.author.indexOf(this.filterText) != -1
);
});
}
},
methods: {
repoLink(repo) {
if (repo) {
return /^https?:/.test(repo) ? repo : `https://github.com/${repo}`;
}
return null;
},
copyCommand(plugin) {
copy(`nb plugin install ${plugin.id}`, {
format: "text/plain"
});
this.snackbar = true;
}
}
};
</script>
<style>
.v-application--wrap {
min-height: 0 !important;
}
</style>
<style scoped>
.repo-link {
text-decoration: none !important;
display: inline-block;
}
.repo-link:hover i {
color: #ea5252;
}
</style>

View File

@@ -13,7 +13,8 @@ module.exports = context => ({
*/
head: [
["link", { rel: "icon", href: "/logo.png" }],
["meta", { name: "theme-color", content: "#d32f2f" }],
["link", { rel: "manifest", href: "/manifest.json" }],
["meta", { name: "theme-color", content: "#ea5252" }],
["meta", { name: "application-name", content: "NoneBot" }],
["meta", { name: "apple-mobile-web-app-title", content: "NoneBot" }],
["meta", { name: "apple-mobile-web-app-capable", content: "yes" }],
@@ -21,6 +22,26 @@ module.exports = context => ({
"meta",
{ name: "apple-mobile-web-app-status-bar-style", content: "black" }
],
[
"link",
{ rel: "apple-touch-icon", href: "/icons/apple-touch-icon-180x180.png" }
],
[
"link",
{
rel: "mask-icon",
href: "/icons/safari-pinned-tab.svg",
color: "#ea5252"
}
],
[
"meta",
{
name: "msapplication-TileImage",
content: "/icons/mstile-150x150.png"
}
],
["meta", { name: "msapplication-TileColor", content: "#ea5252" }],
[
"link",
{
@@ -38,12 +59,12 @@ module.exports = context => ({
}
},
theme: "titanium",
theme: "nonebot",
themeConfig: {
logo: "/logo.png",
repo: "nonebot/nonebot",
repo: "nonebot/nonebot2",
docsDir: "docs",
docsBranch: "dev2",
docsBranch: "dev",
docsDirVersioned: "archive",
docsDirPages: "pages",
editLinks: true,
@@ -58,7 +79,8 @@ module.exports = context => ({
nav: [
{ text: "主页", link: "/" },
{ text: "指南", link: "/guide/" },
{ text: "API", link: "/api/" }
{ text: "API", link: "/api/" },
{ text: "插件广场", link: "/plugin-store" }
],
sidebarDepth: 2,
sidebar: {
@@ -88,21 +110,17 @@ module.exports = context => ({
title: "nonebot 模块",
path: "nonebot"
},
{
title: "nonebot.typing 模块",
path: "typing"
},
{
title: "nonebot.config 模块",
path: "config"
},
{
title: "nonebot.sched 模块",
path: "sched"
title: "nonebot.plugin 模块",
path: "plugin"
},
{
title: "nonebot.log 模块",
path: "log"
title: "nonebot.matcher 模块",
path: "matcher"
},
{
title: "nonebot.rule 模块",
@@ -112,14 +130,34 @@ module.exports = context => ({
title: "nonebot.permission 模块",
path: "permission"
},
{
title: "nonebot.sched 模块",
path: "sched"
},
{
title: "nonebot.log 模块",
path: "log"
},
{
title: "nonebot.utils 模块",
path: "utils"
},
{
title: "nonebot.typing 模块",
path: "typing"
},
{
title: "nonebot.exception 模块",
path: "exception"
},
{
title: "nonebot.drivers 模块",
path: "drivers/"
},
{
title: "nonebot.drivers.fastapi 模块",
path: "drivers/fastapi"
},
{
title: "nonebot.adapters 模块",
path: "adapters/"
@@ -139,6 +177,16 @@ module.exports = context => ({
plugins: [
"@vuepress/plugin-back-to-top",
"@vuepress/plugin-medium-zoom",
[
"@vuepress/pwa",
{
serviceWorker: true,
updatePopup: {
message: "发现新内容",
buttonText: "刷新"
}
}
],
[
"versioning",
{

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1008 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="500.000000pt" height="500.000000pt" viewBox="0 0 500.000000 500.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,500.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2478 4525 c-2 -1 -57 -5 -122 -9 -65 -4 -122 -8 -125 -10 -3 -2 -30
-7 -60 -11 -141 -18 -404 -102 -561 -180 -68 -34 -238 -132 -250 -145 -3 -3
-32 -25 -65 -49 -246 -182 -465 -442 -604 -719 -212 -422 -271 -933 -159
-1377 11 -44 21 -88 23 -97 8 -38 86 -238 120 -308 206 -415 542 -749 952
-945 90 -43 265 -111 309 -119 11 -2 48 -12 84 -21 82 -21 94 -24 155 -31 28
-4 53 -8 56 -10 11 -6 172 -17 264 -17 88 -1 239 9 282 17 11 3 43 8 70 12
280 40 611 181 855 363 118 87 221 181 311 284 173 195 309 424 392 657 14 41
28 82 30 90 2 8 13 51 25 95 25 93 27 103 34 159 3 22 8 52 11 65 9 46 16 166
17 276 1 704 -363 1359 -960 1728 -201 125 -484 234 -677 263 -16 3 -52 9 -79
15 -27 5 -92 12 -145 15 -53 3 -115 7 -138 8 -23 2 -43 2 -45 1z m162 -89
c691 -39 1330 -482 1622 -1121 74 -163 137 -364 152 -490 4 -27 9 -63 11 -80
17 -106 21 -305 10 -430 -8 -84 -49 -307 -60 -325 -2 -4 -6 -20 -9 -36 -3 -17
-23 -77 -44 -135 -194 -521 -605 -937 -1124 -1135 -562 -214 -1202 -155 -1713
160 -317 195 -564 466 -735 806 -60 120 -141 358 -156 465 -3 17 -9 55 -15 85
-11 61 -23 217 -22 295 3 525 201 1002 571 1373 340 340 782 542 1247 568 55
3 101 7 102 8 2 1 20 1 40 0 21 -2 76 -5 123 -8z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,20 @@
{
"name": "NoneBot",
"short_name": "NoneBot",
"background-color": "#ffffff",
"theme-color": "#ea5252",
"description": "An asynchronous python bot framework.",
"display": "standalone",
"icons": [
{
"src": "/icons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/android-chrome-384x384.png",
"sizes": "384x384",
"type": "image/png"
}
]
}

View File

@@ -0,0 +1,10 @@
[
{
"id": "nonebot-plugin-status",
"link": "nonebot-plugin-status",
"name": "状态监控",
"desc": "通过戳一戳获取服务器状态",
"author": "nonebot",
"repo": "nonebot/nonebot2"
}
]

View File

@@ -1,3 +1,3 @@
[
"2.0.0a2"
"2.0.0a4"
]

View File

@@ -7,16 +7,13 @@
* [nonebot](nonebot.html)
* [nonebot.typing](typing.html)
* [nonebot.config](config.html)
* [nonebot.sched](sched.html)
* [nonebot.plugin](plugin.html)
* [nonebot.log](log.html)
* [nonebot.matcher](matcher.html)
* [nonebot.rule](rule.html)
@@ -25,10 +22,28 @@
* [nonebot.permission](permission.html)
* [nonebot.sched](sched.html)
* [nonebot.log](log.html)
* [nonebot.utils](utils.html)
* [nonebot.typing](typing.html)
* [nonebot.exception](exception.html)
* [nonebot.drivers](drivers/)
* [nonebot.drivers.fastapi](drivers/fastapi.html)
* [nonebot.adapters](adapters/)
* [nonebot.adapters.cqhttp](adapters/cqhttp.html)

View File

@@ -5,37 +5,319 @@ sidebarDepth: 0
# NoneBot.adapters 模块
## 协议适配基类
各协议请继承以下基类,并使用 `driver.register_adapter` 注册适配器
## _class_ `BaseBot`
基类:`abc.ABC`
Bot 基类。用于处理上报消息,并提供 API 调用接口。
### _abstract_ `__init__(driver, connection_type, config, self_id, *, websocket=None)`
* **参数**
* `driver: Driver`: Driver 对象
* `connection_type: str`: http 或者 websocket
* `config: Config`: Config 对象
* `self_id: str`: 机器人 ID
* `websocket: Optional[WebSocket]`: Websocket 连接对象
### `driver`
Driver 对象
### `connection_type`
连接类型
### `config`
Config 配置对象
### `self_id`
机器人 ID
### `websocket`
Websocket 连接对象
### _abstract property_ `type`
Adapter 类型
### _abstract async_ `handle_message(message)`
* **说明**
处理上报消息的函数,转换为 `Event` 事件后调用 `nonebot.message.handle_event` 进一步处理事件。
* **参数**
* `message: dict`: 收到的上报消息
### _abstract async_ `call_api(api, **data)`
* **说明**
调用机器人 API 接口,可以通过该函数或直接通过 bot 属性进行调用
* **参数**
* `api: str`: API 名称
* `**data`: API 数据
* **示例**
```python
await bot.call_api("send_msg", data={"message": "hello world"})
await bot.send_msg(message="hello world")
```
### _abstract async_ `send(event, message, **kwargs)`
* **说明**
调用机器人基础发送消息接口
* **参数**
* `event: Event`: 上报事件
* `message: Union[str, Message, MessageSegment]`: 要发送的消息
* `**kwargs`
## _class_ `BaseEvent`
基类:`abc.ABC`
Event 基类。提供上报信息的关键信息,其余信息可从原始上报消息获取。
### `_raw_event`
原始 event
### `__init__(raw_event)`
* **参数**
* `raw_event: dict`: 原始上报消息
### _property_ `raw_event`
原始上报消息
### _abstract property_ `id`
事件 ID
### _abstract property_ `name`
事件名称
### _abstract property_ `self_id`
机器人 ID
### _abstract property_ `time`
事件发生时间
### _abstract property_ `type`
事件主类型
### _abstract property_ `detail_type`
事件详细类型
### _abstract property_ `sub_type`
事件子类型
### _abstract property_ `user_id`
触发事件的主体 ID
### _abstract property_ `group_id`
触发事件的主体群 ID
### _abstract property_ `to_me`
事件是否为发送给机器人的消息
### _abstract property_ `message`
消息内容
### _abstract property_ `reply`
回复的消息
### _abstract property_ `raw_message`
原始消息
### _abstract property_ `plain_text`
纯文本消息
### _abstract property_ `sender`
消息发送者信息
## _class_ `BaseMessageSegment`
基类:`abc.ABC`
消息段基类
### `type`
* 类型: `str`
* 说明: 消息段类型
### `data`
* 类型: `Dict[str, Union[str, list]]`
* 说明: 消息段数据
## _class_ `BaseMessage`
基类:`list`, `abc.ABC`
消息数组
### `__init__(message=None, *args, **kwargs)`
* **参数**
* `message: Union[str, dict, list, MessageSegment, Message]`: 消息内容
### `append(obj)`
Append object to the end of the list.
* **说明**
添加一个消息段到消息数组末尾
* **参数**
* `obj: Union[str, MessageSegment]`: 要添加的消息段
### `extend(obj)`
Extend list by appending elements from the iterable.
* **说明**
拼接一个消息数组或多个消息段到消息数组末尾
* **参数**
* `obj: Union[Message, Iterable[MessageSegment]]`: 要添加的消息数组
### `reduce()`
* **说明**
缩减消息数组,即拼接相邻纯文本消息段
### `extract_plain_text()`
* **说明**
提取消息内纯文本消息

View File

@@ -323,10 +323,10 @@ CQHTTP 协议 Event 适配。继承属性参考 [BaseEvent](./#class-baseevent)
### _property_ `sub_type`
* 类型: `str`
* 类型: `Optional[str]`
* 说明: 事件类型
* 说明: 事件类型
### _property_ `user_id`
@@ -405,7 +405,11 @@ CQHTTP 协议 Event 适配。继承属性参考 [BaseEvent](./#class-baseevent)
基类:[`nonebot.adapters.BaseMessageSegment`](#None)
CQHTTP 协议 MessageSegment 适配。具体方法参考协议消息段类型或源码。
## _class_ `Message`
基类:[`nonebot.adapters.BaseMessage`](#None)
CQHTTP 协议 Message 适配。

View File

@@ -194,10 +194,10 @@ SUPER_USERS=[12345789]
### `nickname`
* 类型: `Union[str, Set[str]]`
* 类型: `Set[str]`
* 默认值: `""`
* 默认值: `set()`
* 说明:

246
docs/api/drivers/README.md Normal file
View File

@@ -0,0 +1,246 @@
---
contentSidebar: true
sidebarDepth: 0
---
# NoneBot.drivers 模块
## 后端驱动适配基类
各驱动请继承以下基类
## _class_ `BaseDriver`
基类:`abc.ABC`
Driver 基类。将后端框架封装,以满足适配器使用。
### `_adapters`
* **类型**
`Dict[str, Type[Bot]]`
* **说明**
已注册的适配器列表
### _abstract_ `__init__(env, config)`
* **参数**
* `env: Env`: 包含环境信息的 Env 对象
* `config: Config`: 包含配置信息的 Config 对象
### `env`
* **类型**
`str`
* **说明**
环境名称
### `config`
* **类型**
`Config`
* **说明**
配置对象
### `_clients`
* **类型**
`Dict[str, Bot]`
* **说明**
已连接的 Bot
### _classmethod_ `register_adapter(name, adapter)`
* **说明**
注册一个协议适配器
* **参数**
* `name: str`: 适配器名称,用于在连接时进行识别
* `adapter: Type[Bot]`: 适配器 Class
### _abstract property_ `type`
驱动类型名称
### _abstract property_ `server_app`
驱动 APP 对象
### _abstract property_ `asgi`
驱动 ASGI 对象
### _abstract property_ `logger`
驱动专属 logger 日志记录器
### _property_ `bots`
* **类型**
`Dict[str, Bot]`
* **说明**
获取当前所有已连接的 Bot
### _abstract_ `on_startup(func)`
注册一个在驱动启动时运行的函数
### _abstract_ `on_shutdown(func)`
注册一个在驱动停止时运行的函数
### _abstract_ `run(host=None, port=None, *args, **kwargs)`
* **说明**
启动驱动框架
* **参数**
* `host: Optional[str]`: 驱动绑定 IP
* `post: Optional[int]`: 驱动绑定端口
* `*args`
* `**kwargs`
### _abstract async_ `_handle_http()`
用于处理 HTTP 类型请求的函数
### _abstract async_ `_handle_ws_reverse()`
用于处理 WebSocket 类型请求的函数
## _class_ `BaseWebSocket`
基类:`object`
WebSocket 连接封装,统一接口方便外部调用。
### _abstract_ `__init__(websocket)`
* **参数**
* `websocket: Any`: WebSocket 连接对象
### _property_ `websocket`
WebSocket 连接对象
### _abstract property_ `closed`
* **类型**
`bool`
* **说明**
连接是否已经关闭
### _abstract async_ `accept()`
接受 WebSocket 连接请求
### _abstract async_ `close(code)`
关闭 WebSocket 连接请求
### _abstract async_ `receive()`
接收一条 WebSocket 信息
### _abstract async_ `send(data)`
发送一条 WebSocket 信息

125
docs/api/drivers/fastapi.md Normal file
View File

@@ -0,0 +1,125 @@
---
contentSidebar: true
sidebarDepth: 0
---
# NoneBot.drivers.fastapi 模块
## FastAPI 驱动适配
后端使用方法请参考: [FastAPI 文档](https://fastapi.tiangolo.com/)
## _class_ `Driver`
基类:[`nonebot.drivers.BaseDriver`](#None)
FastAPI 驱动框架
### `__init__(env, config)`
* **参数**
* `env: Env`: 包含环境信息的 Env 对象
* `config: Config`: 包含配置信息的 Config 对象
### _property_ `type`
驱动名称: `fastapi`
### _property_ `server_app`
`FastAPI APP` 对象
### _property_ `asgi`
`FastAPI APP` 对象
### _property_ `logger`
fastapi 使用的 logger
### `on_startup(func)`
参考文档: [Events](https://fastapi.tiangolo.com/advanced/events/#startup-event)
### `on_shutdown(func)`
参考文档: [Events](https://fastapi.tiangolo.com/advanced/events/#startup-event)
### `run(host=None, port=None, *, app=None, **kwargs)`
使用 `uvicorn` 启动 FastAPI
### _async_ `_handle_http(adapter, data=Body(Ellipsis), x_self_id=Header(None), x_signature=Header(None), auth=Depends(get_auth_bearer))`
用于处理 HTTP 类型请求的函数
### _async_ `_handle_ws_reverse(adapter, websocket, x_self_id=Header(None), auth=Depends(get_auth_bearer))`
用于处理 WebSocket 类型请求的函数
## _class_ `WebSocket`
基类:[`nonebot.drivers.BaseWebSocket`](#None)
### `__init__(websocket)`
* **参数**
* `websocket: Any`: WebSocket 连接对象
### _property_ `closed`
* **类型**
`bool`
* **说明**
连接是否已经关闭
### _async_ `accept()`
接受 WebSocket 连接请求
### _async_ `close(code=1000)`
关闭 WebSocket 连接请求
### _async_ `receive()`
接收一条 WebSocket 信息
### _async_ `send(data)`
发送一条 WebSocket 信息

497
docs/api/matcher.md Normal file
View File

@@ -0,0 +1,497 @@
---
contentSidebar: true
sidebarDepth: 0
---
# NoneBot.matcher 模块
## 事件响应器
该模块实现事件响应器的创建与运行,并提供一些快捷方法来帮助用户更好的与机器人进行 对话 。
## `matchers`
* **类型**
`Dict[int, List[Type[Matcher]]]`
* **说明**
用于存储当前所有的事件响应器
## _class_ `Matcher`
基类:`object`
事件响应器类
### `module`
* **类型**
`Optional[str]`
* **说明**
事件响应器所在模块名称
### `type`
* **类型**
`str`
* **说明**
事件响应器类型
### `rule`
* **类型**
`Rule`
* **说明**
事件响应器匹配规则
### `permission`
* **类型**
`Permission`
* **说明**
事件响应器触发权限
### `priority`
* **类型**
`int`
* **说明**
事件响应器优先级
### `block`
* **类型**
`bool`
* **说明**
事件响应器是否阻止事件传播
### `temp`
* **类型**
`bool`
* **说明**
事件响应器是否为临时
### `expire_time`
* **类型**
`Optional[datetime]`
* **说明**
事件响应器过期时间点
### `_default_state`
* **类型**
`dict`
* **说明**
事件响应器默认状态
### `_default_parser`
* **类型**
`Optional[ArgsParser]`
* **说明**
事件响应器默认参数解析函数
### `__init__()`
实例化 Matcher 以便运行
### `handlers`
* **类型**
`List[Handler]`
* **说明**
事件响应器拥有的事件处理函数列表
### _classmethod_ `new(type_='', rule=None, permission=None, handlers=None, temp=False, priority=1, block=False, *, module=None, default_state=None, expire_time=None)`
* **说明**
创建一个新的事件响应器,并存储至 [matchers](#matchers)
* **参数**
* `type_: str`: 事件响应器类型,与 `event.type` 一致时触发,空字符串表示任意
* `rule: Optional[Rule]`: 匹配规则
* `permission: Optional[Permission]`: 权限
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器,即触发一次后删除
* `priority: int`: 响应优先级
* `block: bool`: 是否阻止事件向更低优先级的响应器传播
* `module: Optional[str]`: 事件响应器所在模块名称
* `default_state: Optional[dict]`: 默认状态 `state`
* `expire_time: Optional[datetime]`: 事件响应器最终有效时间点,过时即被删除
* **返回**
* `Type[Matcher]`: 新的事件响应器类
### _async classmethod_ `check_perm(bot, event)`
* **说明**
检查是否满足触发权限
* **参数**
* `bot: Bot`: Bot 对象
* `event: Event`: 上报事件
* **返回**
* `bool`: 是否满足权限
### _async classmethod_ `check_rule(bot, event, state)`
* **说明**
检查是否满足匹配规则
* **参数**
* `bot: Bot`: Bot 对象
* `event: Event`: 上报事件
* `state: dict`: 当前状态
* **返回**
* `bool`: 是否满足匹配规则
### _classmethod_ `args_parser(func)`
* **说明**
装饰一个函数来更改当前事件响应器的默认参数解析函数
* **参数**
* `func: ArgsParser`: 参数解析函数
### _classmethod_ `handle()`
* **说明**
装饰一个函数来向事件响应器直接添加一个处理函数
* **参数**
*
### _classmethod_ `receive()`
* **说明**
装饰一个函数来指示 NoneBot 在接收用户新的一条消息后继续运行该函数
* **参数**
*
### _classmethod_ `got(key, prompt=None, args_parser=None)`
* **说明**
装饰一个函数来指示 NoneBot 当要获取的 `key` 不存在时接收用户新的一条消息并经过 `ArgsParser` 处理后再运行该函数,如果 `key` 已存在则直接继续运行
* **参数**
* `key: str`: 参数名
* `prompt: Optional[Union[str, Message, MessageSegment]]`: 在参数不存在时向用户发送的消息
* `args_parser: Optional[ArgsParser]`: 可选参数解析函数,空则使用默认解析函数
### _async classmethod_ `send(message, **kwargs)`
* **说明**
发送一条消息给当前交互用户
* **参数**
* `message: Union[str, Message, MessageSegment]`: 消息内容
* `**kwargs`: 其他传递给 `bot.send` 的参数,请参考对应 adapter 的 bot 对象 api
### _async classmethod_ `finish(message=None, **kwargs)`
* **说明**
发送一条消息给当前交互用户并结束当前事件响应器
* **参数**
* `message: Union[str, Message, MessageSegment]`: 消息内容
* `**kwargs`: 其他传递给 `bot.send` 的参数,请参考对应 adapter 的 bot 对象 api
### _async classmethod_ `pause(prompt=None, **kwargs)`
* **说明**
发送一条消息给当前交互用户并暂停事件响应器,在接收用户新的一条消息后继续下一个处理函数
* **参数**
* `prompt: Union[str, Message, MessageSegment]`: 消息内容
* `**kwargs`: 其他传递给 `bot.send` 的参数,请参考对应 adapter 的 bot 对象 api
### _async classmethod_ `reject(prompt=None, **kwargs)`
* **说明**
发送一条消息给当前交互用户并暂停事件响应器,在接收用户新的一条消息后重新运行当前处理函数
* **参数**
* `prompt: Union[str, Message, MessageSegment]`: 消息内容
* `**kwargs`: 其他传递给 `bot.send` 的参数,请参考对应 adapter 的 bot 对象 api
## _class_ `MatcherGroup`
基类:`object`
事件响应器组合,统一管理。用法同 `Matcher`
### `__init__(type_='', rule=None, permission=None, handlers=None, temp=False, priority=1, block=False, *, module=None, default_state=None, expire_time=None)`
* **说明**
创建一个事件响应器组合,参数为默认值,与 `Matcher.new` 一致
### `matchers`
* **类型**
`List[Type[Matcher]]`
* **说明**
组内事件响应器列表
### `new(type_='', rule=None, permission=None, handlers=None, temp=False, priority=1, block=False, *, module=None, default_state=None, expire_time=None)`
* **说明**
在组中创建一个新的事件响应器,参数留空则使用组合默认值
:::danger 警告
如果使用 handlers 参数覆盖组合默认值则该事件响应器不会随组合一起添加新的事件处理函数
:::

View File

@@ -5,6 +5,55 @@ sidebarDepth: 0
# NoneBot 模块
## 快捷导入
为方便使用,`nonebot` 模块从子模块导入了部分内容
* `on_message` => `nonebot.plugin.on_message`
* `on_notice` => `nonebot.plugin.on_notice`
* `on_request` => `nonebot.plugin.on_request`
* `on_metaevent` => `nonebot.plugin.on_metaevent`
* `on_startswith` => `nonebot.plugin.on_startswith`
* `on_endswith` => `nonebot.plugin.on_endswith`
* `on_command` => `nonebot.plugin.on_command`
* `on_regex` => `nonebot.plugin.on_regex`
* `on_regex` => `nonebot.plugin.on_regex`
* `on_regex` => `nonebot.plugin.on_regex`
* `CommandGroup` => `nonebot.plugin.CommandGroup`
* `load_plugin` => `nonebot.plugin.load_plugin`
* `load_plugins` => `nonebot.plugin.load_plugins`
* `load_builtin_plugins` => `nonebot.plugin.load_builtin_plugins`
* `get_loaded_plugins` => `nonebot.plugin.get_loaded_plugins`
## `get_driver()`

629
docs/api/plugin.md Normal file
View File

@@ -0,0 +1,629 @@
---
contentSidebar: true
sidebarDepth: 0
---
# NoneBot.plugin 模块
## 插件
为 NoneBot 插件开发提供便携的定义函数。
## `plugins`
* **类型**
`Dict[str, Plugin]`
* **说明**
已加载的插件
## _class_ `Plugin`
基类:`object`
存储插件信息
### `name`
* **类型**: `str`
* **说明**: 插件名称,使用 文件/文件夹 名称作为插件名
### `module`
* **类型**: `ModuleType`
* **说明**: 插件模块对象
### `matcher`
* **类型**: `Set[Type[Matcher]]`
* **说明**: 插件内定义的 `Matcher`
## `on(type='', rule=None, permission=None, *, handlers=None, temp=False, priority=1, block=False, state=None)`
* **说明**
注册一个基础事件响应器,可自定义类型。
* **参数**
* `type: str`: 事件响应器类型
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `permission: Optional[Permission]`: 事件响应权限
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_metaevent(rule=None, *, handlers=None, temp=False, priority=1, block=False, state=None)`
* **说明**
注册一个元事件响应器。
* **参数**
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_message(rule=None, permission=None, *, handlers=None, temp=False, priority=1, block=True, state=None)`
* **说明**
注册一个消息事件响应器。
* **参数**
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `permission: Optional[Permission]`: 事件响应权限
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_notice(rule=None, *, handlers=None, temp=False, priority=1, block=False, state=None)`
* **说明**
注册一个通知事件响应器。
* **参数**
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_request(rule=None, *, handlers=None, temp=False, priority=1, block=False, state=None)`
* **说明**
注册一个请求事件响应器。
* **参数**
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_startswith(msg, rule=None, **kwargs)`
* **说明**
注册一个消息事件响应器,并且当消息的\*\*文本部分\*\*以指定内容开头时响应。
* **参数**
* `msg: str`: 指定消息开头内容
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `permission: Optional[Permission]`: 事件响应权限
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_endswith(msg, rule=None, **kwargs)`
* **说明**
注册一个消息事件响应器,并且当消息的\*\*文本部分\*\*以指定内容结尾时响应。
* **参数**
* `msg: str`: 指定消息结尾内容
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `permission: Optional[Permission]`: 事件响应权限
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_keyword(keywords, rule=None, **kwargs)`
* **说明**
注册一个消息事件响应器,并且当消息纯文本部分包含关键词时响应。
* **参数**
* `keywords: Set[str]`: 关键词列表
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `permission: Optional[Permission]`: 事件响应权限
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_command(cmd, rule=None, aliases=None, **kwargs)`
* **说明**
注册一个消息事件响应器,并且当消息以指定命令开头时响应。
命令匹配规则参考: [命令形式匹配](rule.html#command-command)
* **参数**
* `cmd: Union[str, Tuple[str, ...]]`: 指定命令内容
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `aliases: Optional[Set[Union[str, Tuple[str, ...]]]]`: 命令别名
* `permission: Optional[Permission]`: 事件响应权限
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## `on_regex(pattern, flags=0, rule=None, **kwargs)`
* **说明**
注册一个消息事件响应器,并且当消息匹配正则表达式时响应。
命令匹配规则参考: [正则匹配](rule.html#regex-regex-flags-0)
* **参数**
* `pattern: str`: 正则表达式
* `flags: Union[int, re.RegexFlag]`: 正则匹配标志
* `rule: Optional[Union[Rule, RuleChecker]]`: 事件响应规则
* `permission: Optional[Permission]`: 事件响应权限
* `handlers: Optional[List[Handler]]`: 事件处理函数列表
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
* `priority: int`: 事件响应器优先级
* `block: bool`: 是否阻止事件向更低优先级传递
* `state: Optional[dict]`: 默认的 state
* **返回**
* `Type[Matcher]`
## _class_ `CommandGroup`
基类:`object`
命令组,用于声明一组有相同名称前缀的命令。
### `__init__(cmd, **kwargs)`
* **参数**
* `cmd: Union[str, Tuple[str, ...]]`: 命令前缀
* `**kwargs`: 其他传递给 `on_command` 的参数默认值,参考 [on_command](#on-command-cmd-rule-none-aliases-none-kwargs)
### `basecmd`
* **类型**: `Tuple[str, ...]`
* **说明**: 命令前缀
### `base_kwargs`
* **类型**: `Dict[str, Any]`
* **说明**: 其他传递给 `on_command` 的参数默认值
### `command(cmd, **kwargs)`
* **说明**
注册一个新的命令。
* **参数**
* `cmd: Union[str, Tuple[str, ...]]`: 命令前缀
* `**kwargs`: 其他传递给 `on_command` 的参数,将会覆盖命令组默认值
* **返回**
* `Type[Matcher]`
## `load_plugin(module_path)`
* **说明**
使用 `importlib` 加载单个插件,可以是本地插件或是通过 `pip` 安装的插件。
* **参数**
* `module_path: str`: 插件名称 `path.to.your.plugin`
* **返回**
* `Optional[Plugin]`
## `load_plugins(*plugin_dir)`
* **说明**
导入目录下多个插件,以 `_` 开头的插件不会被导入!
* **参数**
* `*plugin_dir: str`: 插件路径
* **返回**
* `Set[Plugin]`
## `load_builtin_plugins()`
* **说明**
导入 NoneBot 内置插件
* **返回**
* `Plugin`
## `get_loaded_plugins()`
* **说明**
获取当前已导入的插件。
* **返回**
* `Set[Plugin]`

View File

@@ -7,10 +7,10 @@ sidebarDepth: 0
## 规则
每个 `Matcher` 拥有一个 `Rule` ,其中是 **异步** `RuleChecker` 的集合,只有当所有 `RuleChecker` 检查结果为 `True` 时继续运行。
每个事件响应器 `Matcher` 拥有一个匹配规则 `Rule` ,其中是 **异步** `RuleChecker` 的集合,只有当所有 `RuleChecker` 检查结果为 `True` 时继续运行。
:::tip 提示
`RuleChecker` 既可以是 async function 也可以是 sync function
`RuleChecker` 既可以是 async function 也可以是 sync function,但在最终会被 `nonebot.utils.run_sync` 转换为 async function
:::
@@ -120,3 +120,91 @@ Rule(async_function, run_sync(sync_function))
* `msg: str`: 消息结尾字符串
## `keyword(*keywords)`
* **说明**
匹配消息关键词
* **参数**
* `*keywords: str`: 关键词
## `command(*cmds)`
* **说明**
命令形式匹配,根据配置里提供的 `command_start`, `command_sep` 判断消息是否为命令。
可以通过 `state["_prefix"]["command"]` 获取匹配成功的命令(例:`("test",)`),通过 `state["_prefix"]["raw_command"]` 获取匹配成功的原始命令文本(例:`"/test"`)。
* **参数**
* `*cmds: Union[str, Tuple[str, ...]]`: 命令内容
* **示例**
使用默认 `command_start`, `command_sep` 配置
命令 `("test",)` 可以匹配:`/test` 开头的消息
命令 `("test", "sub")` 可以匹配”`/test.sub` 开头的消息
:::tip 提示
命令内容与后续消息间无需空格!
:::
## `regex(regex, flags=0)`
* **说明**
根据正则表达式进行匹配。
可以通过 `state["_matched"]` 获取正则表达式匹配成功的文本。
* **参数**
* `regex: str`: 正则表达式
* `flags: Union[int, re.RegexFlag]`: 正则标志
:::tip 提示
正则表达式匹配使用 search 而非 match如需从头匹配请使用 `r"^xxx"` 来确保匹配开头
:::
## `to_me()`
* **说明**
通过 `event.to_me` 判断消息是否是发送给机器人
* **参数**
*

View File

@@ -20,11 +20,9 @@ NoneBot2 是一个可扩展的 Python 异步机器人框架,它会对机器人
## 它如何工作?
NoneBot 的运行离不开 酷 Q 和 CQHTTP 插件。酷 Q 扮演着「无头 QQ 客户端」的角色,它进行实际的消息、通知、请求的接收和发送,当 酷 Q 收到消息时,它将这个消息包装为一个事件(通知和请求同理),并通过它自己的插件机制将事件传送给 CQHTTP 插件,后者再根据其配置中的 `post_url``ws_reverse_url` 等项来将事件发送至 NoneBot。
<!-- TODO: how to work -->
在 NoneBot 收到事件前,它底层的 aiocqhttp 实际已经先看到了事件aiocqhttp 根据事件的类型信息,通知到 NoneBot 的相应函数。特别地,对于消息类型的事件,还将消息内容转换成了 `aiocqhttp.message.Message` 类型,以便处理。
NoneBot 的事件处理函数收到通知后对于不同类型的事件再做相应的预处理和解析然后调用对应的插件并向其提供适合此类事件的会话Session对象。NoneBot 插件的编写者要做的,就是利用 Session 对象中提供的数据,在插件的处理函数中实现所需的功能。
~~未填坑~~
## 特色

View File

@@ -136,11 +136,11 @@ QQ 协议端举例:
现在,尝试向你的 QQ 机器人账号发送如下内容:
```default
/say 你好,世界
/echo 你好,世界
```
到这里如果一切 OK你应该会收到机器人给你回复了 `你好,世界`。这一历史性的对话标志着你已经成功地运行了一个 NoneBot 的最小实例,开始了编写更强大的 QQ 机器人的创意之旅!
<ClientOnly>
<Messenger :messages="[{ position: 'right', msg: '/say 你好,世界' }, { position: 'left', msg: '你好,世界' }]"/>
<Messenger :messages="[{ position: 'right', msg: '/echo 你好,世界' }, { position: 'left', msg: '你好,世界' }]"/>
</ClientOnly>

View File

@@ -6,7 +6,10 @@
请确保你的 Python 版本 >= 3.7。
:::
请在安装 nonebot2 之前卸载 nonebot 1.x
```bash
pip uninstall nonebot
pip install nonebot2
```

View File

@@ -3,12 +3,17 @@ NoneBot Api Reference
:模块索引:
- `nonebot <nonebot.html>`_
- `nonebot.typing <typing.html>`_
- `nonebot.config <config.html>`_
- `nonebot.sched <sched.html>`_
- `nonebot.log <log.html>`_
- `nonebot.plugin <plugin.html>`_
- `nonebot.matcher <matcher.html>`_
- `nonebot.rule <rule.html>`_
- `nonebot.permission <permission.html>`_
- `nonebot.sched <sched.html>`_
- `nonebot.log <log.html>`_
- `nonebot.utils <utils.html>`_
- `nonebot.typing <typing.html>`_
- `nonebot.exception <exception.html>`_
- `nonebot.drivers <drivers/>`_
- `nonebot.drivers.fastapi <drivers/fastapi.html>`_
- `nonebot.adapters <adapters/>`_
- `nonebot.adapters.cqhttp <adapters/cqhttp.html>`_

View File

@@ -4,9 +4,10 @@ sidebarDepth: 0
---
NoneBot.adapters 模块
=================
=====================
.. automodule:: nonebot.adapters
:members:
:private-members:
:special-members: __init__
:show-inheritance:

View File

@@ -4,7 +4,7 @@ sidebarDepth: 0
---
NoneBot.adapters.cqhttp 模块
=================
============================
.. automodule:: nonebot.adapters.cqhttp
:members:

View File

@@ -0,0 +1,13 @@
---
contentSidebar: true
sidebarDepth: 0
---
NoneBot.drivers 模块
=====================
.. automodule:: nonebot.drivers
:members:
:private-members:
:special-members: __init__
:show-inheritance:

View File

@@ -0,0 +1,13 @@
---
contentSidebar: true
sidebarDepth: 0
---
NoneBot.drivers.fastapi 模块
=====================
.. automodule:: nonebot.drivers.fastapi
:members:
:private-members:
:special-members: __init__
:show-inheritance:

13
docs_build/matcher.rst Normal file
View File

@@ -0,0 +1,13 @@
---
contentSidebar: true
sidebarDepth: 0
---
NoneBot.matcher 模块
====================
.. automodule:: nonebot.matcher
:members:
:private-members:
:special-members: __init__
:show-inheritance:

View File

@@ -4,7 +4,7 @@ sidebarDepth: 0
---
NoneBot.permission 模块
====================
=======================
.. automodule:: nonebot.permission
:members:

12
docs_build/plugin.rst Normal file
View File

@@ -0,0 +1,12 @@
---
contentSidebar: true
sidebarDepth: 0
---
NoneBot.plugin 模块
====================
.. automodule:: nonebot.plugin
:members:
:show-inheritance:
:special-members: __init__

View File

@@ -1,5 +1,25 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
快捷导入
========
为方便使用,``nonebot`` 模块从子模块导入了部分内容
- ``on_message`` => ``nonebot.plugin.on_message``
- ``on_notice`` => ``nonebot.plugin.on_notice``
- ``on_request`` => ``nonebot.plugin.on_request``
- ``on_metaevent`` => ``nonebot.plugin.on_metaevent``
- ``on_startswith`` => ``nonebot.plugin.on_startswith``
- ``on_endswith`` => ``nonebot.plugin.on_endswith``
- ``on_command`` => ``nonebot.plugin.on_command``
- ``on_regex`` => ``nonebot.plugin.on_regex``
- ``on_regex`` => ``nonebot.plugin.on_regex``
- ``on_regex`` => ``nonebot.plugin.on_regex``
- ``CommandGroup`` => ``nonebot.plugin.CommandGroup``
- ``load_plugin`` => ``nonebot.plugin.load_plugin``
- ``load_plugins`` => ``nonebot.plugin.load_plugins``
- ``load_builtin_plugins`` => ``nonebot.plugin.load_builtin_plugins``
- ``get_loaded_plugins`` => ``nonebot.plugin.get_loaded_plugins``
"""
import importlib
from nonebot.typing import Bot, Dict, Type, Union, Driver, Optional, NoReturn

View File

@@ -1,5 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
协议适配基类
============
各协议请继承以下基类,并使用 ``driver.register_adapter`` 注册适配器
"""
import abc
from functools import reduce, partial
@@ -11,6 +15,9 @@ from nonebot.typing import Any, Dict, Union, Optional, Callable, Iterable, Await
class BaseBot(abc.ABC):
"""
Bot 基类。用于处理上报消息,并提供 API 调用接口。
"""
@abc.abstractmethod
def __init__(self,
@@ -19,12 +26,25 @@ class BaseBot(abc.ABC):
config: Config,
self_id: str,
*,
websocket: WebSocket = None):
websocket: Optional[WebSocket] = None):
"""
:参数:
* ``driver: Driver``: Driver 对象
* ``connection_type: str``: http 或者 websocket
* ``config: Config``: Config 对象
* ``self_id: str``: 机器人 ID
* ``websocket: Optional[WebSocket]``: Websocket 连接对象
"""
self.driver = driver
"""Driver 对象"""
self.connection_type = connection_type
"""连接类型"""
self.config = config
"""Config 配置对象"""
self.self_id = self_id
"""机器人 ID"""
self.websocket = websocket
"""Websocket 连接对象"""
def __getattr__(self, name: str) -> Callable[..., Awaitable[Any]]:
return partial(self.call_api, name)
@@ -32,60 +52,99 @@ class BaseBot(abc.ABC):
@property
@abc.abstractmethod
def type(self) -> str:
"""Adapter 类型"""
raise NotImplementedError
@abc.abstractmethod
async def handle_message(self, message: dict):
"""
:说明:
处理上报消息的函数,转换为 ``Event`` 事件后调用 ``nonebot.message.handle_event`` 进一步处理事件。
:参数:
* ``message: dict``: 收到的上报消息
"""
raise NotImplementedError
@abc.abstractmethod
async def call_api(self, api: str, data: dict):
async def call_api(self, api: str, **data):
"""
:说明:
调用机器人 API 接口,可以通过该函数或直接通过 bot 属性进行调用
:参数:
* ``api: str``: API 名称
* ``**data``: API 数据
:示例:
.. code-block:: python
await bot.call_api("send_msg", data={"message": "hello world"})
await bot.send_msg(message="hello world")
"""
raise NotImplementedError
@abc.abstractmethod
async def send(self, *args, **kwargs):
async def send(self, event: "BaseEvent",
message: Union[str, "BaseMessage",
"BaseMessageSegment"], **kwargs):
"""
:说明:
调用机器人基础发送消息接口
:参数:
* ``event: Event``: 上报事件
* ``message: Union[str, Message, MessageSegment]``: 要发送的消息
* ``**kwargs``
"""
raise NotImplementedError
# TODO: improve event
class BaseEvent(abc.ABC):
"""
Event 基类。提供上报信息的关键信息,其余信息可从原始上报消息获取。
"""
def __init__(self, raw_event: dict):
"""
:参数:
* ``raw_event: dict``: 原始上报消息
"""
self._raw_event = raw_event
"""
原始 event
"""
def __repr__(self) -> str:
return f"<Event {self.self_id}: {self.name} {self.time}>"
@property
def raw_event(self) -> dict:
"""原始上报消息"""
return self._raw_event
@property
@abc.abstractmethod
def id(self) -> int:
"""事件 ID"""
raise NotImplementedError
@property
@abc.abstractmethod
def name(self) -> str:
"""事件名称"""
raise NotImplementedError
@property
@abc.abstractmethod
def self_id(self) -> str:
"""机器人 ID"""
raise NotImplementedError
@property
@abc.abstractmethod
def time(self) -> int:
"""事件发生时间"""
raise NotImplementedError
@property
@abc.abstractmethod
def type(self) -> str:
"""事件主类型"""
raise NotImplementedError
@type.setter
@@ -96,6 +155,7 @@ class BaseEvent(abc.ABC):
@property
@abc.abstractmethod
def detail_type(self) -> str:
"""事件详细类型"""
raise NotImplementedError
@detail_type.setter
@@ -106,6 +166,7 @@ class BaseEvent(abc.ABC):
@property
@abc.abstractmethod
def sub_type(self) -> Optional[str]:
"""事件子类型"""
raise NotImplementedError
@sub_type.setter
@@ -116,6 +177,7 @@ class BaseEvent(abc.ABC):
@property
@abc.abstractmethod
def user_id(self) -> Optional[int]:
"""触发事件的主体 ID"""
raise NotImplementedError
@user_id.setter
@@ -126,6 +188,7 @@ class BaseEvent(abc.ABC):
@property
@abc.abstractmethod
def group_id(self) -> Optional[int]:
"""触发事件的主体群 ID"""
raise NotImplementedError
@group_id.setter
@@ -136,6 +199,7 @@ class BaseEvent(abc.ABC):
@property
@abc.abstractmethod
def to_me(self) -> Optional[bool]:
"""事件是否为发送给机器人的消息"""
raise NotImplementedError
@to_me.setter
@@ -146,6 +210,7 @@ class BaseEvent(abc.ABC):
@property
@abc.abstractmethod
def message(self) -> Optional[Message]:
"""消息内容"""
raise NotImplementedError
@message.setter
@@ -156,6 +221,7 @@ class BaseEvent(abc.ABC):
@property
@abc.abstractmethod
def reply(self) -> Optional[dict]:
"""回复的消息"""
raise NotImplementedError
@reply.setter
@@ -166,6 +232,7 @@ class BaseEvent(abc.ABC):
@property
@abc.abstractmethod
def raw_message(self) -> Optional[str]:
"""原始消息"""
raise NotImplementedError
@raw_message.setter
@@ -176,11 +243,13 @@ class BaseEvent(abc.ABC):
@property
@abc.abstractmethod
def plain_text(self) -> Optional[str]:
"""纯文本消息"""
raise NotImplementedError
@property
@abc.abstractmethod
def sender(self) -> Optional[dict]:
"""消息发送者信息"""
raise NotImplementedError
@sender.setter
@@ -191,8 +260,17 @@ class BaseEvent(abc.ABC):
@dataclass
class BaseMessageSegment(abc.ABC):
"""消息段基类"""
type: str
data: Dict[str, Union[str, list]] = field(default_factory=lambda: {})
"""
- 类型: ``str``
- 说明: 消息段类型
"""
data: Dict[str, Any] = field(default_factory=lambda: {})
"""
- 类型: ``Dict[str, Union[str, list]]``
- 说明: 消息段数据
"""
@abc.abstractmethod
def __str__(self):
@@ -202,14 +280,33 @@ class BaseMessageSegment(abc.ABC):
def __add__(self, other):
raise NotImplementedError
def __getitem__(self, key):
return getattr(self, key)
def __setitem__(self, key, value):
return setattr(self, key, value)
def get(self, key, default=None):
return getattr(self, key, default)
@classmethod
@abc.abstractmethod
def text(cls, text: str) -> "BaseMessageSegment":
return cls("text", {"text": text})
class BaseMessage(list, abc.ABC):
"""消息数组"""
def __init__(self,
message: Union[str, dict, list, BaseMessageSegment,
"BaseMessage"] = None,
*args,
**kwargs):
"""
:参数:
* ``message: Union[str, dict, list, MessageSegment, Message]``: 消息内容
"""
super().__init__(*args, **kwargs)
if isinstance(message, (str, dict, list)):
self.extend(self._construct(message))
@@ -243,6 +340,12 @@ class BaseMessage(list, abc.ABC):
return result.__add__(self)
def append(self, obj: Union[str, BaseMessageSegment]) -> "BaseMessage":
"""
:说明:
添加一个消息段到消息数组末尾
:参数:
* ``obj: Union[str, MessageSegment]``: 要添加的消息段
"""
if isinstance(obj, BaseMessageSegment):
if obj.type == "text" and self and self[-1].type == "text":
self[-1].data["text"] += obj.data["text"]
@@ -257,11 +360,21 @@ class BaseMessage(list, abc.ABC):
def extend(
self, obj: Union["BaseMessage",
Iterable[BaseMessageSegment]]) -> "BaseMessage":
"""
:说明:
拼接一个消息数组或多个消息段到消息数组末尾
:参数:
* ``obj: Union[Message, Iterable[MessageSegment]]``: 要添加的消息数组
"""
for segment in obj:
self.append(segment)
return self
def reduce(self) -> None:
"""
:说明:
缩减消息数组,即拼接相邻纯文本消息段
"""
index = 0
while index < len(self):
if index > 0 and self[
@@ -272,8 +385,13 @@ class BaseMessage(list, abc.ABC):
index += 1
def extract_plain_text(self) -> str:
"""
:说明:
提取消息内纯文本消息
"""
def _concat(x: str, y: BaseMessageSegment) -> str:
return f"{x} {y.data['text']}" if y.type == "text" else x
return reduce(_concat, self, "")
plain_text = reduce(_concat, self, "")
return plain_text[1:] if plain_text else plain_text

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
CQHTTP (OneBot) v11 协议适配
============================
@@ -182,13 +180,13 @@ def _check_nickname(bot: "Bot", event: "Event"):
first_text = first_msg_seg.data["text"]
if bot.config.NICKNAME:
if bot.config.nickname:
# check if the user is calling me with my nickname
if isinstance(bot.config.NICKNAME, str) or \
not isinstance(bot.config.NICKNAME, Iterable):
nicknames = (bot.config.NICKNAME,)
if isinstance(bot.config.nickname, str) or \
not isinstance(bot.config.nickname, Iterable):
nicknames = (bot.config.nickname,)
else:
nicknames = filter(lambda n: n, bot.config.NICKNAME)
nicknames = filter(lambda n: n, bot.config.nickname)
nickname_regex = "|".join(nicknames)
m = re.search(rf"^({nickname_regex})([\s,]*|$)", first_text,
re.IGNORECASE)
@@ -265,7 +263,7 @@ class Bot(BaseBot):
config: Config,
self_id: str,
*,
websocket: WebSocket = None):
websocket: Optional[WebSocket] = None):
if connection_type not in ["http", "websocket"]:
raise ValueError("Unsupported connection type")
@@ -521,7 +519,7 @@ class Event(BaseEvent):
"""
return self._raw_event.get("sub_type")
@type.setter
@sub_type.setter
@overrides(BaseEvent)
def sub_type(self, value) -> None:
self._raw_event["sub_type"] = value
@@ -635,11 +633,14 @@ class Event(BaseEvent):
class MessageSegment(BaseMessageSegment):
"""
CQHTTP 协议 MessageSegment 适配。具体方法参考协议消息段类型或源码。
"""
@overrides(BaseMessageSegment)
def __init__(self, type: str, data: Dict[str, Union[str, list]]) -> None:
def __init__(self, type: str, data: Dict[str, Any]) -> None:
if type == "text":
data["text"] = unescape(data["text"]) # type: ignore
data["text"] = unescape(data["text"])
super().__init__(type=type, data=data)
@overrides(BaseMessageSegment)
@@ -813,6 +814,9 @@ class MessageSegment(BaseMessageSegment):
class Message(BaseMessage):
"""
CQHTTP 协议 Message 适配。
"""
@staticmethod
@overrides(BaseMessage)

View File

@@ -1,7 +1,7 @@
import asyncio
from nonebot.config import Config
from nonebot.adapters import BaseBot
from nonebot.adapters import BaseBot, BaseEvent, BaseMessage, BaseMessageSegment
from nonebot.typing import Any, Dict, List, Union, Driver, Optional, NoReturn, WebSocket, Iterable
@@ -750,7 +750,7 @@ class Bot(BaseBot):
...
class Event:
class Event(BaseEvent):
def __init__(self, raw_event: dict):
...
@@ -791,7 +791,7 @@ class Event:
def sub_type(self) -> Optional[str]:
...
@type.setter
@sub_type.setter
def sub_type(self, value) -> None:
...
@@ -856,9 +856,9 @@ class Event:
...
class MessageSegment:
class MessageSegment(BaseMessageSegment):
def __init__(self, type: str, data: Dict[str, Union[str, list]]) -> None:
def __init__(self, type: str, data: Dict[str, Any]) -> None:
...
def __str__(self):
@@ -982,7 +982,7 @@ class MessageSegment:
...
class Message:
class Message(BaseMessage):
@staticmethod
def _construct(msg: Union[str, dict, list]) -> Iterable[MessageSegment]:

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
配置
====
@@ -211,10 +209,10 @@ class Config(BaseConfig):
SUPER_USERS=[12345789]
"""
nickname: Union[str, Set[str]] = ""
nickname: Set[str] = set()
"""
- 类型: ``Union[str, Set[str]]``
- 默认值: ``""``
- 类型: ``Set[str]``
- 默认值: ``set()``
- 说明:
机器人昵称。
"""

View File

@@ -1,5 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
后端驱动适配基类
===============
各驱动请继承以下基类
"""
import abc
@@ -9,16 +13,48 @@ from nonebot.typing import Bot, Dict, Type, Union, Optional, Callable
class BaseDriver(abc.ABC):
"""
Driver 基类。将后端框架封装,以满足适配器使用。
"""
_adapters: Dict[str, Type[Bot]] = {}
"""
:类型: ``Dict[str, Type[Bot]]``
:说明: 已注册的适配器列表
"""
@abc.abstractmethod
def __init__(self, env: Env, config: Config):
"""
:参数:
* ``env: Env``: 包含环境信息的 Env 对象
* ``config: Config``: 包含配置信息的 Config 对象
"""
self.env = env.environment
"""
:类型: ``str``
:说明: 环境名称
"""
self.config = config
"""
:类型: ``Config``
:说明: 配置对象
"""
self._clients: Dict[str, Bot] = {}
"""
:类型: ``Dict[str, Bot]``
:说明: 已连接的 Bot
"""
@classmethod
def register_adapter(cls, name: str, adapter: Type[Bot]):
"""
:说明:
注册一个协议适配器
:参数:
* ``name: str``: 适配器名称,用于在连接时进行识别
* ``adapter: Type[Bot]``: 适配器 Class
"""
cls._adapters[name] = adapter
logger.opt(
colors=True).debug(f'Succeeded to load adapter "<y>{name}</y>"')
@@ -26,33 +62,43 @@ class BaseDriver(abc.ABC):
@property
@abc.abstractmethod
def type(self):
"""驱动类型名称"""
raise NotImplementedError
@property
@abc.abstractmethod
def server_app(self):
"""驱动 APP 对象"""
raise NotImplementedError
@property
@abc.abstractmethod
def asgi(self):
"""驱动 ASGI 对象"""
raise NotImplementedError
@property
@abc.abstractmethod
def logger(self):
"""驱动专属 logger 日志记录器"""
raise NotImplementedError
@property
def bots(self) -> Dict[str, Bot]:
"""
:类型: ``Dict[str, Bot]``
:说明: 获取当前所有已连接的 Bot
"""
return self._clients
@abc.abstractmethod
def on_startup(self, func: Callable) -> Callable:
"""注册一个在驱动启动时运行的函数"""
raise NotImplementedError
@abc.abstractmethod
def on_shutdown(self, func: Callable) -> Callable:
"""注册一个在驱动停止时运行的函数"""
raise NotImplementedError
@abc.abstractmethod
@@ -61,44 +107,69 @@ class BaseDriver(abc.ABC):
port: Optional[int] = None,
*args,
**kwargs):
"""
:说明:
启动驱动框架
:参数:
* ``host: Optional[str]``: 驱动绑定 IP
* ``post: Optional[int]``: 驱动绑定端口
* ``*args``
* ``**kwargs``
"""
raise NotImplementedError
@abc.abstractmethod
async def _handle_http(self):
"""用于处理 HTTP 类型请求的函数"""
raise NotImplementedError
@abc.abstractmethod
async def _handle_ws_reverse(self):
"""用于处理 WebSocket 类型请求的函数"""
raise NotImplementedError
class BaseWebSocket(object):
"""WebSocket 连接封装,统一接口方便外部调用。"""
@abc.abstractmethod
def __init__(self, websocket):
"""
:参数:
* ``websocket: Any``: WebSocket 连接对象
"""
self._websocket = websocket
@property
def websocket(self):
"""WebSocket 连接对象"""
return self._websocket
@property
@abc.abstractmethod
def closed(self):
"""
:类型: ``bool``
:说明: 连接是否已经关闭
"""
raise NotImplementedError
@abc.abstractmethod
async def accept(self):
"""接受 WebSocket 连接请求"""
raise NotImplementedError
@abc.abstractmethod
async def close(self, code: int):
"""关闭 WebSocket 连接请求"""
raise NotImplementedError
@abc.abstractmethod
async def receive(self) -> dict:
"""接收一条 WebSocket 信息"""
raise NotImplementedError
@abc.abstractmethod
async def send(self, data: dict):
"""发送一条 WebSocket 信息"""
raise NotImplementedError

View File

@@ -1,5 +1,12 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
FastAPI 驱动适配
================
后端使用方法请参考: `FastAPI 文档`_
.. _FastAPI 文档:
https://fastapi.tiangolo.com/
"""
import hmac
import json
@@ -23,7 +30,7 @@ def get_auth_bearer(access_token: Optional[str] = Header(
if not access_token:
return None
scheme, _, param = access_token.partition(" ")
if scheme.lower() != "bearer":
if scheme.lower() not in ["bearer", "token"]:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not authenticated",
headers={"WWW-Authenticate": "Bearer"})
@@ -31,6 +38,7 @@ def get_auth_bearer(access_token: Optional[str] = Header(
class Driver(BaseDriver):
"""FastAPI 驱动框架"""
def __init__(self, env: Env, config: Config):
super().__init__(env, config)
@@ -50,29 +58,35 @@ class Driver(BaseDriver):
@property
@overrides(BaseDriver)
def type(self) -> str:
"""驱动名称: ``fastapi``"""
return "fastapi"
@property
@overrides(BaseDriver)
def server_app(self) -> FastAPI:
"""``FastAPI APP`` 对象"""
return self._server_app
@property
@overrides(BaseDriver)
def asgi(self):
"""``FastAPI APP`` 对象"""
return self._server_app
@property
@overrides(BaseDriver)
def logger(self) -> logging.Logger:
"""fastapi 使用的 logger"""
return logging.getLogger("fastapi")
@overrides(BaseDriver)
def on_startup(self, func: Callable) -> Callable:
"""参考文档: `Events <https://fastapi.tiangolo.com/advanced/events/#startup-event>`_"""
return self.server_app.on_event("startup")(func)
@overrides(BaseDriver)
def on_shutdown(self, func: Callable) -> Callable:
"""参考文档: `Events <https://fastapi.tiangolo.com/advanced/events/#startup-event>`_"""
return self.server_app.on_event("shutdown")(func)
@overrides(BaseDriver)
@@ -82,6 +96,7 @@ class Driver(BaseDriver):
*,
app: Optional[str] = None,
**kwargs):
"""使用 ``uvicorn`` 启动 FastAPI"""
LOGGING_CONFIG = {
"version": 1,
"disable_existing_loggers": False,

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
异常
====

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
日志
====

View File

@@ -1,5 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
事件响应器
==========
该模块实现事件响应器的创建与运行,并提供一些快捷方法来帮助用户更好的与机器人进行 对话 。
"""
from nonebot.log import logger
import typing
@@ -16,6 +20,10 @@ from nonebot.typing import Bot, Event, Handler, Message, ArgsParser, MessageSegm
from nonebot.exception import PausedException, RejectedException, FinishedException
matchers: Dict[int, List[Type["Matcher"]]] = defaultdict(list)
"""
:类型: ``Dict[int, List[Type[Matcher]]]``
:说明: 用于存储当前所有的事件响应器
"""
current_bot: ContextVar = ContextVar("current_bot")
current_event: ContextVar = ContextVar("current_event")
@@ -32,22 +40,65 @@ class MatcherMeta(type):
class Matcher(metaclass=MatcherMeta):
"""`Matcher`类
"""
"""事件响应器类"""
module: Optional[str] = None
"""
:类型: ``Optional[str]``
:说明: 事件响应器所在模块名称
"""
type: str = ""
"""
:类型: ``str``
:说明: 事件响应器类型
"""
rule: Rule = Rule()
"""
:类型: ``Rule``
:说明: 事件响应器匹配规则
"""
permission: Permission = Permission()
"""
:类型: ``Permission``
:说明: 事件响应器触发权限
"""
handlers: List[Handler] = []
temp: bool = False
expire_time: Optional[datetime] = None
"""
:类型: ``List[Handler]``
:说明: 事件响应器拥有的事件处理函数列表
"""
priority: int = 1
"""
:类型: ``int``
:说明: 事件响应器优先级
"""
block: bool = False
"""
:类型: ``bool``
:说明: 事件响应器是否阻止事件传播
"""
temp: bool = False
"""
:类型: ``bool``
:说明: 事件响应器是否为临时
"""
expire_time: Optional[datetime] = None
"""
:类型: ``Optional[datetime]``
:说明: 事件响应器过期时间点
"""
_default_state: dict = {}
"""
:类型: ``dict``
:说明: 事件响应器默认状态
"""
_default_parser: Optional[ArgsParser] = None
"""
:类型: ``Optional[ArgsParser]``
:说明: 事件响应器默认参数解析函数
"""
def __init__(self):
"""实例化 Matcher 以便运行
@@ -65,9 +116,9 @@ class Matcher(metaclass=MatcherMeta):
@classmethod
def new(cls,
type_: str = "",
rule: Rule = Rule(),
permission: Permission = Permission(),
handlers: Optional[list] = None,
rule: Optional[Rule] = None,
permission: Optional[Permission] = None,
handlers: Optional[List[Handler]] = None,
temp: bool = False,
priority: int = 1,
block: bool = False,
@@ -75,18 +126,30 @@ class Matcher(metaclass=MatcherMeta):
module: Optional[str] = None,
default_state: Optional[dict] = None,
expire_time: Optional[datetime] = None) -> Type["Matcher"]:
"""创建新的 Matcher
Returns:
Type["Matcher"]: 新的 Matcher 类
"""
:说明:
创建一个新的事件响应器,并存储至 `matchers <#matchers>`_
:参数:
* ``type_: str``: 事件响应器类型,与 ``event.type`` 一致时触发,空字符串表示任意
* ``rule: Optional[Rule]``: 匹配规则
* ``permission: Optional[Permission]``: 权限
* ``handlers: Optional[List[Handler]]``: 事件处理函数列表
* ``temp: bool``: 是否为临时事件响应器,即触发一次后删除
* ``priority: int``: 响应优先级
* ``block: bool``: 是否阻止事件向更低优先级的响应器传播
* ``module: Optional[str]``: 事件响应器所在模块名称
* ``default_state: Optional[dict]``: 默认状态 ``state``
* ``expire_time: Optional[datetime]``: 事件响应器最终有效时间点,过时即被删除
:返回:
- ``Type[Matcher]``: 新的事件响应器类
"""
NewMatcher = type(
"Matcher", (Matcher,), {
"module": module,
"type": type_,
"rule": rule,
"permission": permission,
"rule": rule or Rule(),
"permission": permission or Permission(),
"handlers": handlers or [],
"temp": temp,
"expire_time": expire_time,
@@ -101,29 +164,51 @@ class Matcher(metaclass=MatcherMeta):
@classmethod
async def check_perm(cls, bot: Bot, event: Event) -> bool:
"""
:说明:
检查是否满足触发权限
:参数:
* ``bot: Bot``: Bot 对象
* ``event: Event``: 上报事件
:返回:
- ``bool``: 是否满足权限
"""
return await cls.permission(bot, event)
@classmethod
async def check_rule(cls, bot: Bot, event: Event, state: dict) -> bool:
"""检查 Matcher 的 Rule 是否成立
Args:
event (Event): 消息事件
Returns:
bool: 条件成立与否
"""
:说明:
检查是否满足匹配规则
:参数:
* ``bot: Bot``: Bot 对象
* ``event: Event``: 上报事件
* ``state: dict``: 当前状态
:返回:
- ``bool``: 是否满足匹配规则
"""
return (event.type == (cls.type or event.type) and
await cls.rule(bot, event, state))
@classmethod
def args_parser(cls, func: ArgsParser) -> ArgsParser:
"""
:说明:
装饰一个函数来更改当前事件响应器的默认参数解析函数
:参数:
* ``func: ArgsParser``: 参数解析函数
"""
cls._default_parser = func
return func
@classmethod
def handle(cls) -> Callable[[Handler], Handler]:
"""直接处理消息事件"""
"""
:说明:
装饰一个函数来向事件响应器直接添加一个处理函数
:参数:
* 无
"""
def _decorator(func: Handler) -> Handler:
cls.handlers.append(func)
@@ -133,7 +218,12 @@ class Matcher(metaclass=MatcherMeta):
@classmethod
def receive(cls) -> Callable[[Handler], Handler]:
"""接收一条新消息并处理"""
"""
:说明:
装饰一个函数来指示 NoneBot 在接收用户新的一条消息后继续运行该函数
:参数:
* 无
"""
async def _receive(bot: Bot, event: Event, state: dict) -> NoReturn:
raise PausedException
@@ -154,9 +244,17 @@ class Matcher(metaclass=MatcherMeta):
def got(
cls,
key: str,
prompt: Optional[str] = None,
prompt: Optional[Union[str, Message, MessageSegment]] = None,
args_parser: Optional[ArgsParser] = None
) -> Callable[[Handler], Handler]:
"""
:说明:
装饰一个函数来指示 NoneBot 当要获取的 ``key`` 不存在时接收用户新的一条消息并经过 ``ArgsParser`` 处理后再运行该函数,如果 ``key`` 已存在则直接继续运行
:参数:
* ``key: str``: 参数名
* ``prompt: Optional[Union[str, Message, MessageSegment]]``: 在参数不存在时向用户发送的消息
* ``args_parser: Optional[ArgsParser]``: 可选参数解析函数,空则使用默认解析函数
"""
async def _key_getter(bot: Bot, event: Event, state: dict):
state["_current_key"] = key
@@ -198,36 +296,70 @@ class Matcher(metaclass=MatcherMeta):
return _decorator
@classmethod
async def finish(
cls,
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)
async def send(cls, message: Union[str, Message, MessageSegment], **kwargs):
"""
:说明:
发送一条消息给当前交互用户
:参数:
* ``message: Union[str, Message, MessageSegment]``: 消息内容
* ``**kwargs``: 其他传递给 ``bot.send`` 的参数,请参考对应 adapter 的 bot 对象 api
"""
bot = current_bot.get()
event = current_event.get()
await bot.send(event=event, message=message, **kwargs)
@classmethod
async def finish(cls,
message: Optional[Union[str, Message,
MessageSegment]] = None,
**kwargs) -> NoReturn:
"""
:说明:
发送一条消息给当前交互用户并结束当前事件响应器
:参数:
* ``message: Union[str, Message, MessageSegment]``: 消息内容
* ``**kwargs``: 其他传递给 ``bot.send`` 的参数,请参考对应 adapter 的 bot 对象 api
"""
bot = current_bot.get()
event = current_event.get()
if message:
await bot.send(event=event, message=message, **kwargs)
raise FinishedException
@classmethod
async def pause(
cls,
prompt: Optional[Union[str, Message,
MessageSegment]] = None) -> NoReturn:
bot: Bot = current_bot.get()
event: Event = current_event.get()
async def pause(cls,
prompt: Optional[Union[str, Message,
MessageSegment]] = None,
**kwargs) -> NoReturn:
"""
:说明:
发送一条消息给当前交互用户并暂停事件响应器,在接收用户新的一条消息后继续下一个处理函数
:参数:
* ``prompt: Union[str, Message, MessageSegment]``: 消息内容
* ``**kwargs``: 其他传递给 ``bot.send`` 的参数,请参考对应 adapter 的 bot 对象 api
"""
bot = current_bot.get()
event = current_event.get()
if prompt:
await bot.send(event=event, message=prompt)
await bot.send(event=event, message=prompt, **kwargs)
raise PausedException
@classmethod
async def reject(
cls,
prompt: Optional[Union[str, Message,
MessageSegment]] = None) -> NoReturn:
bot: Bot = current_bot.get()
event: Event = current_event.get()
async def reject(cls,
prompt: Optional[Union[str, Message,
MessageSegment]] = None,
**kwargs) -> NoReturn:
"""
:说明:
发送一条消息给当前交互用户并暂停事件响应器,在接收用户新的一条消息后重新运行当前处理函数
:参数:
* ``prompt: Union[str, Message, MessageSegment]``: 消息内容
* ``**kwargs``: 其他传递给 ``bot.send`` 的参数,请参考对应 adapter 的 bot 对象 api
"""
bot = current_bot.get()
event = current_event.get()
if prompt:
await bot.send(event=event, message=prompt)
await bot.send(event=event, message=prompt, **kwargs)
raise RejectedException
# 运行handlers
@@ -281,11 +413,12 @@ class Matcher(metaclass=MatcherMeta):
class MatcherGroup:
"""事件响应器组合,统一管理。用法同 ``Matcher``"""
def __init__(self,
type_: str = "",
rule: Rule = Rule(),
permission: Permission = Permission(),
rule: Optional[Rule] = None,
permission: Optional[Permission] = None,
handlers: Optional[list] = None,
temp: bool = False,
priority: int = 1,
@@ -294,19 +427,30 @@ class MatcherGroup:
module: Optional[str] = None,
default_state: Optional[dict] = None,
expire_time: Optional[datetime] = None):
"""
:说明:
创建一个事件响应器组合,参数为默认值,与 ``Matcher.new`` 一致
"""
self.matchers: List[Type[Matcher]] = []
"""
:类型: ``List[Type[Matcher]]``
:说明: 组内事件响应器列表
"""
self.type = type_
self.rule = rule
self.permission = permission
self.rule = rule or Rule()
self.permission = permission or 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
self._default_state = default_state
self._default_parser: Optional[ArgsParser] = None
def __repr__(self) -> str:
return (
f"<MatcherGroup from {self.module or 'unknow'}, type={self.type}, "
@@ -317,8 +461,8 @@ class MatcherGroup:
def new(self,
type_: str = "",
rule: Rule = Rule(),
permission: Permission = Permission(),
rule: Optional[Rule] = None,
permission: Optional[Permission] = None,
handlers: Optional[list] = None,
temp: bool = False,
priority: int = 1,
@@ -327,6 +471,14 @@ class MatcherGroup:
module: Optional[str] = None,
default_state: Optional[dict] = None,
expire_time: Optional[datetime] = None) -> Type[Matcher]:
"""
:说明:
在组中创建一个新的事件响应器,参数留空则使用组合默认值
\:\:\:danger 警告
如果使用 handlers 参数覆盖组合默认值则该事件响应器不会随组合一起添加新的事件处理函数
\:\:\:
"""
matcher = Matcher.new(type_=type_ or self.type,
rule=self.rule & rule,
permission=permission or self.permission,
@@ -335,18 +487,19 @@ class MatcherGroup:
priority=priority or self.priority,
block=block or self.block,
module=module or self.module,
default_state=default_state or self.default_state,
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:
self._default_parser = func
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)
@@ -355,7 +508,6 @@ class MatcherGroup:
return _decorator
def receive(self) -> Callable[[Handler], Handler]:
"""接收一条新消息并处理"""
async def _receive(bot: Bot, event: Event, state: dict) -> NoReturn:
raise PausedException
@@ -418,32 +570,38 @@ class MatcherGroup:
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)
async def send(self, message: Union[str, Message, MessageSegment],
**kwargs):
bot = current_bot.get()
event = current_event.get()
await bot.send(event=event, message=message, **kwargs)
async def finish(self,
message: Optional[Union[str, Message,
MessageSegment]] = None,
**kwargs) -> NoReturn:
bot = current_bot.get()
event = current_event.get()
if message:
await bot.send(event=event, message=message, **kwargs)
raise FinishedException
async def pause(
self,
prompt: Optional[Union[str, Message,
MessageSegment]] = None) -> NoReturn:
bot: Bot = current_bot.get()
event: Event = current_event.get()
async def pause(self,
prompt: Optional[Union[str, Message,
MessageSegment]] = None,
**kwargs) -> NoReturn:
bot = current_bot.get()
event = current_event.get()
if prompt:
await bot.send(event=event, message=prompt)
await bot.send(event=event, message=prompt, **kwargs)
raise PausedException
async def reject(
self,
prompt: Optional[Union[str, Message,
MessageSegment]] = None) -> NoReturn:
bot: Bot = current_bot.get()
event: Event = current_event.get()
async def reject(self,
prompt: Optional[Union[str, Message,
MessageSegment]] = None,
**kwargs) -> NoReturn:
bot = current_bot.get()
event = current_event.get()
if prompt:
await bot.send(event=event, message=prompt)
await bot.send(event=event, message=prompt, **kwargs)
raise RejectedException

View File

@@ -1,6 +1,3 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import asyncio
from datetime import datetime
@@ -57,6 +54,7 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event,
async def handle_event(bot: Bot, event: Event):
show_log = True
log_msg = f"<m>{bot.type.upper()} </m>| {event.self_id} [{event.name}]: "
if event.type == "message":
log_msg += f"Message {event.id} from "
@@ -74,8 +72,10 @@ async def handle_event(bot: Bot, event: Event):
elif event.type == "request":
log_msg += f"Request {event.raw_event}"
elif event.type == "meta_event":
log_msg += f"MetaEvent {event.raw_event}"
logger.opt(colors=True).info(log_msg)
# log_msg += f"MetaEvent {event.detail_type}"
show_log = False
if show_log:
logger.opt(colors=True).info(log_msg)
coros = []
state = {}
@@ -103,7 +103,8 @@ async def handle_event(bot: Bot, event: Event):
for matcher in matchers[priority]
]
logger.debug(f"Checking for matchers in priority {priority}...")
if show_log:
logger.debug(f"Checking for matchers in priority {priority}...")
results = await asyncio.gather(*pending_tasks, return_exceptions=True)
i = 0

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
权限
====

View File

@@ -1,5 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
插件
====
为 NoneBot 插件开发提供便携的定义函数。
"""
import re
import sys
@@ -9,25 +13,43 @@ 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
from nonebot.rule import Rule, startswith, endswith, keyword, command, regex
from nonebot.typing import Any, Set, List, Dict, Type, Tuple, Union, Optional, ModuleType
plugins: Dict[str, "Plugin"] = {}
"""
:类型: ``Dict[str, Plugin]``
:说明: 已加载的插件
"""
_tmp_matchers: Set[Type[Matcher]] = set()
@dataclass(eq=False)
class Plugin(object):
"""存储插件信息"""
name: str
"""
- **类型**: ``str``
- **说明**: 插件名称,使用 文件/文件夹 名称作为插件名
"""
module: ModuleType
"""
- **类型**: ``ModuleType``
- **说明**: 插件模块对象
"""
matcher: Set[Type[Matcher]]
"""
- **类型**: ``Set[Type[Matcher]]``
- **说明**: 插件内定义的 ``Matcher``
"""
def on(rule: Optional[Union[Rule, RuleChecker]] = None,
def on(type: str = "",
rule: Optional[Union[Rule, RuleChecker]] = None,
permission: Optional[Permission] = None,
*,
handlers: Optional[List[Handler]] = None,
@@ -35,7 +57,22 @@ def on(rule: Optional[Union[Rule, RuleChecker]] = None,
priority: int = 1,
block: bool = False,
state: Optional[dict] = None) -> Type[Matcher]:
matcher = Matcher.new("",
"""
:说明:
注册一个基础事件响应器,可自定义类型。
:参数:
* ``type: str``: 事件响应器类型
* ``rule: Optional[Union[Rule, RuleChecker]]``: 事件响应规则
* ``permission: Optional[Permission]``: 事件响应权限
* ``handlers: Optional[List[Handler]]``: 事件处理函数列表
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
* ``priority: int``: 事件响应器优先级
* ``block: bool``: 是否阻止事件向更低优先级传递
* ``state: Optional[dict]``: 默认的 state
:返回:
- ``Type[Matcher]``
"""
matcher = Matcher.new(type,
Rule() & rule,
permission or Permission(),
temp=temp,
@@ -54,6 +91,19 @@ def on_metaevent(rule: Optional[Union[Rule, RuleChecker]] = None,
priority: int = 1,
block: bool = False,
state: Optional[dict] = None) -> Type[Matcher]:
"""
:说明:
注册一个元事件响应器。
:参数:
* ``rule: Optional[Union[Rule, RuleChecker]]``: 事件响应规则
* ``handlers: Optional[List[Handler]]``: 事件处理函数列表
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
* ``priority: int``: 事件响应器优先级
* ``block: bool``: 是否阻止事件向更低优先级传递
* ``state: Optional[dict]``: 默认的 state
:返回:
- ``Type[Matcher]``
"""
matcher = Matcher.new("meta_event",
Rule() & rule,
Permission(),
@@ -74,6 +124,20 @@ def on_message(rule: Optional[Union[Rule, RuleChecker]] = None,
priority: int = 1,
block: bool = True,
state: Optional[dict] = None) -> Type[Matcher]:
"""
:说明:
注册一个消息事件响应器。
:参数:
* ``rule: Optional[Union[Rule, RuleChecker]]``: 事件响应规则
* ``permission: Optional[Permission]``: 事件响应权限
* ``handlers: Optional[List[Handler]]``: 事件处理函数列表
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
* ``priority: int``: 事件响应器优先级
* ``block: bool``: 是否阻止事件向更低优先级传递
* ``state: Optional[dict]``: 默认的 state
:返回:
- ``Type[Matcher]``
"""
matcher = Matcher.new("message",
Rule() & rule,
permission or Permission(),
@@ -93,6 +157,19 @@ def on_notice(rule: Optional[Union[Rule, RuleChecker]] = None,
priority: int = 1,
block: bool = False,
state: Optional[dict] = None) -> Type[Matcher]:
"""
:说明:
注册一个通知事件响应器。
:参数:
* ``rule: Optional[Union[Rule, RuleChecker]]``: 事件响应规则
* ``handlers: Optional[List[Handler]]``: 事件处理函数列表
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
* ``priority: int``: 事件响应器优先级
* ``block: bool``: 是否阻止事件向更低优先级传递
* ``state: Optional[dict]``: 默认的 state
:返回:
- ``Type[Matcher]``
"""
matcher = Matcher.new("notice",
Rule() & rule,
Permission(),
@@ -112,6 +189,19 @@ def on_request(rule: Optional[Union[Rule, RuleChecker]] = None,
priority: int = 1,
block: bool = False,
state: Optional[dict] = None) -> Type[Matcher]:
"""
:说明:
注册一个请求事件响应器。
:参数:
* ``rule: Optional[Union[Rule, RuleChecker]]``: 事件响应规则
* ``handlers: Optional[List[Handler]]``: 事件处理函数列表
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
* ``priority: int``: 事件响应器优先级
* ``block: bool``: 是否阻止事件向更低优先级传递
* ``state: Optional[dict]``: 默认的 state
:返回:
- ``Type[Matcher]``
"""
matcher = Matcher.new("request",
Rule() & rule,
Permission(),
@@ -127,23 +217,88 @@ def on_request(rule: Optional[Union[Rule, RuleChecker]] = None,
def on_startswith(msg: str,
rule: Optional[Optional[Union[Rule, RuleChecker]]] = None,
**kwargs) -> Type[Matcher]:
return on_message(startswith(msg) & rule, **kwargs) if rule else on_message(
startswith(msg), **kwargs)
"""
:说明:
注册一个消息事件响应器,并且当消息的**文本部分**以指定内容开头时响应。
:参数:
* ``msg: str``: 指定消息开头内容
* ``rule: Optional[Union[Rule, RuleChecker]]``: 事件响应规则
* ``permission: Optional[Permission]``: 事件响应权限
* ``handlers: Optional[List[Handler]]``: 事件处理函数列表
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
* ``priority: int``: 事件响应器优先级
* ``block: bool``: 是否阻止事件向更低优先级传递
* ``state: Optional[dict]``: 默认的 state
:返回:
- ``Type[Matcher]``
"""
return on_message(startswith(msg) & rule, **kwargs)
def on_endswith(msg: str,
rule: Optional[Optional[Union[Rule, RuleChecker]]] = None,
**kwargs) -> Type[Matcher]:
return on_message(endswith(msg) & rule, **kwargs) if rule else on_message(
startswith(msg), **kwargs)
"""
:说明:
注册一个消息事件响应器,并且当消息的**文本部分**以指定内容结尾时响应。
:参数:
* ``msg: str``: 指定消息结尾内容
* ``rule: Optional[Union[Rule, RuleChecker]]``: 事件响应规则
* ``permission: Optional[Permission]``: 事件响应权限
* ``handlers: Optional[List[Handler]]``: 事件处理函数列表
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
* ``priority: int``: 事件响应器优先级
* ``block: bool``: 是否阻止事件向更低优先级传递
* ``state: Optional[dict]``: 默认的 state
:返回:
- ``Type[Matcher]``
"""
return on_message(endswith(msg) & rule, **kwargs)
def on_keyword(keywords: Set[str],
rule: Optional[Union[Rule, RuleChecker]] = None,
**kwargs) -> Type[Matcher]:
"""
:说明:
注册一个消息事件响应器,并且当消息纯文本部分包含关键词时响应。
:参数:
* ``keywords: Set[str]``: 关键词列表
* ``rule: Optional[Union[Rule, RuleChecker]]``: 事件响应规则
* ``permission: Optional[Permission]``: 事件响应权限
* ``handlers: Optional[List[Handler]]``: 事件处理函数列表
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
* ``priority: int``: 事件响应器优先级
* ``block: bool``: 是否阻止事件向更低优先级传递
* ``state: Optional[dict]``: 默认的 state
:返回:
- ``Type[Matcher]``
"""
return on_message(keyword(*keywords) & rule, **kwargs)
def on_command(cmd: Union[str, Tuple[str, ...]],
rule: Optional[Union[Rule, RuleChecker]] = None,
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = None,
**kwargs) -> Union[Type[Matcher], MatcherGroup]:
if isinstance(cmd, str):
cmd = (cmd,)
**kwargs) -> Type[Matcher]:
"""
:说明:
注册一个消息事件响应器,并且当消息以指定命令开头时响应。
命令匹配规则参考: `命令形式匹配 <rule.html#command-command>`_
:参数:
* ``cmd: Union[str, Tuple[str, ...]]``: 指定命令内容
* ``rule: Optional[Union[Rule, RuleChecker]]``: 事件响应规则
* ``aliases: Optional[Set[Union[str, Tuple[str, ...]]]]``: 命令别名
* ``permission: Optional[Permission]``: 事件响应权限
* ``handlers: Optional[List[Handler]]``: 事件处理函数列表
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
* ``priority: int``: 事件响应器优先级
* ``block: bool``: 是否阻止事件向更低优先级传递
* ``state: Optional[dict]``: 默认的 state
:返回:
- ``Type[Matcher]``
"""
async def _strip_cmd(bot, event, state: dict):
message = event.message
@@ -153,31 +308,85 @@ def on_command(cmd: Union[str, Tuple[str, ...]],
handlers = kwargs.pop("handlers", [])
handlers.insert(0, _strip_cmd)
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)
commands = set([cmd]) | (aliases or set())
return on_message(command(*commands) & rule, handlers=handlers, **kwargs)
def on_regex(pattern: str,
flags: Union[int, re.RegexFlag] = 0,
rule: Optional[Rule] = None,
**kwargs) -> Type[Matcher]:
return on_message(regex(pattern, flags) &
rule, **kwargs) if rule else on_message(
regex(pattern, flags), **kwargs)
"""
:说明:
注册一个消息事件响应器,并且当消息匹配正则表达式时响应。
命令匹配规则参考: `正则匹配 <rule.html#regex-regex-flags-0>`_
:参数:
* ``pattern: str``: 正则表达式
* ``flags: Union[int, re.RegexFlag]``: 正则匹配标志
* ``rule: Optional[Union[Rule, RuleChecker]]``: 事件响应规则
* ``permission: Optional[Permission]``: 事件响应权限
* ``handlers: Optional[List[Handler]]``: 事件处理函数列表
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
* ``priority: int``: 事件响应器优先级
* ``block: bool``: 是否阻止事件向更低优先级传递
* ``state: Optional[dict]``: 默认的 state
:返回:
- ``Type[Matcher]``
"""
return on_message(regex(pattern, flags) & rule, **kwargs)
class CommandGroup:
"""命令组,用于声明一组有相同名称前缀的命令。"""
def __init__(self, cmd: Union[str, Tuple[str, ...]], **kwargs):
"""
:参数:
* ``cmd: Union[str, Tuple[str, ...]]``: 命令前缀
* ``**kwargs``: 其他传递给 ``on_command`` 的参数默认值,参考 `on_command <#on-command-cmd-rule-none-aliases-none-kwargs>`_
"""
self.basecmd: Tuple[str, ...] = (cmd,) if isinstance(cmd, str) else cmd
"""
- **类型**: ``Tuple[str, ...]``
- **说明**: 命令前缀
"""
if "aliases" in kwargs:
del kwargs["aliases"]
self.base_kwargs: Dict[str, Any] = kwargs
"""
- **类型**: ``Dict[str, Any]``
- **说明**: 其他传递给 ``on_command`` 的参数默认值
"""
def command(self, cmd: Union[str, Tuple[str, ...]],
**kwargs) -> Type[Matcher]:
"""
:说明:
注册一个新的命令。
:参数:
* ``cmd: Union[str, Tuple[str, ...]]``: 命令前缀
* ``**kwargs``: 其他传递给 ``on_command`` 的参数,将会覆盖命令组默认值
:返回:
- ``Type[Matcher]``
"""
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)
def load_plugin(module_path: str) -> Optional[Plugin]:
"""
:说明:
使用 ``importlib`` 加载单个插件,可以是本地插件或是通过 ``pip`` 安装的插件。
:参数:
* ``module_path: str``: 插件名称 ``path.to.your.plugin``
:返回:
- ``Optional[Plugin]``
"""
try:
_tmp_matchers.clear()
if module_path in plugins:
@@ -202,6 +411,14 @@ def load_plugin(module_path: str) -> Optional[Plugin]:
def load_plugins(*plugin_dir: str) -> Set[Plugin]:
"""
:说明:
导入目录下多个插件,以 ``_`` 开头的插件不会被导入!
:参数:
- ``*plugin_dir: str``: 插件路径
:返回:
- ``Set[Plugin]``
"""
loaded_plugins = set()
for module_info in pkgutil.iter_modules(plugin_dir):
_tmp_matchers.clear()
@@ -209,7 +426,7 @@ def load_plugins(*plugin_dir: str) -> Set[Plugin]:
if name.startswith("_"):
continue
spec = module_info.module_finder.find_spec(name)
spec = module_info.module_finder.find_spec(name, None)
if spec.name in plugins:
continue
elif spec.name in sys.modules:
@@ -232,27 +449,21 @@ def load_plugins(*plugin_dir: str) -> Set[Plugin]:
return loaded_plugins
def load_builtin_plugins():
def load_builtin_plugins() -> Optional[Plugin]:
"""
:说明:
导入 NoneBot 内置插件
:返回:
- ``Plugin``
"""
return load_plugin("nonebot.plugins.base")
def get_loaded_plugins() -> Set[Plugin]:
"""
:说明:
获取当前已导入的插件。
:返回:
- ``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)

View File

@@ -1,10 +1,6 @@
#!/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 Rule, Matcher, Handler, Permission, RuleChecker
from nonebot.typing import Set, List, Dict, Type, Tuple, Union, Optional, ModuleType
plugins: Dict[str, "Plugin"] = ...
@@ -18,7 +14,8 @@ class Plugin(object):
matcher: Set[Type[Matcher]]
def on(rule: Optional[Union[Rule, RuleChecker]] = ...,
def on(type: str = ...,
rule: Optional[Union[Rule, RuleChecker]] = ...,
permission: Optional[Permission] = ...,
*,
handlers: Optional[List[Handler]] = ...,
@@ -72,8 +69,8 @@ def on_request(rule: Optional[Union[Rule, RuleChecker]] = ...,
def on_startswith(msg: str,
rule: Optional[Optional[Union[Rule, RuleChecker]]] = ...,
permission: Optional[Permission] = ...,
*,
permission: Optional[Permission] = ...,
handlers: Optional[List[Handler]] = ...,
temp: bool = ...,
priority: int = ...,
@@ -84,8 +81,8 @@ def on_startswith(msg: str,
def on_endswith(msg: str,
rule: Optional[Optional[Union[Rule, RuleChecker]]] = ...,
permission: Optional[Permission] = ...,
*,
permission: Optional[Permission] = ...,
handlers: Optional[List[Handler]] = ...,
temp: bool = ...,
priority: int = ...,
@@ -94,12 +91,10 @@ def on_endswith(msg: str,
...
@overload
def on_command(cmd: Union[str, Tuple[str, ...]],
rule: Optional[Union[Rule, RuleChecker]] = ...,
aliases: None = ...,
permission: Optional[Permission] = ...,
def on_keyword(keywords: Set[str],
rule: Optional[Optional[Union[Rule, RuleChecker]]] = ...,
*,
permission: Optional[Permission] = ...,
handlers: Optional[List[Handler]] = ...,
temp: bool = ...,
priority: int = ...,
@@ -108,25 +103,24 @@ def on_command(cmd: Union[str, Tuple[str, ...]],
...
@overload
def on_command(cmd: Union[str, Tuple[str, ...]],
rule: Optional[Union[Rule, RuleChecker]] = ...,
aliases: Set[Union[str, Tuple[str, ...]]] = ...,
permission: Optional[Permission] = ...,
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ...,
*,
permission: Optional[Permission] = ...,
handlers: Optional[List[Handler]] = ...,
temp: bool = ...,
priority: int = ...,
block: bool = ...,
state: Optional[dict] = ...) -> MatcherGroup:
state: Optional[dict] = ...) -> Type[Matcher]:
...
def on_regex(pattern: str,
flags: Union[int, re.RegexFlag] = 0,
rule: Optional[Rule] = ...,
permission: Optional[Permission] = ...,
*,
permission: Optional[Permission] = ...,
handlers: Optional[List[Handler]] = ...,
temp: bool = ...,
priority: int = ...,
@@ -165,16 +159,15 @@ class CommandGroup:
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]:
def command(self,
cmd: Union[str, Tuple[str, ...]],
rule: Optional[Union[Rule, RuleChecker]] = ...,
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ...,
permission: Optional[Permission] = ...,
*,
handlers: Optional[List[Handler]] = ...,
temp: bool = ...,
priority: int = ...,
block: bool = ...,
state: Optional[dict] = ...) -> Type[Matcher]:
...

View File

@@ -1,10 +1,34 @@
from functools import reduce
from nonebot.rule import to_me
from nonebot.plugin import on_command
from nonebot.typing import Bot, Event
from nonebot.permission import SUPERUSER
from nonebot.typing import Bot, Event, MessageSegment
say = on_command("say", to_me())
say = on_command("say", to_me(), permission=SUPERUSER)
@say.handle()
async def repeat(bot: Bot, event: Event, state: dict):
await bot.send(message=event.message, event=event)
async def say_unescape(bot: Bot, event: Event, state: dict):
Message = event.message.__class__
def _unescape(message: Message, segment: MessageSegment):
if segment.type == "text":
return message.append(segment.data["text"])
return message.append(segment)
message = reduce(_unescape, event.message, Message()) # type: ignore
await bot.send(message=message, event=event)
echo = on_command("echo", to_me())
@echo.handle()
async def echo_escape(bot: Bot, event: Event, state: dict):
Message = event.message.__class__
MessageSegment = event.message[0].__class__
message = Message().append( # type: ignore
MessageSegment.text(str(event.message)))
await bot.send(message=message, event=event)

View File

@@ -1,13 +1,11 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
规则
====
每个 ``Matcher`` 拥有一个 ``Rule`` ,其中是 **异步** ``RuleChecker`` 的集合,只有当所有 ``RuleChecker`` 检查结果为 ``True`` 时继续运行。
每个事件响应器 ``Matcher`` 拥有一个匹配规则 ``Rule`` ,其中是 **异步** ``RuleChecker`` 的集合,只有当所有 ``RuleChecker`` 检查结果为 ``True`` 时继续运行。
\:\:\:tip 提示
``RuleChecker`` 既可以是 async function 也可以是 sync function
``RuleChecker`` 既可以是 async function 也可以是 sync function但在最终会被 ``nonebot.utils.run_sync`` 转换为 async function
\:\:\:
"""
@@ -184,41 +182,97 @@ def endswith(msg: str) -> Rule:
return Rule(_endswith)
def keyword(msg: str) -> Rule:
def keyword(*keywords: str) -> Rule:
"""
:说明:
匹配消息关键词
:参数:
* ``*keywords: str``: 关键词
"""
async def _keyword(bot: Bot, event: Event, state: dict) -> bool:
return bool(event.plain_text and msg in event.plain_text)
return bool(event.plain_text and
any(keyword in event.plain_text for keyword in keywords))
return Rule(_keyword)
def command(command: Tuple[str, ...]) -> Rule:
def command(*cmds: Union[str, Tuple[str, ...]]) -> Rule:
"""
:说明:
命令形式匹配,根据配置里提供的 ``command_start``, ``command_sep`` 判断消息是否为命令。
可以通过 ``state["_prefix"]["command"]`` 获取匹配成功的命令(例:``("test",)``),通过 ``state["_prefix"]["raw_command"]`` 获取匹配成功的原始命令文本(例:``"/test"``)。
:参数:
* ``*cmds: Union[str, Tuple[str, ...]]``: 命令内容
:示例:
使用默认 ``command_start``, ``command_sep`` 配置
命令 ``("test",)`` 可以匹配:``/test`` 开头的消息
命令 ``("test", "sub")`` 可以匹配”``/test.sub`` 开头的消息
\:\:\:tip 提示
命令内容与后续消息间无需空格!
\:\:\:
"""
config = get_driver().config
command_start = config.command_start
command_sep = config.command_sep
if len(command) == 1:
for start in command_start:
TrieRule.add_prefix(f"{start}{command[0]}", command)
else:
for start, sep in product(command_start, command_sep):
TrieRule.add_prefix(f"{start}{sep.join(command)}", command)
commands = list(cmds)
for index, command in enumerate(commands):
if isinstance(command, str):
commands[index] = command = (command,)
if len(command) == 1:
for start in command_start:
TrieRule.add_prefix(f"{start}{command[0]}", command)
else:
for start, sep in product(command_start, command_sep):
TrieRule.add_prefix(f"{start}{sep.join(command)}", command)
async def _command(bot: Bot, event: Event, state: dict) -> bool:
return command == state["_prefix"]["command"]
return state["_prefix"]["command"] in commands
return Rule(_command)
def regex(regex: str, flags: Union[int, re.RegexFlag] = 0) -> Rule:
"""
:说明:
根据正则表达式进行匹配。
可以通过 ``state["_matched"]`` 获取正则表达式匹配成功的文本。
:参数:
* ``regex: str``: 正则表达式
* ``flags: Union[int, re.RegexFlag]``: 正则标志
\:\:\:tip 提示
正则表达式匹配使用 search 而非 match如需从头匹配请使用 ``r"^xxx"`` 来确保匹配开头
\:\:\:
"""
pattern = re.compile(regex, flags)
async def _regex(bot: Bot, event: Event, state: dict) -> bool:
return bool(pattern.search(str(event.message)))
matched = pattern.search(str(event.message))
if matched:
state["_matched"] = matched.group()
return True
else:
state["_matched"] = None
return False
return Rule(_regex)
def to_me() -> Rule:
"""
:说明:
通过 ``event.to_me`` 判断消息是否是发送给机器人
:参数:
* 无
"""
async def _to_me(bot: Bot, event: Event, state: dict) -> bool:
return bool(event.to_me)

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
计划任务
========

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
类型
====

View File

@@ -1,6 +1,3 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import re
import json
import asyncio

1695
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -20,14 +20,16 @@
},
"license": "MIT",
"devDependencies": {
"@vuepress/plugin-back-to-top": "^1.5.4",
"@vuepress/plugin-medium-zoom": "^1.5.4",
"vuepress": "^1.5.4",
"vuepress-plugin-versioning": "^4.5.0",
"vuepress-theme-titanium": "^4.5.1"
"@vuepress/plugin-back-to-top": "^1.7.1",
"@vuepress/plugin-medium-zoom": "^1.7.1",
"@vuepress/plugin-pwa": "^1.7.1",
"vuepress": "^1.7.1",
"vuepress-plugin-versioning": "git+https://github.com/nonebot/vuepress-plugin-versioning.git",
"vuepress-theme-nonebot": "git+https://github.com/nonebot/vuepress-theme-nonebot.git"
},
"dependencies": {
"vuetify": "^2.3.10",
"copy-to-clipboard": "^3.3.1",
"vuetify": "^2.3.16",
"wowjs": "^1.1.3"
}
}

6
pages/plugin-store.md Normal file
View File

@@ -0,0 +1,6 @@
---
---
# 插件广场
<Plugins></Plugins>

1195
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "nonebot2"
version = "2.0.0a2"
version = "2.0.0a4"
description = "An asynchronous python bot framework."
authors = ["yanyongyu <yanyongyu_1@126.com>"]
license = "MIT"
@@ -24,15 +24,15 @@ include = ["nonebot/py.typed"]
[tool.poetry.dependencies]
python = "^3.7"
httpx = "^0.13.3"
httpx = "^0.16.1"
loguru = "^0.5.1"
pygtrie = "^2.3.3"
fastapi = "^0.58.1"
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 }
nonebot-test = { version = "^0.1.0", optional = true }
nb-cli = { version="^0.2.0", optional = true }
[tool.poetry.dev-dependencies]
yapf = "^0.30.0"
@@ -41,7 +41,7 @@ sphinx-markdown-builder = { git = "https://github.com/nonebot/sphinx-markdown-bu
[tool.poetry.extras]
cli = ["nb-cli"]
# test = ["nonebot-test"]
test = ["nonebot-test"]
scheduler = ["apscheduler"]
full = ["nb-cli", "nonebot-test", "scheduler"]

View File

@@ -3,6 +3,9 @@ HOST=0.0.0.0
PORT=2333
DEBUG=true
SUPERUSERS=[123123123]
NICKNAME=["bot"]
COMMAND_START=["", "/", "#"]
COMMAND_SEP=["/", "."]

View File

@@ -1,6 +1,3 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys

View File

@@ -1,6 +1,3 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from nonebot.typing import Bot, Event
from nonebot.plugin import on_metaevent

View File

@@ -1,4 +1 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from . import test_command

View File

@@ -1,6 +1,3 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from nonebot.rule import to_me
from nonebot.typing import Event
from nonebot.plugin import on_command

View File

@@ -1,6 +1,3 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from nonebot.rule import to_me
from nonebot.typing import Event
from nonebot.plugin import on_startswith