mirror of
https://github.com/nonebot/nonebot2.git
synced 2024-11-28 05:49:02 +08:00
Merge branch 'dev' into richardchien-patch-1
This commit is contained in:
commit
4795f01583
7
.github/ISSUE_TEMPLATE/bug-report.md
vendored
7
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@ -28,6 +28,11 @@ A clear and concise description of what you expected to happen.
|
|||||||
- Python Version: [e.g. 3.8]
|
- Python Version: [e.g. 3.8]
|
||||||
- Nonebot Version: [e.g. 2.0.0]
|
- Nonebot Version: [e.g. 2.0.0]
|
||||||
|
|
||||||
**截图**
|
**协议端信息:**
|
||||||
|
|
||||||
|
- 协议端: [e.g. go-cqhttp]
|
||||||
|
- 协议端版本: [e.g. 1.0.0]
|
||||||
|
|
||||||
|
**截图或日志**
|
||||||
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,5 +1,8 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
|
- name: Question
|
||||||
|
url: https://github.com/nonebot/discussions/discussions/new
|
||||||
|
about: Ask questions about nonebot
|
||||||
- name: Plugin Publish
|
- name: Plugin Publish
|
||||||
url: https://v2.nonebot.dev/store.html
|
url: https://v2.nonebot.dev/store.html
|
||||||
about: Publish your plugin to nonebot homepage and nb-cli
|
about: Publish your plugin to nonebot homepage and nb-cli
|
||||||
|
17
.github/ISSUE_TEMPLATE/document.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/document.md
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
name: Document improvement
|
||||||
|
about: Feedback on documentation, including errors and ideas
|
||||||
|
title: 'Docs: some description'
|
||||||
|
labels: documentation
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**描述问题或主题:**
|
||||||
|
|
||||||
|
|
||||||
|
**需做出的修改:**
|
||||||
|
|
||||||
|
* [ ] 一些修改
|
||||||
|
* [ ] 一些修改
|
||||||
|
* [ ] 一些修改
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -189,3 +189,5 @@ dev
|
|||||||
docs_build/_build
|
docs_build/_build
|
||||||
!tests/.env
|
!tests/.env
|
||||||
*.xmind
|
*.xmind
|
||||||
|
yarn.lock
|
||||||
|
.DS_Store
|
||||||
|
9
.prettierrc
Normal file
9
.prettierrc
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"endOfLine": "lf",
|
||||||
|
"arrowParens": "always",
|
||||||
|
"singleQuote": false,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"semi": true
|
||||||
|
}
|
14
README.md
14
README.md
@ -17,7 +17,7 @@ _✨ Python 异步机器人框架 ✨_
|
|||||||
<a href="https://pypi.python.org/pypi/nonebot2">
|
<a href="https://pypi.python.org/pypi/nonebot2">
|
||||||
<img src="https://img.shields.io/pypi/v/nonebot2" alt="pypi">
|
<img src="https://img.shields.io/pypi/v/nonebot2" alt="pypi">
|
||||||
</a>
|
</a>
|
||||||
<img src="https://img.shields.io/badge/python-3.7+-blue" alt="python"><br />
|
<img src="https://img.shields.io/badge/python-3.7.3+-blue" alt="python"><br />
|
||||||
<a href="https://github.com/howmanybots/onebot/blob/master/README.md">
|
<a href="https://github.com/howmanybots/onebot/blob/master/README.md">
|
||||||
<img src="https://img.shields.io/badge/OneBot-v11-black?style=social&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABABAMAAABYR2ztAAAAIVBMVEUAAAAAAAADAwMHBwceHh4UFBQNDQ0ZGRkoKCgvLy8iIiLWSdWYAAAAAXRSTlMAQObYZgAAAQVJREFUSMftlM0RgjAQhV+0ATYK6i1Xb+iMd0qgBEqgBEuwBOxU2QDKsjvojQPvkJ/ZL5sXkgWrFirK4MibYUdE3OR2nEpuKz1/q8CdNxNQgthZCXYVLjyoDQftaKuniHHWRnPh2GCUetR2/9HsMAXyUT4/3UHwtQT2AggSCGKeSAsFnxBIOuAggdh3AKTL7pDuCyABcMb0aQP7aM4AnAbc/wHwA5D2wDHTTe56gIIOUA/4YYV2e1sg713PXdZJAuncdZMAGkAukU9OAn40O849+0ornPwT93rphWF0mgAbauUrEOthlX8Zu7P5A6kZyKCJy75hhw1Mgr9RAUvX7A3csGqZegEdniCx30c3agAAAABJRU5ErkJggg==" alt="cqhttp">
|
<img src="https://img.shields.io/badge/OneBot-v11-black?style=social&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABABAMAAABYR2ztAAAAIVBMVEUAAAAAAAADAwMHBwceHh4UFBQNDQ0ZGRkoKCgvLy8iIiLWSdWYAAAAAXRSTlMAQObYZgAAAQVJREFUSMftlM0RgjAQhV+0ATYK6i1Xb+iMd0qgBEqgBEuwBOxU2QDKsjvojQPvkJ/ZL5sXkgWrFirK4MibYUdE3OR2nEpuKz1/q8CdNxNQgthZCXYVLjyoDQftaKuniHHWRnPh2GCUetR2/9HsMAXyUT4/3UHwtQT2AggSCGKeSAsFnxBIOuAggdh3AKTL7pDuCyABcMb0aQP7aM4AnAbc/wHwA5D2wDHTTe56gIIOUA/4YYV2e1sg713PXdZJAuncdZMAGkAukU9OAn40O849+0ornPwT93rphWF0mgAbauUrEOthlX8Zu7P5A6kZyKCJy75hhw1Mgr9RAUvX7A3csGqZegEdniCx30c3agAAAABJRU5ErkJggg==" alt="cqhttp">
|
||||||
</a>
|
</a>
|
||||||
@ -97,7 +97,13 @@ NoneBot2 的驱动框架 `Driver` 以及通信协议 `Adapter` 均可**自定义
|
|||||||
nb create
|
nb create
|
||||||
```
|
```
|
||||||
|
|
||||||
## 插件
|
## 社区资源
|
||||||
|
|
||||||
|
### 教程/实际项目/经验分享
|
||||||
|
|
||||||
|
- [awesome-nonebot](https://github.com/nonebot/awesome-nonebot)
|
||||||
|
|
||||||
|
### 插件
|
||||||
|
|
||||||
此外,NoneBot2 还有丰富的官方以及第三方现成的插件供大家使用:
|
此外,NoneBot2 还有丰富的官方以及第三方现成的插件供大家使用:
|
||||||
|
|
||||||
@ -107,7 +113,9 @@ NoneBot2 的驱动框架 `Driver` 以及通信协议 `Adapter` 均可**自定义
|
|||||||
nb plugin install nonebot_plugin_docs
|
nb plugin install nonebot_plugin_docs
|
||||||
```
|
```
|
||||||
|
|
||||||
或者尝试 [文档镜像](https://nonebot2-vercel-mirror.vercel.app)
|
或者尝试以下镜像:
|
||||||
|
- [文档镜像(中国境内)](https://nb2.baka.icu)
|
||||||
|
- [文档镜像(vercel)](https://nonebot2-vercel-mirror.vercel.app)
|
||||||
|
|
||||||
- 其他插件请查看 [商店](https://v2.nonebot.dev/store.html)
|
- 其他插件请查看 [商店](https://v2.nonebot.dev/store.html)
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
## 从 NoneBot v1 迁移
|
## 从 NoneBot v1 迁移
|
||||||
|
|
||||||
`APScheduler` 作为 `nonebot` v1 的可选依赖,为众多 bot 提供了方便的定时任务功能。`nonebot2` 已将 `APScheduler` 独立为 `nonebot_plugin_apscheduler` 插件,你可以在 [插件广场](https://v2.nonebot.dev/plugin-store.html) 中找到它。
|
`APScheduler` 作为 `nonebot` v1 的可选依赖,为众多 bot 提供了方便的定时任务功能。`nonebot2` 已将 `APScheduler` 独立为 `nonebot_plugin_apscheduler` 插件,你可以在 [插件广场](https://v2.nonebot.dev/store.html) 中找到它。
|
||||||
|
|
||||||
相比于 `nonebot` v1 ,只需要安装插件并修改 `scheduler` 的导入方式即可完成迁移。
|
相比于 `nonebot` v1 ,只需要安装插件并修改 `scheduler` 的导入方式即可完成迁移。
|
||||||
|
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
|
|
||||||
## 从 NoneBot v1 迁移
|
## 从 NoneBot v1 迁移
|
||||||
|
|
||||||
`APScheduler` 作为 `nonebot` v1 的可选依赖,为众多 bot 提供了方便的定时任务功能。`nonebot2` 已将 `APScheduler` 独立为 `nonebot_plugin_apscheduler` 插件,你可以在 [插件广场](https://v2.nonebot.dev/plugin-store.html) 中找到它。
|
`APScheduler` 作为 `nonebot` v1 的可选依赖,为众多 bot 提供了方便的定时任务功能。`nonebot2` 已将 `APScheduler` 独立为 `nonebot_plugin_apscheduler` 插件,你可以在 [插件广场](https://v2.nonebot.dev/store.html) 中找到它。
|
||||||
|
|
||||||
相比于 `nonebot` v1,`nonebot` v2只需要安装插件并修改 `scheduler` 的导入方式即可完成迁移。
|
相比于 `nonebot` v1,`nonebot` v2 只需要安装插件并修改 `scheduler` 的导入方式即可完成迁移。
|
||||||
|
|
||||||
## 安装插件
|
## 安装插件
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
## 从 v1 迁移
|
## 从 v1 迁移
|
||||||
|
|
||||||
`APScheduler` 作为 `nonebot` v1 的可选依赖,为众多 bot 提供了方便的定时任务功能。`nonebot2` 已将 `APScheduler` 独立为 `nonebot_plugin_apscheduler` 插件,你可以在 [插件广场](https://v2.nonebot.dev/plugin-store.html) 中找到它。
|
`APScheduler` 作为 `nonebot` v1 的可选依赖,为众多 bot 提供了方便的定时任务功能。`nonebot2` 已将 `APScheduler` 独立为 `nonebot_plugin_apscheduler` 插件,你可以在 [插件广场](https://v2.nonebot.dev/store.html) 中找到它。
|
||||||
|
|
||||||
相比于 `nonebot` v1 ,只需要安装插件并修改 `scheduler` 的导入方式即可完成迁移。
|
相比于 `nonebot` v1 ,只需要安装插件并修改 `scheduler` 的导入方式即可完成迁移。
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-card flat class="adapters">
|
<v-card flat class="adapters">
|
||||||
<v-row>
|
<v-row class="justify-center">
|
||||||
<v-col cols="12" sm="4">
|
<v-col cols="12" sm="6">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="filterText"
|
v-model="filterText"
|
||||||
dense
|
dense
|
||||||
@ -9,7 +9,7 @@
|
|||||||
outlined
|
outlined
|
||||||
clearable
|
clearable
|
||||||
hide-details
|
hide-details
|
||||||
label="Filter Adapter"
|
label="搜索适配器"
|
||||||
>
|
>
|
||||||
<template v-slot:prepend-inner>
|
<template v-slot:prepend-inner>
|
||||||
<div class="v-input__icon v-input__icon--prepend-inner">
|
<div class="v-input__icon v-input__icon--prepend-inner">
|
||||||
@ -18,16 +18,16 @@
|
|||||||
</template>
|
</template>
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" sm="4">
|
<v-col cols="12" sm="6">
|
||||||
<v-dialog v-model="dialog" max-width="600px">
|
<v-dialog v-model="dialog" max-width="600px">
|
||||||
<template v-slot:activator="{ on, attrs }">
|
<template v-slot:activator="{ on, attrs }">
|
||||||
<v-btn dark block color="primary" v-bind="attrs" v-on="on"
|
<v-btn dark block color="primary" v-bind="attrs" v-on="on"
|
||||||
>Publish Your Adapter
|
>发布适配器
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-card-title>
|
<v-card-title>
|
||||||
<span class="headline">Adapter Information</span>
|
<span class="headline">适配器信息</span>
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-form ref="newAdapterForm" v-model="valid" lazy-validation>
|
<v-form ref="newAdapterForm" v-model="valid" lazy-validation>
|
||||||
@ -49,14 +49,14 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="newAdapter.id"
|
v-model="newAdapter.link"
|
||||||
label="PyPI 项目名"
|
label="PyPI 项目名"
|
||||||
required
|
required
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="newAdapter.link"
|
v-model="newAdapter.id"
|
||||||
label="协议 import 包名"
|
label="协议 import 包名"
|
||||||
required
|
required
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
@ -75,7 +75,7 @@
|
|||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn color="blue darken-1" text @click="dialog = false">
|
<v-btn color="blue darken-1" text @click="dialog = false">
|
||||||
Close
|
关闭
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
:disabled="!valid"
|
:disabled="!valid"
|
||||||
@ -86,22 +86,23 @@
|
|||||||
publishAdapter();
|
publishAdapter();
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
Publish
|
发布
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" sm="4">
|
</v-row>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12">
|
||||||
<v-pagination
|
<v-pagination
|
||||||
v-model="page"
|
v-model="page"
|
||||||
:length="pageNum"
|
:length="pageNum"
|
||||||
prev-icon="fa-caret-left"
|
prev-icon="fa-caret-left"
|
||||||
next-icon="fa-caret-right"
|
next-icon="fa-caret-right"
|
||||||
></v-pagination>
|
></v-pagination
|
||||||
</v-col>
|
></v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
<hr />
|
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col
|
<v-col
|
||||||
cols="12"
|
cols="12"
|
||||||
@ -138,7 +139,7 @@ import adapters from "../public/adapters.json";
|
|||||||
export default {
|
export default {
|
||||||
name: "Adapters",
|
name: "Adapters",
|
||||||
components: {
|
components: {
|
||||||
PublishCard
|
PublishCard,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -152,8 +153,8 @@ export default {
|
|||||||
desc: null,
|
desc: null,
|
||||||
id: null,
|
id: null,
|
||||||
link: null,
|
link: null,
|
||||||
repo: null
|
repo: null,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -161,7 +162,7 @@ export default {
|
|||||||
return Math.ceil(this.filteredAdapters.length / 10);
|
return Math.ceil(this.filteredAdapters.length / 10);
|
||||||
},
|
},
|
||||||
filteredAdapters() {
|
filteredAdapters() {
|
||||||
return this.adapters.filter(adapter => {
|
return this.adapters.filter((adapter) => {
|
||||||
return (
|
return (
|
||||||
adapter.id.indexOf(this.filterText || "") != -1 ||
|
adapter.id.indexOf(this.filterText || "") != -1 ||
|
||||||
adapter.name.indexOf(this.filterText || "") != -1 ||
|
adapter.name.indexOf(this.filterText || "") != -1 ||
|
||||||
@ -173,7 +174,7 @@ export default {
|
|||||||
displayAdapters() {
|
displayAdapters() {
|
||||||
return this.filteredAdapters.slice((this.page - 1) * 10, this.page * 10);
|
return this.filteredAdapters.slice((this.page - 1) * 10, this.page * 10);
|
||||||
},
|
},
|
||||||
publishPlugin() {
|
publishAdapter() {
|
||||||
if (!this.$refs.newAdapterForm.validate()) {
|
if (!this.$refs.newAdapterForm.validate()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -215,7 +216,7 @@ ${this.newAdapter.repo}
|
|||||||
window.open(
|
window.open(
|
||||||
`https://github.com/nonebot/nonebot2/issues/new?title=${title}&body=${body}&labels=Adapter`
|
`https://github.com/nonebot/nonebot2/issues/new?title=${title}&body=${body}&labels=Adapter`
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-card flat class="bots">
|
<v-card flat class="bots">
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12" sm="4">
|
<v-col cols="12" sm="6">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="filterText"
|
v-model="filterText"
|
||||||
dense
|
dense
|
||||||
@ -9,7 +9,7 @@
|
|||||||
outlined
|
outlined
|
||||||
clearable
|
clearable
|
||||||
hide-details
|
hide-details
|
||||||
label="Filter Bot"
|
label="搜索机器人"
|
||||||
>
|
>
|
||||||
<template v-slot:prepend-inner>
|
<template v-slot:prepend-inner>
|
||||||
<div class="v-input__icon v-input__icon--prepend-inner">
|
<div class="v-input__icon v-input__icon--prepend-inner">
|
||||||
@ -18,16 +18,16 @@
|
|||||||
</template>
|
</template>
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" sm="4">
|
<v-col cols="12" sm="6">
|
||||||
<v-dialog v-model="dialog" max-width="600px">
|
<v-dialog v-model="dialog" max-width="600px">
|
||||||
<template v-slot:activator="{ on, attrs }">
|
<template v-slot:activator="{ on, attrs }">
|
||||||
<v-btn dark block color="primary" v-bind="attrs" v-on="on"
|
<v-btn dark block color="primary" v-bind="attrs" v-on="on"
|
||||||
>Publish Your Bot
|
>发布机器人
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-card-title>
|
<v-card-title>
|
||||||
<span class="headline">Bot Information</span>
|
<span class="headline">机器人信息</span>
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-form ref="newBotForm" v-model="valid" lazy-validation>
|
<v-form ref="newBotForm" v-model="valid" lazy-validation>
|
||||||
@ -61,7 +61,7 @@
|
|||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn color="blue darken-1" text @click="dialog = false">
|
<v-btn color="blue darken-1" text @click="dialog = false">
|
||||||
Close
|
关闭
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
:disabled="!valid"
|
:disabled="!valid"
|
||||||
@ -72,13 +72,15 @@
|
|||||||
publishBot();
|
publishBot();
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
Publish
|
发布
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" sm="4">
|
</v-row>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12">
|
||||||
<v-pagination
|
<v-pagination
|
||||||
v-model="page"
|
v-model="page"
|
||||||
:length="pageNum"
|
:length="pageNum"
|
||||||
@ -87,7 +89,6 @@
|
|||||||
></v-pagination>
|
></v-pagination>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
<hr />
|
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12" sm="6" v-for="(bot, index) in displayBots" :key="index">
|
<v-col cols="12" sm="6" v-for="(bot, index) in displayBots" :key="index">
|
||||||
<PublishCard
|
<PublishCard
|
||||||
@ -118,7 +119,7 @@ import bots from "../public/bots.json";
|
|||||||
export default {
|
export default {
|
||||||
name: "Bots",
|
name: "Bots",
|
||||||
components: {
|
components: {
|
||||||
PublishCard
|
PublishCard,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -130,8 +131,8 @@ export default {
|
|||||||
newBot: {
|
newBot: {
|
||||||
name: null,
|
name: null,
|
||||||
desc: null,
|
desc: null,
|
||||||
repo: null
|
repo: null,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -139,7 +140,7 @@ export default {
|
|||||||
return Math.ceil(this.filteredBots.length / 10);
|
return Math.ceil(this.filteredBots.length / 10);
|
||||||
},
|
},
|
||||||
filteredBots() {
|
filteredBots() {
|
||||||
return this.bots.filter(bot => {
|
return this.bots.filter((bot) => {
|
||||||
return (
|
return (
|
||||||
bot.name.indexOf(this.filterText || "") != -1 ||
|
bot.name.indexOf(this.filterText || "") != -1 ||
|
||||||
bot.desc.indexOf(this.filterText || "") != -1 ||
|
bot.desc.indexOf(this.filterText || "") != -1 ||
|
||||||
@ -183,7 +184,7 @@ ${this.newBot.repo}
|
|||||||
window.open(
|
window.open(
|
||||||
`https://github.com/nonebot/nonebot2/issues/new?title=${title}&body=${body}&labels=Bot`
|
`https://github.com/nonebot/nonebot2/issues/new?title=${title}&body=${body}&labels=Bot`
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -136,8 +136,8 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
messages: {
|
messages: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => [],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
initWOW: function() {
|
initWOW: function() {
|
||||||
@ -146,13 +146,13 @@ export default {
|
|||||||
animateClass: "animate__animated",
|
animateClass: "animate__animated",
|
||||||
offset: 0,
|
offset: 0,
|
||||||
mobile: true,
|
mobile: true,
|
||||||
live: true
|
live: true,
|
||||||
}).init();
|
}).init();
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.initWOW();
|
this.initWOW();
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-card flat class="plugins">
|
<v-card flat class="plugins">
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12" sm="4">
|
<v-col cols="12" sm="6">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="filterText"
|
v-model="filterText"
|
||||||
dense
|
dense
|
||||||
@ -9,7 +9,7 @@
|
|||||||
outlined
|
outlined
|
||||||
clearable
|
clearable
|
||||||
hide-details
|
hide-details
|
||||||
label="Filter Plugin"
|
label="搜索插件"
|
||||||
>
|
>
|
||||||
<template v-slot:prepend-inner>
|
<template v-slot:prepend-inner>
|
||||||
<div class="v-input__icon v-input__icon--prepend-inner">
|
<div class="v-input__icon v-input__icon--prepend-inner">
|
||||||
@ -18,16 +18,16 @@
|
|||||||
</template>
|
</template>
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" sm="4">
|
<v-col cols="12" sm="6">
|
||||||
<v-dialog v-model="dialog" max-width="600px">
|
<v-dialog v-model="dialog" max-width="600px">
|
||||||
<template v-slot:activator="{ on, attrs }">
|
<template v-slot:activator="{ on, attrs }">
|
||||||
<v-btn dark block color="primary" v-bind="attrs" v-on="on"
|
<v-btn dark block color="primary" v-bind="attrs" v-on="on"
|
||||||
>Publish Your Plugin
|
>发布插件
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-card-title>
|
<v-card-title>
|
||||||
<span class="headline">Plugin Information</span>
|
<span class="headline">插件信息</span>
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-form ref="newPluginForm" v-model="valid" lazy-validation>
|
<v-form ref="newPluginForm" v-model="valid" lazy-validation>
|
||||||
@ -75,7 +75,7 @@
|
|||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn color="blue darken-1" text @click="dialog = false">
|
<v-btn color="blue darken-1" text @click="dialog = false">
|
||||||
Close
|
关闭
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
:disabled="!valid"
|
:disabled="!valid"
|
||||||
@ -86,13 +86,15 @@
|
|||||||
publishPlugin();
|
publishPlugin();
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
Publish
|
发布
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" sm="4">
|
</v-row>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12">
|
||||||
<v-pagination
|
<v-pagination
|
||||||
v-model="page"
|
v-model="page"
|
||||||
:length="pageNum"
|
:length="pageNum"
|
||||||
@ -101,7 +103,6 @@
|
|||||||
></v-pagination>
|
></v-pagination>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
<hr />
|
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col
|
<v-col
|
||||||
cols="12"
|
cols="12"
|
||||||
@ -115,7 +116,7 @@
|
|||||||
:id="plugin.id"
|
:id="plugin.id"
|
||||||
:author="plugin.author"
|
:author="plugin.author"
|
||||||
:link="plugin.repo"
|
:link="plugin.repo"
|
||||||
text="copy nb install command"
|
text="点此复制安装命令"
|
||||||
:command="`nb plugin install ${plugin.id}`"
|
:command="`nb plugin install ${plugin.id}`"
|
||||||
></PublishCard>
|
></PublishCard>
|
||||||
</v-col>
|
</v-col>
|
||||||
@ -140,7 +141,7 @@ import plugins from "../public/plugins.json";
|
|||||||
export default {
|
export default {
|
||||||
name: "Plugins",
|
name: "Plugins",
|
||||||
components: {
|
components: {
|
||||||
PublishCard
|
PublishCard,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -154,8 +155,8 @@ export default {
|
|||||||
desc: null,
|
desc: null,
|
||||||
id: null,
|
id: null,
|
||||||
link: null,
|
link: null,
|
||||||
repo: null
|
repo: null,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -163,7 +164,7 @@ export default {
|
|||||||
return Math.ceil(this.filteredPlugins.length / 10);
|
return Math.ceil(this.filteredPlugins.length / 10);
|
||||||
},
|
},
|
||||||
filteredPlugins() {
|
filteredPlugins() {
|
||||||
return this.plugins.filter(plugin => {
|
return this.plugins.filter((plugin) => {
|
||||||
return (
|
return (
|
||||||
plugin.id.indexOf(this.filterText || "") != -1 ||
|
plugin.id.indexOf(this.filterText || "") != -1 ||
|
||||||
plugin.name.indexOf(this.filterText || "") != -1 ||
|
plugin.name.indexOf(this.filterText || "") != -1 ||
|
||||||
@ -217,7 +218,7 @@ ${this.newPlugin.repo}
|
|||||||
window.open(
|
window.open(
|
||||||
`https://github.com/nonebot/nonebot2/issues/new?title=${title}&body=${body}&labels=Plugin`
|
`https://github.com/nonebot/nonebot2/issues/new?title=${title}&body=${body}&labels=Plugin`
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
{{ text }}
|
{{ text }}
|
||||||
<v-icon right small>fa-copy</v-icon>
|
<v-icon right small>fa-copy</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-snackbar v-model="snackbar">Copied!</v-snackbar>
|
<v-snackbar v-model="snackbar">复制成功!</v-snackbar>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
@ -44,17 +44,17 @@ export default {
|
|||||||
author: String,
|
author: String,
|
||||||
link: String,
|
link: String,
|
||||||
text: String,
|
text: String,
|
||||||
command: String
|
command: String,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
snackbar: false
|
snackbar: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
showCommand() {
|
showCommand() {
|
||||||
return this.text && this.command;
|
return this.text && this.command;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
repoLink(repo) {
|
repoLink(repo) {
|
||||||
@ -65,11 +65,11 @@ export default {
|
|||||||
},
|
},
|
||||||
copyCommand() {
|
copyCommand() {
|
||||||
copy(this.command, {
|
copy(this.command, {
|
||||||
format: "text/plain"
|
format: "text/plain",
|
||||||
});
|
});
|
||||||
this.snackbar = true;
|
this.snackbar = true;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
}}</v-tab>
|
}}</v-tab>
|
||||||
</v-tabs>
|
</v-tabs>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
<v-tabs-items class="sub-item" v-model="tab">
|
<v-tabs-items class="sub-item pt-1" v-model="tab">
|
||||||
<v-tab-item>
|
<v-tab-item>
|
||||||
<Adapter></Adapter>
|
<Adapter></Adapter>
|
||||||
</v-tab-item>
|
</v-tab-item>
|
||||||
@ -37,7 +37,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
Adapter,
|
Adapter,
|
||||||
Plugin,
|
Plugin,
|
||||||
Bot
|
Bot,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -45,12 +45,12 @@ export default {
|
|||||||
tabs: {
|
tabs: {
|
||||||
0: "协议",
|
0: "协议",
|
||||||
1: "插件",
|
1: "插件",
|
||||||
2: "机器人"
|
2: "机器人",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {},
|
computed: {},
|
||||||
methods: {}
|
methods: {},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1 +1,64 @@
|
|||||||
# 事件处理函数重载
|
# 事件处理函数重载
|
||||||
|
|
||||||
|
当我们在编写 `nonebot2` 应用时,常常会遇到这样一个问题:该怎么让同一类型的不同事件执行不同的响应逻辑?又或者如何让不同的 `adapter` 针对同一类型的事件作出不同响应?
|
||||||
|
|
||||||
|
针对这个问题, `nonebot2` 提供一个便捷而高效的解决方案:事件处理函数重载机制。简单地说,`handler` (事件处理函数) 会根据其参数的 `type hints` ([PEP484 类型标注](https://www.python.org/dev/peps/pep-0484/)) 来对相对应的 `adapter` 和 `Event` 进行响应,并且会忽略不符合其参数类型标注的情况。
|
||||||
|
|
||||||
|
必须要注意的是,该机制利用了 `inspect` 标准库获取到了事件处理函数的 `singnature` (签名) ,进一步获取到参数名称和类型标注。故而,我们在编写 `handler` 时,参数的名称和类型标注必须要符合 `T_Handler` 规定,详情可以参看 **指南** 中的[事件处理](../guide/creating-a-handler)。
|
||||||
|
|
||||||
|
::: tip 提示
|
||||||
|
|
||||||
|
如果想了解更多关于 `inspect` 标准库的信息,可以查看[官方文档](https://docs.python.org/zh-cn/3.9/library/inspect.html)。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
下面,我们会以 `CQHTTP` 中的 `群聊消息事件` 和 `私聊消息事件` 为例,对该机制的应用进行简单的介绍。
|
||||||
|
|
||||||
|
## 一个例子
|
||||||
|
|
||||||
|
首先,我们需要导入需要的方法、类型。
|
||||||
|
|
||||||
|
```python
|
||||||
|
from nonebot import on_command
|
||||||
|
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, PrivateMessageEvent
|
||||||
|
```
|
||||||
|
|
||||||
|
之后,我们可以注册一个 `Matcher` 来响应 `消息事件` 。
|
||||||
|
|
||||||
|
```python
|
||||||
|
matcher = on_command("testoverload")
|
||||||
|
```
|
||||||
|
|
||||||
|
最后, 我们编写不同的 `handler` 并编写不同的类型标注来实现事件处理函数重载:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@matcher.handle()
|
||||||
|
async def _(bot: Bot, event: GroupMessageEvent):
|
||||||
|
await matcher.send("群聊消息事件响应成功!")
|
||||||
|
|
||||||
|
|
||||||
|
@matcher.handle()
|
||||||
|
async def _(bot: Bot, event: PrivateMessageEvent):
|
||||||
|
await matcher.send("私聊消息事件响应成功!")
|
||||||
|
```
|
||||||
|
|
||||||
|
此时,我们可以在群聊或私聊中对我们的机器人发送 `testoverload` ,它会在不同的场景做出不同的应答。
|
||||||
|
|
||||||
|
这样一个简单的事件处理函数重载就完成了。
|
||||||
|
|
||||||
|
## 进阶
|
||||||
|
|
||||||
|
事件处理函数重载机制同样支持被 `matcher.got` 等装饰器装饰的函数。 例如:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@matcher.got("key1", prompt="群事件提问")
|
||||||
|
async def _(bot: Bot, event: GroupMessageEvent):
|
||||||
|
await matcher.send("群聊消息事件响应成功!")
|
||||||
|
|
||||||
|
|
||||||
|
@matcher.got("key2", prompt="私聊事件提问")
|
||||||
|
async def _(bot: Bot, event: PrivateMessageEvent):
|
||||||
|
await matcher.send("私聊消息事件响应成功!")
|
||||||
|
```
|
||||||
|
|
||||||
|
只有触发事件符合的函数才会触发装饰器。
|
||||||
|
@ -1,2 +1,90 @@
|
|||||||
# 权限控制
|
# 权限控制
|
||||||
|
|
||||||
|
**权限控制**是机器人在实际应用中需要解决的重点问题之一,`Nonebot` 提供了十分完善且灵活的权限控制机制—— `Permission` 机制。接下来我们将对这个机制进行简单的说明。
|
||||||
|
|
||||||
|
## 应用
|
||||||
|
|
||||||
|
如同 `Rule` 一样, `Permission` 可以在[注册事件响应器](../guide/creating-a-matcher)时添加 `permission` 参数来加以应用,这样 `Nonebot` 会在事件响应时检测事件主体的权限。下面我们以 `SUPERUSER` 为例,对该机制的应用做一下介绍。
|
||||||
|
|
||||||
|
```python
|
||||||
|
from nonebot.permission import SUPERUSER
|
||||||
|
from nonebot.adapters import Bot
|
||||||
|
from nonebot import on_command
|
||||||
|
|
||||||
|
matcher = on_command("测试超管", permission=SUPERUSER)
|
||||||
|
|
||||||
|
|
||||||
|
@matcher.handle()
|
||||||
|
async def _(bot: Bot):
|
||||||
|
await matcher.send("超管命令测试成功")
|
||||||
|
|
||||||
|
|
||||||
|
@matcher.got("key1", "超管提问")
|
||||||
|
async def _(bot: Bot, event: Event):
|
||||||
|
await matcher.send("超管命令got成功")
|
||||||
|
```
|
||||||
|
|
||||||
|
在这段代码中,我们事件响应器指定了 `SUPERUSER` 这样一个权限,那么机器人只会响应超级管理员的 `测试超管` 命令,并且会响应该超级管理员的连续对话。
|
||||||
|
|
||||||
|
::: tip 提示
|
||||||
|
|
||||||
|
在这里需要强调的是,`Permission` 与 `Rule` 的表现并不相同, `Rule` 只会在初次响应时生效,在余下的对话中并没有限制事件;但是 `Permission` 会持续生效,在连续对话中会一直对事件主体加以限制。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 进阶
|
||||||
|
|
||||||
|
`Permission` 除了可以在注册事件响应器时加以应用,还可以在编写事件处理函数 `handler` 时主动调用,我们可以利用这个特性在一个 `handler` 里对不同权限的事件主体进行区别响应,下面我们以 `CQHTTP` 中的 `GROUP_ADMIN` (普通管理员非群主)和 `GROUP_OWNER` 为例,说明下怎么进行主动调用。
|
||||||
|
|
||||||
|
```python
|
||||||
|
from nonebot import on_command
|
||||||
|
from nonebot.adapters.cqhttp import Bot
|
||||||
|
from nonebot.adapters.cqhttp import GroupMessageEvent
|
||||||
|
from nonebot.adapters.cqhttp import GROUP_ADMIN, GROUP_OWNER
|
||||||
|
|
||||||
|
matcher = on_command("测试权限")
|
||||||
|
|
||||||
|
@matcher.handle()
|
||||||
|
async def _(bot: Bot, event: GroupMessageEvent):
|
||||||
|
if await GROUP_ADMIN(bot, event):
|
||||||
|
await matcher.send("管理员测试成功")
|
||||||
|
elif await GROUP_OWNER(bot, event):
|
||||||
|
await matcher.send("群主测试成功")
|
||||||
|
else:
|
||||||
|
await matcher.send("群员测试成功")
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
在这段代码里,我们并没有对命令的权限指定,这个命令会响应所有在群聊中的 `测试权限` 命令,但是在 `handler` 里,我们对两个 `Permission` 进行主动调用,从而可以对不同的角色进行不同的响应。
|
||||||
|
|
||||||
|
## 自定义
|
||||||
|
|
||||||
|
如同 `Rule` 一样, `Permission` 也是由非负数个 `PermissionChecker` 组成的,但只需其中一个返回 `True` 时就会匹配成功。下面则是 `PermissionChecker` 和 `Permission` 示例:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from nonebot.adapters import Bot, Event
|
||||||
|
from nonebot.permission import Permission
|
||||||
|
|
||||||
|
async def async_checker(bot: Bot, event: Event) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def sync_checker(bot: Bot, event: Event) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def check(arg1, arg2):
|
||||||
|
|
||||||
|
async def _checker(bot: Bot, event: Event) -> bool:
|
||||||
|
return bool(arg1 + arg2)
|
||||||
|
|
||||||
|
return Permission(_checker)
|
||||||
|
```
|
||||||
|
|
||||||
|
`Permission` 和 `PermissionChecker` 之间可以使用 `或 |` 互相组合:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from nonebot.permission import Permission
|
||||||
|
|
||||||
|
Permission(async_checker1) | sync_checker | async_checker2
|
||||||
|
```
|
||||||
|
|
||||||
|
同样地,如果想用 `Permission(*checkers)` 包裹构造 `Permission` ,函数必须是异步的;但是在利用 `或 |` 符号连接构造时, `Nonebot` 会自动包裹同步函数为异步函数。
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
|
|
||||||
## 从 NoneBot v1 迁移
|
## 从 NoneBot v1 迁移
|
||||||
|
|
||||||
`APScheduler` 作为 `nonebot` v1 的可选依赖,为众多 bot 提供了方便的定时任务功能。`nonebot2` 已将 `APScheduler` 独立为 `nonebot_plugin_apscheduler` 插件,你可以在 [插件广场](https://v2.nonebot.dev/plugin-store.html) 中找到它。
|
`APScheduler` 作为 `nonebot` v1 的可选依赖,为众多 bot 提供了方便的定时任务功能。`nonebot2` 已将 `APScheduler` 独立为 `nonebot_plugin_apscheduler` 插件,你可以在 [商店](https://v2.nonebot.dev/store.html) 中找到它。
|
||||||
|
|
||||||
相比于 `nonebot` v1,`nonebot` v2只需要安装插件并修改 `scheduler` 的导入方式即可完成迁移。
|
相比于 `nonebot` v1,`nonebot` v2 只需要安装插件并修改 `scheduler` 的导入方式即可完成迁移。
|
||||||
|
|
||||||
## 安装插件
|
## 安装插件
|
||||||
|
|
||||||
|
@ -191,9 +191,6 @@ Adapter 类型
|
|||||||
* `api: str`: API 名称
|
* `api: str`: API 名称
|
||||||
|
|
||||||
|
|
||||||
* `self_id: Optional[str]`: 指定调用 API 的机器人
|
|
||||||
|
|
||||||
|
|
||||||
* `**data`: API 数据
|
* `**data`: API 数据
|
||||||
|
|
||||||
|
|
||||||
@ -256,7 +253,7 @@ await bot.send_msg(message="hello world")
|
|||||||
|
|
||||||
## _class_ `Message`
|
## _class_ `Message`
|
||||||
|
|
||||||
基类:`list`, `abc.ABC`
|
基类:`List`[`nonebot.adapters._base.T_MessageSegment`], `abc.ABC`
|
||||||
|
|
||||||
消息数组
|
消息数组
|
||||||
|
|
||||||
|
@ -312,12 +312,108 @@ CQHTTP 协议 Bot 适配。继承属性参考 [BaseBot](./#class-basebot) 。
|
|||||||
CQHTTP 协议 MessageSegment 适配。具体方法参考协议消息段类型或源码。
|
CQHTTP 协议 MessageSegment 适配。具体方法参考协议消息段类型或源码。
|
||||||
|
|
||||||
|
|
||||||
|
### `is_text()`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `anonymous(ignore_failure=None)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `at(user_id)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `contact(type_, id)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `contact_group(group_id)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `contact_user(user_id)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `dice()`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `face(id_)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `forward(id_)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `image(file, type_=None, cache=True, proxy=True, timeout=None)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `json(data)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `location(latitude, longitude, title=None, content=None)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `music(type_, id_)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `music_custom(url, audio, title, content=None, img_url=None)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `node(id_)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `node_custom(user_id, nickname, content)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `poke(type_, id_)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `record(file, magic=None, cache=None, proxy=None, timeout=None)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `reply(id_)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `rps()`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `shake()`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `share(url='', title='', content=None, image=None)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `text(text)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `video(file, cache=None, proxy=None, timeout=None)`
|
||||||
|
|
||||||
|
|
||||||
|
### _static_ `xml(data)`
|
||||||
|
|
||||||
|
|
||||||
|
### `type`
|
||||||
|
|
||||||
|
|
||||||
|
* 类型: `str`
|
||||||
|
|
||||||
|
|
||||||
|
* 说明: 消息段类型
|
||||||
|
|
||||||
|
|
||||||
|
### `data`
|
||||||
|
|
||||||
|
|
||||||
|
* 类型: `Dict[str, Union[str, list]]`
|
||||||
|
|
||||||
|
|
||||||
|
* 说明: 消息段数据
|
||||||
|
|
||||||
|
|
||||||
## _class_ `Message`
|
## _class_ `Message`
|
||||||
|
|
||||||
基类:[`nonebot.adapters._base.Message`](README.md#nonebot.adapters._base.Message)
|
基类:[`nonebot.adapters._base.Message`](README.md#nonebot.adapters._base.Message)[`nonebot.adapters.cqhttp.message.MessageSegment`]
|
||||||
|
|
||||||
CQHTTP 协议 Message 适配。
|
CQHTTP 协议 Message 适配。
|
||||||
|
|
||||||
|
|
||||||
|
### `extract_plain_text()`
|
||||||
|
|
||||||
# NoneBot.adapters.cqhttp.permission 模块
|
# NoneBot.adapters.cqhttp.permission 模块
|
||||||
|
|
||||||
|
|
||||||
|
@ -292,7 +292,7 @@ message += MessageSegment.atDingtalkIds(event.senderId)
|
|||||||
|
|
||||||
## _class_ `Message`
|
## _class_ `Message`
|
||||||
|
|
||||||
基类:[`nonebot.adapters._base.Message`](README.md#nonebot.adapters._base.Message)
|
基类:[`nonebot.adapters._base.Message`](README.md#nonebot.adapters._base.Message)[`nonebot.adapters.ding.message.MessageSegment`]
|
||||||
|
|
||||||
钉钉 协议 Message 适配。
|
钉钉 协议 Message 适配。
|
||||||
|
|
||||||
|
@ -963,7 +963,7 @@ Mirai-API-HTTP 协议 MessageSegment 适配。具体方法参考 [mirai-api-http
|
|||||||
|
|
||||||
## _class_ `MessageChain`
|
## _class_ `MessageChain`
|
||||||
|
|
||||||
基类:[`nonebot.adapters._base.Message`](README.md#nonebot.adapters._base.Message)
|
基类:[`nonebot.adapters._base.Message`](README.md#nonebot.adapters._base.Message)[`nonebot.adapters.mirai.message.MessageSegment`]
|
||||||
|
|
||||||
Mirai 协议 Message 适配
|
Mirai 协议 Message 适配
|
||||||
|
|
||||||
|
@ -106,6 +106,30 @@ NoneBot 主要配置。大小写不敏感。
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### `log_level`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**: `Union[int, str]`
|
||||||
|
|
||||||
|
|
||||||
|
* **默认值**: `None`
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
配置 NoneBot 日志输出等级,可以为 `int` 类型等级或等级名称,参考 [loguru 日志等级](https://loguru.readthedocs.io/en/stable/api/logger.html#levels)。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **示例**
|
||||||
|
|
||||||
|
|
||||||
|
```default
|
||||||
|
LOG_LEVEL=25
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### `api_root`
|
### `api_root`
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
基类:`abc.ABC`
|
基类:`abc.ABC`
|
||||||
|
|
||||||
Driver 基类。将后端框架封装,以满足适配器使用。
|
Driver 基类。
|
||||||
|
|
||||||
|
|
||||||
### `_adapters`
|
### `_adapters`
|
||||||
@ -32,33 +32,33 @@ Driver 基类。将后端框架封装,以满足适配器使用。
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
### `_ws_connection_hook`
|
### `_bot_connection_hook`
|
||||||
|
|
||||||
|
|
||||||
* **类型**
|
* **类型**
|
||||||
|
|
||||||
`Set[T_WebSocketConnectionHook]`
|
`Set[T_BotConnectionHook]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* **说明**
|
* **说明**
|
||||||
|
|
||||||
WebSocket 连接建立时执行的函数
|
Bot 连接建立时执行的函数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### `_ws_disconnection_hook`
|
### `_bot_disconnection_hook`
|
||||||
|
|
||||||
|
|
||||||
* **类型**
|
* **类型**
|
||||||
|
|
||||||
`Set[T_WebSocketDisconnectionHook]`
|
`Set[T_BotDisconnectionHook]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* **说明**
|
* **说明**
|
||||||
|
|
||||||
WebSocket 连接断开时执行的函数
|
Bot 连接断开时执行的函数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -120,6 +120,21 @@ Driver 基类。将后端框架封装,以满足适配器使用。
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### _property_ `bots`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`Dict[str, Bot]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
获取当前所有已连接的 Bot
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### `register_adapter(name, adapter, **kwargs)`
|
### `register_adapter(name, adapter, **kwargs)`
|
||||||
|
|
||||||
|
|
||||||
@ -144,33 +159,33 @@ Driver 基类。将后端框架封装,以满足适配器使用。
|
|||||||
驱动类型名称
|
驱动类型名称
|
||||||
|
|
||||||
|
|
||||||
### _abstract property_ `server_app`
|
|
||||||
|
|
||||||
驱动 APP 对象
|
|
||||||
|
|
||||||
|
|
||||||
### _abstract property_ `asgi`
|
|
||||||
|
|
||||||
驱动 ASGI 对象
|
|
||||||
|
|
||||||
|
|
||||||
### _abstract property_ `logger`
|
### _abstract property_ `logger`
|
||||||
|
|
||||||
驱动专属 logger 日志记录器
|
驱动专属 logger 日志记录器
|
||||||
|
|
||||||
|
|
||||||
### _property_ `bots`
|
### _abstract_ `run(host=None, port=None, *args, **kwargs)`
|
||||||
|
|
||||||
|
|
||||||
* **类型**
|
|
||||||
|
|
||||||
`Dict[str, Bot]`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* **说明**
|
* **说明**
|
||||||
|
|
||||||
获取当前所有已连接的 Bot
|
启动驱动框架
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **参数**
|
||||||
|
|
||||||
|
|
||||||
|
* `host: Optional[str]`: 驱动绑定 IP
|
||||||
|
|
||||||
|
|
||||||
|
* `post: Optional[int]`: 驱动绑定端口
|
||||||
|
|
||||||
|
|
||||||
|
* `*args`
|
||||||
|
|
||||||
|
|
||||||
|
* `**kwargs`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -226,41 +241,47 @@ Driver 基类。将后端框架封装,以满足适配器使用。
|
|||||||
在 WebSocket 连接断开后,调用该函数来注销 bot 对象
|
在 WebSocket 连接断开后,调用该函数来注销 bot 对象
|
||||||
|
|
||||||
|
|
||||||
### _abstract_ `run(host=None, port=None, *args, **kwargs)`
|
## _class_ `ReverseDriver`
|
||||||
|
|
||||||
|
基类:`nonebot.drivers.Driver`
|
||||||
|
|
||||||
|
Reverse Driver 基类。将后端框架封装,以满足适配器使用。
|
||||||
|
|
||||||
|
|
||||||
* **说明**
|
### _abstract property_ `server_app`
|
||||||
|
|
||||||
启动驱动框架
|
驱动 APP 对象
|
||||||
|
|
||||||
|
|
||||||
|
### _abstract property_ `asgi`
|
||||||
|
|
||||||
* **参数**
|
驱动 ASGI 对象
|
||||||
|
|
||||||
|
|
||||||
* `host: Optional[str]`: 驱动绑定 IP
|
### _abstract async_ `_handle_http(*args, **kwargs)`
|
||||||
|
|
||||||
|
|
||||||
* `post: Optional[int]`: 驱动绑定端口
|
|
||||||
|
|
||||||
|
|
||||||
* `*args`
|
|
||||||
|
|
||||||
|
|
||||||
* `**kwargs`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### _abstract async_ `_handle_http()`
|
|
||||||
|
|
||||||
用于处理 HTTP 类型请求的函数
|
用于处理 HTTP 类型请求的函数
|
||||||
|
|
||||||
|
|
||||||
### _abstract async_ `_handle_ws_reverse()`
|
### _abstract async_ `_handle_ws_reverse(*args, **kwargs)`
|
||||||
|
|
||||||
用于处理 WebSocket 类型请求的函数
|
用于处理 WebSocket 类型请求的函数
|
||||||
|
|
||||||
|
|
||||||
|
## _class_ `HTTPRequest`
|
||||||
|
|
||||||
|
基类:`object`
|
||||||
|
|
||||||
|
HTTP 请求封装。参考 [asgi http scope](https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope)。
|
||||||
|
|
||||||
|
|
||||||
|
## _class_ `HTTPResponse`
|
||||||
|
|
||||||
|
基类:`object`
|
||||||
|
|
||||||
|
HTTP 响应封装。参考 [asgi http scope](https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope)。
|
||||||
|
|
||||||
|
|
||||||
## _class_ `WebSocket`
|
## _class_ `WebSocket`
|
||||||
|
|
||||||
基类:`object`
|
基类:`object`
|
||||||
|
@ -79,7 +79,7 @@ FastAPI 驱动框架设置,详情参考 FastAPI 文档
|
|||||||
|
|
||||||
## _class_ `Driver`
|
## _class_ `Driver`
|
||||||
|
|
||||||
基类:[`nonebot.drivers.Driver`](README.md#nonebot.drivers.Driver)
|
基类:[`nonebot.drivers.ReverseDriver`](README.md#nonebot.drivers.ReverseDriver)
|
||||||
|
|
||||||
FastAPI 驱动框架
|
FastAPI 驱动框架
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
## _class_ `Driver`
|
## _class_ `Driver`
|
||||||
|
|
||||||
基类:[`nonebot.drivers.Driver`](README.md#nonebot.drivers.Driver)
|
基类:[`nonebot.drivers.ReverseDriver`](README.md#nonebot.drivers.ReverseDriver)
|
||||||
|
|
||||||
Quart 驱动框架
|
Quart 驱动框架
|
||||||
|
|
||||||
|
@ -35,6 +35,21 @@ sidebarDepth: 0
|
|||||||
### `module`
|
### `module`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`Optional[ModuleType]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
事件响应器所在模块
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### `plugin_name`
|
||||||
|
|
||||||
|
|
||||||
* **类型**
|
* **类型**
|
||||||
|
|
||||||
`Optional[str]`
|
`Optional[str]`
|
||||||
@ -43,7 +58,37 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
* **说明**
|
* **说明**
|
||||||
|
|
||||||
事件响应器所在模块名称
|
事件响应器所在插件名
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### `module_name`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`Optional[str]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
事件响应器所在模块名
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### `module_prefix`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`Optional[str]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
事件响应器所在模块前缀
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ sidebarDepth: 0
|
|||||||
* **说明**: 插件模块对象
|
* **说明**: 插件模块对象
|
||||||
|
|
||||||
|
|
||||||
### `export`
|
### _property_ `export`
|
||||||
|
|
||||||
|
|
||||||
* **类型**: `Export`
|
* **类型**: `Export`
|
||||||
@ -282,7 +282,7 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `on_startswith(msg, rule=None, **kwargs)`
|
## `on_startswith(msg, rule=None, ignorecase=False, **kwargs)`
|
||||||
|
|
||||||
|
|
||||||
* **说明**
|
* **说明**
|
||||||
@ -294,12 +294,15 @@ sidebarDepth: 0
|
|||||||
* **参数**
|
* **参数**
|
||||||
|
|
||||||
|
|
||||||
* `msg: str`: 指定消息开头内容
|
* `msg: Union[str, Tuple[str, ...]]`: 指定消息开头内容
|
||||||
|
|
||||||
|
|
||||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||||
|
|
||||||
|
|
||||||
|
* `ignorecase: bool`: 是否忽略大小写
|
||||||
|
|
||||||
|
|
||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
@ -329,7 +332,7 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `on_endswith(msg, rule=None, **kwargs)`
|
## `on_endswith(msg, rule=None, ignorecase=False, **kwargs)`
|
||||||
|
|
||||||
|
|
||||||
* **说明**
|
* **说明**
|
||||||
@ -341,12 +344,15 @@ sidebarDepth: 0
|
|||||||
* **参数**
|
* **参数**
|
||||||
|
|
||||||
|
|
||||||
* `msg: str`: 指定消息结尾内容
|
* `msg: Union[str, Tuple[str, ...]]`: 指定消息结尾内容
|
||||||
|
|
||||||
|
|
||||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||||
|
|
||||||
|
|
||||||
|
* `ignorecase: bool`: 是否忽略大小写
|
||||||
|
|
||||||
|
|
||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
@ -663,7 +669,7 @@ sidebarDepth: 0
|
|||||||
* `cmd: Union[str, Tuple[str, ...]]`: 命令前缀
|
* `cmd: Union[str, Tuple[str, ...]]`: 命令前缀
|
||||||
|
|
||||||
|
|
||||||
* `**kwargs`: 其他传递给 `on_command` 的参数,将会覆盖命令组默认值
|
* `**kwargs`: 其他传递给 `on_shell_command` 的参数,将会覆盖命令组默认值
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -940,7 +946,10 @@ sidebarDepth: 0
|
|||||||
* **参数**
|
* **参数**
|
||||||
|
|
||||||
|
|
||||||
* `msg: str`: 指定消息开头内容
|
* `msg: Union[str, Tuple[str, ...]]`: 指定消息开头内容
|
||||||
|
|
||||||
|
|
||||||
|
* `ignorecase: bool`: 是否忽略大小写
|
||||||
|
|
||||||
|
|
||||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||||
@ -987,7 +996,10 @@ sidebarDepth: 0
|
|||||||
* **参数**
|
* **参数**
|
||||||
|
|
||||||
|
|
||||||
* `msg: str`: 指定消息结尾内容
|
* `msg: Union[str, Tuple[str, ...]]`: 指定消息结尾内容
|
||||||
|
|
||||||
|
|
||||||
|
* `ignorecase: bool`: 是否忽略大小写
|
||||||
|
|
||||||
|
|
||||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||||
|
@ -91,7 +91,7 @@ Rule(async_function, run_sync(sync_function))
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `startswith(msg)`
|
## `startswith(msg, ignorecase=False)`
|
||||||
|
|
||||||
|
|
||||||
* **说明**
|
* **说明**
|
||||||
@ -107,7 +107,7 @@ Rule(async_function, run_sync(sync_function))
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `endswith(msg)`
|
## `endswith(msg, ignorecase=False)`
|
||||||
|
|
||||||
|
|
||||||
* **说明**
|
* **说明**
|
||||||
|
@ -46,7 +46,7 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `T_WebSocketConnectionHook`
|
## `T_BotConnectionHook`
|
||||||
|
|
||||||
|
|
||||||
* **类型**
|
* **类型**
|
||||||
@ -57,12 +57,12 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
* **说明**
|
* **说明**
|
||||||
|
|
||||||
WebSocket 连接建立时执行的函数
|
Bot 连接建立时执行的函数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `T_WebSocketDisconnectionHook`
|
## `T_BotDisconnectionHook`
|
||||||
|
|
||||||
|
|
||||||
* **类型**
|
* **类型**
|
||||||
@ -73,7 +73,7 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
* **说明**
|
* **说明**
|
||||||
|
|
||||||
WebSocket 连接断开时执行的函数
|
Bot 连接断开时执行的函数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,10 +13,10 @@ pip install nonebot-adapter-cqhttp
|
|||||||
QQ 协议端举例:
|
QQ 协议端举例:
|
||||||
|
|
||||||
- [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) (基于 [MiraiGo](https://github.com/Mrs4s/MiraiGo))
|
- [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) (基于 [MiraiGo](https://github.com/Mrs4s/MiraiGo))
|
||||||
- [cqhttp-mirai-embedded](https://github.com/yyuueexxiinngg/cqhttp-mirai/tree/embedded)
|
- [onebot-kotlin](https://github.com/yyuueexxiinngg/onebot-kotlin)
|
||||||
- [Mirai](https://github.com/mamoe/mirai) + [cqhttp-mirai](https://github.com/yyuueexxiinngg/cqhttp-mirai)
|
- [Mirai](https://github.com/mamoe/mirai) + [onebot-mirai](https://github.com/yyuueexxiinngg/onebot-kotlin)
|
||||||
- [Mirai](https://github.com/mamoe/mirai) + [Mirai Native](https://github.com/iTXTech/mirai-native) + [CQHTTP](https://github.com/richardchien/coolq-http-api)
|
- [Mirai](https://github.com/mamoe/mirai) + [Mirai Native](https://github.com/iTXTech/mirai-native) + [CQHTTP](https://github.com/richardchien/coolq-http-api)
|
||||||
- [OICQ-http-api](https://github.com/takayama-lily/onebot) (基于 [OICQ](https://github.com/takayama-lily/oicq))
|
- [node-onebot](https://github.com/takayama-lily/node-onebot) (基于 [abot](https://github.com/takayama-lily/abot), [OICQ](https://github.com/takayama-lily/oicq))
|
||||||
|
|
||||||
这里以 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 为例
|
这里以 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 为例
|
||||||
|
|
||||||
@ -24,59 +24,74 @@ QQ 协议端举例:
|
|||||||
2. 运行 exe 文件或者使用 `./go-cqhttp` 启动
|
2. 运行 exe 文件或者使用 `./go-cqhttp` 启动
|
||||||
3. 生成默认配置文件并修改默认配置
|
3. 生成默认配置文件并修改默认配置
|
||||||
|
|
||||||
```hjson{2,3,35-36,42}
|
```yml{2,3,18,57,58}
|
||||||
{
|
account:
|
||||||
uin: 机器人QQ号
|
uin: 机器人QQ号
|
||||||
password: 机器人密码
|
password: "机器人密码"
|
||||||
encrypt_password: false
|
encrypt: false
|
||||||
password_encrypted: ""
|
relogin:
|
||||||
enable_db: true
|
disabled: false
|
||||||
access_token: ""
|
delay: 3
|
||||||
relogin: {
|
interval: 0
|
||||||
enabled: true
|
max-times: 0
|
||||||
relogin_delay: 3
|
|
||||||
max_relogin_times: 0
|
use-sso-address: true
|
||||||
}
|
|
||||||
_rate_limit: {
|
heartbeat:
|
||||||
|
disabled: false
|
||||||
|
interval: 5
|
||||||
|
|
||||||
|
message:
|
||||||
|
post-format: array
|
||||||
|
ignore-invalid-cqcode: false
|
||||||
|
force-fragment: false
|
||||||
|
fix-url: false
|
||||||
|
proxy-rewrite: ""
|
||||||
|
report-self-message: false
|
||||||
|
remove-reply-at: false
|
||||||
|
extra-reply-data: false
|
||||||
|
|
||||||
|
output:
|
||||||
|
log-level: warn
|
||||||
|
debug: false
|
||||||
|
|
||||||
|
default-middlewares: &default
|
||||||
|
access-token: ""
|
||||||
|
filter: ""
|
||||||
|
rate-limit:
|
||||||
enabled: false
|
enabled: false
|
||||||
frequency: 1
|
frequency: 1
|
||||||
bucket_size: 1
|
bucket: 1
|
||||||
}
|
|
||||||
ignore_invalid_cqcode: false
|
servers:
|
||||||
force_fragmented: false
|
- http:
|
||||||
heartbeat_interval: 0
|
disabled: true
|
||||||
http_config: {
|
host: 127.0.0.1
|
||||||
enabled: false
|
port: 5700
|
||||||
host: "0.0.0.0"
|
timeout: 5
|
||||||
port: 5700
|
middlewares:
|
||||||
timeout: 0
|
<<: *default
|
||||||
post_urls: {}
|
post:
|
||||||
}
|
|
||||||
ws_config: {
|
- ws:
|
||||||
enabled: false
|
disabled: true
|
||||||
host: "0.0.0.0"
|
host: 127.0.0.1
|
||||||
port: 6700
|
port: 6700
|
||||||
}
|
middlewares:
|
||||||
ws_reverse_servers: [
|
<<: *default
|
||||||
{
|
|
||||||
enabled: true
|
- ws-reverse:
|
||||||
reverse_url: ws://127.0.0.1:8080/cqhttp/ws
|
disabled: false
|
||||||
reverse_api_url: ws://you_websocket_api.server
|
universal: ws://127.0.0.1:8080/cqhttp/ws
|
||||||
reverse_event_url: ws://you_websocket_event.server
|
api: ws://your_websocket_api.server
|
||||||
reverse_reconnect_interval: 3000
|
event: ws://your_websocket_event.server
|
||||||
}
|
reconnect-interval: 3000
|
||||||
]
|
middlewares:
|
||||||
post_message_format: array
|
<<: *default
|
||||||
use_sso_address: false
|
|
||||||
debug: false
|
database:
|
||||||
log_level: ""
|
leveldb:
|
||||||
web_ui: {
|
enable: true
|
||||||
enabled: false
|
|
||||||
host: 127.0.0.1
|
|
||||||
web_ui_port: 9999
|
|
||||||
web_input: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
其中 `ws://127.0.0.1:8080/cqhttp/ws` 中的 `127.0.0.1` 和 `8080` 应分别对应 nonebot 配置的 HOST 和 PORT。
|
其中 `ws://127.0.0.1:8080/cqhttp/ws` 中的 `127.0.0.1` 和 `8080` 应分别对应 nonebot 配置的 HOST 和 PORT。
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
nb plugin new
|
nb plugin new
|
||||||
```
|
```
|
||||||
|
|
||||||
插件通常有两种形式,下面分别介绍
|
下面分别对两种通常的插件形式做具体介绍
|
||||||
|
|
||||||
## 单文件形式
|
## 单文件形式
|
||||||
|
|
||||||
|
@ -37,7 +37,8 @@ AweSome-Bot
|
|||||||
:::warning 提示
|
:::warning 提示
|
||||||
如果您使用如 `VSCode` / `PyCharm` 等 IDE 启动 nonebot,请检查 IDE 当前工作空间目录是否与当前侧边栏打开目录一致。
|
如果您使用如 `VSCode` / `PyCharm` 等 IDE 启动 nonebot,请检查 IDE 当前工作空间目录是否与当前侧边栏打开目录一致。
|
||||||
|
|
||||||
* 在二者不一致的环境下可能导致 nonebot 读取配置文件和插件等不符合预期
|
- 注意:在二者不一致的环境下可能导致 nonebot 读取配置文件和插件等不符合预期
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
通过 `nb-cli`
|
通过 `nb-cli`
|
||||||
|
@ -103,15 +103,15 @@ async def raw_handler(bot: DingBot, event: MessageEvent):
|
|||||||
|
|
||||||
![机器人所在群的 Webhook 地址](./images/ding/webhook.png)
|
![机器人所在群的 Webhook 地址](./images/ding/webhook.png)
|
||||||
|
|
||||||
获取到Webhook地址后,用户可以向这个地址发起HTTP POST 请求,即可实现给该钉钉群发送消息。
|
获取到 Webhook 地址后,用户可以向这个地址发起 HTTP POST 请求,即可实现给该钉钉群发送消息。
|
||||||
|
|
||||||
对于这种通过 Webhook 推送的消息,钉钉需要开发者进行安全方面的设置(目前有3种安全设置方式,请根据需要选择一种),如下:
|
对于这种通过 Webhook 推送的消息,钉钉需要开发者进行安全方面的设置(目前有 3 种安全设置方式,请根据需要选择一种),如下:
|
||||||
|
|
||||||
1. **自定义关键词:** 最多可以设置10个关键词,消息中至少包含其中1个关键词才可以发送成功。
|
1. **自定义关键词:** 最多可以设置 10 个关键词,消息中至少包含其中 1 个关键词才可以发送成功。
|
||||||
例如添加了一个自定义关键词:监控报警,则这个机器人所发送的消息,必须包含监控报警这个词,才能发送成功。
|
例如添加了一个自定义关键词:监控报警,则这个机器人所发送的消息,必须包含监控报警这个词,才能发送成功。
|
||||||
2. **加签:** 发送请求时带上验签的值,可以在机器人设置里看到密钥。
|
2. **加签:** 发送请求时带上验签的值,可以在机器人设置里看到密钥。
|
||||||
![加签密钥](./images/ding/jiaqian.png)
|
![加签密钥](./images/ding/jiaqian.png)
|
||||||
3. **IP地址(段):** 设定后,只有来自IP地址范围内的请求才会被正常处理。支持两种设置方式:IP地址和IP地址段,暂不支持IPv6地址白名单。
|
3. **IP 地址(段):** 设定后,只有来自 IP 地址范围内的请求才会被正常处理。支持两种设置方式:IP 地址和 IP 地址段,暂不支持 IPv6 地址白名单。
|
||||||
|
|
||||||
如果你选择 1/3 两种安全设置,你需要自己确认当前网络和发送的消息能被钉钉接受,然后使用 `bot.send` 的时候将 webhook 地址传入 webhook 参数即可。
|
如果你选择 1/3 两种安全设置,你需要自己确认当前网络和发送的消息能被钉钉接受,然后使用 `bot.send` 的时候将 webhook 地址传入 webhook 参数即可。
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ pip install . # 不推荐
|
|||||||
适配器可以通过 `nb-cli` 在创建项目时根据你的选择自动安装,也可以自行使用 `pip` 安装
|
适配器可以通过 `nb-cli` 在创建项目时根据你的选择自动安装,也可以自行使用 `pip` 安装
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install nonebot-adapter-<adapter-name>
|
pip install <adapter-name>
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -88,9 +88,9 @@ nb adapter list
|
|||||||
# 列出所有的插件
|
# 列出所有的插件
|
||||||
nb plugin list
|
nb plugin list
|
||||||
# 搜索插件
|
# 搜索插件
|
||||||
nb plugin search xxx
|
nb plugin search <plugin-name>
|
||||||
# 安装插件
|
# 安装插件
|
||||||
nb plugin install xxx
|
nb plugin install <plugin-name>
|
||||||
```
|
```
|
||||||
|
|
||||||
如果急于上线 Bot 或想要使用现成的插件,以下插件可作为参考:
|
如果急于上线 Bot 或想要使用现成的插件,以下插件可作为参考:
|
||||||
@ -106,4 +106,10 @@ nb plugin install xxx
|
|||||||
|
|
||||||
### 其他插件
|
### 其他插件
|
||||||
|
|
||||||
还有更多的插件在 [这里](/plugin-store.md) 等着你发现~
|
还有更多的插件在 [这里](/store.html) 等着你发现~
|
||||||
|
|
||||||
|
## 安装开发环境(可选)
|
||||||
|
|
||||||
|
NoneBot v2 全程使用 `VSCode` 搭配 `Pylance` 的开发环境进行开发,在严格的类型检查下,NoneBot v2 具有完善的类型设计与声明。
|
||||||
|
|
||||||
|
在围绕 NoneBot v2 进行开发时,使用 `VSCode` 搭配 `Pylance` 进行类型检查是非常推荐的。这有利于统一代码风格及避免低级错误的发生。
|
||||||
|
@ -39,10 +39,11 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
在 `bot.py` 文件中添加以下行:
|
在 `bot.py` 文件中添加以下行:
|
||||||
|
|
||||||
```python{5}
|
```python{6}
|
||||||
import nonebot
|
import nonebot
|
||||||
|
|
||||||
nonebot.init()
|
nonebot.init()
|
||||||
|
|
||||||
# 加载插件目录,该目录下为各插件,以下划线开头的插件将不会被加载
|
# 加载插件目录,该目录下为各插件,以下划线开头的插件将不会被加载
|
||||||
nonebot.load_plugins("awesome_bot/plugins")
|
nonebot.load_plugins("awesome_bot/plugins")
|
||||||
|
|
||||||
@ -68,10 +69,11 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
在 `bot.py` 文件中添加以下行:
|
在 `bot.py` 文件中添加以下行:
|
||||||
|
|
||||||
```python{5,7}
|
```python{6,8}
|
||||||
import nonebot
|
import nonebot
|
||||||
|
|
||||||
nonebot.init()
|
nonebot.init()
|
||||||
|
|
||||||
# 加载一个 pip 安装的插件
|
# 加载一个 pip 安装的插件
|
||||||
nonebot.load_plugin("nonebot_plugin_status")
|
nonebot.load_plugin("nonebot_plugin_status")
|
||||||
# 加载本地的单独插件
|
# 加载本地的单独插件
|
||||||
@ -83,6 +85,63 @@ if __name__ == "__main__":
|
|||||||
nonebot.run()
|
nonebot.run()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 从 json 文件中加载插件
|
||||||
|
|
||||||
|
在 `bot.py` 文件中添加以下行:
|
||||||
|
|
||||||
|
```python{6}
|
||||||
|
import nonebot
|
||||||
|
|
||||||
|
nonebot.init()
|
||||||
|
|
||||||
|
# 从 plugin.json 加载插件
|
||||||
|
nonebot.load_from_json("plugin.json")
|
||||||
|
|
||||||
|
app = nonebot.get_asgi()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
nonebot.run()
|
||||||
|
```
|
||||||
|
|
||||||
|
**json 文件示例**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"plugins": ["nonebot_plugin_status", "awesome_bot.plugins.xxx"],
|
||||||
|
"plugin_dirs": ["awesome_bot/plugins"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 从 toml 文件中加载插件
|
||||||
|
|
||||||
|
在 `bot.py` 文件中添加以下行:
|
||||||
|
|
||||||
|
```python{6}
|
||||||
|
import nonebot
|
||||||
|
|
||||||
|
nonebot.init()
|
||||||
|
|
||||||
|
# 从 pyproject.toml 加载插件
|
||||||
|
nonebot.load_from_toml("pyproject.toml")
|
||||||
|
|
||||||
|
app = nonebot.get_asgi()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
nonebot.run()
|
||||||
|
```
|
||||||
|
|
||||||
|
**toml 文件示例:**
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[nonebot.plugins]
|
||||||
|
plugins = ["nonebot_plugin_status", "awesome_bot.plugins.xxx"]
|
||||||
|
plugin_dirs = ["awesome_bot/plugins"]
|
||||||
|
```
|
||||||
|
|
||||||
|
::: tip
|
||||||
|
nb-cli 默认使用 `pyproject.toml` 加载插件。
|
||||||
|
:::
|
||||||
|
|
||||||
## 子插件(嵌套插件)
|
## 子插件(嵌套插件)
|
||||||
|
|
||||||
在插件中同样可以加载子插件,例如如下插件目录结构:
|
在插件中同样可以加载子插件,例如如下插件目录结构:
|
||||||
|
@ -67,6 +67,12 @@ pip install nonebot-adapter-mirai
|
|||||||
|
|
||||||
4. 修改配置文件
|
4. 修改配置文件
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
|
||||||
|
由于NoneBot2的架构设计等原因, 部分功能的支持可能需要推迟到MAH 2.0正式发布后再完成
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
::: tip
|
::: tip
|
||||||
|
|
||||||
在此之前, 你可能需要了解我们为 MAH 设计的两种通信方式
|
在此之前, 你可能需要了解我们为 MAH 设计的两种通信方式
|
||||||
@ -92,6 +98,13 @@ pip install nonebot-adapter-mirai
|
|||||||
|
|
||||||
- 这是当使用正向 Websocket 时的配置举例
|
- 这是当使用正向 Websocket 时的配置举例
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
|
||||||
|
在默认情况下, NoneBot和MAH会同时监听8080端口, 这会导致端口冲突的错误
|
||||||
|
请确保二者配置不在同一端口下
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
- MAH 的`setting.yml`文件
|
- MAH 的`setting.yml`文件
|
||||||
|
|
||||||
- ```yaml
|
- ```yaml
|
||||||
@ -106,9 +119,12 @@ pip install nonebot-adapter-mirai
|
|||||||
- `.env`文件
|
- `.env`文件
|
||||||
|
|
||||||
- ```shell
|
- ```shell
|
||||||
|
PORT=2333
|
||||||
|
|
||||||
MIRAI_AUTH_KEY=1234567890
|
MIRAI_AUTH_KEY=1234567890
|
||||||
MIRAI_HOST=127.0.0.1 # 当MAH运行在本机时
|
MIRAI_HOST=127.0.0.1 # 当MAH运行在本机时
|
||||||
MIRAI_PORT=8080 # MAH的监听端口
|
MIRAI_PORT=8080 # MAH的监听端口
|
||||||
|
PORT=2333 # 防止与MAH接口冲突
|
||||||
```
|
```
|
||||||
|
|
||||||
- `bot.py`文件
|
- `bot.py`文件
|
||||||
@ -155,7 +171,7 @@ pip install nonebot-adapter-mirai
|
|||||||
|
|
||||||
- ```shell
|
- ```shell
|
||||||
HOST=127.0.0.1 # 当MAH运行在本机时
|
HOST=127.0.0.1 # 当MAH运行在本机时
|
||||||
PORT=2333
|
PORT=2333 # 防止与MAH接口冲突
|
||||||
|
|
||||||
MIRAI_AUTH_KEY=1234567890
|
MIRAI_AUTH_KEY=1234567890
|
||||||
MIRAI_HOST=127.0.0.1 # 当MAH运行在本机时
|
MIRAI_HOST=127.0.0.1 # 当MAH运行在本机时
|
||||||
|
@ -41,7 +41,7 @@ NoneBot.adapters.cqhttp.message 模块
|
|||||||
|
|
||||||
.. automodule:: nonebot.adapters.cqhttp.message
|
.. automodule:: nonebot.adapters.cqhttp.message
|
||||||
:members:
|
:members:
|
||||||
:private-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
NoneBot.adapters.cqhttp.permission 模块
|
NoneBot.adapters.cqhttp.permission 模块
|
||||||
|
@ -179,15 +179,17 @@ def init(*, _env_file: Optional[str] = None, **kwargs):
|
|||||||
"""
|
"""
|
||||||
global _driver
|
global _driver
|
||||||
if not _driver:
|
if not _driver:
|
||||||
logger.info("NoneBot is initializing...")
|
logger.success("NoneBot is initializing...")
|
||||||
env = Env()
|
env = Env()
|
||||||
logger.opt(
|
|
||||||
colors=True).info(f"Current <y><b>Env: {env.environment}</b></y>")
|
|
||||||
config = Config(**kwargs,
|
config = Config(**kwargs,
|
||||||
_common_config=env.dict(),
|
_common_config=env.dict(),
|
||||||
_env_file=_env_file or f".env.{env.environment}")
|
_env_file=_env_file or f".env.{env.environment}")
|
||||||
|
|
||||||
default_filter.level = "DEBUG" if config.debug else "INFO"
|
default_filter.level = (
|
||||||
|
"DEBUG" if config.debug else
|
||||||
|
"INFO") if config.log_level is None else config.log_level
|
||||||
|
logger.opt(
|
||||||
|
colors=True).info(f"Current <y><b>Env: {env.environment}</b></y>")
|
||||||
logger.opt(colors=True).debug(
|
logger.opt(colors=True).debug(
|
||||||
f"Loaded <y><b>Config</b></y>: {escape_tag(str(config.dict()))}")
|
f"Loaded <y><b>Config</b></y>: {escape_tag(str(config.dict()))}")
|
||||||
|
|
||||||
@ -223,7 +225,7 @@ def run(host: Optional[str] = None,
|
|||||||
nonebot.run(host="127.0.0.1", port=8080)
|
nonebot.run(host="127.0.0.1", port=8080)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
logger.info("Running NoneBot...")
|
logger.success("Running NoneBot...")
|
||||||
get_driver().run(host, port, *args, **kwargs)
|
get_driver().run(host, port, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@ from copy import copy
|
|||||||
from functools import reduce, partial
|
from functools import reduce, partial
|
||||||
from typing_extensions import Protocol
|
from typing_extensions import Protocol
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import (Any, Set, Dict, Union, TypeVar, Mapping, Optional, Iterable,
|
from typing import (Any, Set, List, Dict, Union, TypeVar, Mapping, Optional,
|
||||||
Awaitable, TYPE_CHECKING)
|
Iterable, Awaitable, TYPE_CHECKING)
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
@ -152,7 +152,6 @@ class Bot(abc.ABC):
|
|||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``api: str``: API 名称
|
* ``api: str``: API 名称
|
||||||
* ``self_id: Optional[str]``: 指定调用 API 的机器人
|
|
||||||
* ``**data``: API 数据
|
* ``**data``: API 数据
|
||||||
|
|
||||||
:示例:
|
:示例:
|
||||||
@ -176,11 +175,7 @@ class Bot(abc.ABC):
|
|||||||
result = None
|
result = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if "self_id" in data and data["self_id"]:
|
result = await self._call_api(api, **data)
|
||||||
bot = self.driver.bots[str(data["self_id"])]
|
|
||||||
result = await bot._call_api(api, **data)
|
|
||||||
else:
|
|
||||||
result = await self._call_api(api, **data)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
exception = e
|
exception = e
|
||||||
|
|
||||||
@ -316,7 +311,7 @@ class MessageSegment(abc.ABC, Mapping):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class Message(list, abc.ABC):
|
class Message(List[T_MessageSegment], abc.ABC):
|
||||||
"""消息数组"""
|
"""消息数组"""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
|
@ -15,10 +15,9 @@ NoneBot 使用 `pydantic`_ 以及 `python-dotenv`_ 来读取配置。
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from ipaddress import IPv4Address
|
from ipaddress import IPv4Address
|
||||||
from typing import Any, Set, Dict, Tuple, Mapping, Optional
|
from typing import Any, Set, Dict, Union, Tuple, Mapping, Optional
|
||||||
|
|
||||||
from pydantic import BaseSettings, IPvAnyAddress
|
from pydantic import BaseSettings, IPvAnyAddress
|
||||||
from pydantic.env_settings import SettingsError, InitSettingsSource, EnvSettingsSource
|
from pydantic.env_settings import SettingsError, InitSettingsSource, EnvSettingsSource
|
||||||
@ -173,6 +172,25 @@ class Config(BaseConfig):
|
|||||||
|
|
||||||
是否以调试模式运行 NoneBot。
|
是否以调试模式运行 NoneBot。
|
||||||
"""
|
"""
|
||||||
|
log_level: Optional[Union[int, str]] = None
|
||||||
|
"""
|
||||||
|
- **类型**: ``Union[int, str]``
|
||||||
|
- **默认值**: ``None``
|
||||||
|
|
||||||
|
:说明:
|
||||||
|
|
||||||
|
配置 NoneBot 日志输出等级,可以为 ``int`` 类型等级或等级名称,参考 `loguru 日志等级`_。
|
||||||
|
|
||||||
|
:示例:
|
||||||
|
|
||||||
|
.. code-block:: default
|
||||||
|
|
||||||
|
LOG_LEVEL=25
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
|
||||||
|
.. _loguru 日志等级:
|
||||||
|
https://loguru.readthedocs.io/en/stable/api/logger.html#levels
|
||||||
|
"""
|
||||||
|
|
||||||
# bot connection configs
|
# bot connection configs
|
||||||
api_root: Dict[str, str] = {}
|
api_root: Dict[str, str] = {}
|
||||||
|
@ -7,11 +7,12 @@
|
|||||||
|
|
||||||
import abc
|
import abc
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import Set, Dict, Type, Optional, Callable, TYPE_CHECKING
|
from typing import (Any, Set, List, Dict, Type, Tuple, Optional, Callable,
|
||||||
|
MutableMapping, TYPE_CHECKING)
|
||||||
|
|
||||||
from nonebot.log import logger
|
from nonebot.log import logger
|
||||||
from nonebot.config import Env, Config
|
from nonebot.config import Env, Config
|
||||||
from nonebot.typing import T_WebSocketConnectionHook, T_WebSocketDisconnectionHook
|
from nonebot.typing import T_BotConnectionHook, T_BotDisconnectionHook
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from nonebot.adapters import Bot
|
from nonebot.adapters import Bot
|
||||||
@ -19,7 +20,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
class Driver(abc.ABC):
|
class Driver(abc.ABC):
|
||||||
"""
|
"""
|
||||||
Driver 基类。将后端框架封装,以满足适配器使用。
|
Driver 基类。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_adapters: Dict[str, Type["Bot"]] = {}
|
_adapters: Dict[str, Type["Bot"]] = {}
|
||||||
@ -27,15 +28,15 @@ class Driver(abc.ABC):
|
|||||||
:类型: ``Dict[str, Type[Bot]]``
|
:类型: ``Dict[str, Type[Bot]]``
|
||||||
:说明: 已注册的适配器列表
|
:说明: 已注册的适配器列表
|
||||||
"""
|
"""
|
||||||
_ws_connection_hook: Set[T_WebSocketConnectionHook] = set()
|
_bot_connection_hook: Set[T_BotConnectionHook] = set()
|
||||||
"""
|
"""
|
||||||
:类型: ``Set[T_WebSocketConnectionHook]``
|
:类型: ``Set[T_BotConnectionHook]``
|
||||||
:说明: WebSocket 连接建立时执行的函数
|
:说明: Bot 连接建立时执行的函数
|
||||||
"""
|
"""
|
||||||
_ws_disconnection_hook: Set[T_WebSocketDisconnectionHook] = set()
|
_bot_disconnection_hook: Set[T_BotDisconnectionHook] = set()
|
||||||
"""
|
"""
|
||||||
:类型: ``Set[T_WebSocketDisconnectionHook]``
|
:类型: ``Set[T_BotDisconnectionHook]``
|
||||||
:说明: WebSocket 连接断开时执行的函数
|
:说明: Bot 连接断开时执行的函数
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
@ -62,6 +63,18 @@ class Driver(abc.ABC):
|
|||||||
:说明: 已连接的 Bot
|
:说明: 已连接的 Bot
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bots(self) -> Dict[str, "Bot"]:
|
||||||
|
"""
|
||||||
|
:类型:
|
||||||
|
|
||||||
|
``Dict[str, Bot]``
|
||||||
|
:说明:
|
||||||
|
|
||||||
|
获取当前所有已连接的 Bot
|
||||||
|
"""
|
||||||
|
return self._clients
|
||||||
|
|
||||||
def register_adapter(self, name: str, adapter: Type["Bot"], **kwargs):
|
def register_adapter(self, name: str, adapter: Type["Bot"], **kwargs):
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
@ -88,108 +101,12 @@ class Driver(abc.ABC):
|
|||||||
"""驱动类型名称"""
|
"""驱动类型名称"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@property
|
|
||||||
@abc.abstractmethod
|
|
||||||
def server_app(self):
|
|
||||||
"""驱动 APP 对象"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@property
|
|
||||||
@abc.abstractmethod
|
|
||||||
def asgi(self):
|
|
||||||
"""驱动 ASGI 对象"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def logger(self):
|
def logger(self):
|
||||||
"""驱动专属 logger 日志记录器"""
|
"""驱动专属 logger 日志记录器"""
|
||||||
raise NotImplementedError
|
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
|
|
||||||
|
|
||||||
def on_bot_connect(
|
|
||||||
self, func: T_WebSocketConnectionHook) -> T_WebSocketConnectionHook:
|
|
||||||
"""
|
|
||||||
:说明:
|
|
||||||
|
|
||||||
装饰一个函数使他在 bot 通过 WebSocket 连接成功时执行。
|
|
||||||
|
|
||||||
:函数参数:
|
|
||||||
|
|
||||||
* ``bot: Bot``: 当前连接上的 Bot 对象
|
|
||||||
"""
|
|
||||||
self._ws_connection_hook.add(func)
|
|
||||||
return func
|
|
||||||
|
|
||||||
def on_bot_disconnect(
|
|
||||||
self,
|
|
||||||
func: T_WebSocketDisconnectionHook) -> T_WebSocketDisconnectionHook:
|
|
||||||
"""
|
|
||||||
:说明:
|
|
||||||
|
|
||||||
装饰一个函数使他在 bot 通过 WebSocket 连接断开时执行。
|
|
||||||
|
|
||||||
:函数参数:
|
|
||||||
|
|
||||||
* ``bot: Bot``: 当前连接上的 Bot 对象
|
|
||||||
"""
|
|
||||||
self._ws_disconnection_hook.add(func)
|
|
||||||
return func
|
|
||||||
|
|
||||||
def _bot_connect(self, bot: "Bot") -> None:
|
|
||||||
"""在 WebSocket 连接成功后,调用该函数来注册 bot 对象"""
|
|
||||||
self._clients[bot.self_id] = bot
|
|
||||||
|
|
||||||
async def _run_hook(bot: "Bot") -> None:
|
|
||||||
coros = list(map(lambda x: x(bot), self._ws_connection_hook))
|
|
||||||
if coros:
|
|
||||||
try:
|
|
||||||
await asyncio.gather(*coros)
|
|
||||||
except Exception as e:
|
|
||||||
logger.opt(colors=True, exception=e).error(
|
|
||||||
"<r><bg #f8bbd0>Error when running WebSocketConnection hook. "
|
|
||||||
"Running cancelled!</bg #f8bbd0></r>")
|
|
||||||
|
|
||||||
asyncio.create_task(_run_hook(bot))
|
|
||||||
|
|
||||||
def _bot_disconnect(self, bot: "Bot") -> None:
|
|
||||||
"""在 WebSocket 连接断开后,调用该函数来注销 bot 对象"""
|
|
||||||
if bot.self_id in self._clients:
|
|
||||||
del self._clients[bot.self_id]
|
|
||||||
|
|
||||||
async def _run_hook(bot: "Bot") -> None:
|
|
||||||
coros = list(map(lambda x: x(bot), self._ws_disconnection_hook))
|
|
||||||
if coros:
|
|
||||||
try:
|
|
||||||
await asyncio.gather(*coros)
|
|
||||||
except Exception as e:
|
|
||||||
logger.opt(colors=True, exception=e).error(
|
|
||||||
"<r><bg #f8bbd0>Error when running WebSocketDisConnection hook. "
|
|
||||||
"Running cancelled!</bg #f8bbd0></r>")
|
|
||||||
|
|
||||||
asyncio.create_task(_run_hook(bot))
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def run(self,
|
def run(self,
|
||||||
host: Optional[str] = None,
|
host: Optional[str] = None,
|
||||||
@ -212,17 +129,174 @@ class Driver(abc.ABC):
|
|||||||
f"<g>Loaded adapters: {', '.join(self._adapters)}</g>")
|
f"<g>Loaded adapters: {', '.join(self._adapters)}</g>")
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def _handle_http(self):
|
def on_startup(self, func: Callable) -> Callable:
|
||||||
|
"""注册一个在驱动启动时运行的函数"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def on_shutdown(self, func: Callable) -> Callable:
|
||||||
|
"""注册一个在驱动停止时运行的函数"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def on_bot_connect(self, func: T_BotConnectionHook) -> T_BotConnectionHook:
|
||||||
|
"""
|
||||||
|
:说明:
|
||||||
|
|
||||||
|
装饰一个函数使他在 bot 通过 WebSocket 连接成功时执行。
|
||||||
|
|
||||||
|
:函数参数:
|
||||||
|
|
||||||
|
* ``bot: Bot``: 当前连接上的 Bot 对象
|
||||||
|
"""
|
||||||
|
self._bot_connection_hook.add(func)
|
||||||
|
return func
|
||||||
|
|
||||||
|
def on_bot_disconnect(
|
||||||
|
self, func: T_BotDisconnectionHook) -> T_BotDisconnectionHook:
|
||||||
|
"""
|
||||||
|
:说明:
|
||||||
|
|
||||||
|
装饰一个函数使他在 bot 通过 WebSocket 连接断开时执行。
|
||||||
|
|
||||||
|
:函数参数:
|
||||||
|
|
||||||
|
* ``bot: Bot``: 当前连接上的 Bot 对象
|
||||||
|
"""
|
||||||
|
self._bot_disconnection_hook.add(func)
|
||||||
|
return func
|
||||||
|
|
||||||
|
def _bot_connect(self, bot: "Bot") -> None:
|
||||||
|
"""在 WebSocket 连接成功后,调用该函数来注册 bot 对象"""
|
||||||
|
self._clients[bot.self_id] = bot
|
||||||
|
|
||||||
|
async def _run_hook(bot: "Bot") -> None:
|
||||||
|
coros = list(map(lambda x: x(bot), self._bot_connection_hook))
|
||||||
|
if coros:
|
||||||
|
try:
|
||||||
|
await asyncio.gather(*coros)
|
||||||
|
except Exception as e:
|
||||||
|
logger.opt(colors=True, exception=e).error(
|
||||||
|
"<r><bg #f8bbd0>Error when running WebSocketConnection hook. "
|
||||||
|
"Running cancelled!</bg #f8bbd0></r>")
|
||||||
|
|
||||||
|
asyncio.create_task(_run_hook(bot))
|
||||||
|
|
||||||
|
def _bot_disconnect(self, bot: "Bot") -> None:
|
||||||
|
"""在 WebSocket 连接断开后,调用该函数来注销 bot 对象"""
|
||||||
|
if bot.self_id in self._clients:
|
||||||
|
del self._clients[bot.self_id]
|
||||||
|
|
||||||
|
async def _run_hook(bot: "Bot") -> None:
|
||||||
|
coros = list(map(lambda x: x(bot), self._bot_disconnection_hook))
|
||||||
|
if coros:
|
||||||
|
try:
|
||||||
|
await asyncio.gather(*coros)
|
||||||
|
except Exception as e:
|
||||||
|
logger.opt(colors=True, exception=e).error(
|
||||||
|
"<r><bg #f8bbd0>Error when running WebSocketDisConnection hook. "
|
||||||
|
"Running cancelled!</bg #f8bbd0></r>")
|
||||||
|
|
||||||
|
asyncio.create_task(_run_hook(bot))
|
||||||
|
|
||||||
|
|
||||||
|
class ForwardDriver(Driver):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ReverseDriver(Driver):
|
||||||
|
"""
|
||||||
|
Reverse Driver 基类。将后端框架封装,以满足适配器使用。
|
||||||
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
|
def server_app(self):
|
||||||
|
"""驱动 APP 对象"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
|
def asgi(self):
|
||||||
|
"""驱动 ASGI 对象"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
async def _handle_http(self, *args, **kwargs):
|
||||||
"""用于处理 HTTP 类型请求的函数"""
|
"""用于处理 HTTP 类型请求的函数"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def _handle_ws_reverse(self):
|
async def _handle_ws_reverse(self, *args, **kwargs):
|
||||||
"""用于处理 WebSocket 类型请求的函数"""
|
"""用于处理 WebSocket 类型请求的函数"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class WebSocket(object):
|
class HTTPRequest:
|
||||||
|
"""HTTP 请求封装。参考 `asgi http scope`_。
|
||||||
|
|
||||||
|
.. _asgi http scope:
|
||||||
|
https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, scope: MutableMapping[str, Any]):
|
||||||
|
self._scope = scope
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self) -> str:
|
||||||
|
return "http"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def scope(self) -> MutableMapping[str, Any]:
|
||||||
|
return self._scope
|
||||||
|
|
||||||
|
@property
|
||||||
|
def http_version(self) -> str:
|
||||||
|
raise self.scope["http_version"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def method(self) -> str:
|
||||||
|
raise self.scope["method"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def schema(self) -> str:
|
||||||
|
raise self.scope["schema"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def path(self) -> str:
|
||||||
|
return self.scope["path"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def query_string(self) -> bytes:
|
||||||
|
return self.scope["query_string"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def headers(self) -> List[Tuple[bytes, bytes]]:
|
||||||
|
return list(self.scope["headers"])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def body(self) -> bytes:
|
||||||
|
return self.scope["body"]
|
||||||
|
|
||||||
|
|
||||||
|
class HTTPResponse:
|
||||||
|
"""HTTP 响应封装。参考 `asgi http scope`_。
|
||||||
|
|
||||||
|
.. _asgi http scope:
|
||||||
|
https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, status: int, headers: List[Tuple[bytes, bytes]],
|
||||||
|
body: bytes):
|
||||||
|
self.status: int = status
|
||||||
|
self.headers: List[Tuple[bytes, bytes]] = headers
|
||||||
|
self.body: bytes = body
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self) -> str:
|
||||||
|
return "http"
|
||||||
|
|
||||||
|
|
||||||
|
class WebSocket:
|
||||||
"""WebSocket 连接封装,统一接口方便外部调用。"""
|
"""WebSocket 连接封装,统一接口方便外部调用。"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
|
@ -24,7 +24,7 @@ from nonebot.typing import overrides
|
|||||||
from nonebot.utils import DataclassEncoder
|
from nonebot.utils import DataclassEncoder
|
||||||
from nonebot.exception import RequestDenied
|
from nonebot.exception import RequestDenied
|
||||||
from nonebot.config import Env, Config as NoneBotConfig
|
from nonebot.config import Env, Config as NoneBotConfig
|
||||||
from nonebot.drivers import Driver as BaseDriver, WebSocket as BaseWebSocket
|
from nonebot.drivers import ReverseDriver, WebSocket as BaseWebSocket
|
||||||
|
|
||||||
|
|
||||||
class Config(BaseSettings):
|
class Config(BaseSettings):
|
||||||
@ -76,7 +76,7 @@ class Config(BaseSettings):
|
|||||||
extra = "ignore"
|
extra = "ignore"
|
||||||
|
|
||||||
|
|
||||||
class Driver(BaseDriver):
|
class Driver(ReverseDriver):
|
||||||
"""
|
"""
|
||||||
FastAPI 驱动框架
|
FastAPI 驱动框架
|
||||||
|
|
||||||
@ -106,40 +106,40 @@ class Driver(BaseDriver):
|
|||||||
self._server_app.websocket("/{adapter}/ws/")(self._handle_ws_reverse)
|
self._server_app.websocket("/{adapter}/ws/")(self._handle_ws_reverse)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
def type(self) -> str:
|
def type(self) -> str:
|
||||||
"""驱动名称: ``fastapi``"""
|
"""驱动名称: ``fastapi``"""
|
||||||
return "fastapi"
|
return "fastapi"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
def server_app(self) -> FastAPI:
|
def server_app(self) -> FastAPI:
|
||||||
"""``FastAPI APP`` 对象"""
|
"""``FastAPI APP`` 对象"""
|
||||||
return self._server_app
|
return self._server_app
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
def asgi(self):
|
def asgi(self):
|
||||||
"""``FastAPI APP`` 对象"""
|
"""``FastAPI APP`` 对象"""
|
||||||
return self._server_app
|
return self._server_app
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
def logger(self) -> logging.Logger:
|
def logger(self) -> logging.Logger:
|
||||||
"""fastapi 使用的 logger"""
|
"""fastapi 使用的 logger"""
|
||||||
return logging.getLogger("fastapi")
|
return logging.getLogger("fastapi")
|
||||||
|
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
def on_startup(self, func: Callable) -> Callable:
|
def on_startup(self, func: Callable) -> Callable:
|
||||||
"""参考文档: `Events <https://fastapi.tiangolo.com/advanced/events/#startup-event>`_"""
|
"""参考文档: `Events <https://fastapi.tiangolo.com/advanced/events/#startup-event>`_"""
|
||||||
return self.server_app.on_event("startup")(func)
|
return self.server_app.on_event("startup")(func)
|
||||||
|
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
def on_shutdown(self, func: Callable) -> Callable:
|
def on_shutdown(self, func: Callable) -> Callable:
|
||||||
"""参考文档: `Events <https://fastapi.tiangolo.com/advanced/events/#startup-event>`_"""
|
"""参考文档: `Events <https://fastapi.tiangolo.com/advanced/events/#startup-event>`_"""
|
||||||
return self.server_app.on_event("shutdown")(func)
|
return self.server_app.on_event("shutdown")(func)
|
||||||
|
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
def run(self,
|
def run(self,
|
||||||
host: Optional[str] = None,
|
host: Optional[str] = None,
|
||||||
port: Optional[int] = None,
|
port: Optional[int] = None,
|
||||||
@ -176,7 +176,7 @@ class Driver(BaseDriver):
|
|||||||
log_config=LOGGING_CONFIG,
|
log_config=LOGGING_CONFIG,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
async def _handle_http(self, adapter: str, request: Request):
|
async def _handle_http(self, adapter: str, request: Request):
|
||||||
data = await request.body()
|
data = await request.body()
|
||||||
data_dict = json.loads(data.decode())
|
data_dict = json.loads(data.decode())
|
||||||
@ -211,7 +211,7 @@ class Driver(BaseDriver):
|
|||||||
asyncio.create_task(bot.handle_message(data_dict))
|
asyncio.create_task(bot.handle_message(data_dict))
|
||||||
return Response("", 204)
|
return Response("", 204)
|
||||||
|
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
async def _handle_ws_reverse(self, adapter: str,
|
async def _handle_ws_reverse(self, adapter: str,
|
||||||
websocket: FastAPIWebSocket):
|
websocket: FastAPIWebSocket):
|
||||||
ws = WebSocket(websocket)
|
ws = WebSocket(websocket)
|
||||||
|
@ -16,8 +16,7 @@ import uvicorn
|
|||||||
|
|
||||||
from nonebot.config import Config as NoneBotConfig
|
from nonebot.config import Config as NoneBotConfig
|
||||||
from nonebot.config import Env
|
from nonebot.config import Env
|
||||||
from nonebot.drivers import Driver as BaseDriver
|
from nonebot.drivers import ReverseDriver, WebSocket as BaseWebSocket
|
||||||
from nonebot.drivers import WebSocket as BaseWebSocket
|
|
||||||
from nonebot.exception import RequestDenied
|
from nonebot.exception import RequestDenied
|
||||||
from nonebot.log import logger
|
from nonebot.log import logger
|
||||||
from nonebot.typing import overrides
|
from nonebot.typing import overrides
|
||||||
@ -35,7 +34,7 @@ except ImportError:
|
|||||||
_AsyncCallable = TypeVar("_AsyncCallable", bound=Callable[..., Coroutine])
|
_AsyncCallable = TypeVar("_AsyncCallable", bound=Callable[..., Coroutine])
|
||||||
|
|
||||||
|
|
||||||
class Driver(BaseDriver):
|
class Driver(ReverseDriver):
|
||||||
"""
|
"""
|
||||||
Quart 驱动框架
|
Quart 驱动框架
|
||||||
|
|
||||||
@ -45,7 +44,7 @@ class Driver(BaseDriver):
|
|||||||
* ``/{adapter name}/ws``: WebSocket 上报
|
* ``/{adapter name}/ws``: WebSocket 上报
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
def __init__(self, env: Env, config: NoneBotConfig):
|
def __init__(self, env: Env, config: NoneBotConfig):
|
||||||
super().__init__(env, config)
|
super().__init__(env, config)
|
||||||
|
|
||||||
@ -57,40 +56,40 @@ class Driver(BaseDriver):
|
|||||||
view_func=self._handle_ws_reverse)
|
view_func=self._handle_ws_reverse)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
def type(self) -> str:
|
def type(self) -> str:
|
||||||
"""驱动名称: ``quart``"""
|
"""驱动名称: ``quart``"""
|
||||||
return 'quart'
|
return 'quart'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
def server_app(self) -> Quart:
|
def server_app(self) -> Quart:
|
||||||
"""``Quart`` 对象"""
|
"""``Quart`` 对象"""
|
||||||
return self._server_app
|
return self._server_app
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
def asgi(self):
|
def asgi(self):
|
||||||
"""``Quart`` 对象"""
|
"""``Quart`` 对象"""
|
||||||
return self._server_app
|
return self._server_app
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
def logger(self):
|
def logger(self):
|
||||||
"""fastapi 使用的 logger"""
|
"""fastapi 使用的 logger"""
|
||||||
return self._server_app.logger
|
return self._server_app.logger
|
||||||
|
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
def on_startup(self, func: _AsyncCallable) -> _AsyncCallable:
|
def on_startup(self, func: _AsyncCallable) -> _AsyncCallable:
|
||||||
"""参考文档: `Startup and Shutdown <https://pgjones.gitlab.io/quart/how_to_guides/startup_shutdown.html>`_"""
|
"""参考文档: `Startup and Shutdown <https://pgjones.gitlab.io/quart/how_to_guides/startup_shutdown.html>`_"""
|
||||||
return self.server_app.before_serving(func) # type: ignore
|
return self.server_app.before_serving(func) # type: ignore
|
||||||
|
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
def on_shutdown(self, func: _AsyncCallable) -> _AsyncCallable:
|
def on_shutdown(self, func: _AsyncCallable) -> _AsyncCallable:
|
||||||
"""参考文档: `Startup and Shutdown <https://pgjones.gitlab.io/quart/how_to_guides/startup_shutdown.html>`_"""
|
"""参考文档: `Startup and Shutdown <https://pgjones.gitlab.io/quart/how_to_guides/startup_shutdown.html>`_"""
|
||||||
return self.server_app.after_serving(func) # type: ignore
|
return self.server_app.after_serving(func) # type: ignore
|
||||||
|
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
def run(self,
|
def run(self,
|
||||||
host: Optional[str] = None,
|
host: Optional[str] = None,
|
||||||
port: Optional[int] = None,
|
port: Optional[int] = None,
|
||||||
@ -126,7 +125,7 @@ class Driver(BaseDriver):
|
|||||||
log_config=LOGGING_CONFIG,
|
log_config=LOGGING_CONFIG,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
async def _handle_http(self, adapter: str):
|
async def _handle_http(self, adapter: str):
|
||||||
request: Request = _request
|
request: Request = _request
|
||||||
|
|
||||||
@ -157,7 +156,7 @@ class Driver(BaseDriver):
|
|||||||
asyncio.create_task(bot.handle_message(data))
|
asyncio.create_task(bot.handle_message(data))
|
||||||
return Response('', 204)
|
return Response('', 204)
|
||||||
|
|
||||||
@overrides(BaseDriver)
|
@overrides(ReverseDriver)
|
||||||
async def _handle_ws_reverse(self, adapter: str):
|
async def _handle_ws_reverse(self, adapter: str):
|
||||||
websocket: QuartWebSocket = _websocket
|
websocket: QuartWebSocket = _websocket
|
||||||
if adapter not in self._adapters:
|
if adapter not in self._adapters:
|
||||||
|
@ -12,11 +12,12 @@ NoneBot 使用 `loguru`_ 来记录日志信息。
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
from loguru import logger as logger_
|
import loguru
|
||||||
|
|
||||||
# logger = logging.getLogger("nonebot")
|
# logger = logging.getLogger("nonebot")
|
||||||
logger = logger_
|
logger: "loguru.Logger" = loguru.logger
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -44,11 +45,16 @@ logger = logger_
|
|||||||
class Filter:
|
class Filter:
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.level = "DEBUG"
|
self.level: Union[int, str] = "DEBUG"
|
||||||
|
|
||||||
def __call__(self, record):
|
def __call__(self, record):
|
||||||
|
module = sys.modules.get(record["name"])
|
||||||
|
if module:
|
||||||
|
plugin_name = getattr(module, "__plugin_name__", record["name"])
|
||||||
|
record["name"] = plugin_name
|
||||||
record["name"] = record["name"].split(".")[0]
|
record["name"] = record["name"].split(".")[0]
|
||||||
levelno = logger.level(self.level).no
|
levelno = logger.level(self.level).no if isinstance(self.level,
|
||||||
|
str) else self.level
|
||||||
return record["level"].no >= levelno
|
return record["level"].no >= levelno
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
from types import ModuleType
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from contextvars import ContextVar
|
from contextvars import ContextVar
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
@ -36,6 +37,9 @@ current_event: ContextVar = ContextVar("current_event")
|
|||||||
class MatcherMeta(type):
|
class MatcherMeta(type):
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
module: Optional[str]
|
module: Optional[str]
|
||||||
|
plugin_name: Optional[str]
|
||||||
|
module_name: Optional[str]
|
||||||
|
module_prefix: Optional[str]
|
||||||
type: str
|
type: str
|
||||||
rule: Rule
|
rule: Rule
|
||||||
permission: Permission
|
permission: Permission
|
||||||
@ -46,7 +50,7 @@ class MatcherMeta(type):
|
|||||||
expire_time: Optional[datetime]
|
expire_time: Optional[datetime]
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (f"<Matcher from {self.module or 'unknow'}, "
|
return (f"<Matcher from {self.module_name or 'unknown'}, "
|
||||||
f"type={self.type}, priority={self.priority}, "
|
f"type={self.type}, priority={self.priority}, "
|
||||||
f"temp={self.temp}>")
|
f"temp={self.temp}>")
|
||||||
|
|
||||||
@ -56,10 +60,28 @@ class MatcherMeta(type):
|
|||||||
|
|
||||||
class Matcher(metaclass=MatcherMeta):
|
class Matcher(metaclass=MatcherMeta):
|
||||||
"""事件响应器类"""
|
"""事件响应器类"""
|
||||||
module: Optional[str] = None
|
module: Optional[ModuleType] = None
|
||||||
|
"""
|
||||||
|
:类型: ``Optional[ModuleType]``
|
||||||
|
:说明: 事件响应器所在模块
|
||||||
|
"""
|
||||||
|
plugin_name: Optional[str] = module and getattr(module, "__plugin_name__",
|
||||||
|
None)
|
||||||
"""
|
"""
|
||||||
:类型: ``Optional[str]``
|
:类型: ``Optional[str]``
|
||||||
:说明: 事件响应器所在模块名称
|
:说明: 事件响应器所在插件名
|
||||||
|
"""
|
||||||
|
module_name: Optional[str] = module and getattr(module, "__module_name__",
|
||||||
|
None)
|
||||||
|
"""
|
||||||
|
:类型: ``Optional[str]``
|
||||||
|
:说明: 事件响应器所在模块名
|
||||||
|
"""
|
||||||
|
module_prefix: Optional[str] = module and getattr(module,
|
||||||
|
"__module_prefix__", None)
|
||||||
|
"""
|
||||||
|
:类型: ``Optional[str]``
|
||||||
|
:说明: 事件响应器所在模块前缀
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type: str = ""
|
type: str = ""
|
||||||
@ -136,8 +158,9 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
self.state = self._default_state.copy()
|
self.state = self._default_state.copy()
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (f"<Matcher from {self.module or 'unknown'}, type={self.type}, "
|
return (
|
||||||
f"priority={self.priority}, temp={self.temp}>")
|
f"<Matcher from {self.module_name or 'unknown'}, type={self.type}, "
|
||||||
|
f"priority={self.priority}, temp={self.temp}>")
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return repr(self)
|
return repr(self)
|
||||||
@ -153,7 +176,7 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
priority: int = 1,
|
priority: int = 1,
|
||||||
block: bool = False,
|
block: bool = False,
|
||||||
*,
|
*,
|
||||||
module: Optional[str] = None,
|
module: Optional[ModuleType] = None,
|
||||||
default_state: Optional[T_State] = None,
|
default_state: Optional[T_State] = None,
|
||||||
default_state_factory: Optional[T_StateFactory] = None,
|
default_state_factory: Optional[T_StateFactory] = None,
|
||||||
expire_time: Optional[datetime] = None) -> Type["Matcher"]:
|
expire_time: Optional[datetime] = None) -> Type["Matcher"]:
|
||||||
@ -185,6 +208,12 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
"Matcher", (Matcher,), {
|
"Matcher", (Matcher,), {
|
||||||
"module":
|
"module":
|
||||||
module,
|
module,
|
||||||
|
"plugin_name":
|
||||||
|
module and getattr(module, "__plugin_name__", None),
|
||||||
|
"module_name":
|
||||||
|
module and getattr(module, "__module_name__", None),
|
||||||
|
"module_prefix":
|
||||||
|
module and getattr(module, "__module_prefix__", None),
|
||||||
"type":
|
"type":
|
||||||
type_,
|
type_,
|
||||||
"rule":
|
"rule":
|
||||||
|
@ -7,7 +7,7 @@ NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Set, Type, TYPE_CHECKING
|
from typing import Set, Type, Optional, TYPE_CHECKING
|
||||||
|
|
||||||
from nonebot.log import logger
|
from nonebot.log import logger
|
||||||
from nonebot.rule import TrieRule
|
from nonebot.rule import TrieRule
|
||||||
@ -174,7 +174,7 @@ async def _run_matcher(Matcher: Type[Matcher], bot: "Bot", event: "Event",
|
|||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
async def handle_event(bot: "Bot", event: "Event"):
|
async def handle_event(bot: "Bot", event: "Event") -> Optional[Exception]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -199,7 +199,7 @@ async def handle_event(bot: "Bot", event: "Event"):
|
|||||||
except NoLogException:
|
except NoLogException:
|
||||||
show_log = False
|
show_log = False
|
||||||
if show_log:
|
if show_log:
|
||||||
logger.opt(colors=True).info(log_msg)
|
logger.opt(colors=True).success(log_msg)
|
||||||
|
|
||||||
state = {}
|
state = {}
|
||||||
coros = list(map(lambda x: x(bot, event, state), _event_preprocessors))
|
coros = list(map(lambda x: x(bot, event, state), _event_preprocessors))
|
||||||
@ -208,15 +208,15 @@ async def handle_event(bot: "Bot", event: "Event"):
|
|||||||
if show_log:
|
if show_log:
|
||||||
logger.debug("Running PreProcessors...")
|
logger.debug("Running PreProcessors...")
|
||||||
await asyncio.gather(*coros)
|
await asyncio.gather(*coros)
|
||||||
except IgnoredException:
|
except IgnoredException as e:
|
||||||
logger.opt(colors=True).info(
|
logger.opt(colors=True).info(
|
||||||
f"Event {event.get_event_name()} is <b>ignored</b>")
|
f"Event {event.get_event_name()} is <b>ignored</b>")
|
||||||
return
|
return e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.opt(colors=True, exception=e).error(
|
logger.opt(colors=True, exception=e).error(
|
||||||
"<r><bg #f8bbd0>Error when running EventPreProcessors. "
|
"<r><bg #f8bbd0>Error when running EventPreProcessors. "
|
||||||
"Event ignored!</bg #f8bbd0></r>")
|
"Event ignored!</bg #f8bbd0></r>")
|
||||||
return
|
return e
|
||||||
|
|
||||||
# Trie Match
|
# Trie Match
|
||||||
_, _ = TrieRule.get_value(bot, event, state)
|
_, _ = TrieRule.get_value(bot, event, state)
|
||||||
@ -237,14 +237,16 @@ async def handle_event(bot: "Bot", event: "Event"):
|
|||||||
results = await asyncio.gather(*pending_tasks, return_exceptions=True)
|
results = await asyncio.gather(*pending_tasks, return_exceptions=True)
|
||||||
|
|
||||||
for result in results:
|
for result in results:
|
||||||
|
if not isinstance(result, Exception):
|
||||||
|
continue
|
||||||
if isinstance(result, StopPropagation):
|
if isinstance(result, StopPropagation):
|
||||||
if not break_flag:
|
break_flag = True
|
||||||
break_flag = True
|
logger.debug("Stop event propagation")
|
||||||
logger.debug("Stop event propagation")
|
else:
|
||||||
elif isinstance(result, Exception):
|
|
||||||
logger.opt(colors=True, exception=result).error(
|
logger.opt(colors=True, exception=result).error(
|
||||||
"<r><bg #f8bbd0>Error when checking Matcher.</bg #f8bbd0></r>"
|
"<r><bg #f8bbd0>Error when checking Matcher.</bg #f8bbd0></r>"
|
||||||
)
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
coros = list(map(lambda x: x(bot, event, state), _event_postprocessors))
|
coros = list(map(lambda x: x(bot, event, state), _event_postprocessors))
|
||||||
if coros:
|
if coros:
|
||||||
@ -256,3 +258,4 @@ async def handle_event(bot: "Bot", event: "Event"):
|
|||||||
logger.opt(colors=True, exception=e).error(
|
logger.opt(colors=True, exception=e).error(
|
||||||
"<r><bg #f8bbd0>Error when running EventPostProcessors</bg #f8bbd0></r>"
|
"<r><bg #f8bbd0>Error when running EventPostProcessors</bg #f8bbd0></r>"
|
||||||
)
|
)
|
||||||
|
return e
|
||||||
|
@ -7,9 +7,10 @@
|
|||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
|
from functools import reduce
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from contextvars import Context, ContextVar, copy_context
|
from contextvars import Context, copy_context
|
||||||
from typing import Any, Set, List, Dict, Type, Tuple, Union, Optional, TYPE_CHECKING
|
from typing import Any, Set, List, Dict, Type, Tuple, Union, Optional, TYPE_CHECKING
|
||||||
|
|
||||||
import tomlkit
|
import tomlkit
|
||||||
@ -49,11 +50,14 @@ class Plugin(object):
|
|||||||
- **类型**: ``ModuleType``
|
- **类型**: ``ModuleType``
|
||||||
- **说明**: 插件模块对象
|
- **说明**: 插件模块对象
|
||||||
"""
|
"""
|
||||||
export: Export
|
|
||||||
"""
|
@property
|
||||||
- **类型**: ``Export``
|
def export(self) -> Export:
|
||||||
- **说明**: 插件内定义的导出内容
|
"""
|
||||||
"""
|
- **类型**: ``Export``
|
||||||
|
- **说明**: 插件内定义的导出内容
|
||||||
|
"""
|
||||||
|
return getattr(self.module, "__export__", Export())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def matcher(self) -> Set[Type[Matcher]]:
|
def matcher(self) -> Set[Type[Matcher]]:
|
||||||
@ -61,13 +65,16 @@ class Plugin(object):
|
|||||||
- **类型**: ``Set[Type[Matcher]]``
|
- **类型**: ``Set[Type[Matcher]]``
|
||||||
- **说明**: 插件内定义的 ``Matcher``
|
- **说明**: 插件内定义的 ``Matcher``
|
||||||
"""
|
"""
|
||||||
return _plugin_matchers[self.name]
|
# return reduce(
|
||||||
|
# lambda x, y: x | _plugin_matchers[y],
|
||||||
|
# filter(lambda x: x.startswith(self.name), _plugin_matchers.keys()),
|
||||||
|
# set())
|
||||||
|
return _plugin_matchers.get(self.name, set())
|
||||||
|
|
||||||
|
|
||||||
def _store_matcher(matcher: Type[Matcher]):
|
def _store_matcher(matcher: Type[Matcher]):
|
||||||
if matcher.module:
|
if matcher.plugin_name:
|
||||||
plugin_name = matcher.module.split(".", maxsplit=1)[0]
|
_plugin_matchers[matcher.plugin_name].add(matcher)
|
||||||
_plugin_matchers[plugin_name].add(matcher)
|
|
||||||
|
|
||||||
|
|
||||||
def on(type: str = "",
|
def on(type: str = "",
|
||||||
@ -282,8 +289,9 @@ def on_request(rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
|||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
|
|
||||||
def on_startswith(msg: str,
|
def on_startswith(msg: Union[str, Tuple[str, ...]],
|
||||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = None,
|
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = None,
|
||||||
|
ignorecase: bool = False,
|
||||||
**kwargs) -> Type[Matcher]:
|
**kwargs) -> Type[Matcher]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
@ -292,8 +300,9 @@ def on_startswith(msg: str,
|
|||||||
|
|
||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``msg: str``: 指定消息开头内容
|
* ``msg: Union[str, Tuple[str, ...]]``: 指定消息开头内容
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
|
* ``ignorecase: bool``: 是否忽略大小写
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -306,11 +315,12 @@ def on_startswith(msg: str,
|
|||||||
|
|
||||||
- ``Type[Matcher]``
|
- ``Type[Matcher]``
|
||||||
"""
|
"""
|
||||||
return on_message(startswith(msg) & rule, **kwargs)
|
return on_message(startswith(msg, ignorecase) & rule, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def on_endswith(msg: str,
|
def on_endswith(msg: Union[str, Tuple[str, ...]],
|
||||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = None,
|
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = None,
|
||||||
|
ignorecase: bool = False,
|
||||||
**kwargs) -> Type[Matcher]:
|
**kwargs) -> Type[Matcher]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
@ -319,8 +329,9 @@ def on_endswith(msg: str,
|
|||||||
|
|
||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``msg: str``: 指定消息结尾内容
|
* ``msg: Union[str, Tuple[str, ...]]``: 指定消息结尾内容
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
|
* ``ignorecase: bool``: 是否忽略大小写
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -333,7 +344,7 @@ def on_endswith(msg: str,
|
|||||||
|
|
||||||
- ``Type[Matcher]``
|
- ``Type[Matcher]``
|
||||||
"""
|
"""
|
||||||
return on_message(endswith(msg) & rule, **kwargs)
|
return on_message(endswith(msg, ignorecase) & rule, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def on_keyword(keywords: Set[str],
|
def on_keyword(keywords: Set[str],
|
||||||
@ -546,7 +557,7 @@ class CommandGroup:
|
|||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``cmd: Union[str, Tuple[str, ...]]``: 命令前缀
|
* ``cmd: Union[str, Tuple[str, ...]]``: 命令前缀
|
||||||
* ``**kwargs``: 其他传递给 ``on_command`` 的参数,将会覆盖命令组默认值
|
* ``**kwargs``: 其他传递给 ``on_shell_command`` 的参数,将会覆盖命令组默认值
|
||||||
|
|
||||||
:返回:
|
:返回:
|
||||||
|
|
||||||
@ -631,6 +642,7 @@ class MatcherGroup:
|
|||||||
final_kwargs = self.base_kwargs.copy()
|
final_kwargs = self.base_kwargs.copy()
|
||||||
final_kwargs.update(kwargs)
|
final_kwargs.update(kwargs)
|
||||||
final_kwargs.pop("type", None)
|
final_kwargs.pop("type", None)
|
||||||
|
final_kwargs.pop("permission", None)
|
||||||
matcher = on_metaevent(**final_kwargs)
|
matcher = on_metaevent(**final_kwargs)
|
||||||
self.matchers.append(matcher)
|
self.matchers.append(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
@ -717,7 +729,8 @@ class MatcherGroup:
|
|||||||
self.matchers.append(matcher)
|
self.matchers.append(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
def on_startswith(self, msg: str, **kwargs) -> Type[Matcher]:
|
def on_startswith(self, msg: Union[str, Tuple[str, ...]],
|
||||||
|
**kwargs) -> Type[Matcher]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -725,7 +738,8 @@ class MatcherGroup:
|
|||||||
|
|
||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``msg: str``: 指定消息开头内容
|
* ``msg: Union[str, Tuple[str, ...]]``: 指定消息开头内容
|
||||||
|
* ``ignorecase: bool``: 是否忽略大小写
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
@ -746,7 +760,8 @@ class MatcherGroup:
|
|||||||
self.matchers.append(matcher)
|
self.matchers.append(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
def on_endswith(self, msg: str, **kwargs) -> Type[Matcher]:
|
def on_endswith(self, msg: Union[str, Tuple[str, ...]],
|
||||||
|
**kwargs) -> Type[Matcher]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -754,7 +769,8 @@ class MatcherGroup:
|
|||||||
|
|
||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``msg: str``: 指定消息结尾内容
|
* ``msg: Union[str, Tuple[str, ...]]``: 指定消息结尾内容
|
||||||
|
* ``ignorecase: bool``: 是否忽略大小写
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
@ -928,11 +944,10 @@ def _load_plugin(manager: PluginManager, plugin_name: str) -> Optional[Plugin]:
|
|||||||
try:
|
try:
|
||||||
module = manager.load_plugin(plugin_name)
|
module = manager.load_plugin(plugin_name)
|
||||||
|
|
||||||
plugin = Plugin(plugin_name, module,
|
plugin = Plugin(plugin_name, module)
|
||||||
getattr(module, "__export__", Export()))
|
|
||||||
plugins[plugin_name] = plugin
|
plugins[plugin_name] = plugin
|
||||||
logger.opt(
|
logger.opt(
|
||||||
colors=True).info(f'Succeeded to import "<y>{plugin_name}</y>"')
|
colors=True).success(f'Succeeded to import "<y>{plugin_name}</y>"')
|
||||||
return plugin
|
return plugin
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.opt(colors=True, exception=e).error(
|
logger.opt(colors=True, exception=e).error(
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import re
|
import re
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from contextvars import ContextVar
|
from dataclasses import dataclass
|
||||||
from typing import Any, Set, List, Dict, Type, Tuple, Union, Optional
|
from typing import Set, List, Dict, Type, Tuple, Union, Optional, TYPE_CHECKING
|
||||||
|
|
||||||
from nonebot.matcher import Matcher
|
from nonebot.matcher import Matcher
|
||||||
from nonebot.handler import Handler
|
from nonebot.handler import Handler
|
||||||
@ -9,32 +9,28 @@ from nonebot.permission import Permission
|
|||||||
from nonebot.rule import Rule, ArgumentParser
|
from nonebot.rule import Rule, ArgumentParser
|
||||||
from nonebot.typing import T_State, T_StateFactory, T_Handler, T_RuleChecker
|
from nonebot.typing import T_State, T_StateFactory, T_Handler, T_RuleChecker
|
||||||
|
|
||||||
|
from .export import Export, export
|
||||||
|
from .manager import PluginManager
|
||||||
|
|
||||||
plugins: Dict[str, "Plugin"] = ...
|
plugins: Dict[str, "Plugin"] = ...
|
||||||
|
PLUGIN_NAMESPACE: str = ...
|
||||||
_export: ContextVar["Export"] = ...
|
|
||||||
_tmp_matchers: ContextVar[Set[Type[Matcher]]] = ...
|
|
||||||
|
|
||||||
|
|
||||||
class Export(dict):
|
|
||||||
|
|
||||||
def __call__(self, func, **kwargs):
|
|
||||||
...
|
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
|
||||||
...
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(eq=False)
|
||||||
class Plugin(object):
|
class Plugin(object):
|
||||||
name: str
|
name: str
|
||||||
module: ModuleType
|
module: ModuleType
|
||||||
matcher: Set[Type[Matcher]]
|
|
||||||
export: Export
|
@property
|
||||||
|
def export(self) -> Export:
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def matcher(self) -> Set[Type[Matcher]]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
def on(type: str = ...,
|
def on(type: str = "",
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
*,
|
*,
|
||||||
@ -94,8 +90,9 @@ def on_request(rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
|||||||
|
|
||||||
|
|
||||||
def on_startswith(
|
def on_startswith(
|
||||||
msg: str,
|
msg: Union[str, Tuple[str, ...]],
|
||||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
||||||
|
ignorecase: bool = ...,
|
||||||
*,
|
*,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
@ -107,8 +104,9 @@ def on_startswith(
|
|||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
def on_endswith(msg: str,
|
def on_endswith(msg: Union[str, Tuple[str, ...]],
|
||||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
||||||
|
ignorecase: bool = ...,
|
||||||
*,
|
*,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
@ -121,7 +119,7 @@ def on_endswith(msg: str,
|
|||||||
|
|
||||||
|
|
||||||
def on_keyword(keywords: Set[str],
|
def on_keyword(keywords: Set[str],
|
||||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
*,
|
*,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
@ -147,16 +145,24 @@ def on_command(cmd: Union[str, Tuple[str, ...]],
|
|||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
def on_shell_command(cmd: Union[str, Tuple[str, ...]],
|
def on_shell_command(
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
cmd: Union[str, Tuple[str, ...]],
|
||||||
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = None,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
parser: Optional[ArgumentParser] = None,
|
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ...,
|
||||||
**kwargs) -> Type[Matcher]:
|
parser: Optional[ArgumentParser] = ...,
|
||||||
|
*,
|
||||||
|
permission: Optional[Permission] = ...,
|
||||||
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
|
temp: bool = ...,
|
||||||
|
priority: int = ...,
|
||||||
|
block: bool = ...,
|
||||||
|
state: Optional[T_State] = ...,
|
||||||
|
state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
def on_regex(pattern: str,
|
def on_regex(pattern: str,
|
||||||
flags: Union[int, re.RegexFlag] = 0,
|
flags: Union[int, re.RegexFlag] = ...,
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
*,
|
*,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
@ -169,67 +175,26 @@ def on_regex(pattern: str,
|
|||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
def load_plugin(module_path: str) -> Optional[Plugin]:
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
def load_plugins(*plugin_dir: str) -> Set[Plugin]:
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
def load_all_plugins(module_path: Set[str],
|
|
||||||
plugin_dir: Set[str]) -> Set[Plugin]:
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
def load_from_json(file_path: str, encoding: str = ...) -> Set[Plugin]:
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
def load_from_toml(file_path: str, encoding: str = ...) -> Set[Plugin]:
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
def load_builtin_plugins(name: str = ...):
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
def get_plugin(name: str) -> Optional[Plugin]:
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
def get_loaded_plugins() -> Set[Plugin]:
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
def export() -> Export:
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
def require(name: str) -> Export:
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class CommandGroup:
|
class CommandGroup:
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
cmd: Union[str, Tuple[str, ...]],
|
cmd: Union[str, Tuple[str, ...]],
|
||||||
|
*,
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
*,
|
|
||||||
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
state: Optional[T_State] = ...):
|
state: Optional[T_State] = ...,
|
||||||
self.basecmd: Tuple[str, ...] = ...
|
state_factory: Optional[T_StateFactory] = ...):
|
||||||
self.base_kwargs: Dict[str, Any] = ...
|
...
|
||||||
|
|
||||||
def command(self,
|
def command(self,
|
||||||
cmd: Union[str, Tuple[str, ...]],
|
cmd: Union[str, Tuple[str, ...]],
|
||||||
*,
|
*,
|
||||||
|
aliases: Optional[Set[Union[str, Tuple[str, ...]]]],
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ...,
|
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
@ -244,7 +209,7 @@ class CommandGroup:
|
|||||||
cmd: Union[str, Tuple[str, ...]],
|
cmd: Union[str, Tuple[str, ...]],
|
||||||
*,
|
*,
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ...,
|
aliases: Optional[Set[Union[str, Tuple[str, ...]]]],
|
||||||
parser: Optional[ArgumentParser] = ...,
|
parser: Optional[ArgumentParser] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
@ -267,7 +232,8 @@ class MatcherGroup:
|
|||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
state: Optional[T_State] = ...):
|
state: Optional[T_State] = ...,
|
||||||
|
state_factory: Optional[T_StateFactory] = ...):
|
||||||
...
|
...
|
||||||
|
|
||||||
def on(self,
|
def on(self,
|
||||||
@ -286,57 +252,58 @@ class MatcherGroup:
|
|||||||
def on_metaevent(
|
def on_metaevent(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
handlers: Optional[List[Union[T_Handler, Handler]]] = None,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = False,
|
temp: bool = ...,
|
||||||
priority: int = 1,
|
priority: int = ...,
|
||||||
block: bool = False,
|
block: bool = ...,
|
||||||
state: Optional[T_State] = None,
|
state: Optional[T_State] = ...,
|
||||||
state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]:
|
state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]:
|
||||||
...
|
...
|
||||||
|
|
||||||
def on_message(
|
def on_message(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
permission: Optional[Permission] = None,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[Union[T_Handler, Handler]]] = None,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = False,
|
temp: bool = ...,
|
||||||
priority: int = 1,
|
priority: int = ...,
|
||||||
block: bool = True,
|
block: bool = ...,
|
||||||
state: Optional[T_State] = None,
|
state: Optional[T_State] = ...,
|
||||||
state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]:
|
state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]:
|
||||||
...
|
...
|
||||||
|
|
||||||
def on_notice(
|
def on_notice(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
handlers: Optional[List[Union[T_Handler, Handler]]] = None,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = False,
|
temp: bool = ...,
|
||||||
priority: int = 1,
|
priority: int = ...,
|
||||||
block: bool = False,
|
block: bool = ...,
|
||||||
state: Optional[T_State] = None,
|
state: Optional[T_State] = ...,
|
||||||
state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]:
|
state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]:
|
||||||
...
|
...
|
||||||
|
|
||||||
def on_request(
|
def on_request(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
handlers: Optional[List[Union[T_Handler, Handler]]] = None,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = False,
|
temp: bool = ...,
|
||||||
priority: int = 1,
|
priority: int = ...,
|
||||||
block: bool = False,
|
block: bool = ...,
|
||||||
state: Optional[T_State] = None,
|
state: Optional[T_State] = ...,
|
||||||
state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]:
|
state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]:
|
||||||
...
|
...
|
||||||
|
|
||||||
def on_startswith(
|
def on_startswith(
|
||||||
self,
|
self,
|
||||||
msg: str,
|
msg: Union[str, Tuple[str, ...]],
|
||||||
*,
|
*,
|
||||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
ignorecase: bool = ...,
|
||||||
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
@ -348,9 +315,10 @@ class MatcherGroup:
|
|||||||
|
|
||||||
def on_endswith(
|
def on_endswith(
|
||||||
self,
|
self,
|
||||||
msg: str,
|
msg: Union[str, Tuple[str, ...]],
|
||||||
*,
|
*,
|
||||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
ignorecase: bool = ...,
|
||||||
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
@ -364,7 +332,7 @@ class MatcherGroup:
|
|||||||
self,
|
self,
|
||||||
keywords: Set[str],
|
keywords: Set[str],
|
||||||
*,
|
*,
|
||||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
@ -408,7 +376,7 @@ class MatcherGroup:
|
|||||||
def on_regex(
|
def on_regex(
|
||||||
self,
|
self,
|
||||||
pattern: str,
|
pattern: str,
|
||||||
flags: Union[int, re.RegexFlag] = 0,
|
flags: Union[int, re.RegexFlag] = ...,
|
||||||
*,
|
*,
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
@ -419,3 +387,40 @@ class MatcherGroup:
|
|||||||
state: Optional[T_State] = ...,
|
state: Optional[T_State] = ...,
|
||||||
state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]:
|
state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def load_plugin(module_path: str) -> Optional[Plugin]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def load_plugins(*plugin_dir: str) -> Set[Plugin]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def load_all_plugins(module_path: Set[str],
|
||||||
|
plugin_dir: Set[str]) -> Set[Plugin]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def load_from_json(file_path: str, encoding: str = ...) -> Set[Plugin]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def load_from_toml(file_path: str, encoding: str = ...) -> Set[Plugin]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def load_builtin_plugins(name: str = ...) -> Optional[Plugin]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def get_plugin(name: str) -> Optional[Plugin]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def get_loaded_plugins() -> Set[Plugin]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def require(name: str) -> Optional[Export]:
|
||||||
|
...
|
||||||
|
@ -12,8 +12,8 @@ from importlib.machinery import PathFinder, SourceFileLoader
|
|||||||
|
|
||||||
from .export import _export, Export
|
from .export import _export, Export
|
||||||
|
|
||||||
_current_plugin: ContextVar[Optional[str]] = ContextVar("_current_plugin",
|
_current_plugin: ContextVar[Optional[ModuleType]] = ContextVar(
|
||||||
default=None)
|
"_current_plugin", default=None)
|
||||||
|
|
||||||
_internal_space = ModuleType(__name__ + "._internal")
|
_internal_space = ModuleType(__name__ + "._internal")
|
||||||
_internal_space.__path__ = [] # type: ignore
|
_internal_space.__path__ = [] # type: ignore
|
||||||
@ -53,14 +53,13 @@ class _InternalModule(ModuleType):
|
|||||||
class PluginManager:
|
class PluginManager:
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
namespace: Optional[str] = None,
|
namespace: str,
|
||||||
plugins: Optional[Iterable[str]] = None,
|
plugins: Optional[Iterable[str]] = None,
|
||||||
search_path: Optional[Iterable[str]] = None,
|
search_path: Optional[Iterable[str]] = None,
|
||||||
*,
|
*,
|
||||||
id: Optional[str] = None):
|
id: Optional[str] = None):
|
||||||
self.namespace: Optional[str] = namespace
|
self.namespace: str = namespace
|
||||||
self.namespace_module: Optional[ModuleType] = self._setup_namespace(
|
self.namespace_module: ModuleType = self._setup_namespace(namespace)
|
||||||
namespace)
|
|
||||||
|
|
||||||
self.id: str = id or str(uuid.uuid4())
|
self.id: str = id or str(uuid.uuid4())
|
||||||
self.internal_id: str = md5(
|
self.internal_id: str = md5(
|
||||||
@ -73,12 +72,7 @@ class PluginManager:
|
|||||||
# ensure can be loaded
|
# ensure can be loaded
|
||||||
self.list_plugins()
|
self.list_plugins()
|
||||||
|
|
||||||
def _setup_namespace(self,
|
def _setup_namespace(self, namespace: str) -> ModuleType:
|
||||||
namespace: Optional[str] = None
|
|
||||||
) -> Optional[ModuleType]:
|
|
||||||
if not namespace:
|
|
||||||
return None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
module = importlib.import_module(namespace)
|
module = importlib.import_module(namespace)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@ -156,14 +150,18 @@ class PluginManager:
|
|||||||
def load_all_plugins(self) -> List[ModuleType]:
|
def load_all_plugins(self) -> List[ModuleType]:
|
||||||
return [self.load_plugin(name) for name in self.list_plugins()]
|
return [self.load_plugin(name) for name in self.list_plugins()]
|
||||||
|
|
||||||
def _rewrite_module_name(self, module_name) -> Optional[str]:
|
def _rewrite_module_name(self, module_name: str) -> Optional[str]:
|
||||||
prefix = f"{self.internal_module.__name__}."
|
prefix = f"{self.internal_module.__name__}."
|
||||||
if module_name.startswith(self.namespace + "."):
|
raw_name = module_name[len(self.namespace) +
|
||||||
path = module_name.split(".")
|
1:] if module_name.startswith(self.namespace +
|
||||||
length = self.namespace.count(".") + 1
|
".") else None
|
||||||
return f"{prefix}{'.'.join(path[length:])}"
|
# dir plugins
|
||||||
|
if raw_name and raw_name.split(".")[0] in self.search_plugins():
|
||||||
|
return f"{prefix}{raw_name}"
|
||||||
|
# third party plugin or renamed dir plugins
|
||||||
elif module_name in self.plugins or module_name.startswith(prefix):
|
elif module_name in self.plugins or module_name.startswith(prefix):
|
||||||
return module_name
|
return module_name
|
||||||
|
# dir plugins
|
||||||
elif module_name in self.search_plugins():
|
elif module_name in self.search_plugins():
|
||||||
return f"{prefix}{module_name}"
|
return f"{prefix}{module_name}"
|
||||||
return None
|
return None
|
||||||
@ -194,36 +192,44 @@ class PluginLoader(SourceFileLoader):
|
|||||||
def __init__(self, manager: PluginManager, fullname: str, path) -> None:
|
def __init__(self, manager: PluginManager, fullname: str, path) -> None:
|
||||||
self.manager = manager
|
self.manager = manager
|
||||||
self.loaded = False
|
self.loaded = False
|
||||||
self._plugin_token = None
|
|
||||||
self._export_token = None
|
|
||||||
super().__init__(fullname, path)
|
super().__init__(fullname, path)
|
||||||
|
|
||||||
def create_module(self, spec) -> Optional[ModuleType]:
|
def create_module(self, spec) -> Optional[ModuleType]:
|
||||||
if self.name in sys.modules:
|
if self.name in sys.modules:
|
||||||
self.loaded = True
|
self.loaded = True
|
||||||
return sys.modules[self.name]
|
return sys.modules[self.name]
|
||||||
prefix = self.manager.internal_module.__name__
|
|
||||||
plugin_name = self.name[len(prefix):] if self.name.startswith(
|
|
||||||
prefix) else self.name
|
|
||||||
self._plugin_token = _current_plugin.set(plugin_name.lstrip("."))
|
|
||||||
self._export_token = _export.set(Export())
|
|
||||||
# return None to use default module creation
|
# return None to use default module creation
|
||||||
return super().create_module(spec)
|
return super().create_module(spec)
|
||||||
|
|
||||||
def exec_module(self, module: ModuleType) -> None:
|
def exec_module(self, module: ModuleType) -> None:
|
||||||
if self.loaded:
|
if self.loaded:
|
||||||
return
|
return
|
||||||
# really need?
|
|
||||||
# setattr(module, "__manager__", self.manager)
|
|
||||||
if self._export_token:
|
|
||||||
setattr(module, "__export__", _export.get())
|
|
||||||
|
|
||||||
|
export = Export()
|
||||||
|
_export_token = _export.set(export)
|
||||||
|
|
||||||
|
prefix = self.manager.internal_module.__name__
|
||||||
|
is_dir_plugin = self.name.startswith(prefix + ".")
|
||||||
|
module_name = self.name[len(prefix) +
|
||||||
|
1:] if is_dir_plugin else self.name
|
||||||
|
_plugin_token = _current_plugin.set(module)
|
||||||
|
|
||||||
|
setattr(module, "__export__", export)
|
||||||
|
setattr(module, "__plugin_name__",
|
||||||
|
module_name.split(".")[0] if is_dir_plugin else module_name)
|
||||||
|
setattr(module, "__module_name__", module_name)
|
||||||
|
setattr(module, "__module_prefix__", prefix if is_dir_plugin else "")
|
||||||
|
|
||||||
|
# try:
|
||||||
|
# super().exec_module(module)
|
||||||
|
# except Exception as e:
|
||||||
|
# raise ImportError(
|
||||||
|
# f"Error when executing module {module_name} from {module.__file__}."
|
||||||
|
# ) from e
|
||||||
super().exec_module(module)
|
super().exec_module(module)
|
||||||
|
|
||||||
if self._plugin_token:
|
_current_plugin.reset(_plugin_token)
|
||||||
_current_plugin.reset(self._plugin_token)
|
_export.reset(_export_token)
|
||||||
if self._export_token:
|
|
||||||
_export.reset(self._export_token)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@ -175,7 +175,8 @@ class TrieRule:
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def startswith(msg: str) -> Rule:
|
def startswith(msg: Union[str, Tuple[str, ...]],
|
||||||
|
ignorecase: bool = False) -> Rule:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -185,17 +186,24 @@ def startswith(msg: str) -> Rule:
|
|||||||
|
|
||||||
* ``msg: str``: 消息开头字符串
|
* ``msg: str``: 消息开头字符串
|
||||||
"""
|
"""
|
||||||
|
if isinstance(msg, str):
|
||||||
|
msg = (msg,)
|
||||||
|
|
||||||
|
pattern = re.compile(
|
||||||
|
f"^(?:{'|'.join(re.escape(prefix) for prefix in msg)})",
|
||||||
|
re.IGNORECASE if ignorecase else 0)
|
||||||
|
|
||||||
async def _startswith(bot: "Bot", event: "Event", state: T_State) -> bool:
|
async def _startswith(bot: "Bot", event: "Event", state: T_State) -> bool:
|
||||||
if event.get_type() != "message":
|
if event.get_type() != "message":
|
||||||
return False
|
return False
|
||||||
text = event.get_plaintext()
|
text = event.get_plaintext()
|
||||||
return text.startswith(msg)
|
return bool(pattern.match(text))
|
||||||
|
|
||||||
return Rule(_startswith)
|
return Rule(_startswith)
|
||||||
|
|
||||||
|
|
||||||
def endswith(msg: str) -> Rule:
|
def endswith(msg: Union[str, Tuple[str, ...]],
|
||||||
|
ignorecase: bool = False) -> Rule:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -205,11 +213,18 @@ def endswith(msg: str) -> Rule:
|
|||||||
|
|
||||||
* ``msg: str``: 消息结尾字符串
|
* ``msg: str``: 消息结尾字符串
|
||||||
"""
|
"""
|
||||||
|
if isinstance(msg, str):
|
||||||
|
msg = (msg,)
|
||||||
|
|
||||||
|
pattern = re.compile(
|
||||||
|
f"(?:{'|'.join(re.escape(prefix) for prefix in msg)})$",
|
||||||
|
re.IGNORECASE if ignorecase else 0)
|
||||||
|
|
||||||
async def _endswith(bot: "Bot", event: "Event", state: T_State) -> bool:
|
async def _endswith(bot: "Bot", event: "Event", state: T_State) -> bool:
|
||||||
if event.get_type() != "message":
|
if event.get_type() != "message":
|
||||||
return False
|
return False
|
||||||
return event.get_plaintext().endswith(msg)
|
text = event.get_plaintext()
|
||||||
|
return bool(pattern.match(text))
|
||||||
|
|
||||||
return Rule(_endswith)
|
return Rule(_endswith)
|
||||||
|
|
||||||
|
@ -55,21 +55,21 @@ T_StateFactory = Callable[["Bot", "Event"], Awaitable[T_State]]
|
|||||||
事件处理状态 State 类工厂函数
|
事件处理状态 State 类工厂函数
|
||||||
"""
|
"""
|
||||||
|
|
||||||
T_WebSocketConnectionHook = Callable[["Bot"], Awaitable[None]]
|
T_BotConnectionHook = Callable[["Bot"], Awaitable[None]]
|
||||||
"""
|
"""
|
||||||
:类型: ``Callable[[Bot], Awaitable[None]]``
|
:类型: ``Callable[[Bot], Awaitable[None]]``
|
||||||
|
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
WebSocket 连接建立时执行的函数
|
Bot 连接建立时执行的函数
|
||||||
"""
|
"""
|
||||||
T_WebSocketDisconnectionHook = Callable[["Bot"], Awaitable[None]]
|
T_BotDisconnectionHook = Callable[["Bot"], Awaitable[None]]
|
||||||
"""
|
"""
|
||||||
:类型: ``Callable[[Bot], Awaitable[None]]``
|
:类型: ``Callable[[Bot], Awaitable[None]]``
|
||||||
|
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
WebSocket 连接断开时执行的函数
|
Bot 连接断开时执行的函数
|
||||||
"""
|
"""
|
||||||
T_CallingAPIHook = Callable[["Bot", str, Dict[str, Any]], Awaitable[None]]
|
T_CallingAPIHook = Callable[["Bot", str, Dict[str, Any]], Awaitable[None]]
|
||||||
"""
|
"""
|
||||||
|
@ -17,8 +17,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vuepress dev docs",
|
"dev": "vuepress dev docs",
|
||||||
"build": "vuepress build docs",
|
"build": "vuepress build docs",
|
||||||
"lint": "npx prettier -c docs/**/* !docs/api/**/*",
|
"lint": "npx prettier --config .prettierrc -c docs/**/* !docs/api/**/*",
|
||||||
"lint:fix": "npx prettier --write docs/**/* !docs/api/**/*"
|
"lint:fix": "npx prettier --config .prettierrc --write docs/**/* !docs/api/**/*"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -100,14 +100,14 @@ def _check_at_me(bot: "Bot", event: "Event"):
|
|||||||
# check the first segment
|
# check the first segment
|
||||||
if event.message[0] == at_me_seg:
|
if event.message[0] == at_me_seg:
|
||||||
event.to_me = True
|
event.to_me = True
|
||||||
del event.message[0]
|
event.message.pop(0)
|
||||||
if event.message and event.message[0].type == "text":
|
if event.message and event.message[0].type == "text":
|
||||||
event.message[0].data["text"] = event.message[0].data[
|
event.message[0].data["text"] = event.message[0].data[
|
||||||
"text"].lstrip()
|
"text"].lstrip()
|
||||||
if not event.message[0].data["text"]:
|
if not event.message[0].data["text"]:
|
||||||
del event.message[0]
|
del event.message[0]
|
||||||
if event.message and event.message[0] == at_me_seg:
|
if event.message and event.message[0] == at_me_seg:
|
||||||
del event.message[0]
|
event.message.pop(0)
|
||||||
if event.message and event.message[0].type == "text":
|
if event.message and event.message[0].type == "text":
|
||||||
event.message[0].data["text"] = event.message[0].data[
|
event.message[0].data["text"] = event.message[0].data[
|
||||||
"text"].lstrip()
|
"text"].lstrip()
|
||||||
|
@ -68,8 +68,7 @@ class Bot(BaseBot):
|
|||||||
async def handle_message(self, message: dict):
|
async def handle_message(self, message: dict):
|
||||||
...
|
...
|
||||||
|
|
||||||
async def call_api(self, api: str, *, self_id: Optional[str],
|
async def call_api(self, api: str, *, **data) -> Any:
|
||||||
**data) -> Any:
|
|
||||||
...
|
...
|
||||||
|
|
||||||
async def send(self, event: Event, message: Union[str, Message,
|
async def send(self, event: Event, message: Union[str, Message,
|
||||||
@ -77,12 +76,13 @@ class Bot(BaseBot):
|
|||||||
**kwargs) -> Any:
|
**kwargs) -> Any:
|
||||||
...
|
...
|
||||||
|
|
||||||
async def send_private_msg(self,
|
async def send_private_msg(
|
||||||
*,
|
self,
|
||||||
user_id: int,
|
*,
|
||||||
message: Union[str, Message],
|
user_id: int,
|
||||||
auto_escape: bool = ...,
|
message: Union[str, Message],
|
||||||
self_id: Optional[int] = ...) -> Dict[str, Any]:
|
auto_escape: bool = ...,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -93,16 +93,17 @@ class Bot(BaseBot):
|
|||||||
* ``user_id``: 对方 QQ 号
|
* ``user_id``: 对方 QQ 号
|
||||||
* ``message``: 要发送的内容
|
* ``message``: 要发送的内容
|
||||||
* ``auto_escape``: 消息内容是否作为纯文本发送(即不解析 CQ 码),只在 ``message`` 字段是字符串时有效
|
* ``auto_escape``: 消息内容是否作为纯文本发送(即不解析 CQ 码),只在 ``message`` 字段是字符串时有效
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def send_group_msg(self,
|
async def send_group_msg(
|
||||||
*,
|
self,
|
||||||
group_id: int,
|
*,
|
||||||
message: Union[str, Message],
|
group_id: int,
|
||||||
auto_escape: bool = ...,
|
message: Union[str, Message],
|
||||||
self_id: Optional[int] = ...) -> Dict[str, Any]:
|
auto_escape: bool = ...,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -113,18 +114,19 @@ class Bot(BaseBot):
|
|||||||
* ``group_id``: 群号
|
* ``group_id``: 群号
|
||||||
* ``message``: 要发送的内容
|
* ``message``: 要发送的内容
|
||||||
* ``auto_escape``: 消息内容是否作为纯文本发送(即不解析 CQ 码),只在 ``message`` 字段是字符串时有效
|
* ``auto_escape``: 消息内容是否作为纯文本发送(即不解析 CQ 码),只在 ``message`` 字段是字符串时有效
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def send_msg(self,
|
async def send_msg(
|
||||||
*,
|
self,
|
||||||
message_type: Optional[str] = ...,
|
*,
|
||||||
user_id: Optional[int] = ...,
|
message_type: Optional[str] = ...,
|
||||||
group_id: Optional[int] = ...,
|
user_id: Optional[int] = ...,
|
||||||
message: Union[str, Message],
|
group_id: Optional[int] = ...,
|
||||||
auto_escape: bool = ...,
|
message: Union[str, Message],
|
||||||
self_id: Optional[int] = ...) -> Dict[str, Any]:
|
auto_escape: bool = ...,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -137,14 +139,15 @@ class Bot(BaseBot):
|
|||||||
* ``group_id``: 群号(消息类型为 ``group`` 时需要)
|
* ``group_id``: 群号(消息类型为 ``group`` 时需要)
|
||||||
* ``message``: 要发送的内容
|
* ``message``: 要发送的内容
|
||||||
* ``auto_escape``: 消息内容是否作为纯文本发送(即不解析 CQ 码),只在 ``message`` 字段是字符串时有效
|
* ``auto_escape``: 消息内容是否作为纯文本发送(即不解析 CQ 码),只在 ``message`` 字段是字符串时有效
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def delete_msg(self,
|
async def delete_msg(
|
||||||
*,
|
self,
|
||||||
message_id: int,
|
*,
|
||||||
self_id: Optional[int] = ...) -> None:
|
message_id: int,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -153,14 +156,15 @@ class Bot(BaseBot):
|
|||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``message_id``: 消息 ID
|
* ``message_id``: 消息 ID
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def get_msg(self,
|
async def get_msg(
|
||||||
*,
|
self,
|
||||||
message_id: int,
|
*,
|
||||||
self_id: Optional[int] = ...) -> Dict[str, Any]:
|
message_id: int,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -169,14 +173,15 @@ class Bot(BaseBot):
|
|||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``message_id``: 消息 ID
|
* ``message_id``: 消息 ID
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def get_forward_msg(self,
|
async def get_forward_msg(
|
||||||
*,
|
self,
|
||||||
id: int,
|
*,
|
||||||
self_id: Optional[int] = ...) -> None:
|
id: int,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -185,15 +190,16 @@ class Bot(BaseBot):
|
|||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``id``: 合并转发 ID
|
* ``id``: 合并转发 ID
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def send_like(self,
|
async def send_like(
|
||||||
*,
|
self,
|
||||||
user_id: int,
|
*,
|
||||||
times: int = ...,
|
user_id: int,
|
||||||
self_id: Optional[int] = ...) -> None:
|
times: int = ...,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -203,16 +209,17 @@ class Bot(BaseBot):
|
|||||||
|
|
||||||
* ``user_id``: 对方 QQ 号
|
* ``user_id``: 对方 QQ 号
|
||||||
* ``times``: 赞的次数,每个好友每天最多 10 次
|
* ``times``: 赞的次数,每个好友每天最多 10 次
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def set_group_kick(self,
|
async def set_group_kick(
|
||||||
*,
|
self,
|
||||||
group_id: int,
|
*,
|
||||||
user_id: int,
|
group_id: int,
|
||||||
reject_add_request: bool = ...,
|
user_id: int,
|
||||||
self_id: Optional[int] = ...) -> None:
|
reject_add_request: bool = ...,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -223,16 +230,17 @@ class Bot(BaseBot):
|
|||||||
* ``group_id``: 群号
|
* ``group_id``: 群号
|
||||||
* ``user_id``: 要踢的 QQ 号
|
* ``user_id``: 要踢的 QQ 号
|
||||||
* ``reject_add_request``: 拒绝此人的加群请求
|
* ``reject_add_request``: 拒绝此人的加群请求
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def set_group_ban(self,
|
async def set_group_ban(
|
||||||
*,
|
self,
|
||||||
group_id: int,
|
*,
|
||||||
user_id: int,
|
group_id: int,
|
||||||
duration: int = ...,
|
user_id: int,
|
||||||
self_id: Optional[int] = ...) -> None:
|
duration: int = ...,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -243,17 +251,18 @@ class Bot(BaseBot):
|
|||||||
* ``group_id``: 群号
|
* ``group_id``: 群号
|
||||||
* ``user_id``: 要禁言的 QQ 号
|
* ``user_id``: 要禁言的 QQ 号
|
||||||
* ``duration``: 禁言时长,单位秒,``0`` 表示取消禁言
|
* ``duration``: 禁言时长,单位秒,``0`` 表示取消禁言
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def set_group_anonymous_ban(self,
|
async def set_group_anonymous_ban(
|
||||||
*,
|
self,
|
||||||
group_id: int,
|
*,
|
||||||
anonymous: Optional[Dict[str, Any]] = ...,
|
group_id: int,
|
||||||
anonymous_flag: Optional[str] = ...,
|
anonymous: Optional[Dict[str, Any]] = ...,
|
||||||
duration: int = ...,
|
anonymous_flag: Optional[str] = ...,
|
||||||
self_id: Optional[int] = ...) -> None:
|
duration: int = ...,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -265,15 +274,16 @@ class Bot(BaseBot):
|
|||||||
* ``anonymous``: 可选,要禁言的匿名用户对象(群消息上报的 ``anonymous`` 字段)
|
* ``anonymous``: 可选,要禁言的匿名用户对象(群消息上报的 ``anonymous`` 字段)
|
||||||
* ``anonymous_flag``: 可选,要禁言的匿名用户的 flag(需从群消息上报的数据中获得)
|
* ``anonymous_flag``: 可选,要禁言的匿名用户的 flag(需从群消息上报的数据中获得)
|
||||||
* ``duration``: 禁言时长,单位秒,无法取消匿名用户禁言
|
* ``duration``: 禁言时长,单位秒,无法取消匿名用户禁言
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def set_group_whole_ban(self,
|
async def set_group_whole_ban(
|
||||||
*,
|
self,
|
||||||
group_id: int,
|
*,
|
||||||
enable: bool = ...,
|
group_id: int,
|
||||||
self_id: Optional[int] = ...) -> None:
|
enable: bool = ...,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -283,16 +293,17 @@ class Bot(BaseBot):
|
|||||||
|
|
||||||
* ``group_id``: 群号
|
* ``group_id``: 群号
|
||||||
* ``enable``: 是否禁言
|
* ``enable``: 是否禁言
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def set_group_admin(self,
|
async def set_group_admin(
|
||||||
*,
|
self,
|
||||||
group_id: int,
|
*,
|
||||||
user_id: int,
|
group_id: int,
|
||||||
enable: bool = ...,
|
user_id: int,
|
||||||
self_id: Optional[int] = ...) -> None:
|
enable: bool = ...,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -303,15 +314,16 @@ class Bot(BaseBot):
|
|||||||
* ``group_id``: 群号
|
* ``group_id``: 群号
|
||||||
* ``user_id``: 要设置管理员的 QQ 号
|
* ``user_id``: 要设置管理员的 QQ 号
|
||||||
* ``enable``: ``True`` 为设置,``False`` 为取消
|
* ``enable``: ``True`` 为设置,``False`` 为取消
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def set_group_anonymous(self,
|
async def set_group_anonymous(
|
||||||
*,
|
self,
|
||||||
group_id: int,
|
*,
|
||||||
enable: bool = ...,
|
group_id: int,
|
||||||
self_id: Optional[int] = ...) -> None:
|
enable: bool = ...,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -321,16 +333,17 @@ class Bot(BaseBot):
|
|||||||
|
|
||||||
* ``group_id``: 群号
|
* ``group_id``: 群号
|
||||||
* ``enable``: 是否允许匿名聊天
|
* ``enable``: 是否允许匿名聊天
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def set_group_card(self,
|
async def set_group_card(
|
||||||
*,
|
self,
|
||||||
group_id: int,
|
*,
|
||||||
user_id: int,
|
group_id: int,
|
||||||
card: str = ...,
|
user_id: int,
|
||||||
self_id: Optional[int] = ...) -> None:
|
card: str = ...,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -341,15 +354,16 @@ class Bot(BaseBot):
|
|||||||
* ``group_id``: 群号
|
* ``group_id``: 群号
|
||||||
* ``user_id``: 要设置的 QQ 号
|
* ``user_id``: 要设置的 QQ 号
|
||||||
* ``card``: 群名片内容,不填或空字符串表示删除群名片
|
* ``card``: 群名片内容,不填或空字符串表示删除群名片
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def set_group_name(self,
|
async def set_group_name(
|
||||||
*,
|
self,
|
||||||
group_id: int,
|
*,
|
||||||
group_name: str,
|
group_id: int,
|
||||||
self_id: Optional[int] = ...) -> None:
|
group_name: str,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -359,15 +373,16 @@ class Bot(BaseBot):
|
|||||||
|
|
||||||
* ``group_id``: 群号
|
* ``group_id``: 群号
|
||||||
* ``group_name``: 新群名
|
* ``group_name``: 新群名
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def set_group_leave(self,
|
async def set_group_leave(
|
||||||
*,
|
self,
|
||||||
group_id: int,
|
*,
|
||||||
is_dismiss: bool = ...,
|
group_id: int,
|
||||||
self_id: Optional[int] = ...) -> None:
|
is_dismiss: bool = ...,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -377,17 +392,18 @@ class Bot(BaseBot):
|
|||||||
|
|
||||||
* ``group_id``: 群号
|
* ``group_id``: 群号
|
||||||
* ``is_dismiss``: 是否解散,如果登录号是群主,则仅在此项为 True 时能够解散
|
* ``is_dismiss``: 是否解散,如果登录号是群主,则仅在此项为 True 时能够解散
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def set_group_special_title(self,
|
async def set_group_special_title(
|
||||||
*,
|
self,
|
||||||
group_id: int,
|
*,
|
||||||
user_id: int,
|
group_id: int,
|
||||||
special_title: str = ...,
|
user_id: int,
|
||||||
duration: int = ...,
|
special_title: str = ...,
|
||||||
self_id: Optional[int] = ...) -> None:
|
duration: int = ...,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -399,16 +415,17 @@ class Bot(BaseBot):
|
|||||||
* ``user_id``: 要设置的 QQ 号
|
* ``user_id``: 要设置的 QQ 号
|
||||||
* ``special_title``: 专属头衔,不填或空字符串表示删除专属头衔
|
* ``special_title``: 专属头衔,不填或空字符串表示删除专属头衔
|
||||||
* ``duration``: 专属头衔有效期,单位秒,-1 表示永久,不过此项似乎没有效果,可能是只有某些特殊的时间长度有效,有待测试
|
* ``duration``: 专属头衔有效期,单位秒,-1 表示永久,不过此项似乎没有效果,可能是只有某些特殊的时间长度有效,有待测试
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def set_friend_add_request(self,
|
async def set_friend_add_request(
|
||||||
*,
|
self,
|
||||||
flag: str,
|
*,
|
||||||
approve: bool = ...,
|
flag: str,
|
||||||
remark: str = ...,
|
approve: bool = ...,
|
||||||
self_id: Optional[int] = ...) -> None:
|
remark: str = ...,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -419,17 +436,18 @@ class Bot(BaseBot):
|
|||||||
* ``flag``: 加好友请求的 flag(需从上报的数据中获得)
|
* ``flag``: 加好友请求的 flag(需从上报的数据中获得)
|
||||||
* ``approve``: 是否同意请求
|
* ``approve``: 是否同意请求
|
||||||
* ``remark``: 添加后的好友备注(仅在同意时有效)
|
* ``remark``: 添加后的好友备注(仅在同意时有效)
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def set_group_add_request(self,
|
async def set_group_add_request(
|
||||||
*,
|
self,
|
||||||
flag: str,
|
*,
|
||||||
sub_type: str,
|
flag: str,
|
||||||
approve: bool = ...,
|
sub_type: str,
|
||||||
reason: str = ...,
|
approve: bool = ...,
|
||||||
self_id: Optional[int] = ...) -> None:
|
reason: str = ...,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -441,29 +459,25 @@ class Bot(BaseBot):
|
|||||||
* ``sub_type``: ``add`` 或 ``invite``,请求类型(需要和上报消息中的 ``sub_type`` 字段相符)
|
* ``sub_type``: ``add`` 或 ``invite``,请求类型(需要和上报消息中的 ``sub_type`` 字段相符)
|
||||||
* ``approve``: 是否同意请求/邀请
|
* ``approve``: 是否同意请求/邀请
|
||||||
* ``reason``: 拒绝理由(仅在拒绝时有效)
|
* ``reason``: 拒绝理由(仅在拒绝时有效)
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def get_login_info(self,
|
async def get_login_info(self) -> Dict[str, Any]:
|
||||||
*,
|
|
||||||
self_id: Optional[int] = ...) -> Dict[str, Any]:
|
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
获取登录号信息。
|
获取登录号信息。
|
||||||
|
|
||||||
:参数:
|
|
||||||
|
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def get_stranger_info(self,
|
async def get_stranger_info(
|
||||||
*,
|
self,
|
||||||
user_id: int,
|
*,
|
||||||
no_cache: bool = ...,
|
user_id: int,
|
||||||
self_id: Optional[int] = ...) -> Dict[str, Any]:
|
no_cache: bool = ...,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -473,30 +487,25 @@ class Bot(BaseBot):
|
|||||||
|
|
||||||
* ``user_id``: QQ 号
|
* ``user_id``: QQ 号
|
||||||
* ``no_cache``: 是否不使用缓存(使用缓存可能更新不及时,但响应更快)
|
* ``no_cache``: 是否不使用缓存(使用缓存可能更新不及时,但响应更快)
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def get_friend_list(self,
|
async def get_friend_list(self) -> List[Dict[str, Any]]:
|
||||||
*,
|
|
||||||
self_id: Optional[int] = ...
|
|
||||||
) -> List[Dict[str, Any]]:
|
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
获取好友列表。
|
获取好友列表。
|
||||||
|
|
||||||
:参数:
|
|
||||||
|
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def get_group_info(self,
|
async def get_group_info(
|
||||||
*,
|
self,
|
||||||
group_id: int,
|
*,
|
||||||
no_cache: bool = ...,
|
group_id: int,
|
||||||
self_id: Optional[int] = ...) -> Dict[str, Any]:
|
no_cache: bool = ...,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -506,32 +515,26 @@ class Bot(BaseBot):
|
|||||||
|
|
||||||
* ``group_id``: 群号
|
* ``group_id``: 群号
|
||||||
* ``no_cache``: 是否不使用缓存(使用缓存可能更新不及时,但响应更快)
|
* ``no_cache``: 是否不使用缓存(使用缓存可能更新不及时,但响应更快)
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def get_group_list(self,
|
async def get_group_list(self) -> List[Dict[str, Any]]:
|
||||||
*,
|
|
||||||
self_id: Optional[int] = ...
|
|
||||||
) -> List[Dict[str, Any]]:
|
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
获取群列表。
|
获取群列表。
|
||||||
|
|
||||||
:参数:
|
|
||||||
|
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def get_group_member_info(
|
async def get_group_member_info(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
group_id: int,
|
group_id: int,
|
||||||
user_id: int,
|
user_id: int,
|
||||||
no_cache: bool = ...,
|
no_cache: bool = ...,
|
||||||
self_id: Optional[int] = ...) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -542,15 +545,15 @@ class Bot(BaseBot):
|
|||||||
* ``group_id``: 群号
|
* ``group_id``: 群号
|
||||||
* ``user_id``: QQ 号
|
* ``user_id``: QQ 号
|
||||||
* ``no_cache``: 是否不使用缓存(使用缓存可能更新不及时,但响应更快)
|
* ``no_cache``: 是否不使用缓存(使用缓存可能更新不及时,但响应更快)
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def get_group_member_list(
|
async def get_group_member_list(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
group_id: int,
|
group_id: int,
|
||||||
self_id: Optional[int] = ...) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -559,16 +562,16 @@ class Bot(BaseBot):
|
|||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``group_id``: 群号
|
* ``group_id``: 群号
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def get_group_honor_info(self,
|
async def get_group_honor_info(
|
||||||
*,
|
self,
|
||||||
group_id: int,
|
*,
|
||||||
type: str = ...,
|
group_id: int,
|
||||||
self_id: Optional[int] = ...
|
type: str = ...,
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -578,14 +581,15 @@ class Bot(BaseBot):
|
|||||||
|
|
||||||
* ``group_id``: 群号
|
* ``group_id``: 群号
|
||||||
* ``type``: 要获取的群荣誉类型,可传入 ``talkative`` ``performer`` ``legend`` ``strong_newbie`` ``emotion`` 以分别获取单个类型的群荣誉数据,或传入 ``all`` 获取所有数据
|
* ``type``: 要获取的群荣誉类型,可传入 ``talkative`` ``performer`` ``legend`` ``strong_newbie`` ``emotion`` 以分别获取单个类型的群荣誉数据,或传入 ``all`` 获取所有数据
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def get_cookies(self,
|
async def get_cookies(
|
||||||
*,
|
self,
|
||||||
domain: str = ...,
|
*,
|
||||||
self_id: Optional[int] = ...) -> Dict[str, Any]:
|
domain: str = ...,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -594,28 +598,24 @@ class Bot(BaseBot):
|
|||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``domain``: 需要获取 cookies 的域名
|
* ``domain``: 需要获取 cookies 的域名
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def get_csrf_token(self,
|
async def get_csrf_token(self) -> Dict[str, Any]:
|
||||||
*,
|
|
||||||
self_id: Optional[int] = ...) -> Dict[str, Any]:
|
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
获取 CSRF Token。
|
获取 CSRF Token。
|
||||||
|
|
||||||
:参数:
|
|
||||||
|
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def get_credentials(self,
|
async def get_credentials(
|
||||||
*,
|
self,
|
||||||
domain: str = ...,
|
*,
|
||||||
self_id: Optional[int] = ...) -> Dict[str, Any]:
|
domain: str = ...,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -624,15 +624,16 @@ class Bot(BaseBot):
|
|||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``domain``: 需要获取 cookies 的域名
|
* ``domain``: 需要获取 cookies 的域名
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def get_record(self,
|
async def get_record(
|
||||||
*,
|
self,
|
||||||
file: str,
|
*,
|
||||||
out_format: str,
|
file: str,
|
||||||
self_id: Optional[int] = ...) -> Dict[str, Any]:
|
out_format: str,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -642,14 +643,15 @@ class Bot(BaseBot):
|
|||||||
|
|
||||||
* ``file``: 收到的语音文件名(CQ 码的 ``file`` 参数),如 ``0B38145AA44505000B38145AA4450500.silk``
|
* ``file``: 收到的语音文件名(CQ 码的 ``file`` 参数),如 ``0B38145AA44505000B38145AA4450500.silk``
|
||||||
* ``out_format``: 要转换到的格式,目前支持 ``mp3``、``amr``、``wma``、``m4a``、``spx``、``ogg``、``wav``、``flac``
|
* ``out_format``: 要转换到的格式,目前支持 ``mp3``、``amr``、``wma``、``m4a``、``spx``、``ogg``、``wav``、``flac``
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def get_image(self,
|
async def get_image(
|
||||||
*,
|
self,
|
||||||
file: str,
|
*,
|
||||||
self_id: Optional[int] = ...) -> Dict[str, Any]:
|
file: str,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -658,70 +660,51 @@ class Bot(BaseBot):
|
|||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``file``: 收到的图片文件名(CQ 码的 ``file`` 参数),如 ``6B4DE3DFD1BD271E3297859D41C530F5.jpg``
|
* ``file``: 收到的图片文件名(CQ 码的 ``file`` 参数),如 ``6B4DE3DFD1BD271E3297859D41C530F5.jpg``
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def can_send_image(self,
|
async def can_send_image(self) -> Dict[str, Any]:
|
||||||
*,
|
|
||||||
self_id: Optional[int] = ...) -> Dict[str, Any]:
|
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
检查是否可以发送图片。
|
检查是否可以发送图片。
|
||||||
|
|
||||||
:参数:
|
|
||||||
|
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def can_send_record(self,
|
async def can_send_record(self) -> Dict[str, Any]:
|
||||||
*,
|
|
||||||
self_id: Optional[int] = ...) -> Dict[str, Any]:
|
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
检查是否可以发送语音。
|
检查是否可以发送语音。
|
||||||
|
|
||||||
:参数:
|
|
||||||
|
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def get_status(self,
|
async def get_status(self) -> Dict[str, Any]:
|
||||||
*,
|
|
||||||
self_id: Optional[int] = ...) -> Dict[str, Any]:
|
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
获取插件运行状态。
|
获取插件运行状态。
|
||||||
|
|
||||||
:参数:
|
|
||||||
|
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def get_version_info(self,
|
async def get_version_info(self) -> Dict[str, Any]:
|
||||||
*,
|
|
||||||
self_id: Optional[int] = ...) -> Dict[str, Any]:
|
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
获取版本信息。
|
获取版本信息。
|
||||||
|
|
||||||
:参数:
|
|
||||||
|
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def set_restart(self,
|
async def set_restart(
|
||||||
*,
|
self,
|
||||||
delay: int = ...,
|
*,
|
||||||
self_id: Optional[int] = ...) -> None:
|
delay: int = ...,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -730,18 +713,15 @@ class Bot(BaseBot):
|
|||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``delay``: 要延迟的毫秒数,如果默认情况下无法重启,可以尝试设置延迟为 2000 左右
|
* ``delay``: 要延迟的毫秒数,如果默认情况下无法重启,可以尝试设置延迟为 2000 左右
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def clean_cache(self, *, self_id: Optional[int] = ...) -> None:
|
async def clean_cache(self) -> None:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
清理数据目录。
|
清理数据目录。
|
||||||
|
|
||||||
:参数:
|
|
||||||
|
|
||||||
* ``self_id``: 机器人 QQ 号
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import re
|
import re
|
||||||
|
from io import BytesIO
|
||||||
|
from pathlib import Path
|
||||||
|
from base64 import b64encode
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from typing import Any, Dict, Union, Tuple, Mapping, Iterable, Optional
|
from typing import Any, List, Dict, Union, Tuple, Mapping, Iterable, Optional
|
||||||
|
|
||||||
from nonebot.typing import overrides
|
from nonebot.typing import overrides
|
||||||
from nonebot.adapters import Message as BaseMessage, MessageSegment as BaseMessageSegment
|
from nonebot.adapters import Message as BaseMessage, MessageSegment as BaseMessageSegment
|
||||||
@ -79,17 +82,23 @@ class MessageSegment(BaseMessageSegment):
|
|||||||
return MessageSegment("forward", {"id": id_})
|
return MessageSegment("forward", {"id": id_})
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def image(file: str,
|
def image(file: Union[str, bytes, BytesIO, Path],
|
||||||
type_: Optional[str] = None,
|
type_: Optional[str] = None,
|
||||||
cache: bool = True,
|
cache: bool = True,
|
||||||
proxy: bool = True,
|
proxy: bool = True,
|
||||||
timeout: Optional[int] = None) -> "MessageSegment":
|
timeout: Optional[int] = None) -> "MessageSegment":
|
||||||
|
if isinstance(file, BytesIO):
|
||||||
|
file = file.read()
|
||||||
|
if isinstance(file, bytes):
|
||||||
|
file = f"base64://{b64encode(file).decode()}"
|
||||||
|
elif isinstance(file, Path):
|
||||||
|
file = f"file:///{file.resolve()}"
|
||||||
return MessageSegment(
|
return MessageSegment(
|
||||||
"image", {
|
"image", {
|
||||||
"file": file,
|
"file": file,
|
||||||
"type": type_,
|
"type": type_,
|
||||||
"cache": cache,
|
"cache": _b2s(cache),
|
||||||
"proxy": proxy,
|
"proxy": _b2s(proxy),
|
||||||
"timeout": timeout
|
"timeout": timeout
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -148,17 +157,23 @@ class MessageSegment(BaseMessageSegment):
|
|||||||
return MessageSegment("poke", {"type": type_, "id": id_})
|
return MessageSegment("poke", {"type": type_, "id": id_})
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def record(file: str,
|
def record(file: Union[str, bytes, BytesIO, Path],
|
||||||
magic: Optional[bool] = None,
|
magic: Optional[bool] = None,
|
||||||
cache: Optional[bool] = None,
|
cache: Optional[bool] = None,
|
||||||
proxy: Optional[bool] = None,
|
proxy: Optional[bool] = None,
|
||||||
timeout: Optional[int] = None) -> "MessageSegment":
|
timeout: Optional[int] = None) -> "MessageSegment":
|
||||||
|
if isinstance(file, BytesIO):
|
||||||
|
file = file.read()
|
||||||
|
if isinstance(file, bytes):
|
||||||
|
file = f"base64://{b64encode(file).decode()}"
|
||||||
|
elif isinstance(file, Path):
|
||||||
|
file = f"file:///{file.resolve()}"
|
||||||
return MessageSegment(
|
return MessageSegment(
|
||||||
"record", {
|
"record", {
|
||||||
"file": file,
|
"file": file,
|
||||||
"magic": _b2s(magic),
|
"magic": _b2s(magic),
|
||||||
"cache": cache,
|
"cache": _b2s(cache),
|
||||||
"proxy": proxy,
|
"proxy": _b2s(proxy),
|
||||||
"timeout": timeout
|
"timeout": timeout
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -191,23 +206,30 @@ class MessageSegment(BaseMessageSegment):
|
|||||||
return MessageSegment("text", {"text": text})
|
return MessageSegment("text", {"text": text})
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def video(file: str,
|
def video(file: Union[str, bytes, BytesIO, Path],
|
||||||
cache: Optional[bool] = None,
|
cache: Optional[bool] = None,
|
||||||
proxy: Optional[bool] = None,
|
proxy: Optional[bool] = None,
|
||||||
timeout: Optional[int] = None) -> "MessageSegment":
|
timeout: Optional[int] = None) -> "MessageSegment":
|
||||||
return MessageSegment("video", {
|
if isinstance(file, BytesIO):
|
||||||
"file": file,
|
file = file.read()
|
||||||
"cache": cache,
|
if isinstance(file, bytes):
|
||||||
"proxy": proxy,
|
file = f"base64://{b64encode(file).decode()}"
|
||||||
"timeout": timeout
|
elif isinstance(file, Path):
|
||||||
})
|
file = f"file:///{file.resolve()}"
|
||||||
|
return MessageSegment(
|
||||||
|
"video", {
|
||||||
|
"file": file,
|
||||||
|
"cache": _b2s(cache),
|
||||||
|
"proxy": _b2s(proxy),
|
||||||
|
"timeout": timeout
|
||||||
|
})
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def xml(data: str) -> "MessageSegment":
|
def xml(data: str) -> "MessageSegment":
|
||||||
return MessageSegment("xml", {"data": data})
|
return MessageSegment("xml", {"data": data})
|
||||||
|
|
||||||
|
|
||||||
class Message(BaseMessage):
|
class Message(BaseMessage[MessageSegment]):
|
||||||
"""
|
"""
|
||||||
CQHTTP 协议 Message 适配。
|
CQHTTP 协议 Message 适配。
|
||||||
"""
|
"""
|
||||||
|
10
packages/nonebot-adapter-cqhttp/poetry.lock
generated
10
packages/nonebot-adapter-cqhttp/poetry.lock
generated
@ -167,7 +167,7 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nonebot2"
|
name = "nonebot2"
|
||||||
version = "2.0.0-alpha.12"
|
version = "2.0.0a13.post1"
|
||||||
description = "An asynchronous python bot framework."
|
description = "An asynchronous python bot framework."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@ -226,7 +226,7 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-dotenv"
|
name = "python-dotenv"
|
||||||
version = "0.16.0"
|
version = "0.17.0"
|
||||||
description = "Read key-value pairs from a .env file and set them as environment variables"
|
description = "Read key-value pairs from a .env file and set them as environment variables"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@ -418,7 +418,7 @@ reference = "aliyun"
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.7.3"
|
python-versions = "^3.7.3"
|
||||||
content-hash = "b8ec196a78675b4098ab7509cbdbd311ffcbcf1ce8b625c589f1e95596801c71"
|
content-hash = "f1908ea0987f3c4a50d18c8d0350b73602fa57e9349e1aa95b3190064ebcc881"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
certifi = [
|
certifi = [
|
||||||
@ -500,8 +500,8 @@ pygtrie = [
|
|||||||
{file = "pygtrie-2.4.2.tar.gz", hash = "sha256:43205559d28863358dbbf25045029f58e2ab357317a59b11f11ade278ac64692"},
|
{file = "pygtrie-2.4.2.tar.gz", hash = "sha256:43205559d28863358dbbf25045029f58e2ab357317a59b11f11ade278ac64692"},
|
||||||
]
|
]
|
||||||
python-dotenv = [
|
python-dotenv = [
|
||||||
{file = "python-dotenv-0.16.0.tar.gz", hash = "sha256:9fa413c37d4652d3fa02fea0ff465c384f5db75eab259c4fc5d0c5b8bf20edd4"},
|
{file = "python-dotenv-0.17.0.tar.gz", hash = "sha256:471b782da0af10da1a80341e8438fca5fadeba2881c54360d5fd8d03d03a4f4a"},
|
||||||
{file = "python_dotenv-0.16.0-py2.py3-none-any.whl", hash = "sha256:31d752f5b748f4e292448c9a0cac6a08ed5e6f4cefab85044462dcad56905cec"},
|
{file = "python_dotenv-0.17.0-py2.py3-none-any.whl", hash = "sha256:49782a97c9d641e8a09ae1d9af0856cc587c8d2474919342d5104d85be9890b2"},
|
||||||
]
|
]
|
||||||
pyyaml = [
|
pyyaml = [
|
||||||
{file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
|
{file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "nonebot-adapter-cqhttp"
|
name = "nonebot-adapter-cqhttp"
|
||||||
version = "2.0.0-alpha.12"
|
version = "2.0.0-alpha.13"
|
||||||
description = "OneBot(CQHTTP) adapter for nonebot2"
|
description = "OneBot(CQHTTP) adapter for nonebot2"
|
||||||
authors = ["yanyongyu <yyy@nonebot.dev>"]
|
authors = ["yanyongyu <yyy@nonebot.dev>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@ -25,7 +25,7 @@ exclude = ["nonebot/__init__.py", "nonebot/adapters/__init__.py"]
|
|||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.7.3"
|
python = "^3.7.3"
|
||||||
httpx = "^0.17.0"
|
httpx = "^0.17.0"
|
||||||
nonebot2 = "^2.0.0-alpha.12"
|
nonebot2 = "^2.0.0-alpha.13"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
nonebot2 = { path = "../../", develop = true }
|
nonebot2 = { path = "../../", develop = true }
|
||||||
|
@ -155,7 +155,7 @@ class MessageSegment(BaseMessageSegment):
|
|||||||
return {"msgtype": self.type, self.type: copy(self.data)}
|
return {"msgtype": self.type, self.type: copy(self.data)}
|
||||||
|
|
||||||
|
|
||||||
class Message(BaseMessage):
|
class Message(BaseMessage[MessageSegment]):
|
||||||
"""
|
"""
|
||||||
钉钉 协议 Message 适配。
|
钉钉 协议 Message 适配。
|
||||||
"""
|
"""
|
||||||
|
@ -266,7 +266,7 @@ class MessageSegment(BaseMessageSegment):
|
|||||||
return cls(type=MessageType.POKE, name=name)
|
return cls(type=MessageType.POKE, name=name)
|
||||||
|
|
||||||
|
|
||||||
class MessageChain(BaseMessage):
|
class MessageChain(BaseMessage[MessageSegment]):
|
||||||
"""
|
"""
|
||||||
Mirai 协议 Message 适配
|
Mirai 协议 Message 适配
|
||||||
|
|
||||||
|
@ -4,7 +4,15 @@ sidebar: auto
|
|||||||
|
|
||||||
# 更新日志
|
# 更新日志
|
||||||
|
|
||||||
## v2.0.0a12
|
## v2.0.0a14
|
||||||
|
|
||||||
|
- 修改日志等级,支持输出等级自定义
|
||||||
|
- 修复日志输出模块名错误
|
||||||
|
- 修改 `Matcher` 属性 `module` 类型
|
||||||
|
- 新增 `Matcher` 属性 `plugin_name` `module_name` `module_prefix`
|
||||||
|
- 移除 `bot.call_api` 参数 `self_id` 切换机器人支持
|
||||||
|
|
||||||
|
## v2.0.0a13.post1
|
||||||
|
|
||||||
- 分离 `handler` 与 `matcher`
|
- 分离 `handler` 与 `matcher`
|
||||||
- 修复 `cqhttp` secret 校验出错
|
- 修复 `cqhttp` secret 校验出错
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
ENVIRONMENT=dev
|
ENVIRONMENT=dev
|
||||||
|
LOG_LEVEL=25
|
||||||
CUSTOM_CONFIG=common
|
CUSTOM_CONFIG=common
|
||||||
FASTAPI_RELOAD_DIRS=["test_plugins"]
|
FASTAPI_RELOAD_DIRS=["test_plugins"]
|
||||||
|
@ -2,6 +2,7 @@ DRIVER=nonebot.drivers.fastapi
|
|||||||
HOST=0.0.0.0
|
HOST=0.0.0.0
|
||||||
PORT=2333
|
PORT=2333
|
||||||
DEBUG=true
|
DEBUG=true
|
||||||
|
LOG_LEVEL=DEBUG
|
||||||
|
|
||||||
SUPERUSERS=["123123123"]
|
SUPERUSERS=["123123123"]
|
||||||
NICKNAME=["bot"]
|
NICKNAME=["bot"]
|
||||||
|
Loading…
Reference in New Issue
Block a user