├── .editorconfig
├── .gitattributes
├── .gitignore
├── .php_cs
├── LICENSE
├── QQfaceId.md
├── README.md
├── composer.json
├── phpunit.xml.dist
├── run.sh
├── src
├── CoolQ
│ ├── CQ.php
│ ├── QQ.php
│ └── Url.php
├── Core
│ ├── Blacklist.php
│ ├── CQ.php
│ ├── Exceptions
│ │ └── Exception.php
│ ├── Protocols
│ │ ├── GuzzleProtocol.php
│ │ ├── Protocol.php
│ │ └── WebSocketProtocol.php
│ ├── QQ.php
│ ├── Response.php
│ ├── Whitelist.php
│ └── isCanSend.php
├── Light
│ └── .gitkeep
├── functions.php
└── functions_include.php
└── tests
├── CoolQQTest.php
└── GuzzleProtocolTest.php
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 4
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = false
10 |
11 | [*.{vue,js,scss}]
12 | charset = utf-8
13 | indent_style = space
14 | indent_size = 2
15 | end_of_line = lf
16 | insert_final_newline = true
17 | trim_trailing_whitespace = true
18 |
19 | [*.md]
20 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | /vendor/
3 | .idea
4 | composer.lock
5 | Resopnse.md
6 | /tests/put.json
7 | /tests/logs
--------------------------------------------------------------------------------
/.php_cs:
--------------------------------------------------------------------------------
1 |
6 |
7 | This source file is subject to the MIT license that is bundled.
8 | EOF;
9 |
10 | return PhpCsFixer\Config::create()
11 | ->setRiskyAllowed(true)
12 | ->setRules(array(
13 | '@Y' => true,
14 | 'header_comment' => array('header' => $header),
15 | 'array_syntax' => array('syntax' => 'short'),
16 | 'ordered_imports' => true,
17 | 'no_useless_else' => true,
18 | 'no_useless_return' => true,
19 | 'php_unit_construct' => true,
20 | 'php_unit_strict' => true,
21 | ))
22 | ->setFinder(
23 | PhpCsFixer\Finder::create()
24 | ->exclude('vendor')
25 | ->in(__DIR__)
26 | )
27 | ;
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 kilingzhang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/QQfaceId.md:
--------------------------------------------------------------------------------
1 | 0: 惊讶
2 | 1: 撇嘴
3 | 2: 色
4 | 3: 发呆
5 | 4: 得意
6 | 5: 流泪
7 | 6: 害羞
8 | 7: 闭嘴
9 | 8: 睡
10 | 9: 大哭
11 | 10: 尴尬
12 | 11: 发怒
13 | 12: 调皮
14 | 13: 呲牙
15 | 14: 微笑
16 | 15: 难过
17 | 16: 酷
18 | 18: 抓狂
19 | 19: 吐
20 | 20: 偷笑
21 | 21: 可爱
22 | 22: 白眼
23 | 23: 傲慢
24 | 24: 饥饿
25 | 25: 困
26 | 26: 惊恐
27 | 27: 流汗
28 | 28: 憨笑
29 | 29: 大兵
30 | 30: 奋斗
31 | 31: 咒骂
32 | 32: 疑问
33 | 33: 嘘
34 | 34: 晕
35 | 35: 折磨
36 | 36: 衰
37 | 37: 骷髅
38 | 38: 敲打
39 | 39: 再见
40 | 96: 冷汗
41 | 97: 擦汗
42 | 98: 抠鼻
43 | 99: 鼓掌
44 | 100: 糗大了
45 | 101: 坏笑
46 | 102: 左哼哼
47 | 103: 右哼哼
48 | 104: 哈欠
49 | 105: 鄙视
50 | 106: 委屈
51 | 107: 快哭了
52 | 108: 阴险
53 | 109: 亲亲
54 | 110: 吓
55 | 111: 可怜
56 | 172: 眨眼睛
57 | 182: 笑哭
58 | 179: doge
59 | 173: 泪奔
60 | 174: 无奈
61 | 212: 托腮
62 | 175: 卖萌
63 | 178: 斜眼笑
64 | 177: 喷血
65 | 180: 惊喜
66 | 181: 骚扰
67 | 176: 小纠结
68 | 183: 我最美
69 | 112: 菜刀
70 | 89: 西瓜
71 | 113: 啤酒
72 | 114: 篮球
73 | 115: 乒乓
74 | 171: 茶
75 | 60: 咖啡
76 | 61: 饭
77 | 46: 猪头
78 | 63: 玫瑰
79 | 64: 凋谢
80 | 116: 示爱
81 | 66: 爱心
82 | 67: 心碎
83 | 53: 蛋糕
84 | 54: 闪电
85 | 55: 炸弹
86 | 56: 刀
87 | 57: 足球
88 | 117: 瓢虫
89 | 59: 便便
90 | 75: 月亮
91 | 74: 太阳
92 | 69: 礼物
93 | 49: 拥抱
94 | 76: 强
95 | 77: 弱
96 | 78: 握手
97 | 79: 胜利
98 | 118: 抱拳
99 | 119: 勾引
100 | 120: 拳头
101 | 121: 差劲
102 | 122: 爱你
103 | 123: NO
104 | 124: OK
105 | 42: 爱情
106 | 85: 飞吻
107 | 43: 跳跳
108 | 41: 发抖
109 | 86: 怄火
110 | 125: 转圈
111 | 126: 磕头
112 | 127: 回头
113 | 128: 跳绳
114 | 129: 挥手
115 | 130: 激动
116 | 131: 街舞
117 | 132: 献吻
118 | 133: 左太极
119 | 134: 右太极
120 | 136: 双喜
121 | 137: 鞭炮
122 | 138: 灯笼
123 | 140: K歌
124 | 144: 喝彩
125 | 145: 祈祷
126 | 146: 爆筋
127 | 147: 棒棒糖
128 | 148: 喝奶
129 | 151: 飞机
130 | 158: 钞票
131 | 168: 药
132 | 169: 手枪
133 | 188: 蛋
134 | 192: 红包
135 | 184: 河蟹
136 | 185: 羊驼
137 | 190: 菊花
138 | 187: 幽灵
139 | 193: 大笑
140 | 194: 不开心
141 | 197: 冷漠
142 | 198: 呃
143 | 199: 好棒
144 | 200: 拜托
145 | 201: 点赞
146 | 202: 无聊
147 | 203: 托脸
148 | 204: 吃
149 | 205: 送花
150 | 206: 害怕
151 | 207: 花痴
152 | 208: 小样儿
153 | 210: 飙泪
154 | 211: 我不看
155 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
qq-php-sdk
2 |
3 | A QQ PHP SDK.
4 |
5 |
6 | ## Installing
7 |
8 | ```shell
9 | $ composer require kilingzhang/qq-php-sdk -vvv
10 | ```
11 |
12 | ## Usage
13 |
14 | TODO
15 |
16 | ## Contributing
17 |
18 | You can contribute in one of three ways:
19 |
20 | 1. File bug reports using the [issue tracker](https://github.com/kilingzhang/qq-php-sdk/issues).
21 | 2. Answer questions or fix bugs on the [issue tracker](https://github.com/kilingzhang/qq-php-sdk/issues).
22 | 3. Contribute new features or update the wiki.
23 |
24 | _The code contribution process is not very formal. You just need to make sure that you follow the PSR-0, PSR-1, and PSR-2 coding guidelines. Any new code contributions must be accompanied by unit tests where applicable._
25 |
26 | ## License
27 |
28 | MIT
29 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kilingzhang/qq-php-sdk",
3 | "description": "qq-php-sdk",
4 | "keywords": [
5 | "qq-php-sdk",
6 | "coolq-http-api",
7 | "coolq",
8 | "php",
9 | "library",
10 | "sdk"
11 | ],
12 | "homepage": "https://github.com/kilingzhang/qq-php-sdk",
13 | "license": "MIT",
14 | "authors": [
15 | {
16 | "name": "kilingzhang",
17 | "email": "slight@kilingzhang.com",
18 | "homepage": "https://blog.kilingzhang.com/"
19 | }
20 | ],
21 | "require": {
22 | "php": ">=7.0",
23 | "guzzlehttp/guzzle": "^6.0"
24 | },
25 | "require-dev": {
26 | "phpunit/phpunit": "^7.0",
27 | "mockery/mockery": "^1.0",
28 | "fzaninotto/faker": "^1.4"
29 | },
30 | "autoload": {
31 | "files": [
32 | "src/functions_include.php"
33 | ],
34 | "psr-4": {
35 | "Kilingzhang\\QQ\\": "src/"
36 | }
37 | },
38 | "autoload-dev": {
39 | "psr-4": {
40 | "Kilingzhang\\Tests\\": "tests/"
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 | ./tests/
14 |
15 |
16 |
17 |
18 | src/
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/run.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | php -S 0.0.0.0:8000 -t tests
--------------------------------------------------------------------------------
/src/CoolQ/CQ.php:
--------------------------------------------------------------------------------
1 | $value) {
16 | $res[] = $key . '=' . $value;
17 | }
18 | $res = implode(',', $res);
19 | return "[{$res}]";
20 | }
21 |
22 | public static function at(int $userId)
23 | {
24 | return self::CQ('at', [
25 | 'qq' => $userId
26 | ]);
27 | }
28 |
29 | public static function atAll()
30 | {
31 | return self::CQ('at', [
32 | 'qq' => 'all'
33 | ]);
34 | }
35 |
36 | public static function face(int $id)
37 | {
38 | return self::CQ('face', [
39 | 'id' => $id
40 | ]);
41 | }
42 |
43 | public static function emoji(int $id)
44 | {
45 | return self::CQ('emoji', [
46 | 'id' => $id
47 | ]);
48 | }
49 |
50 | public static function bFace(int $id)
51 | {
52 | return self::CQ('bface', [
53 | 'id' => $id
54 | ]);
55 | }
56 |
57 | public static function sFace(int $id)
58 | {
59 | return self::CQ('sface', [
60 | 'id' => $id
61 | ]);
62 | }
63 |
64 | public static function magic(string $magic)
65 | {
66 | return '[]';
67 | }
68 |
69 | public static function image(string $url)
70 | {
71 | return self::CQ('image', [
72 | 'file' => $url
73 | ]);
74 | }
75 |
76 | public static function flash(string $url)
77 | {
78 | return '[]';
79 | }
80 |
81 | public static function record(string $url, bool $isMagic = false)
82 | {
83 | return self::CQ('record', [
84 | 'file' => $url,
85 | 'magic' => $isMagic,
86 | ]);
87 | }
88 |
89 | public static function rps(string $type)
90 | {
91 | return self::CQ('rps', [
92 | 'type' => $type
93 | ]);
94 | }
95 |
96 | public static function dice(string $type)
97 | {
98 | return self::CQ('dice', [
99 | 'type' => $type
100 | ]);
101 | }
102 |
103 | public static function shake()
104 | {
105 | return self::CQ('shake', []);
106 | }
107 |
108 | public static function anonymouse(bool $ignore)
109 | {
110 | return self::CQ('anonymouse', $ignore ? [
111 | 'ignore' => $ignore
112 | ] : []);
113 | }
114 |
115 | public static function music(string $type, int $id)
116 | {
117 | return self::CQ('music', [
118 | 'type' => $type,
119 | 'id' => $id,
120 | ]);
121 | }
122 |
123 | public static function diyMusic(string $url, string $audio, string $title, string $content, string $image)
124 | {
125 | return self::CQ('music', [
126 | 'type' => 'custom',
127 | 'audio' => $audio,
128 | 'title' => $title,
129 | 'content' => $content,
130 | 'image' => $image,
131 | ]);
132 | }
133 |
134 | public static function share(string $url, string $title, string $content, string $image)
135 | {
136 | return self::CQ('share', [
137 | 'url' => $url,
138 | 'title' => $title,
139 | 'content' => $content,
140 | 'image' => $image,
141 | ]);
142 | }
143 |
144 | public static function rich(string $url, string $text)
145 | {
146 | return self::CQ('rich', [
147 | 'url' => $url,
148 | 'text' => $text
149 | ]);
150 | }
151 |
152 |
153 | public static function filterCQAt(string $string)
154 | {
155 | return preg_replace('/\[CQ:at,qq=\d+\]/', '', $string);
156 | }
157 |
158 | public static function decodeHtml(string $message)
159 | {
160 | $message = preg_replace("/&/", "&", $message);
161 | $message = preg_replace("/[/", "[", $message);
162 | $message = preg_replace("/]/", "]", $message);
163 | return $message;
164 | }
165 |
166 | public static function isAtMe(string $message, $userId)
167 | {
168 | $cq = self::at($userId);
169 | $pos = strpos($message, $cq);
170 | return $pos !== false ? true : false;
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/src/CoolQ/QQ.php:
--------------------------------------------------------------------------------
1 | request = $request;
31 | }
32 |
33 | public function getProtocol(): Protocol
34 | {
35 | return $this->request;
36 | }
37 |
38 | public function event($messageEvent, $noticeEvent, $requestEvent, $otherEvent): Response
39 | {
40 |
41 | if(!$this->getProtocol()->isValidated()){
42 | return Response::signatureError();
43 | }
44 |
45 | try{
46 | $content = $this->getContent();
47 | }catch (Exception $exception){
48 | return Response::eventMissParamsError();
49 | }
50 | try{
51 | switch ($content['post_type']) {
52 | //收到消息
53 | case 'message':
54 | $messageEvent($content);
55 | break;
56 | //群、讨论组变动等非消息类事件
57 | case 'notice': //兼容4.0
58 | case 'event':
59 | $noticeEvent($content);
60 | break;
61 | //加好友请求、加群请求/邀请
62 | case 'request':
63 | $requestEvent($content);
64 | break;
65 | default:
66 | $otherEvent($content);
67 | break;
68 | }
69 | }catch (Exception $exception){
70 | return Response::error($exception->getMessage());
71 | }
72 | return Response::ok();
73 | }
74 |
75 |
76 | public function getContent(): array
77 | {
78 | return $this->getProtocol()->getContent();
79 | }
80 |
81 | /**
82 | * 发送私聊消息 同步
83 | * @param int $userId QQ
84 | * @param string $message 消息
85 | * @param bool $autoEscape 是否转译CQ码等特殊符号
86 | * @return Response
87 | */
88 | public function sendPrivateMsg(int $userId, string $message, bool $autoEscape = false): Response
89 | {
90 | $response = $this->getProtocol()->send(Url::send_private_msg, [
91 | 'user_id' => $userId,
92 | 'message' => $message,
93 | 'auto_escape' => $autoEscape,
94 | ], 'POST');
95 | return $response;
96 | }
97 |
98 | /**
99 | * 发送私聊消息 异步
100 | * @param int $userId QQ
101 | * @param string $message 消息
102 | * @param bool $autoEscape 是否转译CQ码等特殊符号
103 | * @return Response
104 | */
105 | public function sendPrivateMsgAsync(int $userId, string $message, bool $autoEscape = false): Response
106 | {
107 | $response = $this->getProtocol()->send(Url::send_private_msg_async, [
108 | 'user_id' => $userId,
109 | 'message' => $message,
110 | 'auto_escape' => $autoEscape,
111 | ], 'POST');
112 | return $response;
113 | }
114 |
115 | /**
116 | * 发送群聊消息 同步
117 | * @param int $groupId 群号
118 | * @param string $message 消息
119 | * @param bool $autoEscape 是否转译CQ码等特殊符号
120 | * @return Response
121 | */
122 | public function sendGroupMsg(int $groupId, string $message, bool $autoEscape = false): Response
123 | {
124 | $response = $this->getProtocol()->send(Url::send_group_msg, [
125 | 'group_id' => $groupId,
126 | 'message' => $message,
127 | 'auto_escape' => $autoEscape,
128 | ], 'POST');
129 | return $response;
130 | }
131 |
132 | /**
133 | * 发送群聊消息 异步
134 | * @param int $groupId 群号
135 | * @param string $message 消息
136 | * @param bool $autoEscape 是否转译CQ码等特殊符号
137 | * @return Response
138 | */
139 | public function sendGroupMsgAsync(int $groupId, string $message, bool $autoEscape = false): Response
140 | {
141 | $response = $this->getProtocol()->send(Url::send_group_msg_async, [
142 | 'group_id' => $groupId,
143 | 'message' => $message,
144 | 'auto_escape' => $autoEscape,
145 | ], 'POST');
146 | return $response;
147 | }
148 |
149 | /**
150 | * 发送讨论组消息 同步
151 | * @param int $discussId 讨论组id
152 | * @param string $message 消息
153 | * @param bool $autoEscape 是否转译CQ码等特殊符号
154 | * @return Response
155 | */
156 | public function sendDiscussMsg(int $discussId, string $message, bool $autoEscape = false): Response
157 | {
158 | $response = $this->getProtocol()->send(Url::send_discuss_msg, [
159 | 'discuss_id' => $discussId,
160 | 'message' => $message,
161 | 'auto_escape' => $autoEscape,
162 | ], 'POST');
163 | return $response;
164 | }
165 |
166 | /**
167 | * 发送讨论组消息 异步
168 | * @param int $discussId 讨论组id
169 | * @param string $message 消息
170 | * @param bool $autoEscape 是否转译CQ码等特殊符号
171 | * @return Response
172 | */
173 | public function sendDiscussMsgAsync(int $discussId, string $message, bool $autoEscape = false): Response
174 | {
175 | $response = $this->getProtocol()->send(Url::send_discuss_msg_async, [
176 | 'discuss_id' => $discussId,
177 | 'message' => $message,
178 | 'auto_escape' => $autoEscape,
179 | ], 'POST');
180 | return $response;
181 | }
182 |
183 | /**
184 | * 发送消息 同步
185 | * @param string $messageType 消息类型
186 | * @param int $id 消息接受者id
187 | * @param string $message 消息
188 | * @param bool $autoEscape 是否转译CQ码等特殊符号
189 | * @return Response
190 | */
191 | public function sendMsg(string $messageType, int $id, string $message, bool $autoEscape = false): Response
192 | {
193 | $response = $this->getProtocol()->send(Url::send_msg, [
194 | 'message_type' => $messageType,
195 | 'id' => $id,
196 | 'message' => $message,
197 | 'auto_escape' => $autoEscape,
198 | ], 'POST');
199 | return $response;
200 | }
201 |
202 | /**
203 | * 发送消息 异步
204 | * @param string $messageType 消息类型
205 | * @param int $id 消息接受者id
206 | * @param string $message 消息
207 | * @param bool $autoEscape 是否转译CQ码等特殊符号
208 | * @return Response
209 | */
210 | public function sendMsgAsync(string $messageType, int $id, string $message, bool $autoEscape = false): Response
211 | {
212 | $response = $this->getProtocol()->send(Url::send_msg_async, [
213 | 'message_type' => $messageType,
214 | 'id' => $id,
215 | 'message' => $message,
216 | 'auto_escape' => $autoEscape,
217 | ], 'POST');
218 | return $response;
219 | }
220 |
221 | /**
222 | * 撤回消息
223 | * @param int $messageId 消息id
224 | * @return Response
225 | */
226 | public function deleteMsg(int $messageId): Response
227 | {
228 | $response = $this->getProtocol()->send(Url::delete_msg, [
229 | 'message_id' => $messageId,
230 | ], 'POST');
231 | return $response;
232 | }
233 |
234 | /**
235 | * 撤回群内其他成员消息 机器人必须为管理员
236 | * @param int $groupId 群号
237 | * @param int $messageId 消息id
238 | * @return Response
239 | */
240 | public function deleteGroupMsg(int $groupId, int $messageId): Response
241 | {
242 | return $this->deleteMsg($messageId);
243 | }
244 |
245 | /**
246 | * 赞
247 | * @param int $userId
248 | * @param int $times
249 | * @return Response
250 | */
251 | public function sendLike(int $userId, int $times = 1): Response
252 | {
253 | $response = $this->getProtocol()->send(Url::send_like, [
254 | 'user_id' => $userId,
255 | 'times' => $times,
256 | ], 'POST');
257 | return $response;
258 | }
259 |
260 | /**
261 | * 窗口抖动
262 | * @param int $userId
263 | * @return Response
264 | */
265 | public function sendShake(int $userId): Response
266 | {
267 | return $this->sendPrivateMsg($userId, CQ::shake());
268 | }
269 |
270 | /**
271 | * 修改个性签名
272 | * @param string $ignature
273 | * @return Response
274 | */
275 | public function setQQSignature(string $ignature): Response
276 | {
277 | return Response::notFoundResourceError();
278 | }
279 |
280 | /**
281 | * 修改好友备注
282 | * @param int $userId QQ
283 | * @param string $friendName 备注名
284 | * @return Response
285 | */
286 | public function setFriendName(int $userId, string $friendName): Response
287 | {
288 | return Response::notFoundResourceError();
289 | }
290 |
291 | /**
292 | * 删除好友
293 | * @param int $userId QQ
294 | * @return Response
295 | */
296 | public function deleteFriend(int $userId): Response
297 | {
298 | return Response::notFoundResourceError();
299 | }
300 |
301 | /**
302 | * 加好友
303 | * @param int $userId QQ
304 | * @param string $msg 附带信息
305 | * @return Response
306 | */
307 | public function addFriend(int $userId, string $msg): Response
308 | {
309 | return Response::notFoundResourceError();
310 | }
311 |
312 | /**
313 | * 加群
314 | * @param int $groupId 群号
315 | * @param string $msg 附带信息
316 | * @return Response
317 | */
318 | public function addGroup(int $groupId, string $msg): Response
319 | {
320 | return Response::notFoundResourceError();
321 | }
322 |
323 | /**
324 | * 移除群成员
325 | * @param int $groupId
326 | * @param int $userId
327 | * @param bool $rejectAddRequest 是否不再接收加群申请
328 | * @return Response
329 | */
330 | public function setGroupKick(int $groupId, int $userId, bool $rejectAddRequest = false): Response
331 | {
332 | $response = $this->getProtocol()->send(Url::set_group_kick, [
333 | 'group_id' => $groupId,
334 | 'user_id' => $userId,
335 | 'reject_add_request' => $rejectAddRequest,
336 | ], 'POST');
337 | return $response;
338 | }
339 |
340 | /**
341 | * 禁言
342 | * @param int $groupId 群号
343 | * @param int $userId QQ
344 | * @param int $duration 禁言时长 单位:秒 0为解除禁言 默认30分钟
345 | * @return Response
346 | */
347 | public function setGroupBan(int $groupId, int $userId, int $duration = 30 * 60): Response
348 | {
349 | $response = $this->getProtocol()->send(Url::set_group_ban, [
350 | 'group_id' => $groupId,
351 | 'user_id' => $userId,
352 | 'duration' => $duration,
353 | ], 'POST');
354 | return $response;
355 | }
356 |
357 | /**
358 | * 匿名禁言消息
359 | * @param int $groupId 群号
360 | * @param string $flag 用户id
361 | * @param int $duration 禁言时长 单位:秒 0为解除禁言 默认30分钟
362 | * @return Response
363 | */
364 | public function setGroupAnonymousBan(int $groupId, string $flag, int $duration = 30 * 60): Response
365 | {
366 | $response = $this->getProtocol()->send(Url::set_group_anonymous_ban, [
367 | 'group_id' => $groupId,
368 | 'flag' => $flag,
369 | 'duration' => $duration,
370 | ], 'POST');
371 | return $response;
372 | }
373 |
374 | /**
375 | * 设置全员禁言
376 | * @param int $groupId 群号
377 | * @param bool $enable 禁言、解除
378 | * @return Response
379 | */
380 | public function setGroupWholeBan(int $groupId, bool $enable = true): Response
381 | {
382 | $response = $this->getProtocol()->send(Url::set_group_whole_ban, [
383 | 'group_id' => $groupId,
384 | 'enable' => $enable,
385 | ], 'POST');
386 | return $response;
387 | }
388 |
389 | /**
390 | * 设置群管理
391 | * @param int $groupId 群号
392 | * @param int $userId QQ
393 | * @param bool $enable 设置、取消
394 | * @return Response
395 | */
396 | public function setGroupAdmin(int $groupId, int $userId, bool $enable = true): Response
397 | {
398 | $response = $this->getProtocol()->send(Url::set_group_admin, [
399 | 'group_id' => $groupId,
400 | 'user_id' => $userId,
401 | 'enable' => $enable,
402 | ], 'POST');
403 | return $response;
404 | }
405 |
406 | /**
407 | * 匿名设置
408 | * @param int $groupId 群号
409 | * @param bool $enable 开启、关闭
410 | * @return Response
411 | */
412 | public function setGroupAnonymous(int $groupId, bool $enable = true): Response
413 | {
414 | $response = $this->getProtocol()->send(Url::set_group_anonymous, [
415 | 'group_id' => $groupId,
416 | 'enable' => $enable,
417 | ], 'POST');
418 | return $response;
419 | }
420 |
421 | /**
422 | * 修改群内成员的名片
423 | * @param int $groupId 群号
424 | * @param int $userId QQ
425 | * @param string|null $card 新名片
426 | * @return Response
427 | */
428 | public function setGroupCard(int $groupId, int $userId, string $card = null): Response
429 | {
430 | $response = $this->getProtocol()->send(Url::set_group_card, [
431 | 'group_id' => $groupId,
432 | 'user_id' => $userId,
433 | 'card' => $card,
434 | ], 'POST');
435 | return $response;
436 | }
437 |
438 | /**
439 | * 退群
440 | * @param int $groupId 群号
441 | * @param bool $isDismiss 是否解散,如果登录号是群主,则仅在此项为 true 时能够解散
442 | * @return Response
443 | */
444 | public function setGroupLeave(int $groupId, bool $isDismiss = false): Response
445 | {
446 | $response = $this->getProtocol()->send(Url::set_group_leave, [
447 | 'group_id' => $groupId,
448 | 'is_dismiss' => $isDismiss,
449 | ], 'POST');
450 | return $response;
451 | }
452 |
453 | /**
454 | * 解散群
455 | * @param int $groupId 群号
456 | * @return Response
457 | */
458 | public function setRemoveGroup(int $groupId): Response
459 | {
460 | return $this->setGroupLeave($groupId, true);
461 | }
462 |
463 | /**
464 | * 设置群组专属头衔
465 | * @param int $groupId 群号
466 | * @param int $userId QQ
467 | * @param string|null $specialTitle 专属头衔,不填或空字符串表示删除专属头衔
468 | * @param int $duration 专属头衔有效期,单位秒,-1 表示永久,不过此项似乎没有效果,可能是只有某些特殊的时间长度有效,有待测试
469 | * @return Response
470 | */
471 | public function setGroupSpecialTitle(int $groupId, int $userId, string $specialTitle = null, int $duration = -1): Response
472 | {
473 | $response = $this->getProtocol()->send(Url::set_group_special_title, [
474 | 'group_id' => $groupId,
475 | 'user_id' => $userId,
476 | 'special_title' => $specialTitle,
477 | 'duration' => $duration,
478 | ], 'POST');
479 | return $response;
480 | }
481 |
482 | /**
483 | * 修改讨论组名称
484 | * @param int $discussId
485 | * @param string $discussName
486 | * @return Response
487 | */
488 | public function setDiscussName(int $discussId, string $discussName): Response
489 | {
490 | return Response::notFoundResourceError();
491 | }
492 |
493 | /**
494 | * 退讨论组
495 | * @param int $discussId
496 | * @return Response
497 | */
498 | public function setDiscussLeave(int $discussId): Response
499 | {
500 | $response = $this->getProtocol()->send(Url::set_discuss_leave, [
501 | 'discuss_id' => $discussId,
502 | ], 'POST');
503 | return $response;
504 | }
505 |
506 | /**
507 | * 处理加好友请求 (不同驱动实现不同、参数不同。待兼容) CoolQ
508 | * @param string $flag 加好友请求的 flag(需从上报的数据中获得)
509 | * @param bool $approve 是否同意请求
510 | * @param string $remark 添加后的好友备注(仅在同意时有效)
511 | * @return Response
512 | */
513 | public function setFriendAddRequest(string $flag, bool $approve = true, string $remark = ''): Response
514 | {
515 | $response = $this->getProtocol()->send(Url::set_friend_add_request, [
516 | 'flag' => $flag,
517 | 'approve' => $approve,
518 | 'remark' => $remark,
519 | ], 'POST');
520 | return $response;
521 | }
522 |
523 | /**
524 | * 处理加群请求/邀请 (不同驱动实现不同、参数不同。待兼容) CoolQ
525 | * @param string $flag 加好友请求的 flag(需从上报的数据中获得)
526 | * @param string $type add 或 invite,请求类型(需要和上报消息中的 sub_type 字段相符)
527 | * @param bool $approve 是否同意请求/邀请
528 | * @param string $reason 拒绝理由(仅在拒绝时有效)
529 | * @return Response
530 | */
531 | public function setGroupAddRequest(string $flag, string $type, bool $approve = true, string $reason = ''): Response
532 | {
533 | $response = $this->getProtocol()->send(Url::set_group_add_request, [
534 | 'flag' => $flag,
535 | 'type' => $type,
536 | 'approve' => $approve,
537 | 'reason' => $reason,
538 | ], 'POST');
539 | return $response;
540 | }
541 |
542 | /**
543 | * 获取登录号信息
544 | * @return Response
545 | */
546 | public function getQQLoginInfo(): Response
547 | {
548 | $response = $this->getProtocol()->send(Url::get_login_info, [], 'POST');
549 | return $response;
550 | }
551 |
552 | /**
553 | * 获取陌生人信息
554 | * @param int $userId
555 | * @param bool $noCache
556 | * @return Response
557 | */
558 | public function getStrangerInfo(int $userId, bool $noCache = false): Response
559 | {
560 | $response = $this->getProtocol()->send(Url::get_stranger_info, [
561 | 'user_id' => $userId,
562 | 'no_cache' => $noCache,
563 | ], 'POST');
564 | return $response;
565 | }
566 |
567 | /**
568 | * 某QQ个人信息
569 | * @param int $userId
570 | * @param bool $noCache
571 | * @return Response
572 | */
573 | public function getQQInfo(int $userId, bool $noCache = false): Response
574 | {
575 | return Response::notFoundResourceError();
576 | }
577 |
578 | /**
579 | * 获取群列表
580 | * @return Response
581 | */
582 | public function getGroupList(): Response
583 | {
584 | $response = $this->getProtocol()->send(Url::get_group_list, [], 'POST');
585 | return $response;
586 | }
587 |
588 | /**
589 | * 群成员信息
590 | * @param int $groupId
591 | * @param int $userId
592 | * @param bool $noCache
593 | * @return Response
594 | */
595 | public function getGroupMemberInfo(int $groupId, int $userId, bool $noCache = false): Response
596 | {
597 | $response = $this->getProtocol()->send(Url::get_group_member_info, [
598 | 'group_id' => $groupId,
599 | 'user_id' => $userId,
600 | 'no_cache' => $noCache,
601 | ], 'POST');
602 | return $response;
603 | }
604 |
605 | /**
606 | * 群成员列表
607 | * @param int $groupId
608 | * @return Response
609 | */
610 | public function getGroupMemberList(int $groupId): Response
611 | {
612 | $response = $this->getProtocol()->send(Url::get_group_member_list, [
613 | 'group_id' => $groupId,
614 | ], 'POST');
615 | return $response;
616 | }
617 |
618 | /**
619 | * 邀请好友入群
620 | * @param int $groupId
621 | * @param int $userId
622 | * @return Response
623 | */
624 | public function inviteFriendIntoGroup(int $groupId, int $userId): Response
625 | {
626 | return Response::notFoundResourceError();
627 | }
628 |
629 | /**
630 | * 取Cookies
631 | * @return Response
632 | */
633 | public function getCookies(): Response
634 | {
635 | $response = $this->getProtocol()->send(Url::get_cookies, [], 'POST');
636 | return $response;
637 | }
638 |
639 | /**
640 | * 取bkn
641 | * @return Response
642 | */
643 | public function getBkn(): Response
644 | {
645 | return Response::notFoundResourceError();
646 | }
647 |
648 | /**
649 | * 取ClientKey
650 | * @return Response
651 | */
652 | public function getClientKey(): Response
653 | {
654 | return Response::notFoundResourceError();
655 | }
656 |
657 | /**
658 | * 获取 QQ 相关接口凭证 (为了兼容不同平台返回的不同值)
659 | * @return Response
660 | */
661 | public function getCredentials(): Response
662 | {
663 | $response = $this->getProtocol()->send(Url::get_credentials, [], 'POST');
664 | return $response;
665 | }
666 |
667 | public function returnApi(Response $response)
668 | {
669 | $this->getProtocol()->returnApi($response);
670 | }
671 | }
672 |
673 |
--------------------------------------------------------------------------------
/src/CoolQ/Url.php:
--------------------------------------------------------------------------------
1 | isBlackList;
43 | }
44 |
45 | /**
46 | * @param bool $isBlackList
47 | */
48 | public function setIsBlackList(bool $isBlackList)
49 | {
50 | $this->isBlackList = $isBlackList;
51 | }
52 |
53 |
54 | /**
55 | * @return array
56 | */
57 | public function getPrivateBlackList(): array
58 | {
59 | return $this->privateBlackList;
60 | }
61 |
62 | /**
63 | * @param array $privateBlackList
64 | */
65 | public function setPrivateBlackList(array $privateBlackList)
66 | {
67 | $this->privateBlackList = $privateBlackList;
68 | }
69 |
70 | /**
71 | * @return array
72 | */
73 | public function getGroupBlackList(): array
74 | {
75 | return $this->groupBlackList;
76 | }
77 |
78 | /**
79 | * @param array $groupBlackList
80 | */
81 | public function setGroupBlackList(array $groupBlackList)
82 | {
83 | $this->groupBlackList = $groupBlackList;
84 | }
85 |
86 | /**
87 | * @return array
88 | */
89 | public function getDiscussBlackList(): array
90 | {
91 | return $this->discussBlackList;
92 | }
93 |
94 | /**
95 | * @param array $discussBlackList
96 | */
97 | public function setDiscussBlackList(array $discussBlackList)
98 | {
99 | $this->discussBlackList = $discussBlackList;
100 | }
101 |
102 | }
--------------------------------------------------------------------------------
/src/Core/CQ.php:
--------------------------------------------------------------------------------
1 | url = $url;
44 | $hosts = explode(':', $this->url);
45 | if (count($hosts) != 2) {
46 | throw new Exception('missing url or port');
47 | }
48 | $this->host = $hosts[0];
49 | $this->port = $hosts[1];
50 | $this->accessToken = $access_token;
51 | $this->secret = $secret;
52 |
53 | $this->options['headers'] = [
54 | 'Authorization' => 'Token ' . $this->accessToken
55 | ];
56 |
57 | $this->client = new Client([
58 | // Base URI is used with relative requests
59 | 'base_uri' => $this->url,
60 | // You can set any number of default request options.
61 | 'timeout' => 10.0,
62 | ]);
63 |
64 | }
65 |
66 | public function send($uri, $param = [], $method = 'POST'): Response
67 | {
68 |
69 | try {
70 | $this->response = $this->client->request($method, $uri, array_merge($this->options, [
71 | 'query' => $param
72 | ]));
73 | $response = $this->response;
74 | if ($response->getStatusCode() == 200) {
75 | $response = $response->getBody();
76 | $response = json_decode($response, true);
77 | $code = $response['retcode'];
78 | $data = $response['data'];
79 | if ($code != 0) {
80 | return Response::response(200, $code, []);
81 | }
82 | $data = empty($data) ? [] : $data;
83 | return Response::ok($data);
84 | }
85 | } catch (ClientException $e) {
86 | //如果 http_errors 请求参数设置成true,在400级别的错误的时候将会抛出
87 | switch ($e->getCode()) {
88 | case 400:
89 | return Response::notFoundResourceError();
90 | break;
91 | case 401:
92 | //401 配置文件中已填写access_token 初始化CoolQ对象时未传值
93 | return Response::accessTokenNoneError();
94 | break;
95 | case 403:
96 | //403 验证access_token错误
97 | return Response::accessTokenError();
98 | break;
99 | case 404:
100 | return Response::notFoundResourceError();
101 | break;
102 | case 406:
103 | return Response::contentTypeError();
104 | break;
105 | default:
106 | return Response::error([
107 | 'message' => $e->getMessage()
108 | ]);
109 | break;
110 | }
111 | } catch (RequestException $e) {
112 | //在发送网络错误(连接超时、DNS错误等)时,将会抛出 GuzzleHttp\Exception\RequestException 异常。
113 | //一般为coolq-http-api插件未开启 接口地址无法访问
114 | switch ($e->getCode()) {
115 | case 0:
116 | return Response::pluginServerError($this->client);
117 | break;
118 | default:
119 | return Response::error([
120 | 'message' => $e->getMessage()
121 | ]);
122 | break;
123 | }
124 | }
125 |
126 | return $response;
127 |
128 | }
129 |
130 | public function sendAsync($uri, $param = [], $method = 'POST'): Response
131 | {
132 |
133 | $promise = $this->client->requestAsync($method, $uri, array_merge($this->options, [
134 | 'query' => $param
135 | ]));
136 |
137 | $promise->then(
138 | function (ResponseInterface $res) {
139 | echo $res->getBody() . "\n";
140 | },
141 | function (RequestException $e) {
142 | echo $e->getMessage() . "\n";
143 | echo $e->getRequest()->getMethod();
144 | })->wait();
145 |
146 | // return $promise;
147 | }
148 |
149 | public function isValidated(): bool
150 | {
151 | $signature = http_server('HTTP_X_SIGNATURE');
152 | $signature = $signature == '' ? '' : substr($signature, 5, strlen($signature));
153 | $putParams = http_put();
154 | if ($this->isSignature && !empty($signature) && (hash_hmac('sha1', \GuzzleHttp\json_encode($putParams, JSON_UNESCAPED_UNICODE), $this->secret) != $signature)) {
155 | //sha1验证失败
156 | return false;
157 | }
158 | return true;
159 | }
160 |
161 | /**
162 | * @return array
163 | * @throws Exception
164 | */
165 | public function getContent(): array
166 | {
167 | $content = http_put();
168 | if (empty($content)) {
169 | throw new Exception('put params not be empty');
170 | }
171 | switch ($content['post_type']) {
172 | //收到消息
173 | case 'message':
174 | $message_type = $content['message_type'];
175 | switch ($message_type) {
176 | //私聊消息
177 | case "private":
178 | $this->content = [
179 | 'message_type' => $content['message_type'],
180 | 'message_id' => $content['message_id'],
181 | 'font' => $content['font'],
182 | 'user_id' => $content['user_id'],
183 | 'message' => $content['message'],
184 | //消息子类型,如果是好友则是 "friend",
185 | //如果从群或讨论组来的临时会话则分别是 "group"、"discuss"
186 | //"friend"、"group"、"discuss"、"other"
187 | 'sub_type' => $content['sub_type'],
188 | ];
189 | break;
190 | //群消息
191 | case "group":
192 | $this->content = [
193 | 'message_type' => $content['message_type'],
194 | 'message_id' => $content['message_id'],
195 | 'font' => $content['font'],
196 | 'user_id' => $content['user_id'],
197 | 'message' => $content['message'],
198 | 'group_id' => $content['group_id'],
199 | //匿名用户显示名
200 | 'anonymous' => $content['anonymous'],
201 | //匿名用户 flag,在调用禁言 API 时需要传入
202 | 'anonymous_flag' => empty($content['anonymous']['flag']) ? '' : $content['anonymous']['flag'],
203 | ];
204 | // {"reply":"message","block": true,"at_sender":true,"kick":false,"ban":false}
205 | break;
206 | //讨论组消息
207 | case "discuss":
208 | $this->content = [
209 | 'message_type' => $content['message_type'],
210 | 'message_id' => $content['message_id'],
211 | 'font' => $content['font'],
212 | 'discuss_id' => $content['discuss_id'],
213 | 'user_id' => $content['user_id'],
214 | 'message' => $content['message'],
215 | ];
216 | // {"reply":"message","block": true,"at_sender":true}
217 | break;
218 | }
219 | break;
220 | //群、讨论组变动等非消息类事件
221 | case 'notice': //兼容4.0
222 | case 'event':
223 | $event = empty($content['event']) ? $content['notice_type'] : $content['event'];//兼容4.0
224 | switch ($event) {
225 | //群管理员变动
226 | case "group_admin":
227 | $this->content = [
228 | 'event' => empty($content['event']) ? $content['notice_type'] : $content['event'],
229 | //"set"、"unset" 事件子类型,分别表示设置和取消管理员
230 | 'sub_type' => $content['sub_type'],
231 | 'group_id' => $content['group_id'],
232 | 'user_id' => $content['user_id'],
233 | ];
234 | break;
235 | //群成员减少
236 | case "group_decrease":
237 | $this->content = [
238 | 'event' => empty($content['event']) ? $content['notice_type'] : $content['event'],
239 | //"leave"、"kick"、"kick_me" 事件子类型,分别表示主动退群、成员被踢、登录号被踢
240 | 'sub_type' => $content['sub_type'],
241 | 'group_id' => $content['group_id'],
242 | 'user_id' => $content['user_id'],
243 | 'operator_id' => $content['operator_id'],
244 | ];
245 | break;
246 | //群成员增加
247 | case "group_increase":
248 | $this->content = [
249 | 'event' => empty($content['event']) ? $content['notice_type'] : $content['event'],
250 | //"approve"、"invite" 事件子类型,分别表示管理员已同意入群、管理员邀请入群
251 | 'sub_type' => $content['sub_type'],
252 | 'group_id' => $content['group_id'],
253 | 'user_id' => $content['user_id'],
254 | 'operator_id' => $content['operator_id'],
255 | ];
256 | break;
257 | //群文件上传
258 | case "group_upload":
259 | $this->content = [
260 | 'event' => empty($content['event']) ? $content['notice_type'] : $content['event'],
261 | 'group_id' => $content['group_id'],
262 | 'user_id' => $content['user_id'],
263 | #字段名 数据类型 说明
264 | #id string 文件 ID
265 | #name string 文件名
266 | #size number 文件大小(字节数)
267 | #busid number busid(目前不清楚有什么作用)
268 | 'file' => $content['file'],
269 | ];
270 | break;
271 | //好友添加
272 | case "friend_added":
273 | $this->content = [
274 | 'event' => empty($content['event']) ? $content['notice_type'] : $content['event'],
275 | 'user_id' => $content['user_id'],
276 | ];
277 | break;
278 | }
279 | break;
280 | //加好友请求、加群请求/邀请
281 | case 'request':
282 | $request_type = $content['request_type'];
283 | switch ($request_type) {
284 | case "friend":
285 | $this->content = [
286 | 'request_type' => $content['request_type'],
287 | 'user_id' => $content['user_id'],
288 | 'message' => empty($content['message']) ? $content['comment'] : $content['message'],//兼容4.0
289 | 'flag' => $content['flag'],
290 | ];
291 | //{"block": true,"approve":true,"reason":"就是拒绝你 不行啊"}
292 | break;
293 | case "group":
294 | $this->content = [
295 | 'request_type' => $content['request_type'],
296 | //"add"、"invite" 请求子类型,分别表示加群请求、邀请登录号入群
297 | 'sub_type' => $content['sub_type'],
298 | 'group_id' => $content['group_id'],
299 | 'user_id' => $content['user_id'],
300 | 'message' => empty($content['message']) ? $content['comment'] : $content['message'],//兼容4.0
301 | 'flag' => $content['flag'],
302 | ];
303 | //{"block": true,"approve":true,"reason":"就是拒绝你 不行啊"}
304 | break;
305 | }
306 | break;
307 | default:
308 | $this->content = $content;
309 | break;
310 | }
311 | $this->content['post_type'] = $content['post_type'];
312 | return $this->content;
313 | }
314 |
315 | public function returnApi(Response $response)
316 | {
317 | echo json_encode($response, JSON_UNESCAPED_UNICODE);
318 | exit();
319 | }
320 |
321 | public function isCli(): bool
322 | {
323 | return false;
324 | }
325 | }
--------------------------------------------------------------------------------
/src/Core/Protocols/Protocol.php:
--------------------------------------------------------------------------------
1 | code = $code;
24 | $this->message = $message;
25 | $this->errorCode = $errorCode;
26 | $this->errorMsg = $errorMsg;
27 | $this->data = $data;
28 | }
29 |
30 | /**
31 | * @return int
32 | */
33 | public
34 | function getCode(): int
35 | {
36 | return $this->code;
37 | }
38 |
39 | /**
40 | * @return int
41 | */
42 | public function getErrorCode(): int
43 | {
44 | return $this->errorCode;
45 | }
46 |
47 |
48 | /**
49 | * @return string
50 | */
51 | public function getMessage(): string
52 | {
53 | return $this->message;
54 | }
55 |
56 |
57 | /**
58 | * @return array
59 | */
60 | public function getData(): array
61 | {
62 | return $this->data;
63 | }
64 |
65 |
66 | public function getErrorMsg(): string
67 | {
68 | return $this->errorMsg;
69 | }
70 |
71 |
72 | public function toArray(): array
73 | {
74 | return [
75 | 'code' => $this->getCode(),
76 | 'message' => $this->getMessage(),
77 | 'errorCode' => $this->getErrorCode(),
78 | 'errorMsg' => $this->getErrorMsg(),
79 | 'data' => $this->getData(),
80 | ];
81 | }
82 |
83 | public function toJson(): string
84 | {
85 | return json_encode($this->toArray(), JSON_UNESCAPED_UNICODE);
86 | }
87 |
88 | public static function response($code, $errorCode, $data)
89 | {
90 |
91 |
92 | $message = [
93 | 0 => '接口请求成功',
94 | 100 => '请参数参数错误',
95 | 200 => 'HTTP请求成功',
96 | 400 => '资源请求被拒绝',
97 | 500 => '服务器故障',
98 | 65535 => '未获取到上报事件发送的上报数据',
99 | ];
100 |
101 | if (!array_key_exists($code, $message)) {
102 | $message = '未知状态码';
103 | }
104 |
105 | $message = $message[$code];
106 |
107 | $msg = [
108 | -1 => '请求发送失败',
109 | -2 => '未收到服务器回复,可能未发送成功',
110 | -3 => '消息过长或为空',
111 | -4 => '消息解析过程异常',
112 | -5 => '日志功能未启用',
113 | -6 => '日志优先级错误',
114 | -7 => '数据入库失败',
115 | -8 => '不支持对系统帐号操作',
116 | -9 => '帐号不在该群内,消息无法发送',
117 | -10 => '该用户不存在/不在群内',
118 | -11 => '数据错误,无法请求发送',
119 | -12 => '不支持对匿名成员解除禁言',
120 | -13 => '无法解析要禁言的匿名成员数据',
121 | -14 => '由于未知原因,操作失败',
122 | -15 => '群未开启匿名发言功能,或匿名帐号被禁言',
123 | -16 => '帐号不在群内或网络错误,无法退出/解散该群',
124 | -17 => '帐号为群主,无法退出该群',
125 | -18 => '帐号非群主,无法解散该群',
126 | -19 => '临时消息已失效或未建立',
127 | -20 => '参数错误',
128 | -21 => '临时消息已失效或未建立',
129 | -22 => '获取QQ信息失败',
130 | -23 => '找不到与目标QQ的关系,消息无法发送',
131 | -99 => 'Air 不支持此操作',
132 | -101 => '应用过大',
133 | -102 => '不是合法的应用',
134 | -103 => '不是合法的应用',
135 | -104 => '应用不存在公开的Information函数',
136 | -105 => '无法载入应用信息',
137 | -106 => '文件名与应用ID不同',
138 | -107 => '返回信息解析错误',
139 | -108 => 'AppInfo返回的Api版本不支持直接加载,仅支持Api版本为9(及以上)的应用直接加载',
140 | -109 => 'AppInfo返回的AppID错误',
141 | -110 => '缺失AppInfo返回的AppID对应的[Appid].json文件',
142 | -111 => '[Appid].json文件内的AppID与其文件名不同',
143 | -120 => '无Api授权接收函数(Initialize)',
144 | -121 => 'Api授权接收函数(Initialize)返回值非0',
145 | -122 => '尝试恶意修改酷Q配置文件,将取消加载并关闭酷Q',
146 | -150 => '无法载入应用信息',
147 | -151 => '应用信息Json串解析失败,请检查Json串是否正确',
148 | -152 => 'Api版本过旧或过新',
149 | -153 => '应用信息错误或存在缺失',
150 | -154 => 'Appid不合法',
151 | -160 => '事件类型(Type)错误或缺失',
152 | -161 => '事件函数(Function)错误或缺失',
153 | -162 => '应用优先级不为10000、20000、30000、40000中的一个',
154 | -163 => '事件类型(Api)不支持应用Api版本',
155 | -164 => '应用Api版本大于8,但使用了新版本已停用的事件类型(Type):1(好友消息)、3(临时消息)',
156 | -165 => '事件类型为2(群消息)、4(讨论组消息)、21(私聊消息),但缺少正则表达式(regex)的表达式部分(expression)',
157 | -166 => '存在为空的正则表达式(regex)的key',
158 | -167 => '存在为空的正则表达式(regex)的表达式部分(expression)',
159 | -168 => '应用事件(event)id参数不存在或为0',
160 | -169 => '应用事件(event)id参数有重复=>',
161 | -180 => '应用状态(status)id参数不存在或为0',
162 | -181 => '应用状态(status)period参数不存在或设置错误',
163 | -182 => '应用状态(status)id参数有重复=>',
164 | -201 => '无法载入应用,可能是应用文件已损坏',
165 | -202 => 'Api版本过旧或过新',
166 | -997 => '应用未启用',
167 | -998 => '应用调用在Auth声明之外的 酷Q A。',
168 | -2333 => 'CQHTTP插件未开启,或插件服务器启动失败。访问被拒绝。请检测Docker端口是否开启,网络是否通畅。',
169 | -1000 => '无法找到swoole扩展,请先安装.',
170 | -1001 => 'must be used in PHP CLI mode.',
171 | -1002 => '无法找到Pcntl扩展,请先安装.',
172 | 0 => '同时 status 为 ok,表示操作成功',
173 | 1 => '同时 status 为 async,表示操作已进入异步执行,具体结果未知',
174 | 100 => '参数缺失或参数无效,通常是因为没有传入必要参数,某些接口中也可能因为参数明显无效(比如传入的 QQ 号小于等于 0,此时无需调用酷 Q 函数即可确定失败),此项和以下的 status 均为 failed',
175 | 102 => '酷 Q 函数返回的数据无效,一般是因为传入参数有效但没有权限,比如试图获取没有加入的群组的成员列表,或调取语音相关接口未安装语音组件',
176 | 103 => '操作失败,一般是因为用户权限不足,或文件系统异常、不符合预期',
177 | 201 => '工作线程池未正确初始化(无法执行异步任务)',
178 | 400 => ' POST 请求的正文格式不正确',
179 | 401 => 'access token 未提供',
180 | 403 => 'access token 或 HTTP_X_SIGNATURE 不符合',
181 | 404 => 'API 不存在',
182 | 405 => '该账号id已被禁止,无法对其进行操作',
183 | 406 => 'POST 请求的 Content-Type 不支持',
184 | 500 => '未知错误',
185 | 65535 => '未获取到上报事件发送的上报数据',
186 | ];
187 |
188 | if (!array_key_exists($errorCode, $msg)) {
189 | $msg = '未知状态码';
190 | }
191 |
192 | $msg = $msg[$errorCode];
193 |
194 | $response = new Response($code, $message, $errorCode, $msg, $data);
195 | return $response;
196 | }
197 |
198 | public static function ok(array $data = [])
199 | {
200 | $response = new Response(0, 'success', 200, 'success', $data);
201 | return $response;
202 | }
203 |
204 | public static function notFoundResourceError()
205 | {
206 | return Response::response(400, 404, []);
207 | }
208 |
209 | public static function accessTokenError()
210 | {
211 | return Response::response(400, 403, []);
212 | }
213 |
214 | public static function signatureError()
215 | {
216 | return Response::response(400, 403, []);
217 | }
218 |
219 | public static function accessTokenNoneError()
220 | {
221 | return Response::response(400, 401, []);
222 | }
223 |
224 | public static function banAccountError($data = [])
225 | {
226 | return Response::response(400, 405, $data);
227 | }
228 |
229 | public static function contentTypeError()
230 | {
231 | return Response::response(400, 406, []);
232 | }
233 |
234 | public static function pluginServerError($data = [])
235 | {
236 | return Response::response(500, -2333, $data);
237 | }
238 |
239 | public static function eventMissParamsError($data = [])
240 | {
241 | return Response::response(100, 65535, $data);
242 | }
243 |
244 |
245 | public static function NotExitsSwoolError($data = [])
246 | {
247 | return Response::response(500, -1000, $data);
248 | }
249 |
250 | public static function NotBeCliError($data = [])
251 | {
252 | return Response::response(500, -1001, $data);
253 | }
254 |
255 | public static function NotExitsPcntlError($data = [])
256 | {
257 | return Response::response(500, -1002, $data);
258 | }
259 |
260 | public static function error($data = [])
261 | {
262 | return Response::response(500, 500, $data);
263 | }
264 |
265 | }
--------------------------------------------------------------------------------
/src/Core/Whitelist.php:
--------------------------------------------------------------------------------
1 | isWhiteList;
45 | }
46 |
47 | /**
48 | * @param bool $isWhiteList
49 | */
50 | public function setIsWhiteList(bool $isWhiteList)
51 | {
52 | $this->isWhiteList = $isWhiteList;
53 | }
54 |
55 | /**
56 | * @return array
57 | */
58 | public function getPrivateWhiteList(): array
59 | {
60 | return $this->privateWhiteList;
61 | }
62 |
63 | /**
64 | * @param array $privateWhiteList
65 | */
66 | public function setPrivateWhiteList(array $privateWhiteList)
67 | {
68 | $this->privateWhiteList = $privateWhiteList;
69 | }
70 |
71 | /**
72 | * @return array
73 | */
74 | public function getGroupWhiteList(): array
75 | {
76 | return $this->groupWhiteList;
77 | }
78 |
79 | /**
80 | * @param array $groupWhiteList
81 | */
82 | public function setGroupWhiteList(array $groupWhiteList)
83 | {
84 | $this->groupWhiteList = $groupWhiteList;
85 | }
86 |
87 | /**
88 | * @return array
89 | */
90 | public function getDiscussWhiteList(): array
91 | {
92 | return $this->discussWhiteList;
93 | }
94 |
95 | /**
96 | * @param array $discussWhiteList
97 | */
98 | public function setDiscussWhiteList(array $discussWhiteList)
99 | {
100 | $this->discussWhiteList = $discussWhiteList;
101 | }
102 |
103 | }
--------------------------------------------------------------------------------
/src/Core/isCanSend.php:
--------------------------------------------------------------------------------
1 | isWhiteList() && !in_array($userId, $this->getPrivateWhiteList())) {
19 | return false;
20 | }
21 | if (!$this->isWhiteList() && $this->isBlackList() && in_array($userId, $this->getPrivateBlackList())) {
22 | return false;
23 | }
24 | return true;
25 | }
26 |
27 | public function isCanSendGroup(int $groupId): bool
28 | {
29 | if ($this->isWhiteList() && !in_array($groupId, $this->getGroupWhiteList())) {
30 | return false;
31 | }
32 | if (!$this->isWhiteList() && $this->isBlackList() && in_array($groupId, $this->getGroupBlackList())) {
33 | return false;
34 | }
35 | return true;
36 | }
37 |
38 | public function isCanSendDiscuss(int $discussId): bool
39 | {
40 | if ($this->isWhiteList() && !in_array($discussId, $this->getDiscussWhiteList())) {
41 | return false;
42 | }
43 | if (!$this->isWhiteList() && $this->isBlackList() && in_array($discussId, $this->getDiscussBlackList())) {
44 | return false;
45 | }
46 | return true;
47 | }
48 | }
--------------------------------------------------------------------------------
/src/Light/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/functions.php:
--------------------------------------------------------------------------------
1 | QQ = new QQ(new GuzzleProtocol($this->url, $this->accessToken, $this->secret));
43 | $this->faker = Factory::create();
44 | }
45 |
46 | public function testSendPrivateMsg()
47 | {
48 | $text = $this->faker->text;
49 | $response = $this->QQ->sendPrivateMsg($this->devQQ, $text, false);
50 | $this->assertInstanceOf(Response::class, $response);
51 | }
52 |
53 | public function testSendPrivateMsgAsync()
54 | {
55 | $text = $this->faker->text;
56 | $response = $this->QQ->sendPrivateMsgAsync($this->devQQ, $text, false);
57 | $this->assertInstanceOf(Response::class, $response);
58 | }
59 |
60 |
61 | public function testSendGroupMsg()
62 | {
63 | $text = $this->faker->text;
64 | $response = $this->QQ->sendGroupMsg($this->devGroupId, $text, false);
65 | $this->assertInstanceOf(Response::class, $response);
66 | }
67 |
68 | public function testSendGroupMsgAsync()
69 | {
70 | $text = $this->faker->text;
71 | $response = $this->QQ->sendGroupMsgAsync($this->devGroupId, $text, false);
72 | $this->assertInstanceOf(Response::class, $response);
73 | }
74 |
75 | public function testSendDiscussMsg()
76 | {
77 | $text = $this->faker->text;
78 | $response = $this->QQ->sendDiscussMsg($this->devDiscussId, $text, false);
79 | $this->assertInstanceOf(Response::class, $response);
80 | }
81 |
82 | public function testSendDiscussMsgAsync()
83 | {
84 | $text = $this->faker->text;
85 | $response = $this->QQ->sendDiscussMsgAsync($this->devDiscussId, $text, false);
86 | $this->assertInstanceOf(Response::class, $response);
87 | }
88 |
89 | public function testSendMsg()
90 | {
91 | $text = $this->faker->text;
92 | $response = $this->QQ->sendMsg('private', $this->devQQ, $text, false);
93 | $this->assertInstanceOf(Response::class, $response);
94 | }
95 |
96 | public function testSendMsgAsync()
97 | {
98 | $text = $this->faker->text;
99 | $response = $this->QQ->sendMsgAsync('private', $this->devQQ, $text, false);
100 | $this->assertInstanceOf(Response::class, $response);
101 | }
102 |
103 | public function testDeleteMsg()
104 | {
105 | $text = $this->faker->text;
106 | $response = $this->QQ->sendPrivateMsg($this->devQQ, $text, false);
107 | $data = $response->getData();
108 | $this->messageId = $data['message_id'];
109 | $response = $this->QQ->deleteMsg($this->messageId);
110 | $this->assertInstanceOf(Response::class, $response);
111 | }
112 |
113 | public function testDeleteGroupMsg()
114 | {
115 | $text = $this->faker->text;
116 | $response = $this->QQ->sendGroupMsg($this->devGroupId, $text, false);
117 | $data = $response->getData();
118 | $this->messageGroupId = $data['message_id'];
119 | $response = $this->QQ->deleteGroupMsg($this->devGroupId, $this->messageGroupId);
120 | $this->assertInstanceOf(Response::class, $response);
121 | }
122 |
123 | public function testSendLike()
124 | {
125 | $response = $this->QQ->sendLike($this->devQQ, 1);
126 | $this->assertInstanceOf(Response::class, $response);
127 | }
128 |
129 | public function testSendShake()
130 | {
131 | $response = $this->QQ->sendShake($this->devQQ);
132 | $this->assertInstanceOf(Response::class, $response);
133 | }
134 |
135 | public function testSetQQSignature()
136 | {
137 | $text = $this->faker->text;
138 | $response = $this->QQ->setQQSignature($text);
139 | $this->assertInstanceOf(Response::class, $response);
140 | }
141 |
142 | public function testSetFriendName()
143 | {
144 | $name = $this->faker->name;
145 | $response = $this->QQ->setFriendName($this->devQQ, $name);
146 | $this->assertInstanceOf(Response::class, $response);
147 | }
148 |
149 | // public function testDeleteFriend()
150 | // {
151 | // $response = $this->QQ->deleteFriend($this->devQQ);
152 | // $this->assertInstanceOf(Response::class, $response);
153 | // }
154 |
155 | public function testAddFriend()
156 | {
157 | $name = $this->faker->name;
158 | $response = $this->QQ->addFriend($this->devQQ, $name);
159 | $this->assertInstanceOf(Response::class, $response);
160 | }
161 |
162 | public function testAddGroup()
163 | {
164 | $name = $this->faker->name;
165 | $response = $this->QQ->addGroup($this->devGroupId, $name);
166 | $this->assertInstanceOf(Response::class, $response);
167 | }
168 |
169 | // public function testSetGroupKick()
170 | // {
171 | // $name = $this->faker->name;
172 | // $response = $this->QQ->setGroupKick($this->devGroupId, $this->devQQ, $name);
173 | // $this->assertInstanceOf(Response::class, $response);
174 | // }
175 |
176 | public function testSetGroupBan()
177 | {
178 | $response = $this->QQ->setGroupBan($this->devGroupId, $this->devQQ, 1);
179 | $this->assertInstanceOf(Response::class, $response);
180 | }
181 |
182 | // public function testSetGroupAnonymousBan()
183 | // {
184 | // $response = $this->QQ->setGroupAnonymousBan($this->devGroupId, $this->flagId);
185 | // $this->assertInstanceOf(Response::class, $response);
186 | // }
187 |
188 | public function testSetGroupWholeBan()
189 | {
190 | $response = $this->QQ->setGroupWholeBan($this->devGroupId);
191 | $this->assertInstanceOf(Response::class, $response);
192 | }
193 |
194 | public function testSetGroupWholeOpen()
195 | {
196 | $response = $this->QQ->setGroupWholeBan($this->devGroupId, false);
197 | $this->assertInstanceOf(Response::class, $response);
198 | }
199 |
200 | public function testSetGroupAdmin()
201 | {
202 | $response = $this->QQ->setGroupAdmin($this->devGroupId, $this->devQQ);
203 | $this->assertInstanceOf(Response::class, $response);
204 | }
205 |
206 | public function testSetGroupAnonymous()
207 | {
208 | $response = $this->QQ->setGroupAnonymous($this->devGroupId);
209 | $this->assertInstanceOf(Response::class, $response);
210 | }
211 |
212 | public function testSetGroupCard()
213 | {
214 | $name = $this->faker->name;
215 | $response = $this->QQ->setGroupCard($this->devGroupId, $this->devQQ, $name);
216 | $this->assertInstanceOf(Response::class, $response);
217 | }
218 |
219 | // public function testSetGroupLeave()
220 | // {
221 | // $response = $this->QQ->setGroupLeave($this->devGroupId, false);
222 | // $this->assertInstanceOf(Response::class, $response);
223 | // }
224 |
225 | // public function testSetRemoveGroup()
226 | // {
227 | // $response = $this->QQ->setRemoveGroup($this->devGroupId);
228 | // $this->assertInstanceOf(Response::class, $response);
229 | // }
230 |
231 | public function testSetGroupSpecialTitle()
232 | {
233 | $name = $this->faker->name;
234 | $response = $this->QQ->setGroupSpecialTitle($this->devGroupId, $this->devQQ, $name);
235 | $this->assertInstanceOf(Response::class, $response);
236 | }
237 |
238 | public function testSetDiscussName()
239 | {
240 | $name = $this->faker->name;
241 | $response = $this->QQ->setDiscussName($this->devDiscussId, $name);
242 | $this->assertInstanceOf(Response::class, $response);
243 | }
244 |
245 | // public function testSetDiscussLeave()
246 | // {
247 | // $response = $this->QQ->setDiscussLeave($this->devDiscussId);
248 | // $this->assertInstanceOf(Response::class, $response);
249 | // }
250 |
251 | // public function testSetFriendAddRequest()
252 | // {
253 | // $remark = $this->faker->title;
254 | // $response = $this->QQ->setFriendAddRequest($this->flagId, true, $remark);
255 | // $this->assertInstanceOf(Response::class, $response);
256 | // }
257 | //
258 | // public function testSetGroupAddRequest()
259 | // {
260 | // $reason = $this->faker->title;
261 | // $response = $this->QQ->setGroupAddRequest($this->flagId, true, $reason);
262 | // $this->assertInstanceOf(Response::class, $response);
263 | // }
264 |
265 | public function testGetQQLoginInfo()
266 | {
267 | $response = $this->QQ->getQQLoginInfo();
268 | $this->assertInstanceOf(Response::class, $response);
269 | }
270 |
271 | public function testGetStrangerInfo()
272 | {
273 | $response = $this->QQ->getStrangerInfo($this->devQQ);
274 | $this->assertInstanceOf(Response::class, $response);
275 | }
276 |
277 | public function testGetQQInfo()
278 | {
279 | $response = $this->QQ->getQQInfo($this->devQQ);
280 | $this->assertInstanceOf(Response::class, $response);
281 | }
282 |
283 | public function testGetGroupList()
284 | {
285 | $response = $this->QQ->getGroupList();
286 | $this->assertInstanceOf(Response::class, $response);
287 | }
288 |
289 | public function testGetGroupMemberInfo()
290 | {
291 | $response = $this->QQ->getGroupMemberInfo($this->devGroupId, $this->devQQ);
292 | $this->assertInstanceOf(Response::class, $response);
293 | }
294 |
295 | public function testGetGroupMemberList()
296 | {
297 | $response = $this->QQ->getGroupMemberList($this->devGroupId);
298 | $this->assertInstanceOf(Response::class, $response);
299 | }
300 |
301 | public function testInviteFriendIntoGroup()
302 | {
303 | $response = $this->QQ->inviteFriendIntoGroup($this->devGroupId, $this->devQQ);
304 | $this->assertInstanceOf(Response::class, $response);
305 | }
306 |
307 | public function testGetCookies()
308 | {
309 | $response = $this->QQ->getCookies();
310 | $this->assertInstanceOf(Response::class, $response);
311 | }
312 |
313 | public function testGetBkn()
314 | {
315 | $response = $this->QQ->getBkn();
316 | $this->assertInstanceOf(Response::class, $response);
317 | }
318 |
319 | public function testGetClientKey()
320 | {
321 | $response = $this->QQ->getClientKey();
322 | $this->assertInstanceOf(Response::class, $response);
323 | }
324 |
325 | public function testGetCredentials()
326 | {
327 | $response = $this->QQ->getCredentials();
328 | $this->assertInstanceOf(Response::class, $response);
329 | }
330 | }
331 |
--------------------------------------------------------------------------------
/tests/GuzzleProtocolTest.php:
--------------------------------------------------------------------------------
1 | protocol = new GuzzleProtocol($this->url, $this->accessToken, $this->secret);
38 | $this->faker = Factory::create();
39 | }
40 |
41 | public function testSend()
42 | {
43 | $message = $this->faker->text;
44 | $response = $this->protocol->send(Url::send_private_msg, [
45 | 'user_id' => $this->devQQ,
46 | 'message' => $message,
47 | 'auto_escape' => false,
48 | ]);
49 |
50 | $this->assertInstanceOf(Response::class, $response);
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------