mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-23 16:45:35 +08:00
1 line
14 KiB
JavaScript
1 line
14 KiB
JavaScript
(window.webpackJsonp=window.webpackJsonp||[]).push([[57],{768:function(s,a,t){"use strict";t.r(a);var n=t(84),e=Object(n.a)({},(function(){var s=this,a=s._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[a("h1",{attrs:{id:"插件开发"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#插件开发"}},[s._v("#")]),s._v(" 插件开发")]),s._v(" "),a("p",[s._v("在框架环境准备就绪后,可以使用框架提供的命令、工具集进行插件的开发。")]),s._v(" "),a("p",[s._v("如果你还没有阅读 "),a("RouterLink",{attrs:{to:"/guide/get-started.html"}},[s._v("快速上手-聊天机器人")]),s._v(" 章节的内容,请先阅读。")],1),s._v(" "),a("h2",{attrs:{id:"创建插件"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#创建插件"}},[s._v("#")]),s._v(" 创建插件")]),s._v(" "),a("p",[s._v("框架默认支持两种形式的插件,一种是 "),a("code",[s._v("file")]),s._v(" 单文件模式,另一种是 "),a("code",[s._v("psr4")]),s._v(" 多文件模式。一般情况下,写一个较为简单的功能插件,推荐使用单文件模式,便于管理。\n编写功能较多、需要分多个文件写逻辑的插件,推荐使用 "),a("code",[s._v("psr4")]),s._v(" 多文件模式。")]),s._v(" "),a("p",[s._v("框架的插件命名方式基于 Composer,Composer 要求组件、插件的名称要有开发者名称和项目名称两部分组成。\n例如,我的开发者代号仓库是 "),a("code",[s._v("jackson")]),s._v(",插件名称是 "),a("code",[s._v("group-manager")]),s._v(",最终你的插件名称就是 "),a("code",[s._v("jackson/group-manager")]),s._v("。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 使用终端问答向导创建插件,可根据终端的提示选择你要创建插件的名称、类型和命名空间等内容。")]),s._v("\n./zhamao plugin:make "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("--type")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("file\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 使用 file 类型的示例骨架创建插件,插件名称为 foobar/test-plugin")]),s._v("\n./zhamao plugin:make "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("--type")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("file foobar/test-plugin\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 使用 psr4 类型的示例骨架创建插件,插件名称为 foobar/psr4-plugin,命名空间为 foobar")]),s._v("\n./zhamao plugin:make "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("--type")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("psr4 "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("--namespace")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("foobar foobar/psr4-plugin\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 设置插件作者名称、描述信息")]),s._v("\n./zhamao plugin:make "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("--author")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("tom "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("--description")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"示例插件"')]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br")])]),a("h3",{attrs:{id:"单文件模式"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#单文件模式"}},[s._v("#")]),s._v(" 单文件模式")]),s._v(" "),a("p",[s._v("单文件模式的优势在于以下几点:")]),s._v(" "),a("ul",[a("li",[s._v("代码较少的情况下,逻辑表达更清晰,可直接在单文件结构下编写机器人、HTTP 服务器的逻辑。")]),s._v(" "),a("li",[s._v("可以添加额外的框架事件 "),a("code",[s._v("onPluginLoad")]),s._v("(框架在读取插件元信息后触发的事件)。")]),s._v(" "),a("li",[s._v("可以添加额外的框架事件 "),a("code",[s._v("onPack")]),s._v("(插件在被打包时执行的回调函数)。")])]),s._v(" "),a("p",[s._v("单文件模式下的插件目录结构如下图所示:")]),s._v(" "),a("div",{staticClass:"language-text line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("plugins/\n└── test-app/\n ├── main.php # 你的插件源代码文件\n └── composer.json # 插件元信息(如名称、版本等)\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[s._v("使用 file 模式创建的插件会在 "),a("code",[s._v("./plugins/")]),s._v(" 目录下新建一个文件夹,文件夹名称一般为插件名称 "),a("code",[s._v("xxx/yyy")]),s._v(" 的 "),a("code",[s._v("yyy")]),s._v("。如果遇到重名文件夹时,框架会提示重名。")]),s._v(" "),a("p",[s._v("当然,单文件模式也有劣势:")]),s._v(" "),a("ul",[a("li",[s._v("插件依赖了其他外部 Composer 组件时,依赖管理不方便。")]),s._v(" "),a("li",[s._v("插件逻辑较为复杂时,写到一个文件内会导致代码过长,不利于维护。")]),s._v(" "),a("li",[s._v("一些注解的绑定、中间件,均无法在单文件模式中使用。")])]),s._v(" "),a("h3",{attrs:{id:"多文件模式"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#多文件模式"}},[s._v("#")]),s._v(" 多文件模式")]),s._v(" "),a("p",[s._v("多文件模式适用于几乎任何情况,有以下优势:")]),s._v(" "),a("ul",[a("li",[s._v("逻辑复杂时,使用 Class 类名和注解绑定的形式,有助于编写大型项目。")]),s._v(" "),a("li",[s._v("逻辑简单但功能点繁多时,可以使用类成员的多个绑定注解的方法区分功能逻辑,避免单文件的混乱。")]),s._v(" "),a("li",[s._v("可以使用注解的全部特性,例如中间件绑定、依赖注入等。")])]),s._v(" "),a("p",[s._v("多文件模式下的插件目录结构如下图所示:")]),s._v(" "),a("div",{staticClass:"language-text line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("plugins/\n└── psr4-plugin/\n ├── composer.json # 插件元信息(如名称、版本等) \n ├── vendor/ # Composer 生成的插件依赖库目录和自动加载文件等 \n └── src/ # 你的插件源代码文件目录,符合 PSR-4 格式的\n ├── Psr4Plugin.php # 使用创建插件命令生成的初始插件逻辑\n └── ... # 如果你的插件想要分成多个 PHP 文件,那么这里可以有多个\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br")])]),a("h3",{attrs:{id:"混合模式"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#混合模式"}},[s._v("#")]),s._v(" 混合模式")]),s._v(" "),a("p",[s._v("混合模式为单文件模式和多文件模式的结合,框架不提供创建混合模式插件脚手架的命令,但你可以自行混合。")]),s._v(" "),a("p",[s._v("混合模式其实就是多文件模式,只不过同时也可以引入一个单文件插件,弥补多文件模式没办法绑定 "),a("code",[s._v("onPack")]),s._v(" 和 "),a("code",[s._v("onPluginLoad")]),s._v(" 事件的问题。")]),s._v(" "),a("p",[s._v("混合模式在多文件模式的基础上多了一个 "),a("code",[s._v("main.php")]),s._v(" 的单文件:")]),s._v(" "),a("div",{staticClass:"language-text line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("plugins/\n└── psr4-plugin/\n ├── composer.json # 插件元信息(如名称、版本等) \n ├── vendor/ # Composer 生成的插件依赖库目录和自动加载文件等 \n ├── main.php # 插件逻辑(单文件部分)\n └── src/ # 你的插件源代码文件目录(多文件部分)\n ├── Psr4Plugin.php # 使用创建插件命令生成的初始插件逻辑\n └── ... # 如果你的插件想要分成多个 PHP 文件,那么这里可以有多个\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br")])]),a("p",[s._v("在通过以上三种方式创建插件后,你可以通过 IDE、记事本等形式开始编写你的插件逻辑啦!")]),s._v(" "),a("h2",{attrs:{id:"编写插件-wip"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#编写插件-wip"}},[s._v("#")]),s._v(" 编写插件(WIP)")]),s._v(" "),a("blockquote",[a("p",[s._v("此处文档正在努力编写中!")])]),s._v(" "),a("p",[s._v("插件的编写内容当然取决于异想天开的你,这里用最基本和最典型的例子来说明如何从零编写一个自己的插件。")]),s._v(" "),a("h3",{attrs:{id:"机器人命令问答"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#机器人命令问答"}},[s._v("#")]),s._v(" 机器人命令问答")]),s._v(" "),a("p",[s._v("在使用框架的上方创建插件命令创建了一个插件目录和骨架文件后,打开文件可以看到默认生成了一个 BotCommand 机器人聊天命令事件,返回的内容为一句简单的话。\n你可以在创建插件后直接使用此命令,在对接机器人实现端(OneBot 12 实现)后,在与机器人对话的窗口或 App 内发送指令,即可测试机器人插件是否正常运行。")]),s._v(" "),a("p",[s._v("我们这里假设通过 psr4 模式创建了插件 "),a("code",[s._v("foobar/demo2")]),s._v(",初始化时预置的机器人聊天命令代码可能是下面这样:")]),s._v(" "),a("div",{staticClass:"language-php line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-php"}},[a("code",[a("span",{pre:!0,attrs:{class:"token attribute"}},[a("span",{pre:!0,attrs:{class:"token delimiter punctuation"}},[s._v("#[")]),a("span",{pre:!0,attrs:{class:"token attribute-content"}},[a("span",{pre:!0,attrs:{class:"token attribute-class-name class-name"}},[s._v("BotCommand")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token attribute-class-name class-name"}},[s._v("match")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string single-quoted-string"}},[s._v("'测试demo2'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")])]),a("span",{pre:!0,attrs:{class:"token delimiter punctuation"}},[s._v("]")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("function")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token function-definition function"}},[s._v("firstBotCommand")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name type-declaration"}},[s._v("BotContext")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token variable"}},[s._v("$ctx")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token keyword return-type"}},[s._v("void")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token variable"}},[s._v("$ctx")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("->")]),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("reply")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token string single-quoted-string"}},[s._v("'这是foobar/demo2插件的第一个命令!'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br")])]),a("chat-box",{attrs:{"my-chats":[{type:0,content:"测试demo2"},{type:1,content:"这是foobar/demo2插件的第一个命令!"}]}}),s._v(" "),a("p",[s._v("在编写插件逻辑时,你可以自由使用框架内的各个组件和注解,这里不再过多描述。")]),s._v(" "),a("h2",{attrs:{id:"插件打包"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#插件打包"}},[s._v("#")]),s._v(" 插件打包")]),s._v(" "),a("p",[s._v("在写了在写了。")]),s._v(" "),a("h2",{attrs:{id:"插件发布"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#插件发布"}},[s._v("#")]),s._v(" 插件发布")]),s._v(" "),a("p",[s._v("在写了!")])],1)}),[],!1,null,null,null);a.default=e.exports}}]); |