🚧 add handler di example

This commit is contained in:
yanyongyu 2021-12-31 23:58:59 +08:00
parent ec35f292bd
commit 78d0ec847e
12 changed files with 780 additions and 1055 deletions

View File

@ -1,228 +0,0 @@
<template>
<v-card flat class="adapters">
<v-row class="justify-center">
<v-col cols="12" sm="6">
<v-text-field
v-model="filterText"
dense
rounded
outlined
clearable
hide-details
label="搜索适配器"
>
<template v-slot:prepend-inner>
<div class="v-input__icon v-input__icon--prepend-inner">
<v-icon small>fa-filter</v-icon>
</div>
</template>
</v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-dialog v-model="dialog" max-width="600px">
<template v-slot:activator="{ on, attrs }">
<v-btn dark block color="primary" v-bind="attrs" v-on="on"
>发布适配器
</v-btn>
</template>
<v-card>
<v-card-title>
<span class="headline">适配器信息</span>
</v-card-title>
<v-card-text>
<v-form ref="newAdapterForm" v-model="valid" lazy-validation>
<v-container>
<v-row>
<v-col cols="12">
<v-text-field
v-model="newAdapter.name"
:rules="rules"
label="协议名称"
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
v-model="newAdapter.desc"
:rules="rules"
label="协议介绍"
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
v-model="newAdapter.link"
:rules="rules"
label="PyPI 项目名"
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
v-model="newAdapter.id"
:rules="rules"
label="协议 import 包名"
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
v-model="newAdapter.repo"
:rules="rules"
label="仓库/主页链接"
required
></v-text-field>
</v-col>
</v-row>
</v-container>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text @click="dialog = false">
关闭
</v-btn>
<v-btn
:disabled="!valid"
color="blue darken-1"
text
@click="publishAdapter"
>
发布
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-pagination
v-model="page"
:length="pageNum"
prev-icon="fa-caret-left"
next-icon="fa-caret-right"
></v-pagination
></v-col>
</v-row>
<v-row>
<v-col
cols="12"
sm="6"
v-for="(adapter, index) in displayAdapters"
:key="index"
>
<PublishCard
:name="adapter.name"
:desc="adapter.desc"
:id="adapter.id"
:author="adapter.author"
:link="adapter.repo"
></PublishCard>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-pagination
v-model="page"
:length="pageNum"
prev-icon="fa-caret-left"
next-icon="fa-caret-right"
></v-pagination>
</v-col>
</v-row>
</v-card>
</template>
<script>
import PublishCard from "./PublishCard.vue";
import adapters from "../public/adapters.json";
export default {
name: "Adapters",
components: {
PublishCard,
},
data() {
return {
adapters: adapters,
filterText: "",
page: 1,
dialog: false,
valid: false,
rules: [(v) => !!v || "This field is required"],
newAdapter: {
name: null,
desc: null,
id: null,
link: null,
repo: null,
},
};
},
computed: {
pageNum() {
return Math.ceil(this.filteredAdapters.length / 10);
},
filteredAdapters() {
return this.adapters.filter((adapter) => {
return (
adapter.id.indexOf(this.filterText || "") != -1 ||
adapter.name.indexOf(this.filterText || "") != -1 ||
adapter.desc.indexOf(this.filterText || "") != -1 ||
adapter.author.indexOf(this.filterText || "") != -1
);
});
},
displayAdapters() {
return this.filteredAdapters.slice((this.page - 1) * 10, this.page * 10);
},
},
methods: {
publishAdapter() {
if (!this.$refs.newAdapterForm.validate()) {
return;
}
this.dialog = false;
const title = encodeURIComponent(
`Adapter: ${this.newAdapter.name}`
).replace(/%2B/gi, "+");
const body = encodeURIComponent(
`
**协议名称**
${this.newAdapter.name}
**协议功能**
${this.newAdapter.desc}
**PyPI 项目名**
${this.newAdapter.link}
**协议 import 包名**
${this.newAdapter.id}
**协议项目仓库/主页链接**
${this.newAdapter.repo}
<!-- DO NOT EDIT ! -->
<!--
- id: ${this.newAdapter.id}
- link: ${this.newAdapter.link}
- name: ${this.newAdapter.name}
- desc: ${this.newAdapter.desc}
- repo: ${this.newAdapter.repo}
-->
`.trim()
).replace(/%2B/gi, "+");
window.open(
`https://github.com/nonebot/nonebot2/issues/new?title=${title}&body=${body}&labels=Adapter`
);
},
},
};
</script>

View File

@ -1,194 +0,0 @@
<template>
<v-card flat class="bots">
<v-row>
<v-col cols="12" sm="6">
<v-text-field
v-model="filterText"
dense
rounded
outlined
clearable
hide-details
label="搜索机器人"
>
<template v-slot:prepend-inner>
<div class="v-input__icon v-input__icon--prepend-inner">
<v-icon small>fa-filter</v-icon>
</div>
</template>
</v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-dialog v-model="dialog" max-width="600px">
<template v-slot:activator="{ on, attrs }">
<v-btn dark block color="primary" v-bind="attrs" v-on="on"
>发布机器人
</v-btn>
</template>
<v-card>
<v-card-title>
<span class="headline">机器人信息</span>
</v-card-title>
<v-card-text>
<v-form ref="newBotForm" v-model="valid" lazy-validation>
<v-container>
<v-row>
<v-col cols="12">
<v-text-field
v-model="newBot.name"
:rules="rules"
label="机器人名称"
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
v-model="newBot.desc"
:rules="rules"
label="机器人介绍"
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
v-model="newBot.repo"
:rules="rules"
label="仓库/主页链接"
required
></v-text-field>
</v-col>
</v-row>
</v-container>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text @click="dialog = false">
关闭
</v-btn>
<v-btn
:disabled="!valid"
color="blue darken-1"
text
@click="publishBot"
>
发布
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-pagination
v-model="page"
:length="pageNum"
prev-icon="fa-caret-left"
next-icon="fa-caret-right"
></v-pagination>
</v-col>
</v-row>
<v-row>
<v-col cols="12" sm="6" v-for="(bot, index) in displayBots" :key="index">
<PublishCard
:name="bot.name"
:desc="bot.desc"
:author="bot.author"
:link="bot.repo"
></PublishCard>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-pagination
v-model="page"
:length="pageNum"
prev-icon="fa-caret-left"
next-icon="fa-caret-right"
></v-pagination>
</v-col>
</v-row>
</v-card>
</template>
<script>
import PublishCard from "./PublishCard.vue";
import bots from "../public/bots.json";
export default {
name: "Bots",
components: {
PublishCard,
},
data() {
return {
bots: bots,
filterText: "",
page: 1,
dialog: false,
valid: false,
rules: [(v) => !!v || "This field is required"],
newBot: {
name: null,
desc: null,
repo: null,
},
};
},
computed: {
pageNum() {
return Math.ceil(this.filteredBots.length / 10);
},
filteredBots() {
return this.bots.filter((bot) => {
return (
bot.name.indexOf(this.filterText || "") != -1 ||
bot.desc.indexOf(this.filterText || "") != -1 ||
bot.author.indexOf(this.filterText || "") != -1
);
});
},
displayBots() {
return this.filteredBots.slice((this.page - 1) * 10, this.page * 10);
},
},
methods: {
publishBot() {
if (!this.$refs.newBotForm.validate()) {
return;
}
this.dialog = false;
const title = encodeURIComponent(`Bot: ${this.newBot.name}`).replace(
/%2B/gi,
"+"
);
const body = encodeURIComponent(
`
**机器人名称**
${this.newBot.name}
**机器人功能**
${this.newBot.desc}
**机器人项目仓库/主页链接**
${this.newBot.repo}
<!-- DO NOT EDIT ! -->
<!--
- name: ${this.newBot.name}
- desc: ${this.newBot.desc}
- repo: ${this.newBot.repo}
-->
`.trim()
).replace(/%2B/gi, "+");
window.open(
`https://github.com/nonebot/nonebot2/issues/new?title=${title}&body=${body}&labels=Bot`
);
},
},
};
</script>

View File

@ -1,219 +0,0 @@
<template>
<div class="qq-chat">
<v-app>
<v-main>
<v-card class="elevation-6">
<v-toolbar color="primary" dark dense flat>
<v-row no-gutters>
<v-col>
<v-row no-gutters justify="space-between">
<v-col cols="auto">
<v-icon small>fa-chevron-left</v-icon>
</v-col>
<v-col cols="auto">
<h3>🔥</h3>
</v-col>
</v-row>
</v-col>
<v-col cols="auto">
<h3 class="white--text">NoneBot</h3>
</v-col>
<v-col class="text-right">
<v-icon small>fa-user</v-icon>
</v-col>
</v-row>
</v-toolbar>
<v-container fluid ref="chat" class="chat chat-bg">
<template v-for="(item, index) in messages">
<v-row
v-if="item.position === 'right'"
justify="end"
:key="index"
class="message wow animate__fadeInRight"
data-wow-duration="0.7s"
>
<div
class="message-box"
v-html="
item.msg.replace(/\n/g, '<br/>').replace(/ /g, '&nbsp;')
"
></div>
<v-avatar color="blue lighten-2" size="36">
<v-icon small>fa-user</v-icon>
</v-avatar>
</v-row>
<v-row
v-else-if="item.position === 'left'"
justify="start"
:key="index"
class="message wow animate__fadeInLeft"
data-wow-duration="0.7s"
>
<v-avatar color="transparent" size="36">
<v-img src="/logo.png"></v-img>
</v-avatar>
<div
class="message-box"
v-html="
item.msg.replace(/\n/g, '<br/>').replace(/ /g, '&nbsp;')
"
></div>
</v-row>
<v-row
v-else
justify="center"
:key="index"
class="notify mt-1 wow animate__fadeIn"
data-wow-duration="0.7s"
>
<div class="notify-box">
<span style="display: inline; white-space: nowrap">
<v-icon x-small color="blue" left>fa-info-circle</v-icon>
</span>
<span
v-html="
item.msg.replace(/\n/g, '<br/>').replace(/ /g, '&nbsp;')
"
></span>
</div>
</v-row>
</template>
</v-container>
<v-container fluid class="chat-bg py-0">
<v-row dense class="mx-0">
<v-col>
<v-text-field
dense
solo
hide-details
height="28px"
></v-text-field>
</v-col>
<v-col cols="auto">
<v-btn
style="font-size: 0.8rem"
color="primary"
small
rounded
depressed
>发送</v-btn
>
</v-col>
</v-row>
<v-row class="text-center" no-gutters>
<v-col class="pa-1" cols="2">
<v-icon small>fa-microphone</v-icon>
</v-col>
<v-col class="pa-1" cols="2">
<v-icon small>fa-image</v-icon>
</v-col>
<v-col class="pa-1" cols="2">
<v-icon small>fa-camera</v-icon>
</v-col>
<v-col class="pa-1" cols="2">
<v-icon small>fa-wallet</v-icon>
</v-col>
<v-col class="pa-1" cols="2">
<v-icon small>fa-smile-wink</v-icon>
</v-col>
<v-col class="pa-1" cols="2">
<v-icon small>fa-plus-circle</v-icon>
</v-col>
</v-row>
</v-container>
</v-card>
</v-main>
</v-app>
</div>
</template>
<script>
import { WOW } from "wowjs";
import "animate.css/animate.min.css";
export default {
name: "Messenger",
props: {
messages: {
type: Array,
default: () => [],
},
},
methods: {
initWOW: function() {
new WOW({
noxClass: "wow",
animateClass: "animate__animated",
offset: 0,
mobile: true,
live: true,
}).init();
},
},
mounted() {
this.initWOW();
},
};
</script>
<style scoped>
.wow {
visibility: hidden;
}
.chat {
min-height: 150px;
overflow-x: hidden;
}
.chat-bg {
background-color: #f3f6f9;
}
.message {
position: relative;
margin: 0;
}
.message .message-box {
position: relative;
width: fit-content;
max-width: 55%;
border-radius: 0.5rem;
padding: 0.6rem 0.8rem;
margin: 0.4rem 0.8rem;
background-color: #fff;
}
.message .message-box::after {
content: "";
position: absolute;
right: 100%;
top: 0;
width: 8px;
height: 12px;
color: #fff;
border: 0 solid transparent;
border-bottom: 7px solid;
border-radius: 0 0 0 8px;
}
.message.justify-end .message-box::after {
left: 100%;
right: auto;
border-radius: 0 0 8px 0;
}
.notify {
position: relative;
}
.notify .notify-box {
max-width: 70%;
background: #e0e0e0;
border-radius: 10px;
padding: 5px 12px;
font-size: 12px;
}
</style>
<style>
.v-application--wrap {
min-height: 0 !important;
}
</style>

View File

@ -1,230 +0,0 @@
<template>
<v-card flat class="plugins">
<v-row>
<v-col cols="12" sm="6">
<v-text-field
v-model="filterText"
dense
rounded
outlined
clearable
hide-details
label="搜索插件"
>
<template v-slot:prepend-inner>
<div class="v-input__icon v-input__icon--prepend-inner">
<v-icon small>fa-filter</v-icon>
</div>
</template>
</v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-dialog v-model="dialog" max-width="600px">
<template v-slot:activator="{ on, attrs }">
<v-btn dark block color="primary" v-bind="attrs" v-on="on"
>发布插件
</v-btn>
</template>
<v-card>
<v-card-title>
<span class="headline">插件信息</span>
</v-card-title>
<v-card-text>
<v-form ref="newPluginForm" v-model="valid" lazy-validation>
<v-container>
<v-row>
<v-col cols="12">
<v-text-field
v-model="newPlugin.name"
:rules="rules"
label="插件名称"
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
v-model="newPlugin.desc"
:rules="rules"
label="插件介绍"
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
v-model="newPlugin.link"
:rules="rules"
label="PyPI 项目名"
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
v-model="newPlugin.id"
:rules="rules"
label="插件 import 包名"
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
v-model="newPlugin.repo"
:rules="rules"
label="仓库/主页链接"
required
></v-text-field>
</v-col>
</v-row>
</v-container>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text @click="dialog = false">
关闭
</v-btn>
<v-btn
:disabled="!valid"
color="blue darken-1"
text
@click="publishPlugin"
>
发布
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-pagination
v-model="page"
:length="pageNum"
prev-icon="fa-caret-left"
next-icon="fa-caret-right"
></v-pagination>
</v-col>
</v-row>
<v-row>
<v-col
cols="12"
sm="6"
v-for="(plugin, index) in displayPlugins"
:key="index"
>
<PublishCard
:name="plugin.name"
:desc="plugin.desc"
:id="plugin.id"
:author="plugin.author"
:link="plugin.repo"
text="点此复制安装命令"
:command="`nb plugin install ${plugin.id}`"
></PublishCard>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-pagination
v-model="page"
:length="pageNum"
prev-icon="fa-caret-left"
next-icon="fa-caret-right"
></v-pagination>
</v-col>
</v-row>
</v-card>
</template>
<script>
import PublishCard from "./PublishCard.vue";
import plugins from "../public/plugins.json";
export default {
name: "Plugins",
components: {
PublishCard,
},
data() {
return {
plugins: plugins,
filterText: "",
page: 1,
dialog: false,
valid: false,
rules: [(v) => !!v || "This field is required"],
newPlugin: {
name: null,
desc: null,
id: null,
link: null,
repo: null,
},
};
},
computed: {
pageNum() {
return Math.ceil(this.filteredPlugins.length / 10);
},
filteredPlugins() {
return this.plugins.filter((plugin) => {
return (
plugin.id.indexOf(this.filterText || "") != -1 ||
plugin.name.indexOf(this.filterText || "") != -1 ||
plugin.desc.indexOf(this.filterText || "") != -1 ||
plugin.author.indexOf(this.filterText || "") != -1
);
});
},
displayPlugins() {
return this.filteredPlugins.slice((this.page - 1) * 10, this.page * 10);
},
},
methods: {
publishPlugin() {
if (!this.$refs.newPluginForm.validate()) {
return;
}
this.dialog = false;
const title = encodeURIComponent(
`Plugin: ${this.newPlugin.name}`
).replace(/%2B/gi, "+");
const body = encodeURIComponent(
`
**插件名称**
${this.newPlugin.name}
**插件功能**
${this.newPlugin.desc}
**PyPI 项目名**
${this.newPlugin.link}
**插件 import 包名**
${this.newPlugin.id}
**插件项目仓库/主页链接**
${this.newPlugin.repo}
<!-- DO NOT EDIT ! -->
<!--
- id: ${this.newPlugin.id}
- link: ${this.newPlugin.link}
- name: ${this.newPlugin.name}
- desc: ${this.newPlugin.desc}
- repo: ${this.newPlugin.repo}
-->
`.trim()
).replace(/%2B/gi, "+");
window.open(
`https://github.com/nonebot/nonebot2/issues/new?title=${title}&body=${body}&labels=Plugin`
);
},
},
};
</script>

View File

@ -1,84 +0,0 @@
<template>
<v-card>
<v-card-title>
{{ name }}
<v-spacer></v-spacer>
<a
class="repo-link"
v-if="repoLink(link)"
rel="noopener noreferrer"
target="_blank"
:title="link"
:href="repoLink(link)"
>
<v-icon>fab fa-github</v-icon>
</a>
</v-card-title>
<v-card-text>{{ desc }}</v-card-text>
<v-card-text>
<template v-if="id">
<v-icon x-small>fa-fingerprint</v-icon>
{{ id }}
</template>
<v-icon x-small class="ml-2">fa-user</v-icon>
{{ author }}
</v-card-text>
<v-card-actions v-if="showCommand">
<v-btn block depressed class="btn-copy" @click="copyCommand()">
{{ text }}
<v-icon right small>fa-copy</v-icon>
</v-btn>
<v-snackbar v-model="snackbar">复制成功</v-snackbar>
</v-card-actions>
</v-card>
</template>
<script>
import copy from "copy-to-clipboard";
export default {
props: {
name: String,
desc: String,
id: String,
author: String,
link: String,
text: String,
command: String,
},
data() {
return {
snackbar: false,
};
},
computed: {
showCommand() {
return this.text && this.command;
},
},
methods: {
repoLink(repo) {
if (repo) {
return /^https?:/.test(repo) ? repo : `https://github.com/${repo}`;
}
return null;
},
copyCommand() {
copy(this.command, {
format: "text/plain",
});
this.snackbar = true;
},
},
};
</script>
<style scoped>
.repo-link {
text-decoration: none !important;
display: inline-block;
}
.repo-link:hover i {
color: #ea5252;
}
</style>

View File

@ -1,70 +0,0 @@
<template>
<div class="store">
<v-app>
<v-main>
<v-card>
<v-toolbar dense flat>
<v-tabs v-model="tab" centered>
<v-tab v-for="(name, index) in tabs" :key="index">{{
name
}}</v-tab>
</v-tabs>
</v-toolbar>
<v-tabs-items class="sub-item pt-1" v-model="tab">
<v-tab-item>
<Adapter></Adapter>
</v-tab-item>
<v-tab-item>
<Plugin></Plugin>
</v-tab-item>
<v-tab-item>
<Bot></Bot>
</v-tab-item>
</v-tabs-items>
</v-card>
</v-main>
</v-app>
</div>
</template>
<script>
import Adapter from "./Adapter.vue";
import Plugin from "./Plugin.vue";
import Bot from "./Bot.vue";
export default {
name: "Store",
components: {
Adapter,
Plugin,
Bot,
},
data() {
return {
tab: 1,
tabs: {
0: "协议",
1: "插件",
2: "机器人",
},
};
},
computed: {},
methods: {},
};
</script>
<style>
.v-application--wrap {
min-height: 0 !important;
}
</style>
<style scoped>
.store {
margin: 0 -20px;
}
.sub-item {
padding: 0 10px;
}
</style>

View File

@ -183,19 +183,20 @@ class BotParam(Param):
) -> Optional["BotParam"]:
if param.default == param.empty:
if generic_check_issubclass(param.annotation, Bot):
dependent.pre_checkers.append(
_BotChecker(
Required,
field=ModelField(
name="",
type_=param.annotation,
class_validators=None,
model_config=CustomConfig,
default=None,
required=True,
),
if param.annotation is not Bot:
dependent.pre_checkers.append(
_BotChecker(
Required,
field=ModelField(
name="",
type_=param.annotation,
class_validators=None,
model_config=CustomConfig,
default=None,
required=True,
),
)
)
)
return cls(Required)
elif param.annotation == param.empty and name == "bot":
return cls(Required)
@ -223,19 +224,20 @@ class EventParam(Param):
) -> Optional["EventParam"]:
if param.default == param.empty:
if generic_check_issubclass(param.annotation, Event):
dependent.pre_checkers.append(
_EventChecker(
Required,
field=ModelField(
name="",
type_=param.annotation,
class_validators=None,
model_config=CustomConfig,
default=None,
required=True,
),
if param.annotation is not Event:
dependent.pre_checkers.append(
_EventChecker(
Required,
field=ModelField(
name="",
type_=param.annotation,
class_validators=None,
model_config=CustomConfig,
default=None,
required=True,
),
)
)
)
return cls(Required)
elif param.annotation == param.empty and name == "event":
return cls(Required)

View File

@ -15,3 +15,252 @@ options:
## 添加一个处理依赖
## 事件处理流程
## 获取上下文信息
### Bot
```python {7-9}
from typing import Union
from nonebot.adapters import Bot
from nonebot.adapters.ding import Bot as DingBot
from nonebot.adapters.onebot.v11 import Bot as OneBotV11Bot
async def _(foo: Bot): ...
async def _(foo: Union[DingBot, OneBotV11Bot]): ...
async def _(bot): ... # 兼容性处理
```
### Event
```python {6-8}
from typing import Union
from nonebot.adapters import Event
from nonebot.adapters.onebot.v11 import PrivateMessageEvent, GroupMessageEvent
async def _(foo: Event): ...
async def _(foo: Union[PrivateMessageEvent, GroupMessageEvent]): ...
async def _(event): ... # 兼容性处理
```
### EventType
```python {3}
from nonebot.params import EventType
async def _(foo: str = EventType()): ...
```
### EventMessage
```python {4}
from nonebot.adapters import Message
from nonebot.params import EventMessage
async def _(foo: str = EventMessage()): ...
```
### EventPlainText
```python {3}
from nonebot.params import EventPlainText
async def _(foo: str = EventPlainText()): ...
```
### EventToMe
```python {3}
from nonebot.params import EventToMe
async def _(foo: bool = EventToMe()): ...
```
### State
```python {4}
from nonebot.params import State
from nonebot.typing import T_State
async def _(foo: T_State = State()): ...
```
### Command
```python {7}
from nonebot import on_command
from nonebot.params import Command
matcher = on_command("cmd")
@matcher.handle()
async def _(foo: Tuple[str, ...] = Command()): ...
```
### CommandArg
```python {8}
from nonebot import on_command
from nonebot.adapters import Message
from nonebot.params import CommandArg
matcher = on_command("cmd")
@matcher.handle()
async def _(foo: Message = CommandArg()): ...
```
### ShellCommandArgs
```python {7}
from nonebot import on_command
from nonebot.params import ShellCommandArgs
matcher = on_shell_command("cmd", parser)
@matcher.handle()
async def _(foo: Dict[str, Any] = ShellCommandArgs()): ...
```
### ShellCommandArgv
```python {7}
from nonebot import on_command
from nonebot.params import ShellCommandArgs
matcher = on_shell_command("cmd")
@matcher.handle()
async def _(foo: List[str] = ShellCommandArgv()): ...
```
### RegexMatched
```python {7}
from nonebot import on_regex
from nonebot.params import RegexMatched
matcher = on_regex("regex")
@matcher.handle()
async def _(foo: str = RegexMatched()): ...
```
### RegexGroup
```python {7}
from nonebot import on_regex
from nonebot.params import RegexGroup
matcher = on_regex("regex")
@matcher.handle()
async def _(foo: Tuple[Any, ...] = RegexGroup()): ...
```
### RegexDict
```python {7}
from nonebot import on_regex
from nonebot.params import RegexDict
matcher = on_regex("regex")
@matcher.handle()
async def _(foo: Dict[str, Any] = RegexDict()): ...
```
### Matcher
```python {7}
from nonebot import on_message
from nonebot.matcher import Matcher
foo = on_message()
@foo.handle()
async def _(matcher: Matcher): ...
```
### Received
```python {8}
from nonebot import on_message
from nonebot.adapters import Event
from nonebot.params import Received
matcher = on_message()
@matcher.receive("id")
async def _(foo: Event = Received("id")): ...
```
### LastReceived
```python {8}
from nonebot import on_message
from nonebot.adapters import Event
from nonebot.params import LastReceived
matcher = on_message()
@matcher.receive("any")
async def _(foo: Event = LastReceived()): ...
```
### Arg
```python {8-9}
from nonebot.params import Arg
from nonebot import on_message
from nonebot.adapters import Message
matcher = on_message()
@matcher.got("key")
async def _(key: Message = Arg()): ...
async def _(foo: Message = Arg("key")): ...
```
### ArgStr
```python {7-8}
from nonebot import on_message
from nonebot.params import ArgStr
matcher = on_message()
@matcher.got("key")
async def _(key: str = ArgStr()): ...
async def _(foo: str = ArgStr("key")): ...
```
### ArgPlainText
```python {7-8}
from nonebot import on_message
from nonebot.params import ArgPlainText
matcher = on_message()
@matcher.got("key")
async def _(key: str = ArgPlainText()): ...
async def _(foo: str = ArgPlainText("key")): ...
```
### Exception
```python {4}
from nonebot.message import run_postprocessor
@run_postprocessor
async def _(e: Exception): ...
```
### Default
```python {1}
async def _(foo="bar"): ...
```

View File

@ -1,12 +1,20 @@
import React from "react";
import clsx from "clsx";
import React, { useRef, useState } from "react";
import { ChromePicker } from "react-color";
import { usePagination } from "react-use-pagination";
import adapters from "../../static/adapters.json";
import { useFilteredObjs } from "../libs/store";
import { Tag, useFilteredObjs } from "../libs/store";
import Card from "./Card";
import Modal from "./Modal";
import ModalAction from "./ModalAction";
import ModalContent from "./ModalContent";
import ModalTitle from "./ModalTitle";
import Paginate from "./Paginate";
import TagComponent from "./Tag";
export default function Adapter(): JSX.Element {
const [modalOpen, setModalOpen] = useState<boolean>(false);
const {
filter,
setFilter,
@ -20,6 +28,93 @@ export default function Adapter(): JSX.Element {
const { startIndex, endIndex } = props;
const currentAdapters = filteredAdapters.slice(startIndex, endIndex + 1);
const [form, setForm] = useState<{
name: string;
desc: string;
projectLink: string;
moduleName: string;
homepage: string;
}>({ name: "", desc: "", projectLink: "", moduleName: "", homepage: "" });
const ref = useRef<HTMLInputElement>(null);
const [tags, setTags] = useState<Tag[]>([]);
const [label, setLabel] = useState<string>("");
const [color, setColor] = useState<string>("#ea5252");
const onSubmit = () => {
setModalOpen(false);
const title = encodeURIComponent(`Adapter: ${form.name}`).replace(
/%2B/gi,
"+"
);
const body = encodeURIComponent(
`
****
${form.name}
****
${form.desc}
**PyPI **
${form.projectLink}
** import **
${form.moduleName}
**/**
${form.homepage}
****
${JSON.stringify(tags)}
`.trim()
).replace(/%2B/gi, "+");
window.open(
`https://github.com/nonebot/nonebot2/issues/new?title=${title}&body=${body}&labels=Adapter`
);
};
const onChange = (event) => {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
setForm({
...form,
[name]: value,
});
event.preventDefault();
};
const onChangeLabel = (event) => {
setLabel(event.target.value);
};
const onChangeColor = (color) => {
setColor(color.hex);
};
const validateTag = () => {
return label.length >= 1 && label.length <= 10;
};
const newTag = () => {
if (tags.length >= 3) {
return;
}
if (validateTag()) {
const tag = { label, color };
setTags([...tags, tag]);
}
};
const delTag = (index: number) => {
setTags(tags.filter((_, i) => i !== index));
};
const insertTagType = (text: string) => {
setLabel(text + label);
ref.current.value = text + label;
};
return (
<>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-4 px-4">
@ -29,7 +124,10 @@ export default function Adapter(): JSX.Element {
placeholder="搜索适配器"
onChange={(event) => setFilter(event.target.value)}
/>
<button className="w-full rounded-lg bg-hero text-white">
<button
className="w-full rounded-lg bg-hero text-white"
onClick={() => setModalOpen(true)}
>
</button>
</div>
@ -44,6 +142,129 @@ export default function Adapter(): JSX.Element {
<div className="grid grid-cols-1 p-4">
<Paginate {...props} />
</div>
<Modal active={modalOpen} setActive={setModalOpen}>
<ModalTitle title={"适配器信息"} />
<ModalContent>
<form onSubmit={onSubmit}>
<div className="grid grid-cols-1 gap-4 p-4">
<label className="flex flex-wrap">
<span className="mr-2">:</span>
<input
type="text"
name="name"
maxLength={20}
className="px-2 flex-grow rounded bg-light-nonepress-200 dark:bg-dark-nonepress-200"
onChange={onChange}
/>
</label>
<label className="flex flex-wrap">
<span className="mr-2">:</span>
<input
type="text"
name="desc"
className="px-2 flex-grow rounded bg-light-nonepress-200 dark:bg-dark-nonepress-200"
onChange={onChange}
/>
</label>
<label className="flex flex-wrap">
<span className="mr-2">PyPI :</span>
<input
type="text"
name="projectLink"
className="px-2 flex-grow rounded bg-light-nonepress-200 dark:bg-dark-nonepress-200"
onChange={onChange}
/>
</label>
<label className="flex flex-wrap">
<span className="mr-2">import :</span>
<input
type="text"
name="moduleName"
className="px-2 flex-grow rounded bg-light-nonepress-200 dark:bg-dark-nonepress-200"
onChange={onChange}
/>
</label>
<label className="flex flex-wrap">
<span className="mr-2">/:</span>
<input
type="text"
name="homepage"
className="px-2 flex-grow rounded bg-light-nonepress-200 dark:bg-dark-nonepress-200"
onChange={onChange}
/>
</label>
</div>
</form>
<div className="px-4">
<label className="flex flex-wrap">
<span className="mr-2">:</span>
{tags.map((tag, index) => (
<TagComponent
key={index}
{...tag}
className="cursor-pointer"
onClick={() => delTag(index)}
/>
))}
</label>
</div>
<div className="px-4 pt-4">
<input
ref={ref}
type="text"
className="px-2 flex-grow rounded bg-light-nonepress-200 dark:bg-dark-nonepress-200"
onChange={onChangeLabel}
/>
<ChromePicker
className="mt-2"
color={color}
disableAlpha={true}
onChangeComplete={onChangeColor}
/>
<div className="flex flex-wrap mt-2 items-center">
<span className="mr-2">Type:</span>
<button
className="px-2 h-9 min-w-[64px] rounded text-hero hover:bg-hero hover:bg-opacity-[.08]"
onClick={() => insertTagType("a:")}
>
Adapter
</button>
<button
className="px-2 h-9 min-w-[64px] rounded text-hero hover:bg-hero hover:bg-opacity-[.08]"
onClick={() => insertTagType("t:")}
>
Topic
</button>
</div>
<div className="flex mt-2">
<TagComponent label={label} color={color} />
<button
className={clsx(
"px-2 h-9 min-w-[64px] rounded text-hero hover:bg-hero hover:bg-opacity-[.08]",
{ "pointer-events-none opacity-60": !validateTag() }
)}
onClick={newTag}
>
</button>
</div>
</div>
</ModalContent>
<ModalAction>
<button
className="px-2 h-9 min-w-[64px] rounded text-hero hover:bg-hero hover:bg-opacity-[.08]"
onClick={() => setModalOpen(false)}
>
</button>
<button
className="ml-2 px-2 h-9 min-w-[64px] rounded text-hero hover:bg-hero hover:bg-opacity-[.08]"
onClick={onSubmit}
>
</button>
</ModalAction>
</Modal>
</>
);
}

View File

@ -1,12 +1,20 @@
import React from "react";
import clsx from "clsx";
import React, { useRef, useState } from "react";
import { ChromePicker } from "react-color";
import { usePagination } from "react-use-pagination";
import bots from "../../static/bots.json";
import { useFilteredObjs } from "../libs/store";
import { Tag, useFilteredObjs } from "../libs/store";
import Card from "./Card";
import Modal from "./Modal";
import ModalAction from "./ModalAction";
import ModalContent from "./ModalContent";
import ModalTitle from "./ModalTitle";
import Paginate from "./Paginate";
import TagComponent from "./Tag";
export default function Adapter(): JSX.Element {
const [modalOpen, setModalOpen] = useState<boolean>(false);
const {
filter,
setFilter,
@ -20,6 +28,80 @@ export default function Adapter(): JSX.Element {
const { startIndex, endIndex } = props;
const currentBots = filteredBots.slice(startIndex, endIndex + 1);
const [form, setForm] = useState<{
name: string;
desc: string;
homepage: string;
}>({ name: "", desc: "", homepage: "" });
const ref = useRef<HTMLInputElement>(null);
const [tags, setTags] = useState<Tag[]>([]);
const [label, setLabel] = useState<string>("");
const [color, setColor] = useState<string>("#ea5252");
const onSubmit = () => {
setModalOpen(false);
const title = encodeURIComponent(`Bot: ${form.name}`).replace(/%2B/gi, "+");
const body = encodeURIComponent(
`
****
${form.name}
****
${form.desc}
**/**
${form.homepage}
****
${JSON.stringify(tags)}
`.trim()
).replace(/%2B/gi, "+");
window.open(
`https://github.com/nonebot/nonebot2/issues/new?title=${title}&body=${body}&labels=Bot`
);
};
const onChange = (event) => {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
setForm({
...form,
[name]: value,
});
event.preventDefault();
};
const onChangeLabel = (event) => {
setLabel(event.target.value);
};
const onChangeColor = (color) => {
setColor(color.hex);
};
const validateTag = () => {
return label.length >= 1 && label.length <= 10;
};
const newTag = () => {
if (tags.length >= 3) {
return;
}
if (validateTag()) {
const tag = { label, color };
setTags([...tags, tag]);
}
};
const delTag = (index: number) => {
setTags(tags.filter((_, i) => i !== index));
};
const insertTagType = (text: string) => {
setLabel(text + label);
ref.current.value = text + label;
};
return (
<>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-4 px-4">
@ -29,7 +111,10 @@ export default function Adapter(): JSX.Element {
placeholder="搜索机器人"
onChange={(event) => setFilter(event.target.value)}
/>
<button className="w-full rounded-lg bg-hero text-white">
<button
className="w-full rounded-lg bg-hero text-white"
onClick={() => setModalOpen(true)}
>
</button>
</div>
@ -44,6 +129,111 @@ export default function Adapter(): JSX.Element {
<div className="grid grid-cols-1 p-4">
<Paginate {...props} />
</div>
<Modal active={modalOpen} setActive={setModalOpen}>
<ModalTitle title={"机器人信息"} />
<ModalContent>
<form onSubmit={onSubmit}>
<div className="grid grid-cols-1 gap-4 p-4">
<label className="flex flex-wrap">
<span className="mr-2">:</span>
<input
type="text"
name="name"
maxLength={20}
className="px-2 flex-grow rounded bg-light-nonepress-200 dark:bg-dark-nonepress-200"
onChange={onChange}
/>
</label>
<label className="flex flex-wrap">
<span className="mr-2">:</span>
<input
type="text"
name="desc"
className="px-2 flex-grow rounded bg-light-nonepress-200 dark:bg-dark-nonepress-200"
onChange={onChange}
/>
</label>
<label className="flex flex-wrap">
<span className="mr-2">/:</span>
<input
type="text"
name="homepage"
className="px-2 flex-grow rounded bg-light-nonepress-200 dark:bg-dark-nonepress-200"
onChange={onChange}
/>
</label>
</div>
</form>
<div className="px-4">
<label className="flex flex-wrap">
<span className="mr-2">:</span>
{tags.map((tag, index) => (
<TagComponent
key={index}
{...tag}
className="cursor-pointer"
onClick={() => delTag(index)}
/>
))}
</label>
</div>
<div className="px-4 pt-4">
<input
ref={ref}
type="text"
className="px-2 flex-grow rounded bg-light-nonepress-200 dark:bg-dark-nonepress-200"
onChange={onChangeLabel}
/>
<ChromePicker
className="mt-2"
color={color}
disableAlpha={true}
onChangeComplete={onChangeColor}
/>
<div className="flex flex-wrap mt-2 items-center">
<span className="mr-2">Type:</span>
<button
className="px-2 h-9 min-w-[64px] rounded text-hero hover:bg-hero hover:bg-opacity-[.08]"
onClick={() => insertTagType("a:")}
>
Adapter
</button>
<button
className="px-2 h-9 min-w-[64px] rounded text-hero hover:bg-hero hover:bg-opacity-[.08]"
onClick={() => insertTagType("t:")}
>
Topic
</button>
</div>
<div className="flex mt-2">
<TagComponent label={label} color={color} />
<button
className={clsx(
"px-2 h-9 min-w-[64px] rounded text-hero hover:bg-hero hover:bg-opacity-[.08]",
{ "pointer-events-none opacity-60": !validateTag() }
)}
onClick={newTag}
>
</button>
</div>
</div>
</ModalContent>
<ModalAction>
<button
className="px-2 h-9 min-w-[64px] rounded text-hero hover:bg-hero hover:bg-opacity-[.08]"
onClick={() => setModalOpen(false)}
>
</button>
<button
className="ml-2 px-2 h-9 min-w-[64px] rounded text-hero hover:bg-hero hover:bg-opacity-[.08]"
onClick={onSubmit}
>
</button>
</ModalAction>
</Modal>
</>
);
}

View File

@ -94,5 +94,13 @@
"homepage": "https://github.com/ssttkkl/PixivBot",
"tags": [],
"is_official": false
},
{
"name": "SeaBot_QQ",
"desc": "一个能够获取新闻资讯并推送至QQ的群聊机器人。",
"author": "B1ue1nWh1te",
"homepage": "https://github.com/B1ue1nWh1te/SeaBot_QQ",
"tags": [],
"is_official": false
}
]

View File

@ -688,5 +688,85 @@
"homepage": "https://github.com/yzyyz1387/nonebot_plugin_heisi",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_picsbank",
"project_link": "nonebot-plugin-picsbank",
"name": "picsbank",
"desc": "匹配图片进行回答",
"author": "Diaosi1111",
"homepage": "https://github.com/Diaosi1111/nonebot_plugin_picsbank",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_tvseries",
"project_link": "nonebot-plugin-tvseries",
"name": "剧集更新列表",
"desc": "获取聚集更新",
"author": "kexue-z",
"homepage": "https://github.com/kexue-z/nonebot-plugin-tvseries",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_lolmatch",
"project_link": "nonebot-plugin-lolmatch",
"name": "lol比赛信息",
"desc": "简单的lol比赛信息插件订阅后会定时推送当日比赛结果",
"author": "Diaosi1111",
"homepage": "https://github.com/Diaosi1111/nonebot_plugin_lolmatch",
"tags": [],
"is_official": false
},
{
"module_name": "OlivOS",
"project_link": "olivos.nb2",
"name": "OlivOS.nb2",
"desc": "在 NoneBot2 中加载 OlivOS 插件",
"author": "j1g5awi",
"homepage": "https://github.com/nonepkg/OlivOS.nb2",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_htmlrender",
"project_link": "nonebot-plugin-htmlrender",
"name": "通过浏览器来生成图片",
"desc": "通过playwright加一点点前端知识来简单的生成图片",
"author": "kexue-z",
"homepage": "https://github.com/kexue-z/nonebot-plugin-htmlrender",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_admin",
"project_link": "nonebot-plugin-admin",
"name": "简易群管",
"desc": "简易群管 踢 禁 改",
"author": "yzyyz1387",
"homepage": "https://github.com/yzyyz1387/nonebot_plugin_admin",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_logo",
"project_link": "nonebot-plugin-logo",
"name": "nonebot-plugin-logo",
"desc": "PornHub、Youtube 等风格logo生成",
"author": "MeetWq",
"homepage": "https://github.com/MeetWq/nonebot-plugin-logo",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_memes",
"project_link": "nonebot-plugin-memes",
"name": "Memes generator",
"desc": "表情包制作",
"author": "MeetWq",
"homepage": "https://github.com/MeetWq/nonebot-plugin-memes",
"tags": [],
"is_official": false
}
]