diff --git a/docs/advanced/index.md b/docs/advanced/index.md new file mode 100644 index 00000000..fa8e1a9d --- /dev/null +++ b/docs/advanced/index.md @@ -0,0 +1,3 @@ +# 进阶开发 +## 深入 +还没填坑,敬请期待! \ No newline at end of file diff --git a/docs/advanced/to-v2.md b/docs/advanced/to-v2.md new file mode 100644 index 00000000..5da3fc87 --- /dev/null +++ b/docs/advanced/to-v2.md @@ -0,0 +1,39 @@ +# 从炸毛框架 V1 升级 + +> 这里只写明可能在升级过程中会影响原先代码执行的部分,不包含新增的特性等。 + +### 需要改变命名空间的类 + +- `Framework\Console` -> `ZM\Console\Console` +- `Swlib\Util\SingletonTrait` -> `ZM\Utils\SingletonTrait` +- `ZM\Annotation\Http\Before` -> `ZM\Annotation\Http\HandleBefore` +- `ZM\Annotation\Http\After` -> `ZM\Annotation\Http\HandleAfter` +- `@SwooleEventAt` -> `@OnSwooleEvent` +- 删除 `@SwooleEventAfter` +- 删除 `ModBase` +- `@HandleEvent` -> `@SwooleHandler` +- `ZM\Utils\ZMRobot` -> `\ZM\API\ZMRobot` + +### 方法名称变更 + +- `ZM\Console::stackTrace()` -> `ZM\Console::trace()` + +### 注解的变化 + +`@OnSwooleEvent`(原 `@SwooleEventAt`)中,`rule` 参数不再是自定义语法的东西了(比如之前的 `connectType:qq` 之类的鸡肋语法),直接是可执行的 PHP 代码,比如 `3 == 4`,`connectIsQQ()` 之类的。 + +去除 `@CQAPISend`,因为目前没什么意义。 + +`@CQCommand` 中,`regexMatch` 变成 `pattern`,`fullMatch` 变成 `regex`,消除歧义(第一个是 * 号匹配符进行匹配的,第二个是标准的正则表达式匹配)。同时新增 `start_with`,`end_with`,`keyword` 平行选项。 + +`@OnTick` 注解新增第二个参数 `worker_id`,其中默认是 0,代表只在 `#0` 号工作进程上运行计时器。 + +### 中间件编写的改变 + +原先的 Middleware 是需要含有 `getName()` 方法才合法,现在不需要了,但是对 `@MiddlewareClass` 注解需要增加参数,也就是说原先 `getName()` 返回的名称现在需要写到 `@MiddlewareClass("xxx")` 这样的形式。 + +### ZMBuf 的变化 + +由于 2.0 框架使用了多进程模型,所以不能使用原先适用于单进程下全局变量的方式(ZMBuf)进行存取变量,所以 ZMBuf 下的所有方法都需要更改,其中 `get, set` 等对缓存操作的模型请根据 2.0 的文档变更使用 `Redis` 或内置的多进程共享内存可用的 `LightCache` 轻量缓存。 + +而获取全局配置文件,如 `global.php` 文件,也发生了变化,新框架引入了 `ZMConfig` 对象,可以快速地区分各类环境变量从而读取不同的配置文件。比如我们获取原先的 global 配置文件中的一项:`ZMBuf::globals("port")`,在 2.0 中需要使用 `ZMConfig::get("global", "port")` 方式。以此类推,`ZMBuf::config("xxx")` 也直接变为 `ZMConfig::get("xxx")` 了。 \ No newline at end of file diff --git a/docs/assets/css/extra.css b/docs/assets/css/extra.css new file mode 100644 index 00000000..585990c1 --- /dev/null +++ b/docs/assets/css/extra.css @@ -0,0 +1,85 @@ + +.md-header-nav__button.md-logo { + padding: .2rem; + margin: .2rem; +} + +.md-header-nav__button.md-logo img, .md-header-nav__button.md-logo svg { + width: 1.6rem; + height: 1.6rem; +} + +.doc-chat-container { + width: 100%; + min-height: 30px; + background-color: #f3f6f9; + padding: 12px; + margin-right: auto; + margin-left: auto; + box-shadow: 0 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12); +} + +.doc-chat-row { + margin: 0; + display: flex; + flex-wrap: wrap; + flex: 1 1 auto; + justify-content: flex-end; +} + +.doc-chat-row-robot { + justify-content: flex-start !important; +} + +.doc-chat-box { + position: relative; + width: fit-content; + max-width: 55%; + border-radius: .5rem; + padding: .4rem .6rem; + margin: .4rem .8rem; + background-color: #fff; + line-height: 1.5; + font-size: 16px; + outline: none; + overflow-wrap: break-word; + white-space: normal; + box-shadow: 0 2px 12px 0 rgba(0,0,0,.1); +} + +.doc-chat-box:after { + content: ""; + position: absolute; + right: auto; + top: 0; + width: 8px; + height: 12px; + color: #fff; + border: 0 solid transparent; + border-bottom: 7px solid; + border-radius: 0 0 8px 0; + left: calc(100% - 4px); + box-sizing: inherit; +} + +.doc-chat-box-robot:after { + content: ""; + position: absolute; + right: calc(100% - 4px); + top: 0; + width: 8px; + height: 12px; + color: #fff; + border: 0 solid transparent; + border-bottom: 7px solid; + border-radius: 0 0 0 8px; + left: auto; + box-sizing: inherit; +} + +.doc-chat-avatar { + background-color: aquamarine; + width: 36px !important; + height: 36px !important; + border-radius: 18px; +} \ No newline at end of file diff --git a/docs/assets/extra.css b/docs/assets/extra.css deleted file mode 100644 index 4b4b3cee..00000000 --- a/docs/assets/extra.css +++ /dev/null @@ -1,9 +0,0 @@ -.md-header-nav__button.md-logo { - padding: .2rem; - margin: .2rem; -} - -.md-header-nav__button.md-logo img, .md-header-nav__button.md-logo svg { - width: 1.6rem; - height: 1.6rem; -} \ No newline at end of file diff --git a/docs/event/index.md b/docs/event/index.md index 787224a4..c4955a2c 100644 --- a/docs/event/index.md +++ b/docs/event/index.md @@ -1,4 +1,56 @@ # 事件和注解 -还没写到这里,不着急 +## 注解事件概念 +我们知道事件,是一个底层的 event loop 收到消息后调用对应的各类方法的一个模型,比如给机器人发送消息后框架要做的就是指定到一个你定义的函数上,处理你的业务逻辑代码。比如在默认模块中,提供了 **你好** 的回复:**你好啊,我是由炸毛框架构建的机器人!**。这项简单回复的任务就是一个事件的触发到响应的全过程。 + +**注解**(Annotation)又称标注,Java 最早在 2004 年的 JDK 5 中引入的一种注释机制。目前 PHP 官方版本并未提供内置元注解和注解概念,但我们通过 `ReflectionClass` 反射类解析 PHP 代码注释从而实现了自己的一套注解机制。如果你没有写过 Java,并且不了解注解是什么,你可以理解为对 function 或 class 的一个修饰,因为传统的 PHP 代码逻辑我们都知道,不能简单给原先存在的函数贴标签,就比如,你不能在原本的 PHP 代码中给函数贴上一个可以影响它一生并且改变它行为的标签,而有了注解,就相当于有了给函数贴标签的机会。 + +在常见框架如 Spring,Swoft 等代码结构里面,注解更是其核心的存在。 + +在炸毛框架中,我们所有事件的绑定均采用这一方式进行调用模块内各个方法。包括 Swoole 自身的框架启动事件、WebSocket 连接握手事件、HTTP 请求事件等等,也包括 CQHTTP 发来的事件,如`message`,`notice`,`request` 等。 + +## 如何使用注解 + +就像我们日常开发写注释一样,只需在类、方法或成员变量上方按规则添加注释即可,这里以默认自带的 `Hello` 模块类为例子: + +```php + 机器人客户端 -> 连接客户端的框架(炸毛框架) +- 框架处理逻辑后返回给用户的消息 -> 机器人客户端 -> QQ用户 + +第一步,我们以框架这边的角度考虑,我们称之为“事件”,我们编写代码所做的就是要响应这一事件。 + +首先我们以一句简单的功能——查天气,我们要从零实现一个查天气的功能进行示范如何快捷有效地开发一个功能。 + +### 确定问法 + +我们首先要确定的用户问法是一般由我们自己定义,但最好贴合用户的自然语言来进行定义。比如我们这里提到的天气功能,用户一般就会询问“北京天气”,“北京天气怎么样”,“天气 北京”。 + +### 注册消息事件 + +我们以最简单的命令方式“天气 北京”进行处理。问法为参数化的,通过空格来分开,这也是炸毛框架默认支持最基本的聊天事件之一。我们通过上一部分的方式新建一个单文件模块 `Weather.php` 在 `src/Module` 目录下,并编写: + +```php +getNextArg("请告诉我你要查询的城市"); // 发送 “天气 北京”时,变量为“北京” + // 这里假设是天气API接口的对接,返回了天气的数据 + $weather = "2020年12月22日,晴,-2~9℃ blablabla"; + return "$city 天气情况:".$weather; + } +} +``` + +!!! note "提示" + 为了简单起见,我们在这里的例子中没有接入真实的天气数据,但要接入也非常简单,你可以使用中国天气网、和风天气等网站提供的 API,本教程的进阶一栏后期会详细编写如何对接一个天气 API 接口。 + +在上方代码编写完毕后,运行框架(或运行过程中终端输入 `reload`),然后使用机器人客户端连接到炸毛框架,即可实现我们的第一个功能。我们在代码中编写了一个**注解事件**:`@CQCommand`,此注解事件是用于接收用户的普通消息并切分成各类命令规则的一个注解事件绑定。代码中的注解事件省去了注解中的键名,也可以写作 `@CQCommand(match="天气")`。 + +这里注解事件的概念请看 [事件和注解](/event/) 一栏描述的概念即可。到这里,我们就完成一个可以处理命令 `天气 xxx` 的方法了! + +### 处理消息事件 + +第一行 `ctx()` 是炸毛框架内的上下文获取方式,每条用户聊天信息发过来,被炸毛框架收到,都会创建一次上下文,同时这次聊天的全部信息,比如用户的 ID(QQ 号码),发消息的时间,如果是群消息的话所在的群号等等,都被存到了上下文中。`ctx()` 获取的是一个上下文对象,内部有许多可操作上下文的方法,其中代码的 `getNextArg()`,作用是根据空格分隔获取命令中的下一个参数。 + +在之前我们知道:`天气 北京` 是我们发送的消息,我们要获取到用户发送的参数 `北京`。`getNextArg()` 是框架封装好的一个快速获取下一个参数的方法,我们这里直接使用它来获取。 + +对于 `getNextArg()` 中的文本,可为空,不为空的时候如果用户只发送天气两个字,机器人还是会响应,但是它会询问你这句话,然后你回复机器人“北京”,这里 `$city` 变量就接受到并赋值为“北京”了,代码会继续执行,和直接一次性发送机器人“天气 北京”是一个效果,此为框架封装的消息会话机制,以贴近自然会话的方式来编写代码逻辑。 + +最后,函数直接返回了一个字符串,作为事件的响应,炸毛框架会自动处理并调用机器人客户端的接口,最后返回给用户消息。这里也可以使用上下文的 `ctx()->reply("xxx")` 方法替代,不返回字符串。注意两者只能选择一种方式,取决于开发者的开发习惯。 + + +) 天气 北京 +( 北京 天气情况:2020年12月22日,晴,-2~9℃ blablabla +) 天气 +( 请告诉我你要查询的城市 +) 北京 +( 北京 天气情况:2020年12月22日,晴,-2~9℃ blablabla + diff --git a/docs/guide/编写模块.md b/docs/guide/编写模块.md index 0d2453af..d681d7d8 100644 --- a/docs/guide/编写模块.md +++ b/docs/guide/编写模块.md @@ -66,15 +66,3 @@ zhamao-framework-starter/ (暂未支持,敬请期待) -## 注册事件响应(机器人篇) - -现在模块已经创建完毕,我们可以开始编写实际代码了。本段以机器人会话为例子来讲述事件注册和响应,有关 HTTP 服务器等注册事件响应请看后面事件和注解章节。 - -首先知道,QQ 等聊天机器人的消息我们的处理逻辑为点到点的模式: - -- QQ 用户消息 -> 机器人客户端 -> 连接客户端的框架(炸毛框架) -- 框架处理逻辑后返回给用户的消息 -> 机器人客户端 -> QQ用户 - -第一步,我们以框架这边的角度考虑,我们称之为“事件”,我们编写代码所做的就是要响应这一事件。 - -TODO:写到这里了。 \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 930d6aa4..74ac20b5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,6 +2,8 @@ > 本文档为炸毛框架 v2 版本,如需查看 v1 版本,[点我](https://docs-v1.zhamao.xin/)。 +> 如果是从 v1.x 版本升级到 v2.x,[点我看升级指南](/advanced/to-v2/)。 + 炸毛框架使用 PHP 编写,采用 Swoole 扩展为基础,主要面向 API 服务,聊天机器人(CQHTTP 对接),包含 websocket、http 等监听和请求库,用户代码采用模块化处理,使用注解可以方便地编写各类功能。 框架主要用途为 HTTP 服务器,机器人搭建框架。尤其对于 QQ 机器人消息处理较为方便和全面,提供了众多会话机制和内部调用机制,可以以各种方式设计你自己的模块。 @@ -54,6 +56,7 @@ public function index() { ## 文档主题 +### 主题
@@ -72,6 +75,7 @@ public function index() { }) +### 主色调
@@ -96,6 +100,9 @@ public function index() {
+### 辅色调 +
+ + + diff --git a/docs/javascripts/config.js b/docs/javascripts/config.js index 067ae85b..d8bb350f 100644 --- a/docs/javascripts/config.js +++ b/docs/javascripts/config.js @@ -8,6 +8,26 @@ var _hmt = _hmt || []; s.parentNode.insertBefore(hm, s); })(); +function appendChatModule(id, chatDialogs) { + let insertDiv = document.getElementById(id); + let ss = ''; + ss += '
'; + for(let i of chatDialogs) { + if (i.role === 0) { + ss += '
\n' + + ' \n' + + '
' + i.msg + '
\n' + + '
'; + } else { + ss += '
\n' + + '
' + i.msg + '
\n' + + ' \n' + + '
'; + } + } + insertDiv.innerHTML = ss + '
'; +} + function getCookie(name) { var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)"); @@ -34,4 +54,32 @@ name.textContent = s_theme s_primary=getCookie("_primary_color"); document.body.setAttribute("data-md-color-primary", s_primary); var name2 = document.querySelector("#__code_2 code span:nth-child(7)"); -name2.textContent = s_primary.replace("-", " "); +if(s_primary !== null) name2.textContent = s_primary.replace("-", " "); + +s_accent=getCookie("_accent_color"); +document.body.setAttribute("data-md-color-accent", s_accent); +var name3 = document.querySelector("#__code_3 code span:nth-child(7)"); +if(s_accent !== null) name3.textContent = s_accent.replace("-", " "); + +setTimeout(() => { + let ls = document.querySelectorAll("chat-box"); + for(let i of ls) { + let final = '
'; + let dialogs = i.innerHTML.split("\n"); + for(let j of dialogs) { + if(j === '') continue; + if(j.substr(0, 2) === ') ') { + final += '
\n' + + '
' + j.substr(2) + '
\n' + + ' \n' + + '
'; + } else if (j.substr(0, 2) === '( ') { + final += '
\n' + + ' \n' + + '
' + j.substr(2) + '
\n' + + '
'; + } + } + i.innerHTML = final; + } +}, 500); diff --git a/mkdocs.yml b/mkdocs.yml index 6e29bef8..6ebf5149 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -15,7 +15,7 @@ extra_javascript: - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js - javascripts/config.js extra_css: - - assets/extra.css + - assets/css/extra.css - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/default.min.css markdown_extensions: - admonition @@ -58,12 +58,14 @@ nav: - 选择聊天机器人实例: guide/OneBot实例.md - 基本配置: guide/基本配置.md - 编写模块: guide/编写模块.md + - 注册事件响应: guide/注册事件响应.md - 事件和注解: - 事件和注解: event/index.md - 框架组件: - 框架组件: component/index.md - 进阶开发: - - Swoole 进阶: test.md + - 进阶开发: advanced/index.md + - 从 v1 升级: advanced/to-v2.md - FAQ: - FAQ: FAQ.md - 更新日志: