📝 Docs: 更新到 nonepress 3.8.0 (#4126)

This commit is contained in:
StarHeart
2026-08-01 16:57:44 +08:00
committed by GitHub
parent b38b1e5261
commit 1b88a6e77d
198 changed files with 2042 additions and 1641 deletions
+7 -6
View File
@@ -18,20 +18,21 @@
"source.organizeImports": "explicit" "source.organizeImports": "explicit"
} }
}, },
"oxc.fmt.configPath": ".oxfmtrc.json",
"[javascript]": { "[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "oxc.oxc-vscode"
}, },
"[html]": { "[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "oxc.oxc-vscode"
}, },
"[typescript]": { "[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "oxc.oxc-vscode"
}, },
"[javascriptreact]": { "[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "oxc.oxc-vscode"
}, },
"[typescriptreact]": { "[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "oxc.oxc-vscode"
}, },
"files.exclude": { "files.exclude": {
"**/__pycache__": true "**/__pycache__": true
@@ -46,7 +47,7 @@
"ms-python.vscode-pylance", "ms-python.vscode-pylance",
"charliermarsh.ruff", "charliermarsh.ruff",
"EditorConfig.EditorConfig", "EditorConfig.EditorConfig",
"esbenp.prettier-vscode", "oxc.oxc-vscode",
"bradlc.vscode-tailwindcss" "bradlc.vscode-tailwindcss"
] ]
} }
-6
View File
@@ -1,6 +0,0 @@
dist
node_modules
.yarn
.history
build
lib
-403
View File
@@ -1,403 +0,0 @@
const OFF = 0;
const WARNING = 1;
const ERROR = 2;
// Prevent importing lodash, usually for browser bundle size reasons
const LodashImportPatterns = ["lodash", "lodash.**", "lodash/**"];
module.exports = {
root: true,
env: {
browser: true,
commonjs: true,
node: true,
},
parser: "@typescript-eslint/parser",
parserOptions: {},
globals: {
JSX: true,
},
extends: [
"eslint:recommended",
"plugin:react-hooks/recommended",
"airbnb",
"plugin:@typescript-eslint/recommended",
// 'plugin:@typescript-eslint/recommended-requiring-type-checking',
// 'plugin:@typescript-eslint/strict',
"plugin:regexp/recommended",
"prettier",
"plugin:@docusaurus/all",
],
settings: {
"import/resolver": {
node: {
extensions: [".js", ".jsx", ".ts", ".tsx"],
},
},
},
reportUnusedDisableDirectives: true,
plugins: ["react-hooks", "@typescript-eslint", "regexp", "@docusaurus"],
rules: {
"react/jsx-uses-react": OFF, // JSX runtime: automatic
"react/react-in-jsx-scope": OFF, // JSX runtime: automatic
"array-callback-return": WARNING,
camelcase: WARNING,
"class-methods-use-this": OFF, // It's a way of allowing private variables.
curly: [WARNING, "all"],
"global-require": WARNING,
"lines-between-class-members": OFF,
"max-classes-per-file": OFF,
"max-len": [
WARNING,
{
code: Infinity, // Code width is already enforced by Prettier
tabWidth: 2,
comments: 80,
ignoreUrls: true,
ignorePattern: "(eslint-disable|@)",
},
],
"arrow-body-style": OFF,
"no-await-in-loop": OFF,
"no-case-declarations": WARNING,
"no-console": OFF,
"no-constant-binary-expression": ERROR,
"no-continue": OFF,
"no-control-regex": WARNING,
"no-else-return": OFF,
"no-empty": [WARNING, { allowEmptyCatch: true }],
"no-lonely-if": WARNING,
"no-nested-ternary": WARNING,
"no-param-reassign": [WARNING, { props: false }],
"no-prototype-builtins": WARNING,
"no-restricted-exports": OFF,
"no-restricted-properties": [
ERROR,
.../** @type {[string, string][]} */ ([
// TODO: TS doesn't make Boolean a narrowing function yet,
// so filter(Boolean) is problematic type-wise
// ['compact', 'Array#filter(Boolean)'],
["concat", "Array#concat"],
["drop", "Array#slice(n)"],
["dropRight", "Array#slice(0, -n)"],
["fill", "Array#fill"],
["filter", "Array#filter"],
["find", "Array#find"],
["findIndex", "Array#findIndex"],
["first", "foo[0]"],
["flatten", "Array#flat"],
["flattenDeep", "Array#flat(Infinity)"],
["flatMap", "Array#flatMap"],
["fromPairs", "Object.fromEntries"],
["head", "foo[0]"],
["indexOf", "Array#indexOf"],
["initial", "Array#slice(0, -1)"],
["join", "Array#join"],
// Unfortunately there's no great alternative to _.last yet
// Candidates: foo.slice(-1)[0]; foo[foo.length - 1]
// Array#at is ES2022; could replace _.nth as well
// ['last'],
["map", "Array#map"],
["reduce", "Array#reduce"],
["reverse", "Array#reverse"],
["slice", "Array#slice"],
["take", "Array#slice(0, n)"],
["takeRight", "Array#slice(-n)"],
["tail", "Array#slice(1)"],
]).map(([property, alternative]) => ({
object: "_",
property,
message: `Use ${alternative} instead.`,
})),
...[
"readdirSync",
"readFileSync",
"statSync",
"lstatSync",
"existsSync",
"pathExistsSync",
"realpathSync",
"mkdirSync",
"mkdirpSync",
"mkdirsSync",
"writeFileSync",
"writeJsonSync",
"outputFileSync",
"outputJsonSync",
"moveSync",
"copySync",
"copyFileSync",
"ensureFileSync",
"ensureDirSync",
"ensureLinkSync",
"ensureSymlinkSync",
"unlinkSync",
"removeSync",
"emptyDirSync",
].map((property) => ({
object: "fs",
property,
message: "Do not use sync fs methods.",
})),
],
"no-restricted-syntax": [
WARNING,
// Copied from airbnb, removed for...of statement, added export all
{
selector: "ForInStatement",
message:
"for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.",
},
{
selector: "LabeledStatement",
message:
"Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.",
},
{
selector: "WithStatement",
message:
"`with` is disallowed in strict mode because it makes code impossible to predict and optimize.",
},
{
selector: "ExportAllDeclaration",
message:
"Export all does't work well if imported in ESM due to how they are transpiled, and they can also lead to unexpected exposure of internal methods.",
},
// TODO make an internal plugin to ensure this
// {
// selector:
// @ 'ExportDefaultDeclaration > Identifier, ExportNamedDeclaration[source=null] > ExportSpecifier',
// message: 'Export in one statement'
// },
...["path", "fs-extra", "webpack", "lodash"].map((m) => ({
selector: `ImportDeclaration[importKind=value]:has(Literal[value=${m}]) > ImportSpecifier[importKind=value]`,
message:
"Default-import this, both for readability and interoperability with ESM",
})),
],
"no-template-curly-in-string": WARNING,
"no-unused-expressions": [
WARNING,
{ allowTaggedTemplates: true, allowShortCircuit: true },
],
"no-useless-escape": WARNING,
"no-void": [ERROR, { allowAsStatement: true }],
"prefer-destructuring": WARNING,
"prefer-named-capture-group": WARNING,
"prefer-template": WARNING,
yoda: WARNING,
"import/extensions": OFF,
// This rule doesn't yet support resolving .js imports when the actual file
// is .ts. Plus it's not all that useful when our code is fully TS-covered.
"import/no-unresolved": [
OFF,
{
// Ignore certain webpack aliases because they can't be resolved
ignore: [
"^@theme",
"^@docusaurus",
"^@generated",
"^@site",
"^@testing-utils",
],
},
],
"import/order": [
WARNING,
{
groups: [
"builtin",
"external",
"internal",
["parent", "sibling", "index"],
"type",
],
"newlines-between": "always",
pathGroups: [
// always put css import to the last, ref:
// https://github.com/import-js/eslint-plugin-import/issues/1239
{
pattern: "*.+(css|sass|less|scss|pcss|styl)",
group: "unknown",
patternOptions: { matchBase: true },
position: "after",
},
{ pattern: "react", group: "builtin", position: "before" },
{ pattern: "react-dom", group: "builtin", position: "before" },
{ pattern: "react-dom/**", group: "builtin", position: "before" },
{ pattern: "stream", group: "builtin", position: "before" },
{ pattern: "fs-extra", group: "builtin" },
{ pattern: "lodash", group: "external", position: "before" },
{ pattern: "clsx", group: "external", position: "before" },
// 'Bit weird to not use the `import/internal-regex` option, but this
// way, we can make `import type { Props } from "@theme/*"` appear
// before `import styles from "styles.module.css"`, which is what we
// always did. This should be removable once we stop using ambient
// module declarations for theme aliases.
{ pattern: "@theme/**", group: "internal" },
{ pattern: "@site/**", group: "internal" },
{ pattern: "@theme-init/**", group: "internal" },
{ pattern: "@theme-original/**", group: "internal" },
{ pattern: "@/components/**", group: "internal" },
{ pattern: "@/libs/**", group: "internal" },
{ pattern: "@/types/**", group: "type" },
],
pathGroupsExcludedImportTypes: [],
// example: let `import './nprogress.css';` after importing others
// in `packages/docusaurus-theme-classic/src/nprogress.ts`
// see more: https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/order.md#warnonunassignedimports-truefalse
warnOnUnassignedImports: true,
},
],
"import/prefer-default-export": OFF,
"jsx-a11y/click-events-have-key-events": WARNING,
"jsx-a11y/no-noninteractive-element-interactions": WARNING,
"jsx-a11y/html-has-lang": OFF,
"react-hooks/rules-of-hooks": ERROR,
"react-hooks/exhaustive-deps": ERROR,
// Sometimes we do need the props as a whole, e.g. when spreading
"react/destructuring-assignment": OFF,
"react/function-component-definition": [
WARNING,
{
namedComponents: "function-declaration",
unnamedComponents: "arrow-function",
},
],
"react/jsx-filename-extension": OFF,
"react/jsx-key": [ERROR, { checkFragmentShorthand: true }],
"react/jsx-no-useless-fragment": [ERROR, { allowExpressions: true }],
"react/jsx-props-no-spreading": OFF,
"react/no-array-index-key": OFF, // We build a static site, and nearly all components don't change.
"react/no-unstable-nested-components": [WARNING, { allowAsProps: true }],
"react/prefer-stateless-function": WARNING,
"react/prop-types": OFF,
"react/require-default-props": [
ERROR,
{ ignoreFunctionalComponents: true },
],
"@typescript-eslint/consistent-type-definitions": OFF,
"@typescript-eslint/require-await": OFF,
"@typescript-eslint/ban-ts-comment": [
ERROR,
{ "ts-expect-error": "allow-with-description" },
],
"@typescript-eslint/consistent-indexed-object-style": OFF,
"@typescript-eslint/consistent-type-imports": [
WARNING,
{ disallowTypeAnnotations: false },
],
"@typescript-eslint/explicit-module-boundary-types": WARNING,
"@typescript-eslint/method-signature-style": ERROR,
"@typescript-eslint/no-empty-function": OFF,
"@typescript-eslint/no-empty-interface": [
ERROR,
{
allowSingleExtends: true,
},
],
"@typescript-eslint/no-inferrable-types": OFF,
"@typescript-eslint/no-namespace": [WARNING, { allowDeclarations: true }],
"no-use-before-define": OFF,
"@typescript-eslint/no-use-before-define": [
ERROR,
{ functions: false, classes: false, variables: true },
],
"@typescript-eslint/no-non-null-assertion": OFF,
"no-redeclare": OFF,
"@typescript-eslint/no-redeclare": ERROR,
"no-shadow": OFF,
"@typescript-eslint/no-shadow": ERROR,
"no-unused-vars": OFF,
// We don't provide any escape hatches for this rule. Rest siblings and
// function placeholder params are always ignored, and any other unused
// locals must be justified with a disable comment.
"@typescript-eslint/no-unused-vars": [ERROR, { ignoreRestSiblings: true }],
"@typescript-eslint/prefer-optional-chain": ERROR,
"@docusaurus/no-html-links": ERROR,
"@docusaurus/prefer-docusaurus-heading": ERROR,
"@docusaurus/no-untranslated-text": [
WARNING,
{
ignoredStrings: [
"·",
"-",
"—",
"×",
"", // zwj: ​
"@",
"WebContainers",
"Twitter",
"GitHub",
"Dev.to",
"1.x",
],
},
],
},
overrides: [
{
files: ["packages/*/src/theme/**/*.{js,ts,tsx}"],
excludedFiles: "*.test.{js,ts,tsx}",
rules: {
"no-restricted-imports": [
"error",
{
patterns: LodashImportPatterns.concat(
// Prevents relative imports between React theme components
[
"../**",
"./**",
// Allows relative styles module import with consistent filename
"!./styles.module.css",
// Allows relative tailwind css import with consistent filename
"!./styles.css",
]
),
},
],
},
},
{
files: ["packages/*/src/theme/**/*.{js,ts,tsx}"],
rules: {
"import/no-named-export": ERROR,
},
},
{
files: ["*.d.ts"],
rules: {
"import/no-duplicates": OFF,
},
},
{
files: ["*.{ts,tsx}"],
rules: {
"no-undef": OFF,
"import/no-import-module-exports": OFF,
},
},
{
files: ["*.{js,mjs,cjs}"],
rules: {
// Make JS code directly runnable in Node.
"@typescript-eslint/no-var-requires": OFF,
"@typescript-eslint/explicit-module-boundary-types": OFF,
},
},
{
// Internal files where extraneous deps don't matter much at long as
// they run
files: ["website/**"],
rules: {
"import/no-extraneous-dependencies": OFF,
},
},
],
};
+1 -1
View File
@@ -46,7 +46,7 @@ jobs:
- name: Commit and Push - name: Commit and Push
run: | run: |
pnpm prettier pnpm format
git config user.name noneflow[bot] git config user.name noneflow[bot]
git config user.email 129742071+noneflow[bot]@users.noreply.github.com git config user.email 129742071+noneflow[bot]@users.noreply.github.com
git add . git add .
+1 -1
View File
@@ -40,7 +40,7 @@ jobs:
- name: Archive Files - name: Archive Files
run: | run: |
pnpm archive $(uv version --short) pnpm archive $(uv version --short)
pnpm prettier pnpm format
- name: Push Tag - name: Push Tag
run: | run: |
+36
View File
@@ -0,0 +1,36 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"arrowParens": "always",
"ignorePatterns": [
"node_modules",
".yarn",
".history",
"coverage",
"**/build/**",
"**/dist/**",
".docusaurus",
"*.svg",
"pnpm-lock.yaml",
"CITATION.cff",
"/*.md",
"*.yml",
"*.yaml",
".github/",
"assets/",
"nonebot/",
"packages/nonebot-plugin-docs/",
"scripts/",
"tests/",
"pyproject.toml",
"uv.lock",
"website/versions.json",
"website/versioned_docs/",
"website/versioned_sidebars/*.json",
"website/docs/tutorial/application.mdx"
],
"insertFinalNewline": true,
"printWidth": 80,
"singleQuote": false,
"sortPackageJson": false,
"trailingComma": "all"
}
-3
View File
@@ -1,3 +0,0 @@
.github/**/*.md
website/docs/tutorial/application.mdx
website/versioned_docs/*/tutorial/application.mdx
-21
View File
@@ -1,21 +0,0 @@
{
"tabWidth": 2,
"useTabs": false,
"endOfLine": "lf",
"arrowParens": "always",
"singleQuote": false,
"trailingComma": "es5",
"semi": true,
"overrides": [
{
"files": [
"**/devcontainer.json",
"**/tsconfig.json",
"**/tsconfig.*.json"
],
"options": {
"parser": "json"
}
}
]
}
+7
View File
@@ -0,0 +1,7 @@
# Stylelint runs on everything by default; we only lint CSS files.
*
!*/
!*.css
build
coverage
packages/nonebot-plugin-docs/
+18 -1
View File
@@ -1,10 +1,26 @@
module.exports = { module.exports = {
extends: ["stylelint-config-standard", "stylelint-prettier/recommended"], extends: ["stylelint-config-standard"],
rules: {
"selector-pseudo-class-no-unknown": [
true,
{
// :global is a CSS modules feature to escape from class name hashing
ignorePseudoClasses: ["global"],
},
],
"custom-property-empty-line-before": null,
"selector-id-pattern": null,
"declaration-empty-line-before": null,
"comment-empty-line-before": null,
"value-keyword-case": ["lower", { camelCaseSvgKeywords: true }],
},
overrides: [ overrides: [
{ {
files: ["*.css"], files: ["*.css"],
rules: { rules: {
"function-no-unknown": [true, { ignoreFunctions: ["theme"] }], "function-no-unknown": [true, { ignoreFunctions: ["theme"] }],
// Tailwind's screen() function in media queries
"media-query-no-invalid": [true, { ignoreFunctions: ["screen"] }],
"selector-class-pattern": [ "selector-class-pattern": [
"^([a-z][a-z0-9]*)(-[a-z0-9]+)*$", "^([a-z][a-z0-9]*)(-[a-z0-9]+)*$",
{ {
@@ -15,6 +31,7 @@ module.exports = {
], ],
}, },
}, },
// Must come after "*.css" so the camelCase pattern wins for CSS modules
{ {
files: ["*.module.css"], files: ["*.module.css"],
rules: { rules: {
+73
View File
@@ -0,0 +1,73 @@
import { defineConfig, globalIgnores } from "eslint/config";
import tseslint from "typescript-eslint";
import globals from "globals";
import js from "@eslint/js";
import react from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import importPlugin from "eslint-plugin-import";
// @ts-expect-error: no types provided
import jsxA11y from "eslint-plugin-jsx-a11y";
import docusaurus from "@docusaurus/eslint-plugin";
import regexp from "eslint-plugin-regexp";
import prettier from "eslint-config-prettier/flat";
import rules from "./eslint.rules";
const plugins = defineConfig([
js.configs.recommended,
tseslint.configs.recommended,
react.configs.flat.recommended,
reactHooks.configs.flat.recommended,
importPlugin.flatConfigs.recommended,
jsxA11y.flatConfigs.recommended,
regexp.configs["flat/recommended"],
prettier,
// The published @docusaurus/eslint-plugin doesn't provide a flat config yet
// This adapts its legacy "all" config to flat config
{
plugins: {
"@docusaurus": docusaurus,
},
rules: docusaurus.configs.all.rules,
},
]);
const ignores = globalIgnores([
"**/dist/**",
"**/lib/**",
"**/build/**",
"**/.docusaurus/**",
"node_modules",
".yarn",
".history",
"coverage",
]);
export default defineConfig(plugins, rules, ignores, {
languageOptions: {
ecmaVersion: 2022,
sourceType: "module",
globals: {
...globals.browser,
...globals.node,
...globals.commonjs,
JSX: true,
},
},
settings: {
"import/resolver": {
node: {
extensions: [".js", ".jsx", ".ts", ".tsx"],
},
},
react: {
version: "19",
},
},
linterOptions: {
reportUnusedDisableDirectives: true,
},
});
+392
View File
@@ -0,0 +1,392 @@
import { defineConfig } from "eslint/config";
const OFF = 0;
const WARNING = 1;
const ERROR = 2;
// Prevent importing lodash, usually for browser bundle size reasons
const LodashImportPatterns = ["lodash", "lodash.**", "lodash/**"];
export default defineConfig(
{
rules: {
"array-callback-return": WARNING,
camelcase: WARNING,
"class-methods-use-this": OFF, // It's a way of allowing private variables.
curly: [WARNING, "all"],
"global-require": OFF, // Deprecated, @typescript-eslint/no-require-import is enough
"no-alert": WARNING,
"lines-between-class-members": OFF,
"max-classes-per-file": OFF,
"max-len": [
WARNING,
{
code: Infinity, // Code width is already enforced by Prettier
tabWidth: 2,
comments: 80,
ignoreUrls: true,
ignorePattern: "(eslint-disable|@)",
},
],
"arrow-body-style": OFF,
"no-await-in-loop": OFF,
"no-case-declarations": WARNING,
"no-console": OFF,
"no-constant-binary-expression": ERROR,
"no-continue": OFF,
"no-control-regex": WARNING,
"no-else-return": OFF,
"no-empty": [WARNING, { allowEmptyCatch: true }],
"no-lonely-if": WARNING,
"no-nested-ternary": WARNING,
"no-param-reassign": [WARNING, { props: false }],
"no-prototype-builtins": WARNING,
"no-restricted-exports": OFF,
"no-restricted-properties": [
ERROR,
.../** @type {[string, string][]} */ [
// TODO: TS doesn't make Boolean a narrowing function yet,
// so filter(Boolean) is problematic type-wise
// ['compact', 'Array#filter(Boolean)'],
["concat", "Array#concat"],
["drop", "Array#slice(n)"],
["dropRight", "Array#slice(0, -n)"],
["fill", "Array#fill"],
["filter", "Array#filter"],
["find", "Array#find"],
["findIndex", "Array#findIndex"],
["first", "foo[0]"],
["flatten", "Array#flat"],
["flattenDeep", "Array#flat(Infinity)"],
["flatMap", "Array#flatMap"],
["fromPairs", "Object.fromEntries"],
["head", "foo[0]"],
["indexOf", "Array#indexOf"],
["initial", "Array#slice(0, -1)"],
["join", "Array#join"],
// Unfortunately there's no great alternative to _.last yet
// Candidates: foo.slice(-1)[0]; foo[foo.length - 1]
// Array#at is ES2022; could replace _.nth as well
// ['last'],
["map", "Array#map"],
["reduce", "Array#reduce"],
["reverse", "Array#reverse"],
["slice", "Array#slice"],
["take", "Array#slice(0, n)"],
["takeRight", "Array#slice(-n)"],
["tail", "Array#slice(1)"],
].map(([property, alternative]) => ({
object: "_",
property,
message: `Use ${alternative} instead.`,
})),
...[
"readdirSync",
"readFileSync",
"statSync",
"lstatSync",
"existsSync",
"pathExistsSync",
"realpathSync",
"mkdirSync",
"mkdirpSync",
"mkdirsSync",
"writeFileSync",
"writeJsonSync",
"outputFileSync",
"outputJsonSync",
"moveSync",
"copySync",
"copyFileSync",
"ensureFileSync",
"ensureDirSync",
"ensureLinkSync",
"ensureSymlinkSync",
"unlinkSync",
"removeSync",
"emptyDirSync",
].map((property) => ({
object: "fs",
property,
message: "Do not use sync fs methods.",
})),
],
"no-restricted-syntax": [
WARNING,
// Copied from airbnb, removed for...of statement, added export all
{
selector: "ForInStatement",
message:
"for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.",
},
{
selector: "LabeledStatement",
message:
"Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.",
},
{
selector: "WithStatement",
message:
"`with` is disallowed in strict mode because it makes code impossible to predict and optimize.",
},
{
selector: "ExportAllDeclaration",
message:
"Export all does't work well if imported in ESM due to how they are transpiled, and they can also lead to unexpected exposure of internal methods.",
},
...["path", "fs-extra", "webpack", "lodash"].map((m) => ({
selector: `ImportDeclaration[importKind=value]:has(Literal[value=${m}]) > ImportSpecifier[importKind=value]`,
message:
"Default-import this, both for readability and interoperability with ESM",
})),
],
"no-template-curly-in-string": WARNING,
"no-unused-expressions": OFF,
"no-useless-escape": WARNING,
"no-void": [ERROR, { allowAsStatement: true }],
"prefer-destructuring": OFF,
"prefer-named-capture-group": WARNING,
"prefer-template": WARNING,
yoda: WARNING,
"import/extensions": OFF,
// This rule doesn't yet support resolving .js imports when the actual file
// is .ts. Plus it's not all that useful when our code is fully TS-covered.
"import/no-unresolved": [
OFF,
{
// Ignore certain webpack aliases because they can't be resolved
ignore: [
"^@theme",
"^@docusaurus",
"^@generated",
"^@site",
"^@testing-utils",
],
},
],
"import/order": [
WARNING,
{
groups: [
"builtin",
"external",
"internal",
["parent", "sibling", "index"],
"type",
],
"newlines-between": "always",
pathGroups: [
// always put css import to the last, ref:
// https://github.com/import-js/eslint-plugin-import/issues/1239
{
pattern: "*.+(css|sass|less|scss|pcss|styl)",
group: "unknown",
patternOptions: { matchBase: true },
position: "after",
},
{ pattern: "react", group: "builtin", position: "before" },
{ pattern: "react-dom", group: "builtin", position: "before" },
{ pattern: "react-dom/**", group: "builtin", position: "before" },
{ pattern: "stream", group: "builtin", position: "before" },
{ pattern: "fs-extra", group: "builtin" },
{ pattern: "lodash", group: "external", position: "before" },
{ pattern: "clsx", group: "external", position: "before" },
// 'Bit weird to not use the `import/internal-regex` option, but this
// way, we can make `import type { Props } from "@theme/*"` appear
// before `import styles from "styles.module.css"`, which is what we
// always did. This should be removable once we stop using ambient
// module declarations for theme aliases.
{ pattern: "@theme/**", group: "internal" },
{ pattern: "@site/**", group: "internal" },
{ pattern: "@theme-init/**", group: "internal" },
{ pattern: "@theme-original/**", group: "internal" },
{ pattern: "@/components/**", group: "internal" },
{ pattern: "@/libs/**", group: "internal" },
{ pattern: "@/types/**", group: "type" },
],
pathGroupsExcludedImportTypes: [],
// example: let `import './nprogress.css';` after importing others
// in `packages/docusaurus-theme-classic/src/nprogress.ts`
// see more: https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/order.md#warnonunassignedimports-truefalse
warnOnUnassignedImports: true,
},
],
"import/prefer-default-export": OFF,
// `import clsx from "clsx"` triggers it since clsx v2 also provides a
// named export; renaming the imports would diverge from upstream themes
"import/no-named-as-default": OFF,
"jsx-a11y/click-events-have-key-events": WARNING,
"jsx-a11y/no-noninteractive-element-interactions": WARNING,
"jsx-a11y/html-has-lang": OFF,
// Sometimes we do need the props as a whole, e.g. when spreading
"react/destructuring-assignment": OFF,
"react/function-component-definition": [
WARNING,
{
namedComponents: "function-declaration",
unnamedComponents: "arrow-function",
},
],
"react/jsx-filename-extension": OFF,
"react/jsx-key": [ERROR, { checkFragmentShorthand: true }],
"react/jsx-no-useless-fragment": [ERROR, { allowExpressions: true }],
"react/jsx-props-no-spreading": OFF,
"react/no-array-index-key": OFF, // We build a static site, and nearly all components don't change.
"react/no-unstable-nested-components": [WARNING, { allowAsProps: true }],
"react/prefer-stateless-function": WARNING,
"react/prop-types": OFF,
"react/require-default-props": [
ERROR,
{ ignoreFunctionalComponents: true },
],
"react/jsx-uses-react": OFF, // JSX runtime: automatic
"react/react-in-jsx-scope": OFF, // JSX runtime: automatic
"react-hooks/set-state-in-effect": WARNING, // TODO re-enable later?
"react-hooks/rules-of-hooks": ERROR,
"react-hooks/exhaustive-deps": ERROR,
"@typescript-eslint/no-empty-object-type": OFF,
"@typescript-eslint/prefer-optional-chain": OFF,
"@typescript-eslint/consistent-type-definitions": OFF,
"@typescript-eslint/require-await": OFF,
"@typescript-eslint/no-explicit-any": WARNING,
"@typescript-eslint/no-unused-expressions": [
WARNING,
{ allowTaggedTemplates: true, allowShortCircuit: true },
],
"@typescript-eslint/ban-ts-comment": [
ERROR,
{ "ts-expect-error": "allow-with-description" },
],
"@typescript-eslint/consistent-indexed-object-style": OFF,
"@typescript-eslint/consistent-type-imports": [
WARNING,
{ disallowTypeAnnotations: false },
],
"@typescript-eslint/explicit-module-boundary-types": WARNING,
"@typescript-eslint/method-signature-style": ERROR,
"@typescript-eslint/no-empty-function": OFF,
"@typescript-eslint/no-empty-interface": [
ERROR,
{
allowSingleExtends: true,
},
],
"@typescript-eslint/no-inferrable-types": OFF,
"@typescript-eslint/no-namespace": [WARNING, { allowDeclarations: true }],
"no-use-before-define": OFF,
"@typescript-eslint/no-use-before-define": [
ERROR,
{ functions: false, classes: false, variables: true },
],
"@typescript-eslint/no-non-null-assertion": OFF,
"no-redeclare": OFF,
"@typescript-eslint/no-redeclare": ERROR,
"no-shadow": OFF,
"@typescript-eslint/no-shadow": ERROR,
"no-unused-vars": OFF,
// We don't provide any escape hatches for this rule. Rest siblings and
// function placeholder params are always ignored, and any other unused
// locals must be justified with a disable comment.
"@typescript-eslint/no-unused-vars": [
ERROR,
{
ignoreRestSiblings: true,
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
},
],
"@docusaurus/no-html-links": ERROR,
"@docusaurus/prefer-docusaurus-heading": ERROR,
"@docusaurus/no-untranslated-text": [
WARNING,
{
ignoredStrings: [
"·",
"-",
"—",
"×",
"", // zwj: ​
"@",
"WebContainers",
"Twitter",
"X",
"GitHub",
"Dev.to",
"1.x",
],
},
],
},
},
{
files: ["packages/*/src/theme/**/*.{js,ts,tsx}"],
ignores: ["**/*.test.{js,ts,tsx}", "**/__tests__/**"],
rules: {
"no-restricted-imports": [
ERROR,
{
patterns: LodashImportPatterns.concat(
// Prevents relative imports between React theme components
[
"../**",
"./**",
// Allows relative styles module import with consistent filename
"!./styles.module.css",
// Allows relative tailwind css import with consistent filename
"!./styles.css",
],
),
},
],
},
},
{
files: ["packages/*/src/theme/**/*.{js,ts,tsx}"],
rules: {
"import/no-named-export": ERROR,
},
},
{
files: ["**/*.d.ts"],
rules: {
"import/no-duplicates": OFF,
},
},
{
files: ["**/*.{ts,tsx}"],
rules: {
"no-undef": OFF,
"import/no-import-module-exports": OFF,
},
},
{
files: ["**/*.{js,mjs,cjs}"],
rules: {
// Make JS code directly runnable in Node.
"@typescript-eslint/no-var-requires": OFF,
"@typescript-eslint/explicit-module-boundary-types": OFF,
},
},
{
// Internal files where extraneous deps don't matter much at long as
// they run
files: ["website/**"],
rules: {
"import/no-extraneous-dependencies": OFF,
},
},
// Website-specific rules
{
files: ["website/**"],
rules: {
"@typescript-eslint/no-require-imports": OFF,
},
},
);
+1 -1
View File
@@ -425,7 +425,7 @@ class Config(BaseSettings):
参考 [记录日志](https://nonebot.dev/docs/appendices/log)[loguru 日志等级](https://loguru.readthedocs.io/en/stable/api/logger.html#levels)。 参考 [记录日志](https://nonebot.dev/docs/appendices/log)[loguru 日志等级](https://loguru.readthedocs.io/en/stable/api/logger.html#levels)。
:::tip 提示 :::tip[提示]
日志等级名称应为大写,如 `INFO`。 日志等级名称应为大写,如 `INFO`。
::: :::
+1 -1
View File
@@ -6,7 +6,7 @@ nb driver install aiohttp
pip install nonebot2[aiohttp] pip install nonebot2[aiohttp]
``` ```
:::tip 提示 :::tip[提示]
本驱动仅支持客户端连接 本驱动仅支持客户端连接
::: :::
+1 -1
View File
@@ -6,7 +6,7 @@ nb driver install fastapi
pip install nonebot2[fastapi] pip install nonebot2[fastapi]
``` ```
:::tip 提示 :::tip[提示]
本驱动仅支持服务端连接 本驱动仅支持服务端连接
::: :::
+1 -1
View File
@@ -6,7 +6,7 @@ nb driver install httpx
pip install nonebot2[httpx] pip install nonebot2[httpx]
``` ```
:::tip 提示 :::tip[提示]
本驱动仅支持客户端 HTTP 连接 本驱动仅支持客户端 HTTP 连接
::: :::
+1 -1
View File
@@ -1,6 +1,6 @@
"""None 驱动适配 """None 驱动适配
:::tip 提示 :::tip[提示]
本驱动不支持任何服务器或客户端连接 本驱动不支持任何服务器或客户端连接
::: :::
+1 -1
View File
@@ -6,7 +6,7 @@ nb driver install quart
pip install nonebot2[quart] pip install nonebot2[quart]
``` ```
:::tip 提示 :::tip[提示]
本驱动仅支持服务端连接 本驱动仅支持服务端连接
::: :::
+1 -1
View File
@@ -6,7 +6,7 @@ nb driver install websockets
pip install nonebot2[websockets] pip install nonebot2[websockets]
``` ```
:::tip 提示 :::tip[提示]
本驱动仅支持客户端 WebSocket 连接 本驱动仅支持客户端 WebSocket 连接
::: :::
+5 -5
View File
@@ -412,7 +412,7 @@ def command(
命令 `("test",)` 可以匹配: `/test` 开头的消息 命令 `("test",)` 可以匹配: `/test` 开头的消息
命令 `("test", "sub")` 可以匹配: `/test.sub` 开头的消息 命令 `("test", "sub")` 可以匹配: `/test.sub` 开头的消息
:::tip 提示 :::tip[提示]
命令内容与后续消息间无需空格! 命令内容与后续消息间无需空格!
::: :::
""" """
@@ -603,7 +603,7 @@ def shell_command(
通过 {ref}`nonebot.params.ShellCommandArgs` 获取解析后的参数字典 通过 {ref}`nonebot.params.ShellCommandArgs` 获取解析后的参数字典
(例: `{"arg": "arg", "h": True}`)。 (例: `{"arg": "arg", "h": True}`)。
:::caution 警告 :::warning[警告]
如果参数解析失败,则通过 {ref}`nonebot.params.ShellCommandArgs` 如果参数解析失败,则通过 {ref}`nonebot.params.ShellCommandArgs`
获取的将是 {ref}`nonebot.exception.ParserExit` 异常。 获取的将是 {ref}`nonebot.exception.ParserExit` 异常。
::: :::
@@ -625,7 +625,7 @@ def shell_command(
rule = shell_command("ls", parser=parser) rule = shell_command("ls", parser=parser)
``` ```
:::tip 提示 :::tip[提示]
命令内容与后续消息间无需空格! 命令内容与后续消息间无需空格!
::: :::
""" """
@@ -704,11 +704,11 @@ def regex(regex: str, flags: int | re.RegexFlag = 0) -> Rule:
regex: 正则表达式 regex: 正则表达式
flags: 正则表达式标记 flags: 正则表达式标记
:::tip 提示 :::tip[提示]
正则表达式匹配使用 search 而非 match,如需从头匹配请使用 `r"^xxx"` 来确保匹配开头 正则表达式匹配使用 search 而非 match,如需从头匹配请使用 `r"^xxx"` 来确保匹配开头
::: :::
:::tip 提示 :::tip[提示]
正则表达式匹配使用 `EventMessage` 的 `str` 字符串, 正则表达式匹配使用 `EventMessage` 的 `str` 字符串,
而非 `EventMessage` 的 `PlainText` 纯文本字符串 而非 `EventMessage` 的 `PlainText` 纯文本字符串
::: :::
+20 -17
View File
@@ -9,30 +9,33 @@
"start": "pnpm --filter nonebot start", "start": "pnpm --filter nonebot start",
"serve": "pnpm --filter nonebot serve", "serve": "pnpm --filter nonebot serve",
"clear": "pnpm --filter nonebot clear", "clear": "pnpm --filter nonebot clear",
"prettier": "prettier --config ./.prettierrc --write \"./website/\"", "format": "oxfmt .",
"format:diff": "oxfmt --list-different .",
"lint": "pnpm lint:js && pnpm lint:style", "lint": "pnpm lint:js && pnpm lint:style",
"lint:ci": "pnpm lint:js --quiet && pnpm lint:style",
"lint:js": "eslint --cache --report-unused-disable-directives \"**/*.{js,jsx,ts,tsx,mjs}\"", "lint:js": "eslint --cache --report-unused-disable-directives \"**/*.{js,jsx,ts,tsx,mjs}\"",
"lint:js:fix": "eslint --cache --report-unused-disable-directives --fix \"**/*.{js,jsx,ts,tsx,mjs}\"", "lint:js:fix": "pnpm lint:js --fix",
"lint:style": "stylelint \"**/*.css\"", "lint:style": "stylelint \"**/*.css\"",
"lint:style:fix": "stylelint --fix \"**/*.css\"", "lint:style:fix": "pnpm lint:style --fix",
"pyright": "pyright" "pyright": "pyright"
}, },
"devDependencies": { "devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.62.0", "@docusaurus/eslint-plugin": "3.10.2",
"@typescript-eslint/parser": "^5.62.0", "@eslint/js": "^10.0.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "^8.48.0", "eslint": "^9.39.4",
"eslint-config-airbnb": "^19.0.4", "eslint-config-prettier": "^10.1.8",
"eslint-config-prettier": "^8.8.0", "eslint-plugin-import": "^2.32.0",
"eslint-plugin-import": "^2.27.5", "eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-regexp": "^3.1.0",
"eslint-plugin-regexp": "^1.15.0", "globals": "^17.6.0",
"prettier": "^3.0.3", "jiti": "^2.7.0",
"oxfmt": "^0.60.0",
"pyright": "1.1.393", "pyright": "1.1.393",
"stylelint": "^15.10.3", "stylelint": "^17.12.0",
"stylelint-config-standard": "^34.0.0", "stylelint-config-standard": "^40.0.0",
"stylelint-prettier": "^4.0.2" "typescript-eslint": "^8.59.3"
} }
} }
+1046 -738
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -10,4 +10,4 @@ nb-autodoc nonebot \
-u nonebot.internal \ -u nonebot.internal \
-u nonebot.internal.* -u nonebot.internal.*
cp -r ./build/nonebot/* ./website/docs/api/ cp -r ./build/nonebot/* ./website/docs/api/
pnpm prettier pnpm format
+1 -1
View File
@@ -40,6 +40,6 @@
"importHelpers": true, "importHelpers": true,
"noEmitHelpers": true "noEmitHelpers": true
}, },
"include": ["./**/.eslintrc.js", "./**/.stylelintrc.js"], "include": ["eslint.config.ts", "eslint.rules.ts", "./**/.stylelintrc.js"],
"exclude": ["node_modules", "**/lib/**/*"] "exclude": ["node_modules", "**/lib/**/*"]
} }
+10 -10
View File
@@ -29,11 +29,11 @@ import TabItem from "@theme/TabItem";
- 根据已经解析的 `Dependency` 依赖,执行调用。 - 根据已经解析的 `Dependency` 依赖,执行调用。
- 将所有 `Dependency` 的返回值根据参数名传入并调用 `Dependent` 。 - 将所有 `Dependency` 的返回值根据参数名传入并调用 `Dependent` 。
:::danger 警告 :::danger[警告]
在依赖注入中,类型注解是非常重要的,因为它不仅可以决定依赖注入的对象,还可以触发[重载机制](../appendices/overload.md#重载)。如果类型注解与实际获得数据类型不一致,将会跳过当前 `Dependent` 对象(即事件处理函数)。 在依赖注入中,类型注解是非常重要的,因为它不仅可以决定依赖注入的对象,还可以触发[重载机制](../appendices/overload.md#重载)。如果类型注解与实际获得数据类型不一致,将会跳过当前 `Dependent` 对象(即事件处理函数)。
::: :::
:::tip 提示 :::tip[提示]
如果对于依赖注入的解析流程有疑问,可以调整[日志等级配置项](../appendices/config.mdx#log-level)为 `TRACE`,查看依赖解析日志。 如果对于依赖注入的解析流程有疑问,可以调整[日志等级配置项](../appendices/config.mdx#log-level)为 `TRACE`,查看依赖解析日志。
::: :::
@@ -349,7 +349,7 @@ async def _(x: int = Depends(random_result, use_cache=False)):
</TabItem> </TabItem>
</Tabs> </Tabs>
:::tip 提示 :::tip[提示]
缓存的生命周期与当前接收到的事件相同。接收到事件后,子依赖在首次执行时缓存,在该事件处理完成后,缓存就会被清除。 缓存的生命周期与当前接收到的事件相同。接收到事件后,子依赖在首次执行时缓存,在该事件处理完成后,缓存就会被清除。
::: :::
@@ -558,7 +558,7 @@ async def _(x: httpx.AsyncClient = Depends(get_client)):
</TabItem> </TabItem>
</Tabs> </Tabs>
:::caution 注意 :::warning[注意]
生成器作为依赖时,其中只能进行一次 `yield`,否则将会触发异常。如果对此有疑问并想探究原因,可以参考 [contextmanager](https://docs.python.org/zh-cn/3/library/contextlib.html#contextlib.contextmanager) 和 [asynccontextmanager](https://docs.python.org/zh-cn/3/library/contextlib.html#contextlib.asynccontextmanager) 文档。事实上,NoneBot 内部就使用了这两个装饰器。 生成器作为依赖时,其中只能进行一次 `yield`,否则将会触发异常。如果对此有疑问并想探究原因,可以参考 [contextmanager](https://docs.python.org/zh-cn/3/library/contextlib.html#contextlib.contextmanager) 和 [asynccontextmanager](https://docs.python.org/zh-cn/3/library/contextlib.html#contextlib.asynccontextmanager) 文档。事实上,NoneBot 内部就使用了这两个装饰器。
::: :::
@@ -715,7 +715,7 @@ async def _(foo: tuple[str, ...] = Command()): ...
</TabItem> </TabItem>
</Tabs> </Tabs>
:::tip 提示 :::tip[提示]
命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。 命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。
::: :::
@@ -745,7 +745,7 @@ async def _(foo: str = RawCommand()): ...
</TabItem> </TabItem>
</Tabs> </Tabs>
:::tip 提示 :::tip[提示]
命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。 命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。
::: :::
@@ -777,7 +777,7 @@ async def _(foo: Message = CommandArg()): ...
</TabItem> </TabItem>
</Tabs> </Tabs>
:::tip 提示 :::tip[提示]
命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。 命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。
::: :::
@@ -807,7 +807,7 @@ async def _(foo: str = CommandStart()): ...
</TabItem> </TabItem>
</Tabs> </Tabs>
:::tip 提示 :::tip[提示]
命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。 命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。
::: :::
@@ -837,7 +837,7 @@ async def _(foo: str = CommandWhitespace()): ...
</TabItem> </TabItem>
</Tabs> </Tabs>
:::tip 提示 :::tip[提示]
命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。 命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。
::: :::
@@ -921,7 +921,7 @@ async def _(foo: list[Union[str, MessageSegment]] = ShellCommandArgv()): ...
获取 shell 命令解析后的参数 Namespace,支持 MessageSegment 富文本(如:图片)。 获取 shell 命令解析后的参数 Namespace,支持 MessageSegment 富文本(如:图片)。
:::tip 提示 :::tip[提示]
如果参数解析成功,则为 parser 返回的 Namespace;如果参数解析失败,则为 [`ParserExit`](../api/exception.md#ParserExit) 异常,并携带错误码与错误信息。在前置词法解析失败时,返回值也为 [`ParserExit`](../api/exception.md#ParserExit) 异常。通过重载机制即可处理两种不同的情况。 如果参数解析成功,则为 parser 返回的 Namespace;如果参数解析失败,则为 [`ParserExit`](../api/exception.md#ParserExit) 异常,并携带错误码与错误信息。在前置词法解析失败时,返回值也为 [`ParserExit`](../api/exception.md#ParserExit) 异常。通过重载机制即可处理两种不同的情况。
由于 `ArgumentParser` 在解析到 `--help` 参数时也会抛出异常,这种情况下错误码为 `0` 且错误信息即为帮助信息。 由于 `ArgumentParser` 在解析到 `--help` 参数时也会抛出异常,这种情况下错误码为 `0` 且错误信息即为帮助信息。
+6 -6
View File
@@ -12,11 +12,11 @@ options:
驱动器 (Driver) 是机器人运行的基石,它是机器人初始化的第一步,主要负责数据收发。 驱动器 (Driver) 是机器人运行的基石,它是机器人初始化的第一步,主要负责数据收发。
:::important 提示 :::info[提示]
驱动器的选择通常与机器人所使用的协议适配器相关,如果不知道该选择哪个驱动器,可以先阅读相关协议适配器文档说明。 驱动器的选择通常与机器人所使用的协议适配器相关,如果不知道该选择哪个驱动器,可以先阅读相关协议适配器文档说明。
::: :::
:::tip 提示 :::tip[提示]
如何**安装**驱动器请参考[安装驱动器](../tutorial/store.mdx#安装驱动器)。 如何**安装**驱动器请参考[安装驱动器](../tutorial/store.mdx#安装驱动器)。
::: :::
@@ -118,7 +118,7 @@ DRIVER=~fastapi
##### `fastapi_reload` ##### `fastapi_reload`
:::caution 警告 :::warning[警告]
不推荐开启该配置项,在 Windows 平台上开启该功能有可能会造成预料之外的影响!替代方案:使用 `nb-cli` 命令行工具以及参数 `--reload` 启动 NoneBot。 不推荐开启该配置项,在 Windows 平台上开启该功能有可能会造成预料之外的影响!替代方案:使用 `nb-cli` 命令行工具以及参数 `--reload` 启动 NoneBot。
```bash ```bash
@@ -200,7 +200,7 @@ DRIVER=~quart
##### `quart_reload` ##### `quart_reload`
:::caution 警告 :::warning[警告]
不推荐开启该配置项,在 Windows 平台上开启该功能有可能会造成预料之外的影响!替代方案:使用 `nb-cli` 命令行工具以及参数 `--reload` 启动 NoneBot。 不推荐开启该配置项,在 Windows 平台上开启该功能有可能会造成预料之外的影响!替代方案:使用 `nb-cli` 命令行工具以及参数 `--reload` 启动 NoneBot。
```bash ```bash
@@ -252,7 +252,7 @@ nonebot.run(app="bot:app")
**类型:**HTTP 客户端驱动器 **类型:**HTTP 客户端驱动器
:::caution 注意 :::warning[注意]
本驱动器仅支持 HTTP 请求,不支持 WebSocket 连接请求。 本驱动器仅支持 HTTP 请求,不支持 WebSocket 连接请求。
::: :::
@@ -266,7 +266,7 @@ DRIVER=~httpx
**类型:**WebSocket 客户端驱动器 **类型:**WebSocket 客户端驱动器
:::caution 注意 :::warning[注意]
本驱动器仅支持 WebSocket 连接请求,不支持 HTTP 请求。 本驱动器仅支持 WebSocket 连接请求,不支持 HTTP 请求。
::: :::
+2 -2
View File
@@ -12,7 +12,7 @@ options:
在[指南](../tutorial/matcher.md)与[深入](../appendices/rule.md)中,我们已经介绍了事件响应器的基本用法以及响应规则、权限控制等功能。在这一节中,我们将介绍事件响应器的组成,内置的响应规则,与第三方响应规则拓展。 在[指南](../tutorial/matcher.md)与[深入](../appendices/rule.md)中,我们已经介绍了事件响应器的基本用法以及响应规则、权限控制等功能。在这一节中,我们将介绍事件响应器的组成,内置的响应规则,与第三方响应规则拓展。
:::tip 提示 :::tip[提示]
事件响应器允许继承,你可以通过直接继承 `Matcher` 类来创建一个新的事件响应器。 事件响应器允许继承,你可以通过直接继承 `Matcher` 类来创建一个新的事件响应器。
::: :::
@@ -228,7 +228,7 @@ matcher = on_shell_command("cmd", parser=parser)
`regex` 响应规则用于匹配消息是否与指定正则表达式匹配。 `regex` 响应规则用于匹配消息是否与指定正则表达式匹配。
:::tip 提示 :::tip[提示]
正则表达式匹配使用 search 而非 match,如需从头匹配请使用 `r"^xxx"` 模式来确保匹配开头。 正则表达式匹配使用 search 而非 match,如需从头匹配请使用 `r"^xxx"` 模式来确保匹配开头。
::: :::
+2 -2
View File
@@ -18,7 +18,7 @@ import Messenger from "@/components/Messenger";
在之前的章节中,我们介绍了如何向用户发送文本消息以及[如何处理平台消息](../tutorial/message.md),现在我们来向用户发送平台特殊消息。 在之前的章节中,我们介绍了如何向用户发送文本消息以及[如何处理平台消息](../tutorial/message.md),现在我们来向用户发送平台特殊消息。
:::caution 注意 :::warning[注意]
在以下的示例中,我们将使用 `Console` 协议适配器来演示如何发送平台消息。在实际使用中,你需要确保你使用的**消息序列类型**与你所要发送的**平台类型**一致。 在以下的示例中,我们将使用 `Console` 协议适配器来演示如何发送平台消息。在实际使用中,你需要确保你使用的**消息序列类型**与你所要发送的**平台类型**一致。
::: :::
@@ -103,7 +103,7 @@ result = await bot.get_user_info(user_id=12345678)
result = await bot.call_api("get_user_info", user_id=12345678) result = await bot.call_api("get_user_info", user_id=12345678)
``` ```
:::caution 注意 :::warning[注意]
实际可以使用的 API 以及参数取决于平台提供的接口以及协议适配器的实现,请参考协议适配器以及平台文档。 实际可以使用的 API 以及参数取决于平台提供的接口以及协议适配器的实现,请参考协议适配器以及平台文档。
::: :::
+6 -6
View File
@@ -19,7 +19,7 @@ NoneBot 使用 [`pydantic`](https://docs.pydantic.dev/) 以及 [`python-dotenv`]
NoneBot 内置的配置项列表及含义可以在[内置配置项](#内置配置项)中查看。 NoneBot 内置的配置项列表及含义可以在[内置配置项](#内置配置项)中查看。
:::caution 注意 :::warning[注意]
NoneBot 自 2.2.0 起兼容了 Pydantic v1 与 v2 版本,以下文档中 Pydantic 相关示例均采用 v2 版本用法。 NoneBot 自 2.2.0 起兼容了 Pydantic v1 与 v2 版本,以下文档中 Pydantic 相关示例均采用 v2 版本用法。
@@ -83,7 +83,7 @@ export CUSTOM_CONFIG='config in environment variables'
那最终 NoneBot 所读取的内容为环境变量中的内容,即 `config in environment variables`。 那最终 NoneBot 所读取的内容为环境变量中的内容,即 `config in environment variables`。
:::caution 注意 :::warning[注意]
如果一个环境变量既不是 NoneBot 的[**内置配置项**](#内置配置项),也不是任何插件所定义的[**插件配置**](#插件配置),那么 NoneBot 不会自发读取该环境变量,需要在 dotenv 配置文件中先行声明。 如果一个环境变量既不是 NoneBot 的[**内置配置项**](#内置配置项),也不是任何插件所定义的[**插件配置**](#插件配置),那么 NoneBot 不会自发读取该环境变量,需要在 dotenv 配置文件中先行声明。
::: :::
@@ -162,7 +162,7 @@ COMMON_CONFIG=common config # 这个配置项在任何环境中都会被加载
这样,我们在启动 NoneBot 时就会从 `.env.dev` 文件中加载剩余配置项。 这样,我们在启动 NoneBot 时就会从 `.env.dev` 文件中加载剩余配置项。
:::tip 提示 :::tip[提示]
在生产环境中,可以通过设置环境变量 `ENVIRONMENT=prod` 来确保 NoneBot 读取正确的环境配置。 在生产环境中,可以通过设置环境变量 `ENVIRONMENT=prod` 来确保 NoneBot 读取正确的环境配置。
::: :::
@@ -242,11 +242,11 @@ weather = on_command(
这种方式可以简洁、高效地读取配置项,同时也可以设置默认值或者在运行时对配置项进行合法性检查,防止由于配置项导致的插件出错等情况出现。 这种方式可以简洁、高效地读取配置项,同时也可以设置默认值或者在运行时对配置项进行合法性检查,防止由于配置项导致的插件出错等情况出现。
:::tip 可配置的事件响应优先级 :::tip[可配置的事件响应优先级]
发布插件应该为自身的事件响应器提供可配置的优先级,以便插件使用者可以自定义多个插件间的响应顺序。 发布插件应该为自身的事件响应器提供可配置的优先级,以便插件使用者可以自定义多个插件间的响应顺序。
::: :::
:::tip 插件配置获取逻辑 :::tip[插件配置获取逻辑]
无论是否在 dotenv 文件中声明了插件配置项,使用 `get_plugin_config` 获取插件配置模型中定义的配置项时都遵循[**配置项的加载**](#配置项的加载)一节中的优先级顺序进行读取。 无论是否在 dotenv 文件中声明了插件配置项,使用 `get_plugin_config` 获取插件配置模型中定义的配置项时都遵循[**配置项的加载**](#配置项的加载)一节中的优先级顺序进行读取。
::: :::
@@ -406,7 +406,7 @@ nonebot.init(port=8080)
NoneBot 日志输出等级,可以为 `int` 类型等级或等级名称。具体等级对照表参考 [loguru 日志等级](https://loguru.readthedocs.io/en/stable/api/logger.html#levels)。 NoneBot 日志输出等级,可以为 `int` 类型等级或等级名称。具体等级对照表参考 [loguru 日志等级](https://loguru.readthedocs.io/en/stable/api/logger.html#levels)。
:::tip 提示 :::tip[提示]
日志等级名称应为大写,如 `INFO`。 日志等级名称应为大写,如 `INFO`。
::: :::
+3 -3
View File
@@ -28,7 +28,7 @@ async def got_location(event: MessageEvent, location: str = ArgPlainText()):
在上面的代码中,我们获取了 `Console` 协议适配器的消息事件提供的发送时间 `time` 属性。 在上面的代码中,我们获取了 `Console` 协议适配器的消息事件提供的发送时间 `time` 属性。
:::caution 注意 :::warning[注意]
如果**基类**就能满足你的需求,那么就**不要修改**事件参数类型注解,这样可以使你的代码更加**通用**,可以在更多平台上运行。如何根据不同平台事件类型进行不同的处理,我们将在[重载](#重载)一节中介绍。 如果**基类**就能满足你的需求,那么就**不要修改**事件参数类型注解,这样可以使你的代码更加**通用**,可以在更多平台上运行。如何根据不同平台事件类型进行不同的处理,我们将在[重载](#重载)一节中介绍。
::: :::
@@ -63,12 +63,12 @@ async def handle_onebot(bot: OneBot):
await bot.send_group_message(group_id=123123, message="OneBot") await bot.send_group_message(group_id=123123, message="OneBot")
``` ```
:::caution 注意 :::warning[注意]
重载机制对所有的参数类型注解都有效,因此,依赖注入也可以使用这个特性来对不同的返回值进行处理。 重载机制对所有的参数类型注解都有效,因此,依赖注入也可以使用这个特性来对不同的返回值进行处理。
但 Bot、Event 和 Matcher 三者的参数类型注解具有最高检查优先级,如果三者任一类型注解不匹配,那么其他依赖注入将不会执行(如:`Depends`)。 但 Bot、Event 和 Matcher 三者的参数类型注解具有最高检查优先级,如果三者任一类型注解不匹配,那么其他依赖注入将不会执行(如:`Depends`)。
::: :::
:::tip 提示 :::tip[提示]
如何更好地编写一个跨平台的插件,我们将在[最佳实践](../best-practice/multi-adapter.mdx)中介绍。 如何更好地编写一个跨平台的插件,我们将在[最佳实践](../best-practice/multi-adapter.mdx)中介绍。
::: :::
+4 -4
View File
@@ -35,7 +35,7 @@ async def got_location():
在上面的代码中,我们使用 `got` 事件响应器操作来向用户发送 `prompt` 消息,并等待用户的回复。用户的回复消息将会被作为 `location` 参数存储于事件响应器状态中。 在上面的代码中,我们使用 `got` 事件响应器操作来向用户发送 `prompt` 消息,并等待用户的回复。用户的回复消息将会被作为 `location` 参数存储于事件响应器状态中。
:::tip 提示 :::tip[提示]
事件处理函数根据定义的顺序依次执行。 事件处理函数根据定义的顺序依次执行。
::: :::
@@ -67,7 +67,7 @@ async def got_location(location: str = ArgPlainText()):
在上面的代码中,我们在 `got_location` 函数中定义了一个依赖注入参数 `location`,他的值将会是用户回复的消息纯文本信息。获取到用户输入的地名参数后,我们就可以进行天气查询并回复了。 在上面的代码中,我们在 `got_location` 函数中定义了一个依赖注入参数 `location`,他的值将会是用户回复的消息纯文本信息。获取到用户输入的地名参数后,我们就可以进行天气查询并回复了。
:::tip 提示 :::tip[提示]
如果想要获取用户回复的消息对象 `Message` ,可以使用 `Arg` 依赖注入。 如果想要获取用户回复的消息对象 `Message` ,可以使用 `Arg` 依赖注入。
::: :::
@@ -130,7 +130,7 @@ async def got_location(location: str = ArgPlainText()):
事件响应器操作可以分为两大类:**交互操作**和**流程控制操作**。我们可以通过交互操作来与用户进行交互,而流程控制操作则可以用来控制事件处理流程的执行。 事件响应器操作可以分为两大类:**交互操作**和**流程控制操作**。我们可以通过交互操作来与用户进行交互,而流程控制操作则可以用来控制事件处理流程的执行。
:::tip 提示 :::tip[提示]
事件处理流程按照事件处理函数添加顺序执行,已经结束的事件处理函数不可能被恢复执行。 事件处理流程按照事件处理函数添加顺序执行,已经结束的事件处理函数不可能被恢复执行。
::: :::
@@ -322,7 +322,7 @@ async def _(matcher: Matcher):
matcher.stop_propagation() matcher.stop_propagation()
``` ```
:::caution 注意 :::warning[注意]
`stop_propagation` 操作是实例方法,需要先通过依赖注入获取事件响应器实例再进行调用。 `stop_propagation` 操作是实例方法,需要先通过依赖注入获取事件响应器实例再进行调用。
::: :::
@@ -237,7 +237,7 @@ args = Args["foo", BasePattern("@\d+")]
传入别名后,选项与子命令会选择其中长度最长的作为其名称。若传入为 "--foo|-f",则命令名称为 "--foo" 传入别名后,选项与子命令会选择其中长度最长的作为其名称。若传入为 "--foo|-f",则命令名称为 "--foo"
:::tip 特别提醒!!! :::tip[特别提醒!!!]
Option 的名字或别名**没有要求**必须在前面写上 `-` Option 的名字或别名**没有要求**必须在前面写上 `-`
@@ -534,7 +534,7 @@ async def message_provider(
该方法可能会调用多次,即对于多个 Extension,选择优先级靠前且实现了该方法的 Extension,若调用的返回值不为 `None` 则作为结果。 该方法可能会调用多次,即对于多个 Extension,选择优先级靠前且实现了该方法的 Extension,若调用的返回值不为 `None` 则作为结果。
:::caution :::warning
该方法的默认实现对结果 (UniMessage) 会进行缓存。`Extension` 的实现也应尽量实现缓存机制。 该方法的默认实现对结果 (UniMessage) 会进行缓存。`Extension` 的实现也应尽量实现缓存机制。
@@ -161,7 +161,7 @@ async def on_startup():
await UniMessage("Hello!").send(target=target) await UniMessage("Hello!").send(target=target)
``` ```
:::caution :::warning
在响应器以外的地方,除非启用了 `alconna_apply_fetch_targets` 配置项,否则 `bot` 参数必须手动传入。 在响应器以外的地方,除非启用了 `alconna_apply_fetch_targets` 配置项,否则 `bot` 参数必须手动传入。
@@ -55,7 +55,7 @@ asycn def _(bot: Bot, event: Event):
</TabItem> </TabItem>
</Tabs> </Tabs>
:::caution :::warning
该方法获取的消息事件 ID 不推荐直接用于各适配器的 API 调用中,可能会操作失败。 该方法获取的消息事件 ID 不推荐直接用于各适配器的 API 调用中,可能会操作失败。
@@ -231,7 +231,7 @@ async def message_edit(
## 表态消息 ## 表态消息
:::caution :::warning
该方法属于实验性功能。其接口可能会在未来的版本中发生变化。 该方法属于实验性功能。其接口可能会在未来的版本中发生变化。
+2 -2
View File
@@ -44,7 +44,7 @@ config_dir = store.get_plugin_config_dir()
config_file = store.get_plugin_config_file("file_name") config_file = store.get_plugin_config_file("file_name")
``` ```
:::danger 警告 :::danger[警告]
在 Windows 和 macOS 系统下,插件的数据目录和配置目录是同一个目录,因此在使用时需要注意避免文件名冲突。 在 Windows 和 macOS 系统下,插件的数据目录和配置目录是同一个目录,因此在使用时需要注意避免文件名冲突。
::: :::
@@ -60,7 +60,7 @@ data_file.write_text("Hello World!")
data = data_file.read_text() data = data_file.read_text()
``` ```
:::note 提示 :::note[提示]
对于嵌套插件,子插件的存储目录将位于父插件存储目录下。 对于嵌套插件,子插件的存储目录将位于父插件存储目录下。
@@ -178,7 +178,7 @@ op.drop_table("weather_weather") # DROP TABLE weather_weather;
对了,不要忘记还有一段注释:`commands auto generated by Alembic - please adjust!`。 对了,不要忘记还有一段注释:`commands auto generated by Alembic - please adjust!`。
它在提醒我们,这些代码是由 Alembic 自动生成的,我们应该检查它们,并且根据需要进行调整。 它在提醒我们,这些代码是由 Alembic 自动生成的,我们应该检查它们,并且根据需要进行调整。
:::caution 注意 :::warning[注意]
迁移脚本冗长且繁琐,我们一般不会手写它们,而是由 Alembic 自动生成。 迁移脚本冗长且繁琐,我们一般不会手写它们,而是由 Alembic 自动生成。
一般情况下,Alembic 足够智能,可以正确地生成迁移脚本。 一般情况下,Alembic 足够智能,可以正确地生成迁移脚本。
但是,在复杂或有歧义的情况下,我们可能需要手动调整迁移脚本。 但是,在复杂或有歧义的情况下,我们可能需要手动调整迁移脚本。
@@ -235,7 +235,7 @@ async def _(session: async_scoped_session, args: Message = CommandArg()):
`async_scoped_session` 是一个有作用域限制的会话,作用域为当前事件、当前事件响应器。 `async_scoped_session` 是一个有作用域限制的会话,作用域为当前事件、当前事件响应器。
会话产生的模型实例(例如此处的 `wea := await session.get(Weather, location)`)作用域与会话相同。 会话产生的模型实例(例如此处的 `wea := await session.get(Weather, location)`)作用域与会话相同。
:::caution 注意 :::warning[注意]
此处提到的“会话”指的是 ORM 会话,而非 [NoneBot 会话](../../../appendices/session-control),两者的生命周期也是不同的(NoneBot 会话的生命周期中可能包含多个事件,不同的事件也会有不同的事件响应器)。 此处提到的“会话”指的是 ORM 会话,而非 [NoneBot 会话](../../../appendices/session-control),两者的生命周期也是不同的(NoneBot 会话的生命周期中可能包含多个事件,不同的事件也会有不同的事件响应器)。
具体而言,就是不要将 ORM 会话和模型实例存储在 NoneBot 会话状态中: 具体而言,就是不要将 ORM 会话和模型实例存储在 NoneBot 会话状态中:
+2 -2
View File
@@ -8,7 +8,7 @@ description: 用户指南
`nonebot-plugin-orm` 功能强大且复杂,使用上有一定难度。 `nonebot-plugin-orm` 功能强大且复杂,使用上有一定难度。
不过,对于用户而言,只需要掌握部分功能即可。 不过,对于用户而言,只需要掌握部分功能即可。
:::caution 注意 :::warning[注意]
请注意区分插件的项目名(如:`nonebot-plugin-wordcloud`)和模块名(如:`nonebot_plugin_wordcloud`)。`nonebot-plugin-orm` 中统一使用插件模块名。参见 [插件命名规范](../../developer/plugin-publishing#插件命名规范)。 请注意区分插件的项目名(如:`nonebot-plugin-wordcloud`)和模块名(如:`nonebot_plugin_wordcloud`)。`nonebot-plugin-orm` 中统一使用插件模块名。参见 [插件命名规范](../../developer/plugin-publishing#插件命名规范)。
::: :::
@@ -152,7 +152,7 @@ SQLALCHEMY_ENGINE_OPTIONS='{
SQLALCHEMY_ECHO=true SQLALCHEMY_ECHO=true
``` ```
:::caution 注意 :::warning[注意]
以上配置之间有覆盖关系,遵循特殊优先于一般的原则,具体为 [`sqlalchemy_database_url`](#sqlalchemy_database_url) > [`sqlalchemy_bind`](#sqlalchemy_bind) > [`sqlalchemy_echo`](#sqlalchemy_echo) > [`sqlalchemy_engine_options`](#sqlalchemy_engine_options)。 以上配置之间有覆盖关系,遵循特殊优先于一般的原则,具体为 [`sqlalchemy_database_url`](#sqlalchemy_database_url) > [`sqlalchemy_bind`](#sqlalchemy_bind) > [`sqlalchemy_echo`](#sqlalchemy_echo) > [`sqlalchemy_engine_options`](#sqlalchemy_engine_options)。
但覆盖顺序并非显而易见,出于清晰考虑,请只配置必要的选项。 但覆盖顺序并非显而易见,出于清晰考虑,请只配置必要的选项。
::: :::
+1 -1
View File
@@ -27,7 +27,7 @@ nb plugin install nonebot-plugin-sentry
### 配置插件 ### 配置插件
:::caution 注意 :::warning[注意]
错误跟踪通常在生产环境中使用,因此开发环境中 `sentry_dsn` 留空即会停用插件。 错误跟踪通常在生产环境中使用,因此开发环境中 `sentry_dsn` 留空即会停用插件。
::: :::
+1 -1
View File
@@ -30,7 +30,7 @@ nb plugin install nonebot-plugin-htmlkit
- Linux x64 (非 Alpine 等 musl 系发行版) - Linux x64 (非 Alpine 等 musl 系发行版)
- Linux arm64 (非 Alpine 等 musl 系发行版) - Linux arm64 (非 Alpine 等 musl 系发行版)
:::caution 访问网络内容 :::warning[访问网络内容]
如果需要访问网络资源(如 http(s) 网页内容),NoneBot 需要客户端型驱动器(Forward)。内置的驱动器有 `~httpx``~aiohttp` 如果需要访问网络资源(如 http(s) 网页内容),NoneBot 需要客户端型驱动器(Forward)。内置的驱动器有 `~httpx``~aiohttp`
+1 -1
View File
@@ -12,7 +12,7 @@ import TabItem from "@theme/TabItem";
由于不同平台的事件与接口之间,存在着极大的差异性,NoneBot 通过[重载](../appendices/overload.md)的方式,使得插件可以在不同平台上正确响应。但为了减少跨平台的兼容性问题,我们应该尽可能的使用基类方法实现原生跨平台,而不是使用特定平台的方法。当基类方法无法满足需求时,我们可以使用依赖注入的方式,将特定平台的事件或机器人注入到事件处理函数中,实现针对特定平台的处理。 由于不同平台的事件与接口之间,存在着极大的差异性,NoneBot 通过[重载](../appendices/overload.md)的方式,使得插件可以在不同平台上正确响应。但为了减少跨平台的兼容性问题,我们应该尽可能的使用基类方法实现原生跨平台,而不是使用特定平台的方法。当基类方法无法满足需求时,我们可以使用依赖注入的方式,将特定平台的事件或机器人注入到事件处理函数中,实现针对特定平台的处理。
:::tip 提示 :::tip[提示]
如果需要在多平台上**使用**跨平台插件,首先应该根据[注册适配器](../advanced/adapter.md#注册适配器)一节,为机器人注册各平台对应的适配器。 如果需要在多平台上**使用**跨平台插件,首先应该根据[注册适配器](../advanced/adapter.md#注册适配器)一节,为机器人注册各平台对应的适配器。
::: :::
+1 -1
View File
@@ -58,7 +58,7 @@ scheduler.add_job(
) )
``` ```
:::caution 注意 :::warning[注意]
由于 APScheduler 的定时任务并不是**由事件响应器所触发的事件**,因此其任务函数无法同[事件处理函数](../tutorial/handler.mdx#事件处理函数)一样通过[依赖注入](../tutorial/event-data.mdx#认识依赖注入)获取上下文信息,也无法通过事件响应器对象的方法进行任何操作,因此我们需要使用[调用平台 API](../appendices/api-calling.mdx#调用平台-api)的方式来获取信息或收发消息。 由于 APScheduler 的定时任务并不是**由事件响应器所触发的事件**,因此其任务函数无法同[事件处理函数](../tutorial/handler.mdx#事件处理函数)一样通过[依赖注入](../tutorial/event-data.mdx#认识依赖注入)获取上下文信息,也无法通过事件响应器对象的方法进行任何操作,因此我们需要使用[调用平台 API](../appendices/api-calling.mdx#调用平台-api)的方式来获取信息或收发消息。
相对于事件处理依赖而言,编写定时任务更像是编写普通的函数,需要我们自行获取信息以及发送信息,请**不要**将事件处理依赖的特殊语法用于定时任务! 相对于事件处理依赖而言,编写定时任务更像是编写普通的函数,需要我们自行获取信息以及发送信息,请**不要**将事件处理依赖的特殊语法用于定时任务!
@@ -14,7 +14,7 @@ import TabItem from "@theme/TabItem";
为了保证代码的正确运行,我们不仅需要对错误进行跟踪,还需要对代码进行正确性检验,也就是测试。NoneBot 提供了一个测试工具——NoneBug,它是一个 [pytest](https://docs.pytest.org/en/stable/) 插件,可以帮助我们便捷地进行单元测试。 为了保证代码的正确运行,我们不仅需要对错误进行跟踪,还需要对代码进行正确性检验,也就是测试。NoneBot 提供了一个测试工具——NoneBug,它是一个 [pytest](https://docs.pytest.org/en/stable/) 插件,可以帮助我们便捷地进行单元测试。
:::tip 提示 :::tip[提示]
建议在阅读本文档前先阅读 [pytest 官方文档](https://docs.pytest.org/en/stable/)来了解 pytest 的相关术语和基本用法。 建议在阅读本文档前先阅读 [pytest 官方文档](https://docs.pytest.org/en/stable/)来了解 pytest 的相关术语和基本用法。
::: :::
@@ -23,7 +23,7 @@ NoneBug 提供了六种定义 `Rule` 和 `Permission` 预期行为的方法:
- `should_not_pass_permission` - `should_not_pass_permission`
- `should_ignore_permission` - `should_ignore_permission`
:::tip 提示 :::tip[提示]
事件响应器类型的检查属于 `Permission` 的一部分,因此可以通过 `should_pass_permission` 和 `should_not_pass_permission` 方法来断言事件响应器类型的检查。 事件响应器类型的检查属于 `Permission` 的一部分,因此可以通过 `should_pass_permission` 和 `should_not_pass_permission` 方法来断言事件响应器类型的检查。
::: :::
+2 -2
View File
@@ -26,7 +26,7 @@ NoneBot 适配器项目通常以 `nonebot-adapter-{adapter-name}` 作为项目
└── 📜 README.md └── 📜 README.md
``` ```
:::tip 提示 :::tip[提示]
上述的项目结构仅作推荐,不做强制要求,保证实际可用性即可。 上述的项目结构仅作推荐,不做强制要求,保证实际可用性即可。
@@ -44,7 +44,7 @@ nb adapter create
## 组成部分 ## 组成部分
:::tip 提示 :::tip[提示]
本章节的代码中提到的 `Adapter``Bot``Event``Message` 等,均为下文中适配器所编写的类,而非 NoneBot 中的基类。 本章节的代码中提到的 `Adapter``Bot``Event``Message` 等,均为下文中适配器所编写的类,而非 NoneBot 中的基类。
+9 -9
View File
@@ -10,13 +10,13 @@ import TabItem from "@theme/TabItem";
NoneBot 为开发者提供了分享插件的官方商店。本指南囊括**从创建项目到发布到 PyPI,最终提交商店审核**的全过程。 NoneBot 为开发者提供了分享插件的官方商店。本指南囊括**从创建项目到发布到 PyPI,最终提交商店审核**的全过程。
:::warning 警告 :::warning[警告]
如果你的插件只是满足自用需求,则完全可以选择**不发布插件**。发布插件**不是**使用插件的必要条件。 如果你的插件只是满足自用需求,则完全可以选择**不发布插件**。发布插件**不是**使用插件的必要条件。
NoneBot 社区对于插件有一定质量要求,对于不符合要求的插件,社区成员将会要求修改,直至符合要求后才能通过审核;如果长期未更新修改,社区将会关闭当前请求,之后如需发布请重新提交发布插件请求。相应的要求会在本章节以下部分介绍。 NoneBot 社区对于插件有一定质量要求,对于不符合要求的插件,社区成员将会要求修改,直至符合要求后才能通过审核;如果长期未更新修改,社区将会关闭当前请求,之后如需发布请重新提交发布插件请求。相应的要求会在本章节以下部分介绍。
::: :::
:::tip 提示 :::tip[提示]
本章节仅包含插件发布流程指导,插件开发请查阅前述章节。 本章节仅包含插件发布流程指导,插件开发请查阅前述章节。
::: :::
@@ -35,7 +35,7 @@ NoneBot 插件使用下述命名规范:
### 项目结构 ### 项目结构
:::tip 提示 :::tip[提示]
本段所述的项目结构仅作推荐,不做强制要求。 本段所述的项目结构仅作推荐,不做强制要求。
::: :::
@@ -58,7 +58,7 @@ NoneBot 插件使用下述命名规范:
为降低新手门槛,我们提供三条清晰、完整、可复制的发布路径。 为降低新手门槛,我们提供三条清晰、完整、可复制的发布路径。
:::tip 提示 :::tip[提示]
你只需选择一条与你习惯一致的路径,**完整跟随即可成功发布**。无需在不同工具间切换或猜测配置。 你只需选择一条与你习惯一致的路径,**完整跟随即可成功发布**。无需在不同工具间切换或猜测配置。
::: :::
@@ -94,7 +94,7 @@ NoneBot 生态目前有如下插件项目模板:
然后在本地克隆仓库,使用编辑器对以下内容进行**全局替换**: 然后在本地克隆仓库,使用编辑器对以下内容进行**全局替换**:
:::tip 提示 :::tip[提示]
此部分以“天气插件”为例,实际的替换内容应该根据你所创建的插件名称等相应调整。 此部分以“天气插件”为例,实际的替换内容应该根据你所创建的插件名称等相应调整。
::: :::
@@ -311,7 +311,7 @@ git push origin --tags
<TabItem value="manual" label="手动发布"> <TabItem value="manual" label="手动发布">
根据选用的构建系统,在项目的 `pyproject.toml` 中填入必要信息后进行构建与发布。 根据选用的构建系统,在项目的 `pyproject.toml` 中填入必要信息后进行构建与发布。
:::tip 提示 :::tip[提示]
不同构建工具的使用可能存在差别。本文仅以 [`pdm`](https://pdm-project.org/zh/latest/), 不同构建工具的使用可能存在差别。本文仅以 [`pdm`](https://pdm-project.org/zh/latest/),
[`poetry`](https://python-poetry.org/docs/), [`setuptools`](https://setuptools.pypa.io/en/latest/) [`poetry`](https://python-poetry.org/docs/), [`setuptools`](https://setuptools.pypa.io/en/latest/)
构建系统**本地构建与发布**为示例讲解,其余构建/管理工具等和自动化构建的用法请读者自行探索。 构建系统**本地构建与发布**为示例讲解,其余构建/管理工具等和自动化构建的用法请读者自行探索。
@@ -357,7 +357,7 @@ twine upload dist/* # 只发布先前的构建
</TabItem> </TabItem>
</Tabs> </Tabs>
:::tip 提示 :::tip[提示]
发布前建议自行测试构建包是否可用,避免遗漏代码文件或资源文件等问题。 发布前建议自行测试构建包是否可用,避免遗漏代码文件或资源文件等问题。
::: :::
@@ -422,7 +422,7 @@ __plugin_meta__ = PluginMetadata(
) )
``` ```
:::caution 注意 :::warning[注意]
`__plugin_meta__` 变量**必须**处于插件最外层(如 `__init__.py` 中),否则无法正常识别。 `__plugin_meta__` 变量**必须**处于插件最外层(如 `__init__.py` 中),否则无法正常识别。
一般做法是在 `__init__.py` 中定义 `__plugin_meta__`。 一般做法是在 `__init__.py` 中定义 `__plugin_meta__`。
@@ -549,7 +549,7 @@ weather_data_file: Path = store.get_plugin_data_file("resource-index.json")
插件发布 Issue 创建后,将会经过 **NoneFlow Bot** 的自动前置检查,以确保插件信息正确无误、插件能被正确加载。 插件发布 Issue 创建后,将会经过 **NoneFlow Bot** 的自动前置检查,以确保插件信息正确无误、插件能被正确加载。
:::tip 提示 :::tip[提示]
若插件检查未通过或信息有误,**不必**关闭当前 Issue。只需更新插件并上传到 PyPI/修改信息后勾选插件测试勾选框即可重新触发插件检查。 若插件检查未通过或信息有误,**不必**关闭当前 Issue。只需更新插件并上传到 PyPI/修改信息后勾选插件测试勾选框即可重新触发插件检查。
::: :::
+1 -1
View File
@@ -51,7 +51,7 @@ description: 配置编辑器以获得最佳体验
如果您是高级用户,希望尝试 Pylance 的替代方案,或遇到 Pylance 在特定环境下的兼容性问题,可以考虑使用 BasedPyright。 如果您是高级用户,希望尝试 Pylance 的替代方案,或遇到 Pylance 在特定环境下的兼容性问题,可以考虑使用 BasedPyright。
:::caution 提示 :::warning[提示]
为避免 `Pylance``BasedPyright` 相互冲突导致配置混乱甚至异常,脚手架默认不允许在创建项目时同时配置这两者。 为避免 `Pylance``BasedPyright` 相互冲突导致配置混乱甚至异常,脚手架默认不允许在创建项目时同时配置这两者。
如果确实需要同时使用,请在创建项目时选择 Pylance/Pyright 并根据[相关文档](https://docs.basedpyright.com/latest/installation/ides/#vscode-vscodium)进行手动配置。 如果确实需要同时使用,请在创建项目时选择 Pylance/Pyright 并根据[相关文档](https://docs.basedpyright.com/latest/installation/ides/#vscode-vscodium)进行手动配置。
+1 -1
View File
@@ -13,7 +13,7 @@ import Messenger from "@site/src/components/Messenger";
# 快速上手 # 快速上手
:::caution 前提条件 :::warning[前提条件]
- 请确保你的 Python 版本 >= 3.9 - 请确保你的 Python 版本 >= 3.9
- **我们强烈建议使用虚拟环境进行开发**,如果没有使用虚拟环境,请确保已经卸载可能存在的 NoneBot v1!!! - **我们强烈建议使用虚拟环境进行开发**,如果没有使用虚拟环境,请确保已经卸载可能存在的 NoneBot v1!!!
+2 -2
View File
@@ -15,7 +15,7 @@ import TabItem from "@theme/TabItem";
在[快速上手](../quick-start.mdx)中,我们已经介绍了如何安装和使用 `nb-cli` 创建一个项目。在本章节中,我们将简要介绍如何在不使用 `nb-cli` 的方式创建一个机器人项目的**最小实例**并启动。如果你想要了解 NoneBot 的启动流程,也可以阅读本章节。 在[快速上手](../quick-start.mdx)中,我们已经介绍了如何安装和使用 `nb-cli` 创建一个项目。在本章节中,我们将简要介绍如何在不使用 `nb-cli` 的方式创建一个机器人项目的**最小实例**并启动。如果你想要了解 NoneBot 的启动流程,也可以阅读本章节。
:::caution 警告 :::warning[警告]
我们十分不推荐直接创建机器人项目,请优先考虑使用 nb-cli 进行项目创建。 我们十分不推荐直接创建机器人项目,请优先考虑使用 nb-cli 进行项目创建。
::: :::
@@ -102,7 +102,7 @@ COMMAND_SEP=["."] # 配置命令分割字符
入口文件( Entrypoint )顾名思义,是用来初始化并运行机器人的 Python 文件。入口文件需要完成框架的初始化、注册适配器、加载插件等工作。 入口文件( Entrypoint )顾名思义,是用来初始化并运行机器人的 Python 文件。入口文件需要完成框架的初始化、注册适配器、加载插件等工作。
:::tip 提示 :::tip[提示]
如果你使用 `nb-cli` 创建项目,入口文件不会被创建,该文件功能会被 `nb run` 命令代替。 如果你使用 `nb-cli` 创建项目,入口文件不会被创建,该文件功能会被 `nb run` 命令代替。
::: :::
+7 -7
View File
@@ -41,7 +41,7 @@ options:
## 创建插件 ## 创建插件
:::caution 注意 :::warning[注意]
如果在之前的[快速上手](../quick-start.mdx)章节中已经使用 `bootstrap` 模板创建了项目,那么你需要做出如下修改: 如果在之前的[快速上手](../quick-start.mdx)章节中已经使用 `bootstrap` 模板创建了项目,那么你需要做出如下修改:
1. 在项目目录中创建一个两层文件夹 `awesome_bot/plugins` 1. 在项目目录中创建一个两层文件夹 `awesome_bot/plugins`
@@ -65,7 +65,7 @@ options:
::: :::
:::caution 注意 :::warning[注意]
如果在之前的[创建项目](./application.mdx)章节中手动创建了相关文件,那么你需要做出如下修改: 如果在之前的[创建项目](./application.mdx)章节中手动创建了相关文件,那么你需要做出如下修改:
1. 在项目目录中创建一个两层文件夹 `awesome_bot/plugins` 1. 在项目目录中创建一个两层文件夹 `awesome_bot/plugins`
@@ -113,7 +113,7 @@ $ nb plugin create
## 加载插件 ## 加载插件
:::danger 警告 :::danger[警告]
请勿在插件被加载前 `import` 插件模块,这会导致 NoneBot 无法将其转换为插件而出现意料之外的情况。 请勿在插件被加载前 `import` 插件模块,这会导致 NoneBot 无法将其转换为插件而出现意料之外的情况。
::: :::
@@ -148,7 +148,7 @@ nonebot.load_plugin("path.to.your.plugin") # 加载第三方插件
nonebot.load_plugin(Path("./path/to/your/plugin.py")) # 加载项目插件 nonebot.load_plugin(Path("./path/to/your/plugin.py")) # 加载项目插件
``` ```
:::caution 注意 :::warning[注意]
请注意,本地插件的路径应该为相对机器人 **入口文件(通常为 bot.py)** 可导入的,例如在项目 `plugins` 目录下。 请注意,本地插件的路径应该为相对机器人 **入口文件(通常为 bot.py)** 可导入的,例如在项目 `plugins` 目录下。
::: :::
@@ -160,7 +160,7 @@ nonebot.load_plugin(Path("./path/to/your/plugin.py")) # 加载项目插件
nonebot.load_plugins("src/plugins", "path/to/your/plugins") nonebot.load_plugins("src/plugins", "path/to/your/plugins")
``` ```
:::caution 注意 :::warning[注意]
请注意,插件目录应该为相对机器人 **入口文件(通常为 bot.py)** 可导入的,例如在项目 `plugins` 目录下。 请注意,插件目录应该为相对机器人 **入口文件(通常为 bot.py)** 可导入的,例如在项目 `plugins` 目录下。
::: :::
@@ -187,7 +187,7 @@ nonebot.load_all_plugins(["path.to.your.plugin"], ["path/to/your/plugins"])
nonebot.load_from_json("plugin_config.json", encoding="utf-8") nonebot.load_from_json("plugin_config.json", encoding="utf-8")
``` ```
:::tip 提示 :::tip[提示]
如果 JSON 配置文件中的字段无法满足你的需求,可以使用 [`load_all_plugins`](#load_all_plugins) 方法自行读取配置来加载插件。 如果 JSON 配置文件中的字段无法满足你的需求,可以使用 [`load_all_plugins`](#load_all_plugins) 方法自行读取配置来加载插件。
::: :::
@@ -209,7 +209,7 @@ plugin_dirs = ["path/to/your/plugins"]
nonebot.load_from_toml("plugin_config.toml", encoding="utf-8") nonebot.load_from_toml("plugin_config.toml", encoding="utf-8")
``` ```
:::tip 提示 :::tip[提示]
如果 TOML 配置文件中的字段无法满足你的需求,可以使用 [`load_all_plugins`](#load_all_plugins) 方法自行读取配置来加载插件。 如果 TOML 配置文件中的字段无法满足你的需求,可以使用 [`load_all_plugins`](#load_all_plugins) 方法自行读取配置来加载插件。
::: :::
+2 -2
View File
@@ -45,11 +45,11 @@ async def handle_function(args: Message = CommandArg()):
如上方示例所示,我们使用了 `args` 作为注入参数名,注入的内容为 `CommandArg()`,也就是**消息命令后跟随的内容**。在这个示例中,我们获得的参数会被检查是否有效,对无效参数则会结束事件。 如上方示例所示,我们使用了 `args` 作为注入参数名,注入的内容为 `CommandArg()`,也就是**消息命令后跟随的内容**。在这个示例中,我们获得的参数会被检查是否有效,对无效参数则会结束事件。
:::tip 提示 :::tip[提示]
命令与参数之间可以不需要空格,`CommandArg()` 获取的信息为命令后跟随的内容并去除了头部空白符。例如:`/天气 上海` 消息的参数为 `上海`。 命令与参数之间可以不需要空格,`CommandArg()` 获取的信息为命令后跟随的内容并去除了头部空白符。例如:`/天气 上海` 消息的参数为 `上海`。
::: :::
:::tip 提示 :::tip[提示]
`:=` 是 Python 3.8 引入的新语法 [Assignment Expressions](https://docs.python.org/zh-cn/3/reference/expressions.html#assignment-expressions),也称为海象表达式,可以在表达式中直接赋值。 `:=` 是 Python 3.8 引入的新语法 [Assignment Expressions](https://docs.python.org/zh-cn/3/reference/expressions.html#assignment-expressions),也称为海象表达式,可以在表达式中直接赋值。
::: :::
+1 -1
View File
@@ -68,7 +68,7 @@ async def handle_function():
值得注意的是,在执行 `finish` 方法时,NoneBot 会在向机器人用户发送消息内容后抛出 `FinishedException` 异常来结束事件响应流程。也就是说,在 `finish` 被执行后,后续的程序是不会被执行的。如果你需要回复机器人用户消息但不想事件处理流程结束,可以使用注释的部分中展示的 `send` 方法。 值得注意的是,在执行 `finish` 方法时,NoneBot 会在向机器人用户发送消息内容后抛出 `FinishedException` 异常来结束事件响应流程。也就是说,在 `finish` 被执行后,后续的程序是不会被执行的。如果你需要回复机器人用户消息但不想事件处理流程结束,可以使用注释的部分中展示的 `send` 方法。
:::danger 警告 :::danger[警告]
由于 `finish` 是通过抛出 `FinishedException` 异常来结束事件的,因此异常可能会被未加限制的 `try-except` 捕获,影响事件处理流程正确处理,导致无法正常结束此事件。请务必在异常捕获中指定错误类型或排除所有 [MatcherException](../api/exception.md#MatcherException) 类型的异常(如下所示),或将 `finish` 移出捕获范围进行使用。 由于 `finish` 是通过抛出 `FinishedException` 异常来结束事件的,因此异常可能会被未加限制的 `try-except` 捕获,影响事件处理流程正确处理,导致无法正常结束此事件。请务必在异常捕获中指定错误类型或排除所有 [MatcherException](../api/exception.md#MatcherException) 类型的异常(如下所示),或将 `finish` 移出捕获范围进行使用。
```python ```python
+2 -2
View File
@@ -36,7 +36,7 @@ weather = on_command("天气")
这样,我们就获得一个名为 `weather` 的事件响应器了,这个事件响应器会对 `/天气` 开头的消息进行响应。 这样,我们就获得一个名为 `weather` 的事件响应器了,这个事件响应器会对 `/天气` 开头的消息进行响应。
:::tip 提示 :::tip[提示]
如果一条消息中包含“@机器人”或以“机器人的昵称”开始,例如 `@bot /天气` 时,协议适配器会将 `event.is_tome()` 判断为 `True` ,同时也会自动去除 `@bot`,即事件响应器收到的信息内容为 `/天气`,方便进行命令匹配。 如果一条消息中包含“@机器人”或以“机器人的昵称”开始,例如 `@bot /天气` 时,协议适配器会将 `event.is_tome()` 判断为 `True` ,同时也会自动去除 `@bot`,即事件响应器收到的信息内容为 `/天气`,方便进行命令匹配。
::: :::
@@ -53,6 +53,6 @@ weather = on_command("天气", rule=to_me(), aliases={"weather", "查天气"}, p
这样,我们就获得了一个可以响应 `天气`、`weather`、`查天气` 三个命令的响应规则,需要私聊或 `@bot` 时才会响应,优先级为 10(越小越优先),阻断事件向后续优先级传播的事件响应器了。这些内容的意义和使用方法将会在后续的章节中一一介绍。 这样,我们就获得了一个可以响应 `天气`、`weather`、`查天气` 三个命令的响应规则,需要私聊或 `@bot` 时才会响应,优先级为 10(越小越优先),阻断事件向后续优先级传播的事件响应器了。这些内容的意义和使用方法将会在后续的章节中一一介绍。
:::tip 提示 :::tip[提示]
需要注意的是,不同的辅助函数有不同的可选参数,在使用之前可以参考[事件响应器进阶 - 基本辅助函数](../advanced/matcher.md#基本辅助函数)或 [API 文档](../api/plugin/on.md#on)。 需要注意的是,不同的辅助函数有不同的可选参数,在使用之前可以参考[事件响应器进阶 - 基本辅助函数](../advanced/matcher.md#基本辅助函数)或 [API 文档](../api/plugin/on.md#on)。
::: :::
+4 -4
View File
@@ -26,7 +26,7 @@ options:
顾名思义,消息段 `MessageSegment` 是一段消息。由于消息序列的本质是由若干消息段所组成的序列,消息段可以被认为是构成消息序列的最小单位。简单来说,消息序列类似于一个自然段,而消息段则是组成自然段的一句话。同时,作为特殊消息载体的存在,绝大多数的平台都有着**独特的消息类型**,这些独特的内容均需要由对应的**协议适配器**所提供,以适应不同平台中的消息模式。**这也意味着,你需要导入对应的协议适配器中的消息序列和消息段后才能使用其特殊的工厂方法。** 顾名思义,消息段 `MessageSegment` 是一段消息。由于消息序列的本质是由若干消息段所组成的序列,消息段可以被认为是构成消息序列的最小单位。简单来说,消息序列类似于一个自然段,而消息段则是组成自然段的一句话。同时,作为特殊消息载体的存在,绝大多数的平台都有着**独特的消息类型**,这些独特的内容均需要由对应的**协议适配器**所提供,以适应不同平台中的消息模式。**这也意味着,你需要导入对应的协议适配器中的消息序列和消息段后才能使用其特殊的工厂方法。**
:::caution 注意 :::warning[注意]
消息段的类型是由协议适配器提供的,因此你需要参考协议适配器的文档并导入对应的消息段后才能使用其特殊的消息类型。 消息段的类型是由协议适配器提供的,因此你需要参考协议适配器的文档并导入对应的消息段后才能使用其特殊的消息类型。
在上一节的[使用依赖注入](./event-data.mdx#使用依赖注入)中,我们导入的为 `nonebot.adapters.Message` 抽象基类,因此我们无法使用平台特有的消息类型。仅能使用 `str` 作为纯文本消息回复。 在上一节的[使用依赖注入](./event-data.mdx#使用依赖注入)中,我们导入的为 `nonebot.adapters.Message` 抽象基类,因此我们无法使用平台特有的消息类型。仅能使用 `str` 作为纯文本消息回复。
@@ -34,7 +34,7 @@ options:
## 使用消息序列 ## 使用消息序列
:::caution 注意 :::warning[注意]
在以下的示例中,为了更好的理解多种类型的消息组成方式,我们将使用 `Console` 协议适配器来演示消息序列的使用方法。在实际使用中,你需要确保你使用的**消息序列类型**与你所要发送的**平台类型**一致。 在以下的示例中,为了更好的理解多种类型的消息组成方式,我们将使用 `Console` 协议适配器来演示消息序列的使用方法。在实际使用中,你需要确保你使用的**消息序列类型**与你所要发送的**平台类型**一致。
::: :::
@@ -296,7 +296,7 @@ msg == Message(
如果 `Message.template` 构建消息模板,那么消息模板将采用消息序列形式的格式化,此时的消息将会是平台特定的: 如果 `Message.template` 构建消息模板,那么消息模板将采用消息序列形式的格式化,此时的消息将会是平台特定的:
:::caution 注意 :::warning[注意]
使用 `Message.template` 构建消息模板时,应注意消息序列为平台适配器提供的类型,不能使用 `nonebot.adapters.Message` 基类作为模板构建。使用基类构建模板与使用 `str` 构建模板的效果是一样的,因此请使用上述的 `MessageTemplate` 类直接构建模板。: 使用 `Message.template` 构建消息模板时,应注意消息序列为平台适配器提供的类型,不能使用 `nonebot.adapters.Message` 基类作为模板构建。使用基类构建模板与使用 `str` 构建模板的效果是一样的,因此请使用上述的 `MessageTemplate` 类直接构建模板。:
::: :::
@@ -336,7 +336,7 @@ Message(
) )
``` ```
:::caution 注意 :::warning[注意]
只有消息序列中的文本类型消息段才能被格式化,其他类型的消息段将会原样添加。 只有消息序列中的文本类型消息段才能被格式化,其他类型的消息段将会原样添加。
::: :::
+1 -1
View File
@@ -14,7 +14,7 @@ import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem"; import TabItem from "@theme/TabItem";
import Asciinema from "@site/src/components/Asciinema"; import Asciinema from "@site/src/components/Asciinema";
:::tip 提示 :::tip[提示]
如果你暂时没有获取商店内容的需求,可以跳过本章节。 如果你暂时没有获取商店内容的需求,可以跳过本章节。
+2 -3
View File
@@ -249,10 +249,9 @@ export default async function createConfigAsync() {
presets: [ presets: [
[ [
"@nullbot/docusaurus-preset-nonepress", "@nullbot/docusaurus-preset-nonepress",
/** @type {import('@nullbot/docusaurus-preset-nonepress').Options} */
{ {
docs: { docs: {
sidebarPath: require.resolve("./sidebars.js"), sidebarPath: require.resolve("./sidebars.ts"),
// Please change this to your repo. // Please change this to your repo.
editUrl: "https://github.com/nonebot/nonebot2/edit/master/website/", editUrl: "https://github.com/nonebot/nonebot2/edit/master/website/",
showLastUpdateAuthor: true, showLastUpdateAuthor: true,
@@ -293,7 +292,7 @@ export default async function createConfigAsync() {
gtag: { gtag: {
trackingID: "G-MRS1GMZG0F", trackingID: "G-MRS1GMZG0F",
}, },
}, } satisfies Preset.Options,
], ],
], ],
+4 -6
View File
@@ -22,15 +22,13 @@
"serve": "docusaurus serve", "serve": "docusaurus serve",
"write-translations": "docusaurus write-translations", "write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids", "write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc", "typecheck": "tsc"
"prettier": "prettier --config ../.prettierrc --write ."
}, },
"dependencies": { "dependencies": {
"@docusaurus/core": "3.10.2", "@docusaurus/core": "3.10.2",
"@docusaurus/eslint-plugin": "3.10.2",
"@mdx-js/react": "^3.0.0", "@mdx-js/react": "^3.0.0",
"@nullbot/docusaurus-plugin-changelog": "^3.7.0", "@nullbot/docusaurus-plugin-changelog": "^3.8.0",
"@nullbot/docusaurus-preset-nonepress": "^3.7.0", "@nullbot/docusaurus-preset-nonepress": "^3.8.0",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"copy-text-to-clipboard": "^3.2.0", "copy-text-to-clipboard": "^3.2.0",
"prism-react-renderer": "^2.3.0", "prism-react-renderer": "^2.3.0",
@@ -42,7 +40,7 @@
"devDependencies": { "devDependencies": {
"@docusaurus/faster": "3.10.2", "@docusaurus/faster": "3.10.2",
"@docusaurus/module-type-aliases": "3.10.2", "@docusaurus/module-type-aliases": "3.10.2",
"@nullbot/docusaurus-tsconfig": "^3.7.0", "@nullbot/docusaurus-tsconfig": "^3.8.0",
"@types/react-color": "^3.0.10", "@types/react-color": "^3.0.10",
"asciinema-player": "^3.5.0", "asciinema-player": "^3.5.0",
"typescript": "~5.7.2" "typescript": "~5.7.2"
+1 -1
View File
@@ -148,7 +148,7 @@ const sidebars: SidebarsConfig = {
type: "link", type: "link",
label: chunk[0]!.title, label: chunk[0]!.title,
href: `/changelog/${index > 0 ? index.toString() : ""}`, href: `/changelog/${index > 0 ? index.toString() : ""}`,
}) }),
), ),
}, },
], ],
@@ -20,7 +20,6 @@ export default function Asciinema(props: Props): React.ReactNode {
} }
> >
{() => { {() => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const AsciinemaContainer = require("./container.tsx").default; const AsciinemaContainer = require("./container.tsx").default;
return <AsciinemaContainer {...props} />; return <AsciinemaContainer {...props} />;
}} }}
+1 -1
View File
@@ -38,7 +38,7 @@ export default function AdapterForm(): React.ReactNode {
template: "adapter_publish.yml", template: "adapter_publish.yml",
title: `Adapter: ${result.name}`, title: `Adapter: ${result.name}`,
...result, ...result,
})}` })}`,
); );
}; };
+1 -1
View File
@@ -32,7 +32,7 @@ export default function BotForm(): React.ReactNode {
template: "bot_publish.yml", template: "bot_publish.yml",
title: `Bot: ${result.name}`, title: `Bot: ${result.name}`,
...result, ...result,
})}` })}`,
); );
}; };
@@ -27,8 +27,8 @@ export default function TagFormItem({
new Set( new Set(
allowTags allowTags
.filter((tag) => tag.label.toLocaleLowerCase().includes(label)) .filter((tag) => tag.label.toLocaleLowerCase().includes(label))
.map((e) => e.label) .map((e) => e.label),
) ),
).slice(0, 5); ).slice(0, 5);
const validateTag = () => { const validateTag = () => {
@@ -29,6 +29,7 @@
.fix-input-color { .fix-input-color {
@apply !text-base-content !bg-base-100; @apply !text-base-content !bg-base-100;
input { input {
@apply !text-base-content !bg-base-100; @apply !text-base-content !bg-base-100;
} }
+1 -1
View File
@@ -24,7 +24,7 @@ export default function PluginForm(): React.ReactNode {
template: "plugin_publish.yml", template: "plugin_publish.yml",
title: `Plugin: ${result.pypi}`, title: `Plugin: ${result.pypi}`,
...result, ...result,
})}` })}`,
); );
}; };
+3 -3
View File
@@ -49,8 +49,8 @@ export function Form({
data data
.filter((item) => item.tags.length > 0) .filter((item) => item.tags.length > 0)
.map((ele) => ele.tags) .map((ele) => ele.tags)
.flat() .flat(),
) ),
) )
.catch((e) => { .catch((e) => {
console.error(e); console.error(e);
@@ -63,7 +63,7 @@ export function Form({
const handleNextStep = () => { const handleNextStep = () => {
const currentStepNames = formItems[currentStep].items.map( const currentStepNames = formItems[currentStep].items.map(
(item) => item.name (item) => item.name,
); );
if (currentStepNames.every((name) => result[name])) { if (currentStepNames.every((name) => result[name])) {
setCurrentStep(currentStep + 1); setCurrentStep(currentStep + 1);
+2 -2
View File
@@ -37,7 +37,7 @@ function MessageBox({
<div <div
className={clsx( className={clsx(
"messenger-chat-avatar", "messenger-chat-avatar",
isRight && "messenger-chat-avatar-user" isRight && "messenger-chat-avatar-user",
)} )}
> >
{isRight ? ( {isRight ? (
@@ -50,7 +50,7 @@ function MessageBox({
<div <div
className={clsx( className={clsx(
"chat-bubble messenger-chat-bubble", "chat-bubble messenger-chat-bubble",
monospace && "font-mono" monospace && "font-mono",
)} )}
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: msg.replace(/\n/g, "<br/>").replace(/ /g, "&nbsp;"), __html: msg.replace(/\n/g, "<br/>").replace(/ /g, "&nbsp;"),
+3 -3
View File
@@ -26,7 +26,7 @@ export type Props = Pick<
export default function Paginate({ export default function Paginate({
className, className,
totalPages, totalPages,
currentPage, currentPage: currentPageProp,
setPreviousPage, setPreviousPage,
setNextPage, setNextPage,
setPage, setPage,
@@ -67,7 +67,7 @@ export default function Paginate({
const even = MAX_LENGTH % 2 === 0 ? 1 : 0; const even = MAX_LENGTH % 2 === 0 ? 1 : 0;
const left = Math.floor(MAX_LENGTH / 2); const left = Math.floor(MAX_LENGTH / 2);
const right = totalPages - left + even + 1; const right = totalPages - left + even + 1;
currentPage += 1; const currentPage = currentPageProp + 1;
if (totalPages <= MAX_LENGTH) { if (totalPages <= MAX_LENGTH) {
pages.push(...range(1, totalPages)); pages.push(...range(1, totalPages));
@@ -110,7 +110,7 @@ export default function Paginate({
className={clsx( className={clsx(
"paginate-button", "paginate-button",
typeof page !== "number" && "ellipsis", typeof page !== "number" && "ellipsis",
currentPage === page && "active" currentPage === page && "active",
)} )}
onClick={() => onClick={() =>
typeof page === "number" && typeof page === "number" &&
@@ -21,7 +21,7 @@ export default function Avatar({ authorLink, authorAvatar, className }: Props) {
<div <div
className={clsx( className={clsx(
"absolute inset-0 rounded-full bg-gray-200", "absolute inset-0 rounded-full bg-gray-200",
"animate-pulse" "animate-pulse",
)} )}
/> />
)} )}
@@ -31,7 +31,7 @@ export default function Avatar({ authorLink, authorAvatar, className }: Props) {
className={clsx( className={clsx(
"w-full h-full rounded-full object-cover", "w-full h-full rounded-full object-cover",
"transition-opacity duration-300", "transition-opacity duration-300",
loaded ? "opacity-100" : "opacity-0" loaded ? "opacity-100" : "opacity-0",
)} )}
alt="Avatar" alt="Avatar"
/> />
@@ -28,7 +28,7 @@ export default function ResourceCard({
className, className,
}: Props): React.ReactNode { }: Props): React.ReactNode {
const isGithub = /^https:\/\/github.com\/[^/]+\/[^/]+/.test( const isGithub = /^https:\/\/github.com\/[^/]+\/[^/]+/.test(
resource.homepage resource.homepage,
); );
const isPlugin = resource.resourceType === "plugin"; const isPlugin = resource.resourceType === "plugin";
@@ -180,7 +180,7 @@ export default function ResourceDetailCard({ resource }: Props) {
`${ `${
pypiData.releases[pypiData.info.version].reduce( pypiData.releases[pypiData.info.version].reduce(
(acc, curr) => acc + curr.size, (acc, curr) => acc + curr.size,
0 0,
) / 1000 ) / 1000
}K`) || }K`) ||
"无"} "无"}
@@ -88,14 +88,14 @@ export default function AdapterPage(): React.ReactNode {
(tag: string) => { (tag: string) => {
addFilter(tagFilter(tag)); addFilter(tagFilter(tag));
}, },
[addFilter] [addFilter],
); );
const onCardAuthorClick = useCallback( const onCardAuthorClick = useCallback(
(author: string) => { (author: string) => {
addFilter(authorFilter(author)); addFilter(authorFilter(author));
}, },
[addFilter] [addFilter],
); );
return ( return (
+2 -2
View File
@@ -80,14 +80,14 @@ export default function PluginPage(): React.ReactNode {
(tag: string) => { (tag: string) => {
addFilter(tagFilter(tag)); addFilter(tagFilter(tag));
}, },
[addFilter] [addFilter],
); );
const onAuthorClick = useCallback( const onAuthorClick = useCallback(
(author: string) => { (author: string) => {
addFilter(authorFilter(author)); addFilter(authorFilter(author));
}, },
[addFilter] [addFilter],
); );
return ( return (
@@ -71,14 +71,14 @@ export default function DriverPage(): React.ReactNode {
(tag: string) => { (tag: string) => {
addFilter(tagFilter(tag)); addFilter(tagFilter(tag));
}, },
[addFilter] [addFilter],
); );
const onAuthorClick = useCallback( const onAuthorClick = useCallback(
(author: string) => { (author: string) => {
addFilter(authorFilter(author)); addFilter(authorFilter(author));
}, },
[addFilter] [addFilter],
); );
return ( return (
@@ -50,7 +50,7 @@ export default function PluginPage(): React.ReactNode {
active: sortMode === SortMode.UpdateDesc, active: sortMode === SortMode.UpdateDesc,
onClick: () => { onClick: () => {
setSortMode( setSortMode(
sortMode === SortMode.Default ? SortMode.UpdateDesc : SortMode.Default sortMode === SortMode.Default ? SortMode.UpdateDesc : SortMode.Default,
); );
}, },
}; };
@@ -58,7 +58,7 @@ export default function PluginPage(): React.ReactNode {
const getSortedPlugins = (plugins: Plugin[]): Plugin[] => { const getSortedPlugins = (plugins: Plugin[]): Plugin[] => {
if (sortMode === SortMode.UpdateDesc) { if (sortMode === SortMode.UpdateDesc) {
return [...plugins].sort( return [...plugins].sort(
(a, b) => new Date(b.time).getTime() - new Date(a.time).getTime() (a, b) => new Date(b.time).getTime() - new Date(a.time).getTime(),
); );
} }
return plugins; return plugins;
@@ -124,14 +124,14 @@ export default function PluginPage(): React.ReactNode {
(tag: string) => { (tag: string) => {
addFilter(tagFilter(tag)); addFilter(tagFilter(tag));
}, },
[addFilter] [addFilter],
); );
const onCardAuthorClick = useCallback( const onCardAuthorClick = useCallback(
(author: string) => { (author: string) => {
addFilter(authorFilter(author)); addFilter(authorFilter(author));
}, },
[addFilter] [addFilter],
); );
return ( return (
+1 -1
View File
@@ -22,7 +22,7 @@ type Props = {
function StorePage({ title, children }: Props): React.ReactNode { function StorePage({ title, children }: Props): React.ReactNode {
const sidebarItems = useVersionedSidebar( const sidebarItems = useVersionedSidebar(
useDocsVersionCandidates()[0].name, useDocsVersionCandidates()[0].name,
SIDEBAR_ID SIDEBAR_ID,
)!; )!;
return ( return (
+2 -2
View File
@@ -125,7 +125,7 @@ export default function StoreToolbar({
<button <button
className={clsx( className={clsx(
"btn btn-sm btn-primary no-animation mr-2", "btn btn-sm btn-primary no-animation mr-2",
!sorter.active && "btn-outline" !sorter.active && "btn-outline",
)} )}
onClick={sorter.onClick} onClick={sorter.onClick}
> >
@@ -154,7 +154,7 @@ export default function StoreToolbar({
<button <button
className={clsx( className={clsx(
"btn btn-sm btn-primary no-animation mr-2", "btn btn-sm btn-primary no-animation mr-2",
!sorter.active && "btn-outline" !sorter.active && "btn-outline",
)} )}
onClick={sorter.onClick} onClick={sorter.onClick}
> >
+1 -1
View File
@@ -6,7 +6,7 @@
export function pickTextColor( export function pickTextColor(
bgColor: string, bgColor: string,
lightColor: string, lightColor: string,
darkColor: string darkColor: string,
) { ) {
const color = bgColor.charAt(0) === "#" ? bgColor.substring(1, 7) : bgColor; const color = bgColor.charAt(0) === "#" ? bgColor.substring(1, 7) : bgColor;
const r = parseInt(color.substring(0, 2), 16); // hexToR const r = parseInt(color.substring(0, 2), 16); // hexToR
+15 -15
View File
@@ -39,7 +39,7 @@ const validStatusDisplayName = {
}; };
export const validStatusFilter = <T extends Resource = Resource>( export const validStatusFilter = <T extends Resource = Resource>(
validStatus: ValidStatus validStatus: ValidStatus,
): Filter<T> => ({ ): Filter<T> => ({
type: "validStatus", type: "validStatus",
id: `validStatus-${validStatus}`, id: `validStatus-${validStatus}`,
@@ -51,7 +51,7 @@ export const validStatusFilter = <T extends Resource = Resource>(
}); });
export const tagFilter = <T extends Resource = Resource>( export const tagFilter = <T extends Resource = Resource>(
tag: string tag: string,
): Filter<T> => ({ ): Filter<T> => ({
type: "tag", type: "tag",
id: `tag-${tag}`, id: `tag-${tag}`,
@@ -61,14 +61,14 @@ export const tagFilter = <T extends Resource = Resource>(
description: "The display name of tag filter", description: "The display name of tag filter",
message: "标签: {tag}", message: "标签: {tag}",
}, },
{ tag } { tag },
), ),
filter: (resource: Resource): boolean => filter: (resource: Resource): boolean =>
resource.tags.map((tag) => tag.label).includes(tag), resource.tags.map((tag) => tag.label).includes(tag),
}); });
export const officialFilter = <T extends Resource = Resource>( export const officialFilter = <T extends Resource = Resource>(
official: boolean = true official: boolean = true,
): Filter<T> => ({ ): Filter<T> => ({
type: "official", type: "official",
id: `official-${official}`, id: `official-${official}`,
@@ -81,7 +81,7 @@ export const officialFilter = <T extends Resource = Resource>(
}); });
export const authorFilter = <T extends Resource = Resource>( export const authorFilter = <T extends Resource = Resource>(
author: string author: string,
): Filter<T> => ({ ): Filter<T> => ({
type: "author", type: "author",
id: `author-${author}`, id: `author-${author}`,
@@ -91,13 +91,13 @@ export const authorFilter = <T extends Resource = Resource>(
description: "The display name of author filter", description: "The display name of author filter",
message: "作者: {author}", message: "作者: {author}",
}, },
{ author } { author },
), ),
filter: (resource: Resource): boolean => resource.author === author, filter: (resource: Resource): boolean => resource.author === author,
}); });
export const queryFilter = <T extends Resource = Resource>( export const queryFilter = <T extends Resource = Resource>(
query: string query: string,
): Filter<T> => ({ ): Filter<T> => ({
type: "query", type: "query",
id: `query-${query}`, id: `query-${query}`,
@@ -123,10 +123,10 @@ export const queryFilter = <T extends Resource = Resource>(
export function filterResources<T extends Resource>( export function filterResources<T extends Resource>(
resources: T[], resources: T[],
filters: Filter<T>[] filters: Filter<T>[],
): T[] { ): T[] {
return resources.filter((resource) => return resources.filter((resource) =>
filters.every((filter) => filter.filter(resource)) filters.every((filter) => filter.filter(resource)),
); );
} }
@@ -138,7 +138,7 @@ type useFilteredResourcesReturn<T extends Resource> = {
}; };
export function useFilteredResources<T extends Resource>( export function useFilteredResources<T extends Resource>(
resources: T[] resources: T[],
): useFilteredResourcesReturn<T> { ): useFilteredResourcesReturn<T> {
const [filters, setFilters] = useState<Filter<T>[]>([]); const [filters, setFilters] = useState<Filter<T>[]>([]);
@@ -149,22 +149,22 @@ export function useFilteredResources<T extends Resource>(
} }
setFilters((filters) => [...filters, filter]); setFilters((filters) => [...filters, filter]);
}, },
[filters, setFilters] [filters, setFilters],
); );
const removeFilter = useCallback( const removeFilter = useCallback(
(filter: Filter<T> | string) => { (filter: Filter<T> | string) => {
setFilters((filters) => setFilters((filters) =>
filters.filter((f) => filters.filter((f) =>
typeof filter === "string" ? f.id !== filter : f !== filter typeof filter === "string" ? f.id !== filter : f !== filter,
) ),
); );
}, },
[setFilters] [setFilters],
); );
const filteredResources = useCallback( const filteredResources = useCallback(
() => filterResources(resources, filters), () => filterResources(resources, filters),
[resources, filters] [resources, filters],
); );
return { return {
+4 -4
View File
@@ -16,7 +16,7 @@ type useSearchControlReturn<T extends Resource> = {
}; };
export function useSearchControl<T extends Resource>( export function useSearchControl<T extends Resource>(
resources: T[] resources: T[],
): useSearchControlReturn<T> { ): useSearchControlReturn<T> {
const [currentFilter, setCurrentFilter] = useState<Filter<T> | null>(null); const [currentFilter, setCurrentFilter] = useState<Filter<T> | null>(null);
@@ -28,7 +28,7 @@ export function useSearchControl<T extends Resource>(
useEffect(() => { useEffect(() => {
setSearcherFilters( setSearcherFilters(
filters.filter((f) => !(currentFilter && f === currentFilter)) filters.filter((f) => !(currentFilter && f === currentFilter)),
); );
}, [filters, currentFilter]); }, [filters, currentFilter]);
@@ -53,7 +53,7 @@ export function useSearchControl<T extends Resource>(
setCurrentFilter(newFilter); setCurrentFilter(newFilter);
addFilter(newFilter); addFilter(newFilter);
}, },
[currentFilter, setCurrentFilter, addFilter, removeFilter] [currentFilter, setCurrentFilter, addFilter, removeFilter],
); );
const onSearchQuerySubmit = useCallback(() => { const onSearchQuerySubmit = useCallback(() => {
@@ -75,7 +75,7 @@ export function useSearchControl<T extends Resource>(
(index: number) => { (index: number) => {
removeFilter(searcherFilters[index]); removeFilter(searcherFilters[index]);
}, },
[removeFilter, searcherFilters] [removeFilter, searcherFilters],
); );
return { return {
+4 -4
View File
@@ -23,21 +23,21 @@ type ResourceTypes = {
export type Resource = Adapter | Bot | Driver | Plugin; export type Resource = Adapter | Bot | Driver | Plugin;
export async function fetchRegistryData<T extends RegistryDataType>( export async function fetchRegistryData<T extends RegistryDataType>(
dataType: T dataType: T,
): Promise<ResourceTypes[T][]> { ): Promise<ResourceTypes[T][]> {
const resp = await fetch( const resp = await fetch(
`https://registry.nonebot.dev/${dataType}s.json` `https://registry.nonebot.dev/${dataType}s.json`,
).catch((e) => { ).catch((e) => {
throw new Error(`Failed to fetch ${dataType}s: ${e}`); throw new Error(`Failed to fetch ${dataType}s: ${e}`);
}); });
if (!resp.ok) { if (!resp.ok) {
throw new Error( throw new Error(
`Failed to fetch ${dataType}s: ${resp.status} ${resp.statusText}` `Failed to fetch ${dataType}s: ${resp.status} ${resp.statusText}`,
); );
} }
const data = (await resp.json()) as RegistryDataResponseTypes[T]; const data = (await resp.json()) as RegistryDataResponseTypes[T];
return data.map( return data.map(
(resource) => ({ ...resource, resourceType: dataType }) as ResourceTypes[T] (resource) => ({ ...resource, resourceType: dataType }) as ResourceTypes[T],
); );
} }
+2 -2
View File
@@ -39,8 +39,8 @@ export function useToolbar<T extends Resource = Resource>({
icon: ["fas", "tag"], icon: ["fas", "tag"],
choices: Array.from( choices: Array.from(
new Set( new Set(
resources.flatMap((resource) => resource.tags.map((tag) => tag.label)) resources.flatMap((resource) => resource.tags.map((tag) => tag.label)),
) ),
), ),
onSubmit: (tag: string) => { onSubmit: (tag: string) => {
addFilter(tagFilter(tag)); addFilter(tagFilter(tag));
+1 -1
View File
@@ -7,7 +7,7 @@ const darkTheme = themes.dark;
function excludeThemeColor( function excludeThemeColor(
theme: { [key: string]: string }, theme: { [key: string]: string },
exclude: string[] exclude: string[],
): { [key: string]: string } { ): { [key: string]: string } {
const newObj: { [key: string]: string } = {}; const newObj: { [key: string]: string } = {};
for (const key in theme) { for (const key in theme) {
@@ -29,11 +29,11 @@ import TabItem from "@theme/TabItem";
- 根据已经解析的 `Dependency` 依赖,执行调用。 - 根据已经解析的 `Dependency` 依赖,执行调用。
- 将所有 `Dependency` 的返回值根据参数名传入并调用 `Dependent` 。 - 将所有 `Dependency` 的返回值根据参数名传入并调用 `Dependent` 。
:::danger 警告 :::danger[警告]
在依赖注入中,类型注解是非常重要的,因为它不仅可以决定依赖注入的对象,还可以触发[重载机制](../appendices/overload.md#重载)。如果类型注解与实际获得数据类型不一致,将会跳过当前 `Dependent` 对象(即事件处理函数)。 在依赖注入中,类型注解是非常重要的,因为它不仅可以决定依赖注入的对象,还可以触发[重载机制](../appendices/overload.md#重载)。如果类型注解与实际获得数据类型不一致,将会跳过当前 `Dependent` 对象(即事件处理函数)。
::: :::
:::tip 提示 :::tip[提示]
如果对于依赖注入的解析流程有疑问,可以调整[日志等级配置项](../appendices/config.mdx#log-level)为 `TRACE`,查看依赖解析日志。 如果对于依赖注入的解析流程有疑问,可以调整[日志等级配置项](../appendices/config.mdx#log-level)为 `TRACE`,查看依赖解析日志。
::: :::
@@ -349,7 +349,7 @@ async def _(x: int = Depends(random_result, use_cache=False)):
</TabItem> </TabItem>
</Tabs> </Tabs>
:::tip 提示 :::tip[提示]
缓存的生命周期与当前接收到的事件相同。接收到事件后,子依赖在首次执行时缓存,在该事件处理完成后,缓存就会被清除。 缓存的生命周期与当前接收到的事件相同。接收到事件后,子依赖在首次执行时缓存,在该事件处理完成后,缓存就会被清除。
::: :::
@@ -558,7 +558,7 @@ async def _(x: httpx.AsyncClient = Depends(get_client)):
</TabItem> </TabItem>
</Tabs> </Tabs>
:::caution 注意 :::warning[注意]
生成器作为依赖时,其中只能进行一次 `yield`,否则将会触发异常。如果对此有疑问并想探究原因,可以参考 [contextmanager](https://docs.python.org/zh-cn/3/library/contextlib.html#contextlib.contextmanager) 和 [asynccontextmanager](https://docs.python.org/zh-cn/3/library/contextlib.html#contextlib.asynccontextmanager) 文档。事实上,NoneBot 内部就使用了这两个装饰器。 生成器作为依赖时,其中只能进行一次 `yield`,否则将会触发异常。如果对此有疑问并想探究原因,可以参考 [contextmanager](https://docs.python.org/zh-cn/3/library/contextlib.html#contextlib.contextmanager) 和 [asynccontextmanager](https://docs.python.org/zh-cn/3/library/contextlib.html#contextlib.asynccontextmanager) 文档。事实上,NoneBot 内部就使用了这两个装饰器。
::: :::
@@ -715,7 +715,7 @@ async def _(foo: tuple[str, ...] = Command()): ...
</TabItem> </TabItem>
</Tabs> </Tabs>
:::tip 提示 :::tip[提示]
命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。 命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。
::: :::
@@ -745,7 +745,7 @@ async def _(foo: str = RawCommand()): ...
</TabItem> </TabItem>
</Tabs> </Tabs>
:::tip 提示 :::tip[提示]
命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。 命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。
::: :::
@@ -777,7 +777,7 @@ async def _(foo: Message = CommandArg()): ...
</TabItem> </TabItem>
</Tabs> </Tabs>
:::tip 提示 :::tip[提示]
命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。 命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。
::: :::
@@ -807,7 +807,7 @@ async def _(foo: str = CommandStart()): ...
</TabItem> </TabItem>
</Tabs> </Tabs>
:::tip 提示 :::tip[提示]
命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。 命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。
::: :::
@@ -837,7 +837,7 @@ async def _(foo: str = CommandWhitespace()): ...
</TabItem> </TabItem>
</Tabs> </Tabs>
:::tip 提示 :::tip[提示]
命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。 命令详情只能在**触发命令型事件响应器时**获取。如果在事件处理后续流程中获取,则会获取到不同的值。
::: :::
@@ -921,7 +921,7 @@ async def _(foo: list[Union[str, MessageSegment]] = ShellCommandArgv()): ...
获取 shell 命令解析后的参数 Namespace,支持 MessageSegment 富文本(如:图片)。 获取 shell 命令解析后的参数 Namespace,支持 MessageSegment 富文本(如:图片)。
:::tip 提示 :::tip[提示]
如果参数解析成功,则为 parser 返回的 Namespace;如果参数解析失败,则为 [`ParserExit`](../api/exception.md#ParserExit) 异常,并携带错误码与错误信息。在前置词法解析失败时,返回值也为 [`ParserExit`](../api/exception.md#ParserExit) 异常。通过重载机制即可处理两种不同的情况。 如果参数解析成功,则为 parser 返回的 Namespace;如果参数解析失败,则为 [`ParserExit`](../api/exception.md#ParserExit) 异常,并携带错误码与错误信息。在前置词法解析失败时,返回值也为 [`ParserExit`](../api/exception.md#ParserExit) 异常。通过重载机制即可处理两种不同的情况。
由于 `ArgumentParser` 在解析到 `--help` 参数时也会抛出异常,这种情况下错误码为 `0` 且错误信息即为帮助信息。 由于 `ArgumentParser` 在解析到 `--help` 参数时也会抛出异常,这种情况下错误码为 `0` 且错误信息即为帮助信息。
@@ -12,11 +12,11 @@ options:
驱动器 (Driver) 是机器人运行的基石,它是机器人初始化的第一步,主要负责数据收发。 驱动器 (Driver) 是机器人运行的基石,它是机器人初始化的第一步,主要负责数据收发。
:::important 提示 :::info[提示]
驱动器的选择通常与机器人所使用的协议适配器相关,如果不知道该选择哪个驱动器,可以先阅读相关协议适配器文档说明。 驱动器的选择通常与机器人所使用的协议适配器相关,如果不知道该选择哪个驱动器,可以先阅读相关协议适配器文档说明。
::: :::
:::tip 提示 :::tip[提示]
如何**安装**驱动器请参考[安装驱动器](../tutorial/store.mdx#安装驱动器)。 如何**安装**驱动器请参考[安装驱动器](../tutorial/store.mdx#安装驱动器)。
::: :::
@@ -118,7 +118,7 @@ DRIVER=~fastapi
##### `fastapi_reload` ##### `fastapi_reload`
:::caution 警告 :::warning[警告]
不推荐开启该配置项,在 Windows 平台上开启该功能有可能会造成预料之外的影响!替代方案:使用 `nb-cli` 命令行工具以及参数 `--reload` 启动 NoneBot。 不推荐开启该配置项,在 Windows 平台上开启该功能有可能会造成预料之外的影响!替代方案:使用 `nb-cli` 命令行工具以及参数 `--reload` 启动 NoneBot。
```bash ```bash
@@ -200,7 +200,7 @@ DRIVER=~quart
##### `quart_reload` ##### `quart_reload`
:::caution 警告 :::warning[警告]
不推荐开启该配置项,在 Windows 平台上开启该功能有可能会造成预料之外的影响!替代方案:使用 `nb-cli` 命令行工具以及参数 `--reload` 启动 NoneBot。 不推荐开启该配置项,在 Windows 平台上开启该功能有可能会造成预料之外的影响!替代方案:使用 `nb-cli` 命令行工具以及参数 `--reload` 启动 NoneBot。
```bash ```bash
@@ -252,7 +252,7 @@ nonebot.run(app="bot:app")
**类型:**HTTP 客户端驱动器 **类型:**HTTP 客户端驱动器
:::caution 注意 :::warning[注意]
本驱动器仅支持 HTTP 请求,不支持 WebSocket 连接请求。 本驱动器仅支持 HTTP 请求,不支持 WebSocket 连接请求。
::: :::
@@ -266,7 +266,7 @@ DRIVER=~httpx
**类型:**WebSocket 客户端驱动器 **类型:**WebSocket 客户端驱动器
:::caution 注意 :::warning[注意]
本驱动器仅支持 WebSocket 连接请求,不支持 HTTP 请求。 本驱动器仅支持 WebSocket 连接请求,不支持 HTTP 请求。
::: :::
@@ -12,7 +12,7 @@ options:
在[指南](../tutorial/matcher.md)与[深入](../appendices/rule.md)中,我们已经介绍了事件响应器的基本用法以及响应规则、权限控制等功能。在这一节中,我们将介绍事件响应器的组成,内置的响应规则,与第三方响应规则拓展。 在[指南](../tutorial/matcher.md)与[深入](../appendices/rule.md)中,我们已经介绍了事件响应器的基本用法以及响应规则、权限控制等功能。在这一节中,我们将介绍事件响应器的组成,内置的响应规则,与第三方响应规则拓展。
:::tip 提示 :::tip[提示]
事件响应器允许继承,你可以通过直接继承 `Matcher` 类来创建一个新的事件响应器。 事件响应器允许继承,你可以通过直接继承 `Matcher` 类来创建一个新的事件响应器。
::: :::
@@ -228,7 +228,7 @@ matcher = on_shell_command("cmd", parser=parser)
`regex` 响应规则用于匹配消息是否与指定正则表达式匹配。 `regex` 响应规则用于匹配消息是否与指定正则表达式匹配。
:::tip 提示 :::tip[提示]
正则表达式匹配使用 search 而非 match,如需从头匹配请使用 `r"^xxx"` 模式来确保匹配开头。 正则表达式匹配使用 search 而非 match,如需从头匹配请使用 `r"^xxx"` 模式来确保匹配开头。
::: :::
@@ -98,7 +98,7 @@ NoneBot 使用 [`pydantic`](https://pydantic-docs.helpmanual.io/) 以及
参考 [记录日志](https://nonebot.dev/docs/appendices/log)[loguru 日志等级](https://loguru.readthedocs.io/en/stable/api/logger.html#levels)。 参考 [记录日志](https://nonebot.dev/docs/appendices/log)[loguru 日志等级](https://loguru.readthedocs.io/en/stable/api/logger.html#levels)。
:::tip 提示 :::tip[提示]
日志等级名称应为大写,如 `INFO` 日志等级名称应为大写,如 `INFO`
::: :::
@@ -15,7 +15,7 @@ nb driver install aiohttp
pip install nonebot2[aiohttp] pip install nonebot2[aiohttp]
``` ```
:::tip 提示 :::tip[提示]
本驱动仅支持客户端连接 本驱动仅支持客户端连接
::: :::
@@ -15,7 +15,7 @@ nb driver install fastapi
pip install nonebot2[fastapi] pip install nonebot2[fastapi]
``` ```
:::tip 提示 :::tip[提示]
本驱动仅支持服务端连接 本驱动仅支持服务端连接
::: :::
@@ -15,7 +15,7 @@ nb driver install httpx
pip install nonebot2[httpx] pip install nonebot2[httpx]
``` ```
:::tip 提示 :::tip[提示]
本驱动仅支持客户端 HTTP 连接 本驱动仅支持客户端 HTTP 连接
::: :::
@@ -9,7 +9,7 @@ description: nonebot.drivers.none 模块
None 驱动适配 None 驱动适配
:::tip 提示 :::tip[提示]
本驱动不支持任何服务器或客户端连接 本驱动不支持任何服务器或客户端连接
::: :::
@@ -15,7 +15,7 @@ nb driver install quart
pip install nonebot2[quart] pip install nonebot2[quart]
``` ```
:::tip 提示 :::tip[提示]
本驱动仅支持服务端连接 本驱动仅支持服务端连接
::: :::
@@ -15,7 +15,7 @@ nb driver install websockets
pip install nonebot2[websockets] pip install nonebot2[websockets]
``` ```
:::tip 提示 :::tip[提示]
本驱动仅支持客户端 WebSocket 连接 本驱动仅支持客户端 WebSocket 连接
::: :::
@@ -187,7 +187,7 @@ description: nonebot.rule 模块
命令 `("test",)` 可以匹配: `/test` 开头的消息 命令 `("test",)` 可以匹配: `/test` 开头的消息
命令 `("test", "sub")` 可以匹配: `/test.sub` 开头的消息 命令 `("test", "sub")` 可以匹配: `/test.sub` 开头的消息
:::tip 提示 :::tip[提示]
命令内容与后续消息间无需空格! 命令内容与后续消息间无需空格!
::: :::
@@ -264,7 +264,7 @@ description: nonebot.rule 模块
通过 [ShellCommandArgs](params.md#ShellCommandArgs) 获取解析后的参数字典 通过 [ShellCommandArgs](params.md#ShellCommandArgs) 获取解析后的参数字典
(例: `{"arg": "arg", "h": True}`)。 (例: `{"arg": "arg", "h": True}`)。
:::caution 警告 :::warning[警告]
如果参数解析失败,则通过 [ShellCommandArgs](params.md#ShellCommandArgs) 如果参数解析失败,则通过 [ShellCommandArgs](params.md#ShellCommandArgs)
获取的将是 [ParserExit](exception.md#ParserExit) 异常。 获取的将是 [ParserExit](exception.md#ParserExit) 异常。
::: :::
@@ -291,7 +291,7 @@ description: nonebot.rule 模块
rule = shell_command("ls", parser=parser) rule = shell_command("ls", parser=parser)
``` ```
:::tip 提示 :::tip[提示]
命令内容与后续消息间无需空格! 命令内容与后续消息间无需空格!
::: :::
@@ -322,10 +322,10 @@ description: nonebot.rule 模块
- **返回** - **返回**
- [Rule](#Rule) - [Rule](#Rule)
:::tip 提示 :::tip[提示]
正则表达式匹配使用 search 而非 match,如需从头匹配请使用 `r"^xxx"` 来确保匹配开头 正则表达式匹配使用 search 而非 match,如需从头匹配请使用 `r"^xxx"` 来确保匹配开头
::: :::
:::tip 提示 :::tip[提示]
正则表达式匹配使用 `EventMessage` 的 `str` 字符串, 正则表达式匹配使用 `EventMessage` 的 `str` 字符串,
而非 `EventMessage` 的 `PlainText` 纯文本字符串 而非 `EventMessage` 的 `PlainText` 纯文本字符串
::: :::
@@ -18,7 +18,7 @@ import Messenger from "@/components/Messenger";
在之前的章节中,我们介绍了如何向用户发送文本消息以及[如何处理平台消息](../tutorial/message.md),现在我们来向用户发送平台特殊消息。 在之前的章节中,我们介绍了如何向用户发送文本消息以及[如何处理平台消息](../tutorial/message.md),现在我们来向用户发送平台特殊消息。
:::caution 注意 :::warning[注意]
在以下的示例中,我们将使用 `Console` 协议适配器来演示如何发送平台消息。在实际使用中,你需要确保你使用的**消息序列类型**与你所要发送的**平台类型**一致。 在以下的示例中,我们将使用 `Console` 协议适配器来演示如何发送平台消息。在实际使用中,你需要确保你使用的**消息序列类型**与你所要发送的**平台类型**一致。
::: :::
@@ -103,7 +103,7 @@ result = await bot.get_user_info(user_id=12345678)
result = await bot.call_api("get_user_info", user_id=12345678) result = await bot.call_api("get_user_info", user_id=12345678)
``` ```
:::caution 注意 :::warning[注意]
实际可以使用的 API 以及参数取决于平台提供的接口以及协议适配器的实现,请参考协议适配器以及平台文档。 实际可以使用的 API 以及参数取决于平台提供的接口以及协议适配器的实现,请参考协议适配器以及平台文档。
::: :::
@@ -19,7 +19,7 @@ NoneBot 使用 [`pydantic`](https://docs.pydantic.dev/) 以及 [`python-dotenv`]
NoneBot 内置的配置项列表及含义可以在[内置配置项](#内置配置项)中查看。 NoneBot 内置的配置项列表及含义可以在[内置配置项](#内置配置项)中查看。
:::caution 注意 :::warning[注意]
NoneBot 自 2.2.0 起兼容了 Pydantic v1 与 v2 版本,以下文档中 Pydantic 相关示例均采用 v2 版本用法。 NoneBot 自 2.2.0 起兼容了 Pydantic v1 与 v2 版本,以下文档中 Pydantic 相关示例均采用 v2 版本用法。
@@ -83,7 +83,7 @@ export CUSTOM_CONFIG='config in environment variables'
那最终 NoneBot 所读取的内容为环境变量中的内容,即 `config in environment variables`。 那最终 NoneBot 所读取的内容为环境变量中的内容,即 `config in environment variables`。
:::caution 注意 :::warning[注意]
NoneBot 不会自发读取未被定义的配置项的环境变量,如果需要读取某一环境变量需要在 dotenv 配置文件中进行声明。 NoneBot 不会自发读取未被定义的配置项的环境变量,如果需要读取某一环境变量需要在 dotenv 配置文件中进行声明。
::: :::
@@ -162,7 +162,7 @@ COMMON_CONFIG=common config # 这个配置项在任何环境中都会被加载
这样,我们在启动 NoneBot 时就会从 `.env.dev` 文件中加载剩余配置项。 这样,我们在启动 NoneBot 时就会从 `.env.dev` 文件中加载剩余配置项。
:::tip 提示 :::tip[提示]
在生产环境中,可以通过设置环境变量 `ENVIRONMENT=prod` 来确保 NoneBot 读取正确的环境配置。 在生产环境中,可以通过设置环境变量 `ENVIRONMENT=prod` 来确保 NoneBot 读取正确的环境配置。
::: :::
@@ -242,7 +242,7 @@ weather = on_command(
这种方式可以简洁、高效地读取配置项,同时也可以设置默认值或者在运行时对配置项进行合法性检查,防止由于配置项导致的插件出错等情况出现。 这种方式可以简洁、高效地读取配置项,同时也可以设置默认值或者在运行时对配置项进行合法性检查,防止由于配置项导致的插件出错等情况出现。
:::tip 提示 :::tip[提示]
发布插件应该为自身的事件响应器提供可配置的优先级,以便插件使用者可以自定义多个插件间的响应顺序。 发布插件应该为自身的事件响应器提供可配置的优先级,以便插件使用者可以自定义多个插件间的响应顺序。
::: :::
@@ -400,7 +400,7 @@ nonebot.init(port=8080)
NoneBot 日志输出等级,可以为 `int` 类型等级或等级名称。具体等级对照表参考 [loguru 日志等级](https://loguru.readthedocs.io/en/stable/api/logger.html#levels)。 NoneBot 日志输出等级,可以为 `int` 类型等级或等级名称。具体等级对照表参考 [loguru 日志等级](https://loguru.readthedocs.io/en/stable/api/logger.html#levels)。
:::tip 提示 :::tip[提示]
日志等级名称应为大写,如 `INFO`。 日志等级名称应为大写,如 `INFO`。
::: :::
@@ -28,7 +28,7 @@ async def got_location(event: MessageEvent, location: str = ArgPlainText()):
在上面的代码中,我们获取了 `Console` 协议适配器的消息事件提供的发送时间 `time` 属性。 在上面的代码中,我们获取了 `Console` 协议适配器的消息事件提供的发送时间 `time` 属性。
:::caution 注意 :::warning[注意]
如果**基类**就能满足你的需求,那么就**不要修改**事件参数类型注解,这样可以使你的代码更加**通用**,可以在更多平台上运行。如何根据不同平台事件类型进行不同的处理,我们将在[重载](#重载)一节中介绍。 如果**基类**就能满足你的需求,那么就**不要修改**事件参数类型注解,这样可以使你的代码更加**通用**,可以在更多平台上运行。如何根据不同平台事件类型进行不同的处理,我们将在[重载](#重载)一节中介绍。
::: :::
@@ -63,12 +63,12 @@ async def handle_onebot(bot: OneBot):
await bot.send_group_message(group_id=123123, message="OneBot") await bot.send_group_message(group_id=123123, message="OneBot")
``` ```
:::caution 注意 :::warning[注意]
重载机制对所有的参数类型注解都有效,因此,依赖注入也可以使用这个特性来对不同的返回值进行处理。 重载机制对所有的参数类型注解都有效,因此,依赖注入也可以使用这个特性来对不同的返回值进行处理。
但 Bot、Event 和 Matcher 三者的参数类型注解具有最高检查优先级,如果三者任一类型注解不匹配,那么其他依赖注入将不会执行(如:`Depends`)。 但 Bot、Event 和 Matcher 三者的参数类型注解具有最高检查优先级,如果三者任一类型注解不匹配,那么其他依赖注入将不会执行(如:`Depends`)。
::: :::
:::tip 提示 :::tip[提示]
如何更好地编写一个跨平台的插件,我们将在[最佳实践](../best-practice/multi-adapter.mdx)中介绍。 如何更好地编写一个跨平台的插件,我们将在[最佳实践](../best-practice/multi-adapter.mdx)中介绍。
::: :::
@@ -35,7 +35,7 @@ async def got_location():
在上面的代码中,我们使用 `got` 事件响应器操作来向用户发送 `prompt` 消息,并等待用户的回复。用户的回复消息将会被作为 `location` 参数存储于事件响应器状态中。 在上面的代码中,我们使用 `got` 事件响应器操作来向用户发送 `prompt` 消息,并等待用户的回复。用户的回复消息将会被作为 `location` 参数存储于事件响应器状态中。
:::tip 提示 :::tip[提示]
事件处理函数根据定义的顺序依次执行。 事件处理函数根据定义的顺序依次执行。
::: :::
@@ -67,7 +67,7 @@ async def got_location(location: str = ArgPlainText()):
在上面的代码中,我们在 `got_location` 函数中定义了一个依赖注入参数 `location`,他的值将会是用户回复的消息纯文本信息。获取到用户输入的地名参数后,我们就可以进行天气查询并回复了。 在上面的代码中,我们在 `got_location` 函数中定义了一个依赖注入参数 `location`,他的值将会是用户回复的消息纯文本信息。获取到用户输入的地名参数后,我们就可以进行天气查询并回复了。
:::tip 提示 :::tip[提示]
如果想要获取用户回复的消息对象 `Message` ,可以使用 `Arg` 依赖注入。 如果想要获取用户回复的消息对象 `Message` ,可以使用 `Arg` 依赖注入。
::: :::
@@ -130,7 +130,7 @@ async def got_location(location: str = ArgPlainText()):
事件响应器操作可以分为两大类:**交互操作**和**流程控制操作**。我们可以通过交互操作来与用户进行交互,而流程控制操作则可以用来控制事件处理流程的执行。 事件响应器操作可以分为两大类:**交互操作**和**流程控制操作**。我们可以通过交互操作来与用户进行交互,而流程控制操作则可以用来控制事件处理流程的执行。
:::tip 提示 :::tip[提示]
事件处理流程按照事件处理函数添加顺序执行,已经结束的事件处理函数不可能被恢复执行。 事件处理流程按照事件处理函数添加顺序执行,已经结束的事件处理函数不可能被恢复执行。
::: :::
@@ -322,7 +322,7 @@ async def _(matcher: Matcher):
matcher.stop_propagation() matcher.stop_propagation()
``` ```
:::caution 注意 :::warning[注意]
`stop_propagation` 操作是实例方法,需要先通过依赖注入获取事件响应器实例再进行调用。 `stop_propagation` 操作是实例方法,需要先通过依赖注入获取事件响应器实例再进行调用。
::: :::
@@ -237,7 +237,7 @@ args = Args["foo", BasePattern("@\d+")]
传入别名后,选项与子命令会选择其中长度最长的作为其名称。若传入为 "--foo|-f",则命令名称为 "--foo" 传入别名后,选项与子命令会选择其中长度最长的作为其名称。若传入为 "--foo|-f",则命令名称为 "--foo"
:::tip 特别提醒!!! :::tip[特别提醒!!!]
Option 的名字或别名**没有要求**必须在前面写上 `-` Option 的名字或别名**没有要求**必须在前面写上 `-`
@@ -534,7 +534,7 @@ async def message_provider(
该方法可能会调用多次,即对于多个 Extension,选择优先级靠前且实现了该方法的 Extension,若调用的返回值不为 `None` 则作为结果。 该方法可能会调用多次,即对于多个 Extension,选择优先级靠前且实现了该方法的 Extension,若调用的返回值不为 `None` 则作为结果。
:::caution :::warning
该方法的默认实现对结果 (UniMessage) 会进行缓存。`Extension` 的实现也应尽量实现缓存机制。 该方法的默认实现对结果 (UniMessage) 会进行缓存。`Extension` 的实现也应尽量实现缓存机制。
@@ -161,7 +161,7 @@ async def on_startup():
await UniMessage("Hello!").send(target=target) await UniMessage("Hello!").send(target=target)
``` ```
:::caution :::warning
在响应器以外的地方,除非启用了 `alconna_apply_fetch_targets` 配置项,否则 `bot` 参数必须手动传入。 在响应器以外的地方,除非启用了 `alconna_apply_fetch_targets` 配置项,否则 `bot` 参数必须手动传入。

Some files were not shown because too many files have changed in this diff Show More