update docs

This commit is contained in:
crazywhalecc
2020-12-22 16:28:34 +08:00
parent 9bd07e5a66
commit e78485273d
11 changed files with 321 additions and 26 deletions

3
docs/advanced/index.md Normal file
View File

@@ -0,0 +1,3 @@
# 进阶开发
## 深入
还没填坑,敬请期待!

39
docs/advanced/to-v2.md Normal file
View File

@@ -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")` 了。

85
docs/assets/css/extra.css Normal file
View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -1,4 +1,56 @@
# 事件和注解
还没写到这里,不着急
## 注解事件概念
我们知道事件,是一个底层的 event loop 收到消息后调用对应的各类方法的一个模型,比如给机器人发送消息后框架要做的就是指定到一个你定义的函数上,处理你的业务逻辑代码。比如在默认模块中,提供了 **你好** 的回复:**你好啊,我是由炸毛框架构建的机器人!**。这项简单回复的任务就是一个事件的触发到响应的全过程。
**注解**Annotation又称标注Java 最早在 2004 年的 JDK 5 中引入的一种注释机制。目前 PHP 官方版本并未提供内置元注解和注解概念,但我们通过 `ReflectionClass` 反射类解析 PHP 代码注释从而实现了自己的一套注解机制。如果你没有写过 Java并且不了解注解是什么你可以理解为对 function 或 class 的一个修饰,因为传统的 PHP 代码逻辑我们都知道,不能简单给原先存在的函数贴标签,就比如,你不能在原本的 PHP 代码中给函数贴上一个可以影响它一生并且改变它行为的标签,而有了注解,就相当于有了给函数贴标签的机会。
在常见框架如 SpringSwoft 等代码结构里面,注解更是其核心的存在。
在炸毛框架中,我们所有事件的绑定均采用这一方式进行调用模块内各个方法。包括 Swoole 自身的框架启动事件、WebSocket 连接握手事件、HTTP 请求事件等等,也包括 CQHTTP 发来的事件,如`message``notice``request` 等。
## 如何使用注解
就像我们日常开发写注释一样,只需在类、方法或成员变量上方按规则添加注释即可,这里以默认自带的 `Hello` 模块类为例子:
```php
<?php
namespace Module\Example;
use ZM\Annotation\CQ\CQCommand;
class Hello {
/**
* @CQCommand(match="你好")
* @return string
*/
public function hello(){
return "你好啊,我是由炸毛框架构建的机器人!";
}
}
```
其中 `@CQCommand()` 就是一个基本的注解应用。注意需引入相关注解Annotation**且必须** 以 `/**` 开始并以 `*/` 结束,否则会导致无法解析!上方 `@return` 为 IDE 自动生成的 PHPDoc不需要管。
有什么用?大有妙用!这个例子内注解类的用途是收到 QQ 消息后如果消息第一个词匹配到 `你好` 的话,框架就会自动处理,最终执行调用此 `hello()` 方法。注意 `CQCommand` 和其他任何后面讲到的注解类一样,需先 `use ZM\Annotation\` 下的对应注解类,否则也不能正常使用。
### 基本语法
先 use先 use先 use重要的事情说三遍`use ZM\Annotation\xxxx;`
**必须**`/**` 开始并以 `*/` 结束。
```
@注解类名(参数名1="参数1的值"[,参数名2="参数2的值"])
```
对于只使用或只有一个参数的注解类,`@注解类名("参数的值")` 可以省略参数名。
对于没有参数的注解类,`@参数名()` 直接使用即可。
## 注解和事件的关系
在炸毛框架里,注解常常被当作事件分发的一个重要角色,但注解本身又不是事件,更恰当的说,是注解代表了事件。
机器人开发过程中常见的 `@CQCommand`,或者是 HTTP 服务器路由绑定 `@RequestMapping` 都是相当于由对应注解代表了事件,而 `@Middleware``@Closed` 等这类注解显然不代表任何事件,只能当作这个函数或类的修饰属性而已。代表了事件的注解,我们称之为**注解事件**,它会在某种事件达成条件后触发注解下方的函数本身。
值得注意的是,注解事件本身概念是我凭空捏造的,我不好解释所以只能创造这么一个词来代指这一抽象的概念,硬要解释的话,大致就好比一个社区里有一个卖牛奶的,有几家人订阅了每日上门送牛奶的服务,只要你打了“给我配送牛奶”的注解,他就会上门。而它送的不止一种奶,可以给你个性化定制,比如让卖牛奶的给你带包糖带瓶水,而描述这个的注解就只能做一个之前注解的修饰。假设你只写了带包糖的注解,没有写给我配送牛奶的注解,那他永远也不会给你送牛奶和糖过来。

View File

@@ -1,3 +1,4 @@
# 快速上手 - HTTP 服务器篇
HTTP 服务器篇暂时先放一放,大家应该主要都是奔着机器人开发来的吧~
HTTP 服务器篇暂时先放一放,大家应该主要都是奔着机器人开发来的吧~

View File

@@ -0,0 +1,66 @@
# 注册事件响应(机器人篇)
现在模块已经创建完毕,我们可以开始编写实际代码了。本段以机器人会话为例子来讲述事件注册和响应,有关 HTTP 服务器等注册事件响应请看后面事件和注解章节。
## 机器人聊天事件处理
首先知道QQ 等聊天机器人的消息我们的处理逻辑为如下简单的模式:
- QQ 用户消息 -> 机器人客户端 -> 连接客户端的框架(炸毛框架)
- 框架处理逻辑后返回给用户的消息 -> 机器人客户端 -> QQ用户
第一步,我们以框架这边的角度考虑,我们称之为“事件”,我们编写代码所做的就是要响应这一事件。
首先我们以一句简单的功能——查天气,我们要从零实现一个查天气的功能进行示范如何快捷有效地开发一个功能。
### 确定问法
我们首先要确定的用户问法是一般由我们自己定义,但最好贴合用户的自然语言来进行定义。比如我们这里提到的天气功能,用户一般就会询问“北京天气”,“北京天气怎么样”,“天气 北京”。
### 注册消息事件
我们以最简单的命令方式“天气 北京”进行处理。问法为参数化的,通过空格来分开,这也是炸毛框架默认支持最基本的聊天事件之一。我们通过上一部分的方式新建一个单文件模块 `Weather.php``src/Module` 目录下,并编写:
```php
<?php
namespace Module;
use ZM\Annotation\CQ\CQCommand;
class Weather {
/**
* @CQCommand("天气")
* @return string
*/
public function searchWeather() {
$city = ctx()->getNextArg("请告诉我你要查询的城市"); // 发送 “天气 北京”时,变量为“北京”
// 这里假设是天气API接口的对接返回了天气的数据
$weather = "2020年12月22日-2~9℃ blablabla";
return "$city 天气情况:".$weather;
}
}
```
!!! note "提示"
为了简单起见,我们在这里的例子中没有接入真实的天气数据,但要接入也非常简单,你可以使用中国天气网、和风天气等网站提供的 API本教程的进阶一栏后期会详细编写如何对接一个天气 API 接口。
在上方代码编写完毕后,运行框架(或运行过程中终端输入 `reload`),然后使用机器人客户端连接到炸毛框架,即可实现我们的第一个功能。我们在代码中编写了一个**注解事件**`@CQCommand`,此注解事件是用于接收用户的普通消息并切分成各类命令规则的一个注解事件绑定。代码中的注解事件省去了注解中的键名,也可以写作 `@CQCommand(match="天气")`
这里注解事件的概念请看 [事件和注解](/event/) 一栏描述的概念即可。到这里,我们就完成一个可以处理命令 `天气 xxx` 的方法了!
### 处理消息事件
第一行 `ctx()` 是炸毛框架内的上下文获取方式,每条用户聊天信息发过来,被炸毛框架收到,都会创建一次上下文,同时这次聊天的全部信息,比如用户的 IDQQ 号码),发消息的时间,如果是群消息的话所在的群号等等,都被存到了上下文中。`ctx()` 获取的是一个上下文对象,内部有许多可操作上下文的方法,其中代码的 `getNextArg()`,作用是根据空格分隔获取命令中的下一个参数。
在之前我们知道:`天气 北京` 是我们发送的消息,我们要获取到用户发送的参数 `北京``getNextArg()` 是框架封装好的一个快速获取下一个参数的方法,我们这里直接使用它来获取。
对于 `getNextArg()` 中的文本,可为空,不为空的时候如果用户只发送天气两个字,机器人还是会响应,但是它会询问你这句话,然后你回复机器人“北京”,这里 `$city` 变量就接受到并赋值为“北京”了,代码会继续执行,和直接一次性发送机器人“天气 北京”是一个效果,此为框架封装的消息会话机制,以贴近自然会话的方式来编写代码逻辑。
最后,函数直接返回了一个字符串,作为事件的响应,炸毛框架会自动处理并调用机器人客户端的接口,最后返回给用户消息。这里也可以使用上下文的 `ctx()->reply("xxx")` 方法替代,不返回字符串。注意两者只能选择一种方式,取决于开发者的开发习惯。
<chat-box>
) 天气 北京
( 北京 天气情况2020年12月22日-2~9℃ blablabla
) 天气
( 请告诉我你要查询的城市
) 北京
( 北京 天气情况2020年12月22日-2~9℃ blablabla
</chat-box>

View File

@@ -66,15 +66,3 @@ zhamao-framework-starter/
(暂未支持,敬请期待)
## 注册事件响应(机器人篇)
现在模块已经创建完毕,我们可以开始编写实际代码了。本段以机器人会话为例子来讲述事件注册和响应,有关 HTTP 服务器等注册事件响应请看后面事件和注解章节。
首先知道QQ 等聊天机器人的消息我们的处理逻辑为点到点的模式:
- QQ 用户消息 -> 机器人客户端 -> 连接客户端的框架(炸毛框架)
- 框架处理逻辑后返回给用户的消息 -> 机器人客户端 -> QQ用户
第一步,我们以框架这边的角度考虑,我们称之为“事件”,我们编写代码所做的就是要响应这一事件。
TODO写到这里了。

View File

@@ -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() {
## 文档主题
### 主题
<div class="tx-switch">
<button data-md-color-scheme="default"><code>默认模式</code></button>
<button data-md-color-scheme="slate"><code>暗黑模式</code></button>
@@ -72,6 +75,7 @@ public function index() {
})
</script>
### 主色调
<div class="tx-switch">
<button data-md-color-primary="red"><code>red</code></button>
<button data-md-color-primary="pink"><code>pink</code></button>
@@ -96,6 +100,9 @@ public function index() {
<button data-md-color-primary="white"><code>white</code></button>
</div>
### 辅色调
<div class="tx-switch"> <button data-md-color-accent="red"><code>red</code></button> <button data-md-color-accent="pink"><code>pink</code></button> <button data-md-color-accent="purple"><code>purple</code></button> <button data-md-color-accent="deep-purple"><code>deep purple</code></button> <button data-md-color-accent="indigo"><code>indigo</code></button> <button data-md-color-accent="blue"><code>blue</code></button> <button data-md-color-accent="light-blue"><code>light blue</code></button> <button data-md-color-accent="cyan"><code>cyan</code></button> <button data-md-color-accent="teal"><code>teal</code></button> <button data-md-color-accent="green"><code>green</code></button> <button data-md-color-accent="light-green"><code>light green</code></button> <button data-md-color-accent="lime"><code>lime</code></button> <button data-md-color-accent="yellow"><code>yellow</code></button> <button data-md-color-accent="amber"><code>amber</code></button> <button data-md-color-accent="orange"><code>orange</code></button> <button data-md-color-accent="deep-orange"><code>deep orange</code></button> </div>
<script>
var buttons = document.querySelectorAll("button[data-md-color-primary]")
buttons.forEach(function(button) {
@@ -108,3 +115,16 @@ public function index() {
})
})
</script>
<script>
var buttons2 = document.querySelectorAll("button[data-md-color-accent]")
buttons2.forEach(function(button) {
button.addEventListener("click", function() {
var attr = this.getAttribute("data-md-color-accent")
setCookie("_accent_color", attr)
document.body.setAttribute("data-md-color-accent", attr)
var name = document.querySelector("#__code_3 code span:nth-child(7)")
name.textContent = attr.replace("-", " ")
})
})
</script>

View File

@@ -8,6 +8,26 @@ var _hmt = _hmt || [];
s.parentNode.insertBefore(hm, s);
})();
function appendChatModule(id, chatDialogs) {
let insertDiv = document.getElementById(id);
let ss = '';
ss += '<div class="doc-chat-container">';
for(let i of chatDialogs) {
if (i.role === 0) {
ss += '<div class="doc-chat-row doc-chat-row-robot">\n' +
' <img class="doc-chat-avatar" src="https://docs-v1.zhamao.xin/logo.png" alt=""/>\n' +
' <div class="doc-chat-box doc-chat-box-robot">' + i.msg + '</div>\n' +
' </div>';
} else {
ss += '<div class="doc-chat-row">\n' +
' <div class="doc-chat-box">' + i.msg + '</div>\n' +
' <img class="doc-chat-avatar" src="http://api.btstu.cn/sjtx/api.php" alt=""/>\n' +
' </div>';
}
}
insertDiv.innerHTML = ss + '</div>';
}
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 = '<div class="doc-chat-container">';
let dialogs = i.innerHTML.split("\n");
for(let j of dialogs) {
if(j === '') continue;
if(j.substr(0, 2) === ') ') {
final += '<div class="doc-chat-row">\n' +
' <div class="doc-chat-box">' + j.substr(2) + '</div>\n' +
' <img class="doc-chat-avatar" src="http://api.btstu.cn/sjtx/api.php" alt=""/>\n' +
' </div>';
} else if (j.substr(0, 2) === '( ') {
final += '<div class="doc-chat-row doc-chat-row-robot">\n' +
' <img class="doc-chat-avatar" src="https://docs-v1.zhamao.xin/logo.png" alt=""/>\n' +
' <div class="doc-chat-box doc-chat-box-robot">' + j.substr(2) + '</div>\n' +
' </div>';
}
}
i.innerHTML = final;
}
}, 500);

View File

@@ -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
- 更新日志: