diff --git a/composer.json b/composer.json
index 4a6ecfc6..f62ca952 100644
--- a/composer.json
+++ b/composer.json
@@ -47,16 +47,7 @@
"src/ZM/global_functions.php"
]
},
- "autoload-dev": {
- "psr-4": {
- "ZMTest\\": "test/ZMTest"
- },
- "files": [
- "test/ZMTest/Mock/mock.php"
- ]
- },
"require-dev": {
- "phpunit/phpunit": "^9.3",
"swoole/ide-helper": "@dev"
}
}
\ No newline at end of file
diff --git a/docs/component/CQ码.md b/docs/component/CQ码.md
index 4c507df4..993ac69e 100644
--- a/docs/component/CQ码.md
+++ b/docs/component/CQ码.md
@@ -74,4 +74,351 @@ class Hello {
) 发送图片
[ https://zhamao.xin/file/hello.jpg
-
\ No newline at end of file
+
+
+## CQ 码列表
+
+### CQ::face() - 发送 QQ 表情
+
+发送 QQ 原生表情。
+
+定义:`CQ::face($id)`
+
+参数:`$id` 为 QQ 表情对应的 ID 号,一些常见的表情 ID 对应的表情样式见 [炸毛框架 1.x 版本文档](https://docs-v1.zhamao.xin/face_list.html)。
+
+```php
+/**
+ * @CQCommand("打盹")
+ */
+public function faceTest() {
+ ctx()->reply("正在打盹...");
+ ctx()->reply(CQ::face(8));
+}
+```
+
+
+) 打盹
+( 正在打盹...
+[ https://docs-v1.zhamao.xin/face/8.gif
+
+
+!!! note "提示"
+ 对于不断更新的 QQ 版本下,可能会持续扩充新的 QQ 表情,如果上表没有新的表情的话,也可以使用消息接收的方式,让机器人收到表情后解析出来对应的 id 然后再发送。
+
+
+### CQ::image() - 发送图片
+
+发送图片。
+
+定义:`image($file, $cache = true, $flash = false, $proxy = true, $timeout = -1)`
+
+参数
+
+| 参数名 | 收 | 发 | 默认值 | 说明 |
+| --------- | ---- | ---- | ------- | ------------------------------------------------------------ |
+| `file` | ✓ | ✓ | 必填 | 图片文件名 |
+| `flash` | ✓ | ✓ | `false` | 图片类型,当参数为 true 时代表发送闪照 |
+| `cache` | | ✓ | `true` | 只在通过网络 URL 发送时有效,表示是否使用已缓存的文件,默认 `true` |
+| `proxy` | | ✓ | `true` | 只在通过网络 URL 发送时有效,表示是否通过代理下载文件(需通过环境变量或配置文件配置代理),默认 `true` |
+| `timeout` | | ✓ | `-1` | 只在通过网络 URL 发送时有效,单位秒,表示下载网络文件的超时时间,默认 -1 不超时 |
+
+发送时,`file` 参数除了支持使用收到的图片文件名直接发送外,还支持:
+
+- 绝对路径,例如 `file:///root/imagetest/1.png`,格式使用 [`file` URI](https://tools.ietf.org/html/rfc8089)
+- 网络 URL,例如 `http://i1.piimg.com/567571/fdd6e7b6d93f1ef0.jpg`
+- Base64 编码,例如 `base64://iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAIAAADJt1n/AAAAKElEQVQ4EWPk5+RmIBcwkasRpG9UM4mhNxpgowFGMARGEwnBIEJVAAAdBgBNAZf+QAAAAABJRU5ErkJggg==`
+
+### CQ::record() - 发送语音
+
+发送语音消息。
+
+定义:`CQ::record($file, $magic = false, $cache = true, $proxy = true, $timeout = -1)`
+
+参数
+
+| 参数名 | 收 | 发 | 默认值 | 说明 |
+| --------- | ---- | ---- | ------- | ------------------------------------------------------------ |
+| `file` | ✓ | ✓ | 必填 | 音频文件名 |
+| `flash` | ✓ | ✓ | `false` | 图片类型,当参数为 true 时代表发送闪照 |
+| `cache` | | ✓ | `true` | 只在通过网络 URL 发送时有效,表示是否使用已缓存的文件,默认 `true` |
+| `proxy` | | ✓ | `true` | 只在通过网络 URL 发送时有效,表示是否通过代理下载文件(需通过环境变量或配置文件配置代理),默认 `true` |
+| `timeout` | | ✓ | `-1` | 只在通过网络 URL 发送时有效,单位秒,表示下载网络文件的超时时间,默认 -1 不超时 |
+
+发送时,`file` 参数除了支持使用收到的语音文件名直接发送外,还支持其它形式,参考上方发送图片。
+
+```php
+/**
+ * @CQCommand("说你好")
+ */
+public function say() {
+ ctx()->reply(CQ::record("https://zhamao.xin/file/hello.mp3"));
+}
+```
+
+
+) 说你好
+( [语音消息,点击收听] 2'' )))
+
+
+> 此 CQ 码只能用于单独一条文本消息中,如果混有其他字符串,则会吞掉其他字符串内容。
+
+### CQ::at() - 群里@某人或全体
+
+在群里 at 某个人或全体成员(全体成员需要有管理员权限)。
+
+定义:`CQ::at($qq)`
+
+参数:`$qq` 参数必填,如果填的是 QQ 号,则是单独 at 某人,如果是 `all`,则是 at 全体成员。
+
+```php
+/**
+ * @CQCommand("at测试")
+ */
+public function atTest() {
+ ctx()->reply(CQ::at(627577391)." 你好啊!");
+}
+```
+
+
+) at测试
+( @鲸鱼 你好啊!
+
+
+### CQ::video() - 发送短视频
+
+发送短视频。
+
+定义:`CQ::video($file, $cache = true, $proxy = true, $timeout = -1)`
+
+参数
+
+| 参数名 | 收 | 发 | 默认值 | 说明 |
+| --------- | ---- | ---- | ------ | ------------------------------------------------------------ |
+| `file` | ✓ | ✓ | 必填 | 短视频文件名 |
+| `cache` | | ✓ | `true` | 只在通过网络 URL 发送时有效,表示是否使用已缓存的文件,默认 `true` |
+| `proxy` | | ✓ | `true` | 只在通过网络 URL 发送时有效,表示是否通过代理下载文件(需通过环境变量或配置文件配置代理),默认 `true` |
+| `timeout` | | ✓ | `-1` | 只在通过网络 URL 发送时有效,单位秒,表示下载网络文件的超时时间,默认 -1 不超时 |
+
+发送时,`file` 参数除了支持使用收到的视频文件名直接发送外,还支持其它形式,参考上方发送图片。
+
+> 此 CQ 码只能用于单独一条文本消息中,如果混有其他字符串,则会吞掉其他字符串内容。
+
+### CQ::rps() - 猜拳
+
+定义:`CQ::rps()`
+
+用法:`CQ::rps()`
+
+> 此 CQ 码只能用于单独一条文本消息中,如果混有其他字符串,则会吞掉其他字符串内容。
+
+### CQ::dice() - 掷骰子
+
+定义:`CQ::dice()`
+
+用法:`CQ::dice()`
+
+> 此 CQ 码只能用于单独一条文本消息中,如果混有其他字符串,则会吞掉其他字符串内容。
+
+### CQ::shake() - 窗口抖动
+
+定义:`CQ::shake()`
+
+用法:`CQ::shake()`
+
+> 此 CQ 码只能用于单独一条文本消息中,如果混有其他字符串,则会吞掉其他字符串内容。
+
+### CQ::poke() - 戳一戳
+
+发送戳一戳。
+
+定义:`CQ::poke($type, $id, $name = "")`
+
+参数
+
+| 参数名 | 收 | 发 | 可能的值 | 说明 |
+| ------ | ---- | ---- | ------------------------------------------------------------ | ------ |
+| `type` | ✓ | ✓ | 见 [Mirai 的 PokeMessage 类](https://github.com/mamoe/mirai/blob/f5eefae7ecee84d18a66afce3f89b89fe1584b78/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/HummerMessage.kt#L49) | 类型 |
+| `id` | ✓ | ✓ | 同上 | ID |
+| `name` | ✓ | | 同上 | 表情名 |
+
+例子:`CQ::poke(6,-1)`
+
+效果:放大招
+
+> 此 CQ 码只能用于单独一条文本消息中,如果混有其他字符串,则会吞掉其他字符串内容。
+
+### CQ::anonymous() - 匿名发消息
+
+匿名发消息。需要在允许匿名发消息的群里发。
+
+!!! tip "提示"
+
+ 当收到匿名消息时,需要通过 [消息事件的群消息](https://github.com/howmanybots/onebot/blob/master/v11/specs/event/message.md#群消息) 的 `anonymous` 字段判断。
+
+定义:`CQ::anonymous($ignore = 1)`
+
+```php
+/**
+ * @CQCommand("匿名测试")
+ */
+public function anonymousTest() {
+ ctx()->reply(CQ::anonymous()."匿名测试");
+}
+```
+
+### CQ::share() - 链接分享
+
+发送链接分享卡片,可自定义内容。
+
+定义:`CQ::share($url, $title, $content = null, $image = null)`
+
+参数
+
+| 参数名 | 收 | 发 | 可能的值 | 说明 |
+| --------- | ---- | ---- | -------- | -------------- |
+| `url` | ✓ | ✓ | - | URL |
+| `title` | ✓ | ✓ | - | 标题 |
+| `content` | ✓ | ✓ | - | 可选,内容描述 |
+| `image` | ✓ | ✓ | - | 可选,图片 URL |
+
+```php
+/**
+ * @CQCommand("链接分享测试")
+ */
+public function shareTest() {
+ ctx()->reply(CQ::share("https://baidu.com", "UC忽悠部", "震惊!我市一男子在光天化日之下..."));
+}
+```
+
+### CQ::contact() - 推荐好友
+
+发送推荐好友的卡片。
+
+定义:`CQ::contact($type, $id)`
+
+参数
+
+| 参数名 | 收 | 发 | 可能的值 | 说明 |
+| ------ | ---- | ---- | ------------- | ---------------------- |
+| `type` | ✓ | ✓ | `qq`,`group` | 推荐好友或群 |
+| `id` | ✓ | ✓ | - | 被推荐人的 QQ 号或群号 |
+
+```php
+/**
+ * @CQCommand("我的名片")
+ */
+public function myCard() {
+ ctx()->reply(CQ::contact("qq", ctx()->getUserId()));
+}
+```
+
+### CQ::location() - 发送位置
+
+发送位置,基于经纬度坐标发的。
+
+定义:`CQ::location($lat, $lon, $title = "", $content = "")`
+
+参数
+
+| 参数名 | 收 | 发 | 可能的值 | 说明 |
+| --------- | ---- | ---- | -------- | -------------- |
+| `lat` | ✓ | ✓ | - | 纬度 |
+| `lon` | ✓ | ✓ | - | 经度 |
+| `title` | ✓ | ✓ | - | 可选,标题 |
+| `content` | ✓ | ✓ | - | 可选,内容描述 |
+
+### CQ::music() - 音乐分享
+
+分享音乐,通过卡片。
+
+发送音乐分享卡片。此 CQ 码如果伴随着其他文字,则文字内容会被丢弃。
+
+定义:`CQ::music($type, $id_or_url, $audio = null, $title = null, $content = null, $image = null)`
+
+- `$type`: 发送类型
+- `$id_or_url`: 音乐的 id 或 音乐卡片点进去打开的链接
+- `$audio`: 音频文件的 HTTP 地址
+- `$title`: 音乐卡片的标题,建议 12 字以内
+- `$content`: 音乐卡片的简介内容(可选)
+- `$image`: 音乐卡片的图片的链接地址(可选)
+
+如果 `$type` 参数为 `qq` 或 `163` 或 `xiami`,则必须且只和第二个参数 `$id_or_url` 配合使用。这三个为内置分享,需要先通过搜索功能获取对应平台歌曲的 id 后使用。
+
+如果 `$type` 参数为 `custom`,则表明此音乐卡片为用户自定义,你可以根据自己的需要自定义卡片内容和音频。此时必须填写 `$id_or_url`, `$audio`, `$title` 三个参数。
+
+```php
+ctx()->reply(CQ::music("163", "730806")); //一首我喜欢的歌
+// 以内置的发送类型发送音乐卡片,我这里挑了网易云音乐的一首歌。
+
+ctx()->reply("custom", "https://baidu.com/", "https://zhamao.xin/file/hello.mp3", "我是Siri说出来的Hello", "不服来打我呀!", "https://zhamao.xin/file/hello.jpg");
+// 自定义整个卡片的每个内容
+```
+
+### CQ::forward() - 合并转发
+
+合并转发消息。
+
+定义:`CQ::forward($id)`
+
+参数
+
+```
+[CQ:forward,id=123456]
+```
+
+| 参数名 | 收 | 发 | 可能的值 | 说明 |
+| ------ | ---- | ---- | -------- | ------------------------------------------------------------ |
+| `id` | ✓ | | 必填 | 合并转发 ID,需通过 [`get_forward_msg` API](https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_forward_msg-获取合并转发消息) 获取具体内容 |
+
+### CQ::node() - 合并转发自定义节点
+
+接收时,此消息段不会直接出现在消息事件的 `message` 中,需通过 [`get_forward_msg` API](https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_forward_msg-获取合并转发消息) 获取。发送时,通过获取回来的 API 节点信息进行发送。
+
+定义:`CQ::node($user_id, $nickname, $content)`
+
+参数
+
+| 参数名 | 收 | 发 | 可能的值 | 说明 |
+| ---------- | ---- | ---- | -------- | ------------------------------------------------------------ |
+| `user_id` | ✓ | ✓ | - | 发送者 QQ 号 |
+| `nickname` | ✓ | ✓ | - | 发送者昵称 |
+| `content` | ✓ | ✓ | - | 消息内容,支持发送消息时的 `message` 数据类型,见 [API 的参数](https://github.com/howmanybots/onebot/blob/master/v11/specs/api/#参数) |
+
+```php
+/**
+ * @CQCommand("node测试")
+ */
+public function nodeTest() {
+ ctx()->reply(CQ::node(123456, "Jack", "[CQ:face,id=123]哈喽~"));
+}
+```
+
+### CQ::xml() - XML 消息
+
+发送 QQ 兼容的 XML 多媒体消息。
+
+定义:`CQ::xml($data)`
+
+参数:`$data` 为 xml 字符串
+
+```php
+/**
+ * @CQCommand("xml测试")
+ */
+public function xmlTest() {
+ ctx()->reply(CQ::xml("时禁用CQ-HTTP-API插件的缓存
* @param $file
* @param bool $cache
+ * @param bool $flash
+ * @param bool $proxy
+ * @param int $timeout
* @return string
*/
- public static function image($file, $cache = true) {
- if ($cache === false)
- return "[CQ:image,file=" . $file . ",cache=0]";
- else
- return "[CQ:image,file=" . $file . "]";
+ public static function image($file, $cache = true, $flash = false, $proxy = true, $timeout = -1) {
+ return
+ "[CQ:image,file=" . $file .
+ (!$cache ? ",cache=0" : "") .
+ ($flash ? ",type=flash" : "") .
+ (!$proxy ? ",proxy=false" : "") .
+ ($timeout != -1 ? (",timeout=" . $timeout) : "") .
+ "]";
}
/**
* 发送语音
- * cache为时禁用CQ-HTTP-API插件的缓存
- * magic为时标记为变声
* @param $file
* @param bool $magic
* @param bool $cache
+ * @param bool $proxy
+ * @param int $timeout
* @return string
*/
- public static function record($file, $magic = false, $cache = true) {
- if ($cache === false) $c = ",cache=0";
- else $c = "";
- if ($magic === true) $m = ",magic=true";
- else $m = "";
- return "[CQ:record,file=" . $file . $c . $m . "]";
+ public static function record($file, $magic = false, $cache = true, $proxy = true, $timeout = -1) {
+ return
+ "[CQ:record,file=" . $file .
+ (!$cache ? ",cache=0" : "") .
+ ($magic ? ",magic=1" : "") .
+ (!$proxy ? ",proxy=false" : "") .
+ ($timeout != -1 ? (",timeout=" . $timeout) : "") .
+ "]";
+ }
+
+ /**
+ * 发送短视频
+ * @param $file
+ * @param bool $cache
+ * @param bool $proxy
+ * @param int $timeout
+ * @return string
+ */
+ public static function video($file, $cache = true, $proxy = true, $timeout = -1) {
+ return
+ "[CQ:video,file=" . $file .
+ (!$cache ? ",cache=0" : "") .
+ (!$proxy ? ",proxy=false" : "") .
+ ($timeout != -1 ? (",timeout=" . $timeout) : "") .
+ "]";
}
/**
@@ -124,6 +113,56 @@ class CQ
return "[CQ:shake]";
}
+ /**
+ * 发送新的戳一戳
+ * @param $type
+ * @param $id
+ * @param string $name
+ * @return string
+ */
+ public static function poke($type, $id, $name = "") {
+ return "[CQ:poke,type=$type,id=$id" . ($name != "" ? ",name=$name" : "") . "]";
+ }
+
+ /**
+ * 发送匿名消息
+ * @param int $ignore
+ * @return string
+ */
+ public static function anonymous($ignore = 1) {
+ return "[CQ:anonymous".($ignore != 1 ? ",ignore=0" : "")."]";
+ }
+
+ /**
+ * 发送链接分享(只能在单条回复中单独使用)
+ * @param $url
+ * @param $title
+ * @param null $content
+ * @param null $image
+ * @return string
+ */
+ public static function share($url, $title, $content = null, $image = null) {
+ if ($content === null) $c = "";
+ else $c = ",content=" . $content;
+ if ($image === null) $i = "";
+ else $i = ",image=" . $image;
+ return "[CQ:share,url=" . $url . ",title=" . $title . $c . $i . "]";
+ }
+
+ /**
+ * 发送好友或群推荐名片
+ * @param $type
+ * @param $id
+ * @return string
+ */
+ public static function contact($type, $id) {
+ return "[CQ:contact,type=$type,id=$id]";
+ }
+
+ public static function location($lat, $lon, $title = "", $content = "") {
+
+ }
+
/**
* 发送音乐分享(只能在单条回复中单独使用)
* qq、163、xiami为内置分享,需要先通过搜索功能获取id后使用
@@ -136,10 +175,10 @@ class CQ
* $image 为音乐卡片的图片链接地址(可忽略)
* @param $type
* @param $id_or_url
- * @param string $audio
- * @param string $title
- * @param string $content
- * @param string $image
+ * @param null $audio
+ * @param null $title
+ * @param null $content
+ * @param null $image
* @return string
*/
public static function music($type, $id_or_url, $audio = null, $title = null, $content = null, $image = null) {
@@ -164,20 +203,12 @@ class CQ
}
}
- /**
- * 发送链接分享(只能在单条回复中单独使用)
- * @param $url
- * @param $title
- * @param null $content
- * @param null $image
- * @return string
- */
- public static function share($url, $title, $content = null, $image = null) {
- if ($content === null) $c = "";
- else $c = ",content=" . $content;
- if ($image === null) $i = "";
- else $i = ",image=" . $image;
- return "[CQ:share,url=" . $url . ",title=" . $title . $c . $i . "]";
+ public static function forward($id) {
+ return "[CQ:forward,id=$id]";
+ }
+
+ public static function node($user_id, $nickname, $content) {
+ return "[CQ:node,user_id=$user_id,nickname=$nickname,content=".self::escape($content)."]";
}
/**