├── .gitignore ├── LICENSE ├── README.md ├── SDK ├── API.php ├── CQCode.php └── CoolQ.php ├── composer.json.example ├── config.ini.example ├── framework ├── DataStorage.php ├── Event │ ├── BaseEvent.php │ ├── EventFactory.php │ ├── FriendRequestEvent.php │ ├── GroupAdminChangeEvent.php │ ├── GroupDecreaseEvent.php │ ├── GroupEvent.php │ ├── GroupFileUploadEvent.php │ ├── GroupIncreaseEvent.php │ ├── GroupMemberChange.php │ ├── GroupMessageEvent.php │ ├── InvitedToGroupEvent.php │ ├── JoinGroupEvent.php │ ├── MessageEvent.php │ ├── MetaEvent.php │ ├── MetaHeartbeatEvent.php │ ├── MetaLifecycleEvent.php │ ├── NewFriendEvent.php │ ├── NoticeEvent.php │ ├── PrivateMessageEvent.php │ ├── RequestCommon.php │ ├── RequestEvent.php │ └── SenderInfo.php ├── KjBot.php ├── Message.php ├── Module.php ├── Plugin.php ├── helpers.php └── miscClass.php ├── modules.php.example ├── modules └── .gitignore ├── plugins.php.example └── public ├── index.php └── init.php /.gitignore: -------------------------------------------------------------------------------- 1 | composer.json 2 | composer.lock 3 | 4 | manifest/ 5 | modules/ 6 | storage/ 7 | vendor/ 8 | .repo/ 9 | config.ini 10 | plugins.php 11 | modules.php 12 | test.http 13 | test.php 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 kjBot-Dev 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kjBot Framework 2 | 3 | kjBot 开发框架 4 | -------------------------------------------------------------------------------- /SDK/API.php: -------------------------------------------------------------------------------- 1 | $value){ 9 | if($value === NULL) continue; 10 | $code.= (','.$key.'='.self::EncodeCQCode($value)); 11 | } 12 | $code.=']'; 13 | return $code; 14 | } 15 | 16 | public static function At($qq){ 17 | return self::CQ('at', ['qq' => $qq]); 18 | } 19 | 20 | public static function Face($id){ 21 | return self::CQ('face', ['id' => $id]); 22 | } 23 | 24 | public static function BFace($id){ 25 | return self::CQ('bface', ['id' => $id]); 26 | } 27 | 28 | public static function SFace($id){ 29 | return self::CQ('sface', ['id' => $id]); 30 | } 31 | 32 | public static function Emoji($id){ 33 | return self::CQ('emoji', ['id' => $id]); 34 | } 35 | 36 | public static function Image($file, $cache = 1, $timeout = 0){ 37 | return self::CQ('image', [ 38 | 'file' => $file, 39 | 'cache' => $cache, 40 | 'timeout' => $timeout, 41 | ]); 42 | } 43 | 44 | public static function Record($file, $magic = NULL, $cache = 1, $timeout = 0){ 45 | return self::CQ('record', [ 46 | 'file' => $file, 47 | 'magic' => $magic, 48 | 'cache' => $cache, 49 | 'timeout' => $timeout, 50 | ]); 51 | } 52 | 53 | public static function Rps($type = NULL){ 54 | return self::CQ('rps', ['type' => $type]); 55 | } 56 | 57 | public static function Dice($type = NULL){ 58 | return self::CQ('dice', ['type' => $type]); 59 | } 60 | 61 | public static function Shake(){ 62 | return self::CQ('shake'); 63 | } 64 | 65 | public static function Anonymous($ignore = NULL){ 66 | return self::CQ('anonymous', ['ignore' => $ignore]); 67 | } 68 | 69 | public static function Location($lat, $lon, $title, $content){ 70 | return self::CQ('location', [ 71 | 'lat' => $lat, 72 | 'lon' => $lon, 73 | 'title' => $title, 74 | 'content' => $content, 75 | ]); 76 | } 77 | 78 | public static function Music($type, $id, $style = NULL){ 79 | return self::CQ('music', [ 80 | 'type' => $type, 81 | 'id' => $id, 82 | 'style' => $style, 83 | ]); 84 | } 85 | 86 | public static function CustomMusic($url, $audio, $title, $content = NULL, $image = NULL){ 87 | return self::CQ('music', [ 88 | 'type' => 'custom', 89 | 'url' => $url, 90 | 'audio' => $audio, 91 | 'title' => $title, 92 | 'content' => $content, 93 | 'image' => $image, 94 | ]); 95 | } 96 | 97 | public static function Share($url, $title, $content = NULL, $image = NULL){ 98 | return self::CQ('share', [ 99 | 'url' => $url, 100 | 'title' => $title, 101 | 'content' => $content, 102 | 'image' => $image, 103 | ]); 104 | } 105 | 106 | public static function EncodeCQCode($str){ 107 | if($str === true) return 'true'; 108 | if($str === false) return 'false'; 109 | //if($str === NULL) return ?; //这种情况的处理待定 110 | return str_replace([ 111 | '&', 112 | '[', 113 | ']', 114 | ',', 115 | ], [ 116 | '&', 117 | '[', 118 | ']', 119 | ',', 120 | ], $str); 121 | } 122 | 123 | public static function DecodeCQCode($str){ 124 | return str_replace([ 125 | '&', 126 | '[', 127 | ']', 128 | ',', 129 | ],[ 130 | '&', 131 | '[', 132 | ']', 133 | ',', 134 | ], $str); 135 | } 136 | 137 | } 138 | 139 | ?> 140 | -------------------------------------------------------------------------------- /SDK/CoolQ.php: -------------------------------------------------------------------------------- 1 | host = $host; 11 | $this->token = $token; 12 | } 13 | 14 | public function sendPrivateMsg($user_id, $message, $auto_escape = false){ 15 | $api = API::send_private_msg; 16 | $param = [ 17 | 'user_id' => $user_id, 18 | 'message' => $message, 19 | 'auto_escape' => $auto_escape, 20 | 'is_raw' => $auto_escape, 21 | ]; 22 | return $this->query($api, $param); 23 | } 24 | 25 | public function sendPrivateMsgAsync($user_id, $message, $auto_escape = false){ 26 | $api = API::send_private_msg_async; 27 | $param = [ 28 | 'user_id' => $user_id, 29 | 'message' => $message, 30 | 'auto_escape' => $auto_escape, 31 | 'is_raw' => $auto_escape, 32 | ]; 33 | return $this->query($api, $param); 34 | } 35 | 36 | public function sendGroupMsg($group_id, $message, $auto_escape = false){ 37 | $api = API::send_group_msg; 38 | $param = [ 39 | 'group_id' => $group_id, 40 | 'message' => $message, 41 | 'auto_escape' => $auto_escape, 42 | 'is_raw' => $auto_escape, 43 | ]; 44 | return $this->query($api, $param); 45 | } 46 | 47 | public function sendGroupMsgAsync($group_id, $message, $auto_escape = false){ 48 | $api = API::send_group_msg_async; 49 | $param = [ 50 | 'group_id' => $group_id, 51 | 'message' => $message, 52 | 'auto_escape' => $auto_escape, 53 | 'is_raw' => $auto_escape, 54 | ]; 55 | return $this->query($api, $param); 56 | } 57 | 58 | public function sendDiscussMsg($discuss_id, $message, $auto_escape = false){ 59 | $api = API::send_discuss_msg; 60 | $param = [ 61 | 'discuss_id' => $discuss_id, 62 | 'message' => $message, 63 | 'auto_escape' => $auto_escape, 64 | 'is_raw' => $auto_escape, 65 | ]; 66 | return $this->query($api, $param); 67 | } 68 | 69 | public function sendMsg($message_type, $id, $message, $auto_escape = false){ 70 | $api = API::send_msg; 71 | $param = [ 72 | 'message_type' => $message_type, 73 | 'user_id' => $id, 74 | 'group_id' => $id, 75 | 'discuss_id' => $id, 76 | 'message' => $message, 77 | 'auto_escape' => $auto_escape, 78 | 'is_raw' => $auto_escape, 79 | ]; 80 | return $this->query($api, $param); 81 | } 82 | 83 | public function sendMsgAsync($message_type, $id, $message, $auto_escape = false){ 84 | $api = API::send_msg_async; 85 | $param = [ 86 | 'message_type' => $message_type, 87 | 'user_id' => $id, 88 | 'group_id' => $id, 89 | 'discuss_id' => $id, 90 | 'message' => $message, 91 | 'auto_escape' => $auto_escape, 92 | 'is_raw' => $auto_escape, 93 | ]; 94 | return $this->query($api, $param); 95 | } 96 | 97 | public function deleteMsg($message_id){ 98 | $api = API::delete_msg; 99 | $param = [ 100 | 'message_id' => $message_id, 101 | ]; 102 | return $this->query($api, $param); 103 | } 104 | 105 | public function sendLike($user_id, $times = 1){ 106 | $api = API::send_like; 107 | $param = [ 108 | 'user_id' => $user_id, 109 | 'times' => $times, 110 | ]; 111 | return $this->query($api, $param); 112 | } 113 | 114 | public function setGroupKick($group_id, $user_id, $reject_add_request = false){ 115 | $api = API::set_group_kick; 116 | $param = [ 117 | 'group_id' => $group_id, 118 | 'user_id' => $user_id, 119 | 'reject_add_request' => $reject_add_request, 120 | ]; 121 | return $this->query($api, $param); 122 | } 123 | 124 | public function setGroupBan($group_id, $user_id, $duration = 30 * 60){ 125 | $api = API::set_group_ban; 126 | $param = [ 127 | 'group_id' => $group_id, 128 | 'user_id' => $user_id, 129 | 'duration' => $duration, 130 | ]; 131 | return $this->query($api, $param); 132 | } 133 | 134 | public function setGroupAnonymousBan($group_id, $flag, $duration = 30 * 60){ 135 | $api = API::set_group_anonymous_ban; 136 | $param = [ 137 | 'group_id' => $group_id, 138 | 'flag' => $flag, 139 | 'duration' => $duration, 140 | ]; 141 | return $this->query($api, $param); 142 | } 143 | 144 | public function setGroupWholeBan($group_id, $enable = true){ 145 | $api = API::set_group_whole_ban; 146 | $param = [ 147 | 'group_id' => $group_id, 148 | 'enable' => $enable, 149 | ]; 150 | return $this->query($api, $param); 151 | } 152 | 153 | public function setGroupAdmin($group_id, $user_id, $enable = true){ 154 | $api = API::set_group_admin; 155 | $param = [ 156 | 'group_id' => $group_id, 157 | 'user_id' => $user_id, 158 | 'enable' => $enable, 159 | ]; 160 | return $this->query($api, $param); 161 | } 162 | 163 | public function setGroupAnonymous($group_id, $enable = true){ 164 | $api = API::set_group_anonymous; 165 | $param = [ 166 | 'group_id' => $group_id, 167 | 'enable' => $enable, 168 | ]; 169 | return $this->query($api, $param); 170 | } 171 | 172 | public function setGroupCard($group_id, $user_id, $card = null){ 173 | $api = API::set_group_card; 174 | $param = [ 175 | 'group_id' => $group_id, 176 | 'user_id' => $user_id, 177 | 'card' => $card, 178 | ]; 179 | return $this->query($api, $param); 180 | } 181 | 182 | public function setGroupLeave($group_id, $is_dismiss = false){ 183 | $api = API::set_group_leave; 184 | $param = [ 185 | 'group_id' => $group_id, 186 | 'is_dismiss' => $is_dismiss, 187 | ]; 188 | return $this->query($api, $param); 189 | } 190 | 191 | public function setGroupSpecialTitle($group_id, $user_id, $special_title = null, $duration = -1){ 192 | $api = API::set_group_special_title; 193 | $param = [ 194 | 'group_id' => $group_id, 195 | 'user_id' => $user_id, 196 | 'special_title' => $special_title, 197 | 'duration' => $duration, 198 | ]; 199 | return $this->query($api, $param); 200 | } 201 | 202 | public function setDiscussLeave($discuss_id){ 203 | $api = API::set_discuss_leave; 204 | $param = [ 205 | 'discuss_id' => $discuss_id, 206 | ]; 207 | return $this->query($api, $param); 208 | } 209 | 210 | public function setFriendAddRequest($flag, $approve = true, $remark = ''){ 211 | $api = API::set_friend_add_request; 212 | $param = [ 213 | 'flag' => $flag, 214 | 'approve' => $approve, 215 | 'remark' => $remark, 216 | ]; 217 | return $this->query($api, $param); 218 | } 219 | 220 | public function setGroupAddRequest($flag, $type, $approve = true, $reason = ''){ 221 | $api = API::set_group_add_request; 222 | $param = [ 223 | 'flag' => $flag, 224 | 'type' => $type, 225 | 'approve' => $approve, 226 | 'reason' => $reason, 227 | ]; 228 | return $this->query($api, $param); 229 | } 230 | 231 | public function getLoginInfo(){ 232 | $api = API::get_login_info; 233 | $param = []; 234 | return $this->query($api, $param); 235 | } 236 | 237 | public function getStrangerInfo($user_id, $no_cache = false){ 238 | $api = API::get_stranger_info; 239 | $param = [ 240 | 'user_id' => $user_id, 241 | 'no_cache' => $no_cache, 242 | ]; 243 | return $this->query($api, $param); 244 | } 245 | 246 | public function getGroupList(){ 247 | $api = API::get_group_list; 248 | $param = []; 249 | return $this->query($api, $param); 250 | } 251 | 252 | public function getGroupMemberInfo($group_id, $user_id, $no_cache = false){ 253 | $api = API::get_group_member_info; 254 | $param = [ 255 | 'group_id' => $group_id, 256 | 'user_id' => $user_id, 257 | 'no_cache' => $no_cache, 258 | ]; 259 | return $this->query($api, $param); 260 | } 261 | 262 | public function getGroupMemberList($group_id){ 263 | $api = API::get_group_member_list; 264 | $param = [ 265 | 'group_id' => $group_id, 266 | ]; 267 | return $this->query($api, $param); 268 | } 269 | 270 | public function getCookies(){ 271 | $api = API::get_cookies; 272 | $param = []; 273 | return $this->query($api, $param); 274 | } 275 | 276 | public function getCsrfToken(){ 277 | $api = API::get_csrf_token; 278 | $param = []; 279 | return $this->query($api, $param); 280 | } 281 | 282 | public function getCredentials(){ 283 | $api = API::get_credentials; 284 | $param = []; 285 | return $this->query($api, $param); 286 | } 287 | 288 | public function getRecord($file, $out_format){ 289 | $api = API::get_record; 290 | $param = [ 291 | 'file' => $file, 292 | 'out_format' => $out_format, 293 | ]; 294 | return $this->query($api, $param); 295 | } 296 | 297 | public function getImage($file) { 298 | $api = API::get_image; 299 | $param = [ 300 | 'file' => $file 301 | ]; 302 | return $this->query($api, $param); 303 | } 304 | 305 | public function canSendImage() { 306 | $api = API::can_send_image; 307 | $param = []; 308 | return $this->query($api, $param); 309 | } 310 | 311 | public function canSendRecord() { 312 | $api = API::can_send_record; 313 | $param = []; 314 | return $this->query($api, $param); 315 | } 316 | 317 | public function getStatus(){ 318 | $api = API::get_status; 319 | $param = []; 320 | return $this->query($api, $param); 321 | } 322 | 323 | public function getVersionInfo(){ 324 | $api = API::get_version_info; 325 | $param = []; 326 | return $this->query($api, $param); 327 | } 328 | 329 | public function setRestart($clean_log = false, $clean_cache = false, $clean_event = false){ 330 | $api = API::set_restart; 331 | $param = [ 332 | 'clean_log' => $clean_log, 333 | 'clean_cache' => $clean_cache, 334 | 'clean_event'=> $clean_event, 335 | ]; 336 | return $this->query($api, $param); 337 | } 338 | 339 | public function setRestartPlugin($delay = 0){ 340 | $api = API::set_restart_plugin; 341 | $param = [ 342 | 'delay' => $delay, 343 | ]; 344 | return $this->query($api, $param); 345 | } 346 | 347 | public function cleanDataDir($data_dir){ 348 | $api = API::clean_data_dir; 349 | $param = [ 350 | 'data_dir' => $data_dir, 351 | ]; 352 | return $this->query($api, $param); 353 | } 354 | 355 | public function cleanPluginLog(){ 356 | $api = API::clean_plugin_log; 357 | $param = []; 358 | return $this->query($api, $param); 359 | } 360 | 361 | public function _getFriendList($flat = false){ 362 | $api = API::_get_friend_list; 363 | $param = [ 364 | 'flat' => $flat, 365 | ]; 366 | return $this->query($api, $param); 367 | } 368 | 369 | public function _getGroupInfo($group_id){ 370 | $api = API::_get_group_info; 371 | $param = [ 372 | 'group_id' => $group_id, 373 | ]; 374 | return $this->query($api, $param); 375 | } 376 | 377 | public function _get_vip_info($user_id){ 378 | $api = API::_get_vip_info; 379 | $param = [ 380 | 'user_id' => $user_id, 381 | ]; 382 | return $this->query($api, $param); 383 | } 384 | 385 | public function __checkUpdate($automatic){ 386 | $api = API::__check_update; 387 | $param = [ 388 | 'automatic' => $automatic, 389 | ]; 390 | return $this->query($api, $param); 391 | } 392 | 393 | public function __handleQuickOperation($context, $operation){ 394 | $api = API::__handle_quick_operation; 395 | $param = [ 396 | 'context' => $context, 397 | 'operation' => $operation, 398 | ]; 399 | return $this->query($api, $param); 400 | } 401 | 402 | private function query($api, $param){ 403 | $queryStr = '?'; 404 | $param['access_token'] = $this->token; //追加access_token到参数表 405 | foreach($param as $key => $value){ 406 | $queryStr.= ($key.'='.urlencode(is_bool($value)?((int)$value):$value).'&'); 407 | } 408 | $result = json_decode(file_get_contents('http://'.$this->host.$api.$queryStr)); 409 | 410 | $responseCode = $this->parseHeaders($http_response_header)['reponse_code']; 411 | if($responseCode === 200){ 412 | switch($result->retcode){ 413 | case 0: 414 | return $result->data; 415 | case 1: 416 | return NULL; 417 | default: 418 | throw new \Exception("Query Failed", $result->retcode); 419 | } 420 | } 421 | throw new \Exception("Query Failed", $responseCode); 422 | } 423 | 424 | private function parseHeaders($headers){ 425 | $head = []; 426 | foreach($headers as $k => $v){ 427 | $t = explode(':', $v, 2); 428 | if(isset($t[1])){ 429 | $head[trim($t[0])] = trim($t[1]); 430 | }else{ 431 | $head[]= $v; 432 | if(preg_match("#HTTP/[0-9\.]+\s+([0-9]+)#",$v, $out)) 433 | $head['reponse_code'] = intval($out[1]); 434 | } 435 | } 436 | return $head; 437 | } 438 | 439 | } -------------------------------------------------------------------------------- /composer.json.example: -------------------------------------------------------------------------------- 1 | { 2 | "license": "MIT", 3 | "type": "project", 4 | "autoload": { 5 | "psr-4": { 6 | "kjBot\\SDK\\": "SDK/", 7 | "kjBot\\Framework\\": "framework/", 8 | "kjBotModule\\": "modules/" 9 | }, 10 | "files": ["framework/helpers.php"] 11 | }, 12 | "require": { 13 | "php": "^7.2", 14 | "ext-openssl": "*", 15 | "ext-sqlite3": "*" 16 | }, 17 | "require-dev": { 18 | }, 19 | "suggest": { 20 | }, 21 | "scripts": { 22 | }, 23 | "config": { 24 | "sort-packages": true, 25 | "optimize-autoloader": true 26 | } 27 | } -------------------------------------------------------------------------------- /config.ini.example: -------------------------------------------------------------------------------- 1 | API=127.0.0.1:5700 2 | token= 3 | master=919815238 4 | self_id=2839098896 5 | log_file="../storage/log" 6 | DEBUG=true -------------------------------------------------------------------------------- /framework/DataStorage.php: -------------------------------------------------------------------------------- 1 | postType = $obj->post_type; 21 | try{ 22 | $this->type = (new \ReflectionObject($obj))->getProperty($obj->post_type.'_type')->getValue($obj); 23 | }catch(\ReflectionException $e){ 24 | $this->type = NULL; 25 | d("Can't reflect {$obj->post_type}: ".\export($obj)); 26 | } 27 | $this->subType = $obj->sub_type??NULL; 28 | $this->time = $obj->time; 29 | @$this->userId = $obj->user_id; 30 | $this->selfId = $obj->self_id; 31 | $this->originalEvent = $obj; 32 | }else{ 33 | q("Can't create Event from ".$obj); 34 | } 35 | } 36 | 37 | //来自自己的事件被用于触发定时任务 38 | public function isSelfEvent(){ 39 | return $this->userId == $this->selfId; 40 | } 41 | 42 | public function getEvent(){ 43 | return $this->originalEvent; 44 | } 45 | 46 | //返回用户ID,注意不是事件ID 47 | public function getId(){ 48 | return $this->userId; 49 | } 50 | 51 | public function sendPrivate(?string $msg): Message{ 52 | return new Message($msg, $this->userId, TargetType::Private); 53 | } 54 | 55 | public function sendBack(?string $msg): Message{ 56 | if(@$this->groupId!==NULL) 57 | return new Message($msg, $this->groupId, TargetType::Group); 58 | else return $this->sendPrivate($msg); 59 | } 60 | 61 | public function sendTo(int $targetType, $target, $msg){ 62 | if (is_array($target)) { 63 | for ($i = 0; $i < count($target); $i++) { 64 | if ($targetType === TargetType::Private) { 65 | require_once('miscClass.php'); 66 | throw new kjBot\Framework\KjBotException('群发消息仅可用发送至群'); 67 | } 68 | $Queue[] = new Message($msg, $target[$i], $targetType); 69 | } 70 | return $Queue; 71 | } 72 | return new Message($msg, $target, $targetType); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /framework/Event/EventFactory.php: -------------------------------------------------------------------------------- 1 | post_type){ 8 | case 'message': 9 | switch($obj->message_type){ 10 | case 'private': 11 | return new PrivateMessageEvent($obj); 12 | case 'group': 13 | return new GroupMessageEvent($obj); 14 | case 'discuss': 15 | default: 16 | throw new \Exception("Won't support {$obj->message_event} event: ".\export($obj)); 17 | } 18 | case 'notice': 19 | switch($obj->notice_type){ 20 | case 'group_upload': 21 | return new GroupFileUploadEvent($obj); 22 | case 'group_admin': 23 | return new GroupAdminChangeEvent($obj); 24 | case 'group_decrease': 25 | return new GroupDecreaseEvent($obj); 26 | case 'group_increase': 27 | return new GroupIncreaseEvent($obj); 28 | case 'friend_add': 29 | return new NewFriendEvent($obj); 30 | default: 31 | throw new \Exception('Unknow notice event: '.\export($obj)); 32 | } 33 | case 'request': 34 | switch($obj->request_type){ 35 | case 'friend': 36 | return new FriendRequestEvent($obj); 37 | case 'group': 38 | switch($obj->sub_type){ 39 | case 'add': 40 | return new JoinGroupEvent($obj); 41 | case 'invite': 42 | return new InvitedToGroupEvent($obj); 43 | default: 44 | throw new \Exception('Unknown group request event: '.\export($obj)); 45 | } 46 | default: 47 | throw new \Exception('Unknown request event: '.\export($obj)); 48 | } 49 | case 'meta_event': 50 | switch($obj->meta_event_type){ 51 | case 'lifecycle': 52 | return new MetaLifecycleEvent($obj); 53 | case 'heartbeat': 54 | return new MetaHeartbeatEvent($obj); 55 | default: 56 | throw new \Exception('Unknown meta event: '.\export($obj)); 57 | } 58 | default: 59 | throw new \Exception("Event {$obj->post_type} undefined: ".\export($obj)); 60 | } 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /framework/Event/FriendRequestEvent.php: -------------------------------------------------------------------------------- 1 | comment = $obj->comment; 12 | $this->flag = $obj->flag; 13 | } 14 | 15 | public function accept(CoolQ $cq, string $remark = ''){ 16 | return $cq->setFriendAddRequest($this->flag, true, $remark); 17 | } 18 | 19 | public function deny(CoolQ $cq){ 20 | return $cq->setFriendAddRequest($this->flag, false); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /framework/Event/GroupAdminChangeEvent.php: -------------------------------------------------------------------------------- 1 | groupId = $obj->group_id; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /framework/Event/GroupDecreaseEvent.php: -------------------------------------------------------------------------------- 1 | groupId = $obj->group_id; 10 | $this->file = $obj->file; 11 | } 12 | } -------------------------------------------------------------------------------- /framework/Event/GroupIncreaseEvent.php: -------------------------------------------------------------------------------- 1 | groupId = $obj->group_id; 10 | $this->operatorId = $obj->operator_id; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /framework/Event/GroupMessageEvent.php: -------------------------------------------------------------------------------- 1 | groupId = $obj->group_id; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /framework/Event/InvitedToGroupEvent.php: -------------------------------------------------------------------------------- 1 | groupId = $obj->group_id; 11 | $this->comment = $obj->comment; 12 | $this->flag = $obj->flag; 13 | } 14 | 15 | public function accept(CoolQ $cq){ 16 | return $cq->setGroupAddRequest($this->flag, 'invite'); 17 | } 18 | 19 | public function deny(CoolQ $cq, string $reason){ 20 | return $cq->setGroupAddRequest($this->flag, 'invite', false, $reason); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /framework/Event/JoinGroupEvent.php: -------------------------------------------------------------------------------- 1 | groupId = $obj->group_id; 11 | $this->comment = $obj->comment; 12 | $this->flag = $obj->flag; 13 | } 14 | 15 | public function accept(CoolQ $cq){ 16 | return $cq->setGroupAddRequest($this->flag, 'add'); 17 | } 18 | 19 | public function deny(CoolQ $cq, string $reason = ''){ 20 | return $cq->setGroupAddRequest($this->flag, 'add', false, $reason = ''); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /framework/Event/MessageEvent.php: -------------------------------------------------------------------------------- 1 | post_type!=='message')throw new Exception(); 15 | parent::__construct($obj); 16 | $this->msgType = $obj->message_type; 17 | $this->subType = $obj->sub_type; 18 | $this->font = $obj->font; 19 | $this->msgId = $obj->message_id; 20 | $this->msg = $obj->message; 21 | $this->rMsg = $obj->raw_message; 22 | $this->senderInfo = new SenderInfo($obj->sender, ($this->msgType == 'group' && $this->subType == 'normal')); 23 | } 24 | 25 | public function __toString(){ 26 | return $this->msg; 27 | } 28 | 29 | public function setMsg($msg){ 30 | $this->msg = $msg; 31 | return $this; 32 | } 33 | 34 | public function getMsg(){ 35 | return $this->msg; 36 | } 37 | 38 | public function getRawMsg(){ 39 | return $this->rMsg; 40 | } 41 | 42 | public function getSenderInfo(){ 43 | return $this->senderInfo; 44 | } 45 | 46 | public function fromGroup($id = NULL){ 47 | if($id !== NULL){ 48 | return $this->groupId == $id; 49 | }else return $this->msgType === 'group'; 50 | } 51 | } -------------------------------------------------------------------------------- /framework/Event/MetaEvent.php: -------------------------------------------------------------------------------- 1 | status = $obj->status; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /framework/Event/MetaLifecycleEvent.php: -------------------------------------------------------------------------------- 1 | flag; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /framework/Event/RequestEvent.php: -------------------------------------------------------------------------------- 1 | nickname = $obj->nickname; 20 | $this->age = $obj->age; 21 | $this->sex = $obj->sex; 22 | $this->userId = $obj->user_id; 23 | 24 | $this->isGroupSender = $isGroup; 25 | 26 | if($isGroup){ 27 | $this->card = $obj->card; 28 | $this->level = $obj->level; 29 | $this->role = $obj->role; 30 | $this->title = $obj->title; 31 | $this->area = $obj->area; 32 | } 33 | } 34 | 35 | public function getAge(){ 36 | return $this->age; 37 | } 38 | 39 | public function getArea(){ 40 | return $this->area; 41 | } 42 | 43 | public function getID(){ 44 | return $this->userId; 45 | } 46 | 47 | public function getSex(){ 48 | return $this->sex; 49 | } 50 | 51 | 52 | } -------------------------------------------------------------------------------- /framework/KjBot.php: -------------------------------------------------------------------------------- 1 | cq = $coolQ; 14 | $this->selfId = $id; 15 | } 16 | 17 | public function getCoolQ(){ 18 | return $this->cq; 19 | } 20 | 21 | /** 22 | * 添加消息 23 | * 24 | * @param array||Message $msg 25 | * @return void 26 | */ 27 | public function addMessage($msg){ 28 | if($msg instanceof Message){ 29 | $this->messageQueue[]= $msg; 30 | }else if(is_array($msg)){ 31 | $this->messageQueue = array_merge($this->messageQueue, $msg); 32 | }else if($msg === NULL){ 33 | return; 34 | }else{ 35 | throw new \Exception("Can't add message: ".\export($msg)); 36 | } 37 | } 38 | 39 | public function postMessage(){ 40 | foreach($this->messageQueue as $message){ 41 | if($message instanceof Message){ 42 | $message->send($this->cq); 43 | // 随机200 - 5000 毫秒间隔 44 | usleep(mt_rand(200000,5000000)); 45 | } 46 | } 47 | } 48 | 49 | public function &getMessageQueue(){ 50 | return $this->messageQueue; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /framework/Message.php: -------------------------------------------------------------------------------- 1 | msg = $message; 13 | $this->id = $target_id; 14 | $this->type = $target_type; 15 | $this->escape = $auto_escape; 16 | $this->async = $async; 17 | } 18 | 19 | public function __toString(){ 20 | return "[To:{$this->id}".($this->type?'*':'')."] {$this->msg}"; //*为私聊 21 | } 22 | 23 | function send($cq){ 24 | if($this->type === TargetType::Group){ 25 | if($this->async){ 26 | $cq->sendGroupMsgAsync($this->id, $this->msg, $this->escape); 27 | }else{ 28 | $cq->sendGroupMsg($this->id, $this->msg, $this->escape); 29 | } 30 | }else if($this->type === TargetType::Private){ 31 | if($this->async){ 32 | $cq->sendPrivateMsgAsync($this->id, $this->msg, $this->escape); 33 | }else{ 34 | $cq->sendPrivateMsg($this->id, $this->msg, $this->escape); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /framework/Module.php: -------------------------------------------------------------------------------- 1 | needCQ; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /framework/Plugin.php: -------------------------------------------------------------------------------- 1 | postType; 50 | $methods[2] = $methods[1].(is_null($event->type)?'':('_'.$event->type)); 51 | $methods[3] = $methods[2].(is_null($event->subType)?'':('_'.$event->subType)); 52 | return $methods; 53 | } 54 | 55 | /** 56 | * @param string $type [description] 57 | * @param string $msg [description] 58 | * @return [type] [description] 59 | */ 60 | function _log(string $type, ?string $msg){ 61 | global $Config; 62 | $log = '<'.date('Y-m-d H:i:s').'>['.$type.'] '.$msg."\n"; 63 | if($Config['log_file'] !== NULL && $Config['log_file']!=='')file_put_contents($Config['log_file'], $log, FILE_APPEND); 64 | } 65 | 66 | function d(string $msg){ 67 | global $Config; 68 | if($Config['DEBUG'])_log('DEBUG', $msg); 69 | } 70 | 71 | /** 72 | * 通知管理员(机器人主人) 73 | * @param string $msg 通知信息 74 | * @return [type] [description] 75 | */ 76 | function notifyMaster(string $msg): Message{ 77 | global $Config; 78 | return new Message($msg, $Config['master'], TargetType::Private); 79 | } 80 | 81 | /** 82 | * 解析QQ 83 | * @param [type] $str 被解析的字符串 84 | * @return [type] [description] 85 | */ 86 | function parseQQ($str){ 87 | if(preg_match('/\d+/', $str, $match) && $match[0] == $str){ 88 | return $str; 89 | } 90 | if(preg_match('/\[CQ:at,qq=(\d+)\]/', $str, $QQ)){ 91 | return $QQ[1]; 92 | } 93 | return NULL; 94 | } 95 | 96 | /** 97 | * 解析命令 98 | * @param string $str 命令字符串 99 | * @return mixed array|bool 解析结果数组 失败返回false 100 | */ 101 | function parseCommand(string $str){ 102 | // 正则表达式 103 | $regEx = '#(?:(?[\'"])?(?.+?)?(?:(?)|(?[^\'"\s]+))#'; 104 | // 匹配所有 105 | if(!preg_match_all($regEx, $str, $exp_list)) return false; 106 | // 遍历所有结果 107 | $cmd = array(); 108 | foreach ($exp_list['s'] as $id => $s) { 109 | // 判断匹配到的值 110 | $cmd[] = empty($s) ? $exp_list['u'][$id] : $exp_list['v'][$id]; 111 | } 112 | return $cmd; 113 | } 114 | 115 | function removeCQCode($text){ 116 | return preg_replace('/\[CQ:\S+\]/', '', $text); 117 | } 118 | 119 | /** 120 | * 发送图片 121 | * @param string $str 图片(字符串形式) 122 | * @return string 图片对应的 base64 格式 CQ码 123 | */ 124 | function sendImg($str):string{ 125 | return kjBot\SDK\CQCode::Image('base64://'.base64_encode($str)); 126 | } 127 | 128 | /** 129 | * 发送录音 130 | * @param string $str 录音(字符串形式) 131 | * @return string 录音对应的 base64 格式 CQ码 132 | */ 133 | function sendRec($str):string{ 134 | return kjBot\SDK\CQCode::Record('base64://'.base64_encode($str)); 135 | } 136 | 137 | function imageFont($file = 1, $size = 12, $color = '#000000', $align = 'left', $valign = 'buttom', $angle = 0){ 138 | return function($font) use ($file, $size, $color, $align, $valign, $angle){ 139 | $font->file($file); 140 | $font->size($size); 141 | $font->color($color); 142 | $font->align($align); 143 | $font->valign($valign); 144 | $font->angle($angle); 145 | }; 146 | } 147 | 148 | function parseHeaders($headers){ 149 | $head = []; 150 | foreach($headers as $k => $v){ 151 | $t = explode(':', $v, 2); 152 | if(isset($t[1])){ 153 | $head[trim($t[0])] = trim($t[1]); 154 | }else{ 155 | $head[]= $v; 156 | if(preg_match("#HTTP/[0-9\.]+\s+([0-9]+)#",$v, $out)) 157 | $head['reponse_code'] = intval($out[1]); 158 | } 159 | } 160 | return $head; 161 | } 162 | 163 | function removeEmoji($text){ return preg_replace('/[\x{1F3F4}](?:\x{E0067}\x{E0062}\x{E0077}\x{E006C}\x{E0073}\x{E007F})|[\x{1F3F4}](?:\x{E0067}\x{E0062}\x{E0073}\x{E0063}\x{E0074}\x{E007F})|[\x{1F3F4}](?:\x{E0067}\x{E0062}\x{E0065}\x{E006E}\x{E0067}\x{E007F})|[\x{1F3F4}](?:\x{200D}\x{2620}\x{FE0F})|[\x{1F3F3}](?:\x{FE0F}\x{200D}\x{1F308})|[\x{0023}\x{002A}\x{0030}\x{0031}\x{0032}\x{0033}\x{0034}\x{0035}\x{0036}\x{0037}\x{0038}\x{0039}](?:\x{FE0F}\x{20E3})|[\x{1F441}](?:\x{FE0F}\x{200D}\x{1F5E8}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F467}\x{200D}\x{1F467})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F467}\x{200D}\x{1F466})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F467})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F466}\x{200D}\x{1F466})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F466})|[\x{1F468}](?:\x{200D}\x{1F468}\x{200D}\x{1F467}\x{200D}\x{1F467})|[\x{1F468}](?:\x{200D}\x{1F468}\x{200D}\x{1F466}\x{200D}\x{1F466})|[\x{1F468}](?:\x{200D}\x{1F468}\x{200D}\x{1F467}\x{200D}\x{1F466})|[\x{1F468}](?:\x{200D}\x{1F468}\x{200D}\x{1F467})|[\x{1F468}](?:\x{200D}\x{1F468}\x{200D}\x{1F466})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F469}\x{200D}\x{1F467}\x{200D}\x{1F467})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F469}\x{200D}\x{1F466}\x{200D}\x{1F466})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F469}\x{200D}\x{1F467}\x{200D}\x{1F466})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F469}\x{200D}\x{1F467})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F469}\x{200D}\x{1F466})|[\x{1F469}](?:\x{200D}\x{2764}\x{FE0F}\x{200D}\x{1F469})|[\x{1F469}\x{1F468}](?:\x{200D}\x{2764}\x{FE0F}\x{200D}\x{1F468})|[\x{1F469}](?:\x{200D}\x{2764}\x{FE0F}\x{200D}\x{1F48B}\x{200D}\x{1F469})|[\x{1F469}\x{1F468}](?:\x{200D}\x{2764}\x{FE0F}\x{200D}\x{1F48B}\x{200D}\x{1F468})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{1F9B3})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{1F9B3})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{1F9B3})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{1F9B3})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{1F9B3})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F9B3})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{1F9B2})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{1F9B2})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{1F9B2})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{1F9B2})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{1F9B2})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F9B2})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{1F9B1})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{1F9B1})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{1F9B1})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{1F9B1})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{1F9B1})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F9B1})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{1F9B0})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{1F9B0})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{1F9B0})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{1F9B0})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{1F9B0})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F9B0})|[\x{1F575}\x{1F3CC}\x{26F9}\x{1F3CB}](?:\x{FE0F}\x{200D}\x{2640}\x{FE0F})|[\x{1F575}\x{1F3CC}\x{26F9}\x{1F3CB}](?:\x{FE0F}\x{200D}\x{2642}\x{FE0F})|[\x{1F46E}\x{1F575}\x{1F482}\x{1F477}\x{1F473}\x{1F471}\x{1F9D9}\x{1F9DA}\x{1F9DB}\x{1F9DC}\x{1F9DD}\x{1F64D}\x{1F64E}\x{1F645}\x{1F646}\x{1F481}\x{1F64B}\x{1F647}\x{1F926}\x{1F937}\x{1F486}\x{1F487}\x{1F6B6}\x{1F3C3}\x{1F9D6}\x{1F9D7}\x{1F9D8}\x{1F3CC}\x{1F3C4}\x{1F6A3}\x{1F3CA}\x{26F9}\x{1F3CB}\x{1F6B4}\x{1F6B5}\x{1F938}\x{1F93D}\x{1F93E}\x{1F939}](?:\x{1F3FF}\x{200D}\x{2640}\x{FE0F})|[\x{1F46E}\x{1F575}\x{1F482}\x{1F477}\x{1F473}\x{1F471}\x{1F9D9}\x{1F9DA}\x{1F9DB}\x{1F9DC}\x{1F9DD}\x{1F64D}\x{1F64E}\x{1F645}\x{1F646}\x{1F481}\x{1F64B}\x{1F647}\x{1F926}\x{1F937}\x{1F486}\x{1F487}\x{1F6B6}\x{1F3C3}\x{1F9D6}\x{1F9D7}\x{1F9D8}\x{1F3CC}\x{1F3C4}\x{1F6A3}\x{1F3CA}\x{26F9}\x{1F3CB}\x{1F6B4}\x{1F6B5}\x{1F938}\x{1F93D}\x{1F93E}\x{1F939}](?:\x{1F3FE}\x{200D}\x{2640}\x{FE0F})|[\x{1F46E}\x{1F575}\x{1F482}\x{1F477}\x{1F473}\x{1F471}\x{1F9D9}\x{1F9DA}\x{1F9DB}\x{1F9DC}\x{1F9DD}\x{1F64D}\x{1F64E}\x{1F645}\x{1F646}\x{1F481}\x{1F64B}\x{1F647}\x{1F926}\x{1F937}\x{1F486}\x{1F487}\x{1F6B6}\x{1F3C3}\x{1F9D6}\x{1F9D7}\x{1F9D8}\x{1F3CC}\x{1F3C4}\x{1F6A3}\x{1F3CA}\x{26F9}\x{1F3CB}\x{1F6B4}\x{1F6B5}\x{1F938}\x{1F93D}\x{1F93E}\x{1F939}](?:\x{1F3FD}\x{200D}\x{2640}\x{FE0F})|[\x{1F46E}\x{1F575}\x{1F482}\x{1F477}\x{1F473}\x{1F471}\x{1F9D9}\x{1F9DA}\x{1F9DB}\x{1F9DC}\x{1F9DD}\x{1F64D}\x{1F64E}\x{1F645}\x{1F646}\x{1F481}\x{1F64B}\x{1F647}\x{1F926}\x{1F937}\x{1F486}\x{1F487}\x{1F6B6}\x{1F3C3}\x{1F9D6}\x{1F9D7}\x{1F9D8}\x{1F3CC}\x{1F3C4}\x{1F6A3}\x{1F3CA}\x{26F9}\x{1F3CB}\x{1F6B4}\x{1F6B5}\x{1F938}\x{1F93D}\x{1F93E}\x{1F939}](?:\x{1F3FC}\x{200D}\x{2640}\x{FE0F})|[\x{1F46E}\x{1F575}\x{1F482}\x{1F477}\x{1F473}\x{1F471}\x{1F9D9}\x{1F9DA}\x{1F9DB}\x{1F9DC}\x{1F9DD}\x{1F64D}\x{1F64E}\x{1F645}\x{1F646}\x{1F481}\x{1F64B}\x{1F647}\x{1F926}\x{1F937}\x{1F486}\x{1F487}\x{1F6B6}\x{1F3C3}\x{1F9D6}\x{1F9D7}\x{1F9D8}\x{1F3CC}\x{1F3C4}\x{1F6A3}\x{1F3CA}\x{26F9}\x{1F3CB}\x{1F6B4}\x{1F6B5}\x{1F938}\x{1F93D}\x{1F93E}\x{1F939}](?:\x{1F3FB}\x{200D}\x{2640}\x{FE0F})|[\x{1F46E}\x{1F9B8}\x{1F9B9}\x{1F482}\x{1F477}\x{1F473}\x{1F471}\x{1F9D9}\x{1F9DA}\x{1F9DB}\x{1F9DC}\x{1F9DD}\x{1F9DE}\x{1F9DF}\x{1F64D}\x{1F64E}\x{1F645}\x{1F646}\x{1F481}\x{1F64B}\x{1F647}\x{1F926}\x{1F937}\x{1F486}\x{1F487}\x{1F6B6}\x{1F3C3}\x{1F46F}\x{1F9D6}\x{1F9D7}\x{1F9D8}\x{1F3C4}\x{1F6A3}\x{1F3CA}\x{1F6B4}\x{1F6B5}\x{1F938}\x{1F93C}\x{1F93D}\x{1F93E}\x{1F939}](?:\x{200D}\x{2640}\x{FE0F})|[\x{1F46E}\x{1F575}\x{1F482}\x{1F477}\x{1F473}\x{1F471}\x{1F9D9}\x{1F9DA}\x{1F9DB}\x{1F9DC}\x{1F9DD}\x{1F64D}\x{1F64E}\x{1F645}\x{1F646}\x{1F481}\x{1F64B}\x{1F647}\x{1F926}\x{1F937}\x{1F486}\x{1F487}\x{1F6B6}\x{1F3C3}\x{1F9D6}\x{1F9D7}\x{1F9D8}\x{1F3CC}\x{1F3C4}\x{1F6A3}\x{1F3CA}\x{26F9}\x{1F3CB}\x{1F6B4}\x{1F6B5}\x{1F938}\x{1F93D}\x{1F93E}\x{1F939}](?:\x{1F3FF}\x{200D}\x{2642}\x{FE0F})|[\x{1F46E}\x{1F575}\x{1F482}\x{1F477}\x{1F473}\x{1F471}\x{1F9D9}\x{1F9DA}\x{1F9DB}\x{1F9DC}\x{1F9DD}\x{1F64D}\x{1F64E}\x{1F645}\x{1F646}\x{1F481}\x{1F64B}\x{1F647}\x{1F926}\x{1F937}\x{1F486}\x{1F487}\x{1F6B6}\x{1F3C3}\x{1F9D6}\x{1F9D7}\x{1F9D8}\x{1F3CC}\x{1F3C4}\x{1F6A3}\x{1F3CA}\x{26F9}\x{1F3CB}\x{1F6B4}\x{1F6B5}\x{1F938}\x{1F93D}\x{1F93E}\x{1F939}](?:\x{1F3FE}\x{200D}\x{2642}\x{FE0F})|[\x{1F46E}\x{1F575}\x{1F482}\x{1F477}\x{1F473}\x{1F471}\x{1F9D9}\x{1F9DA}\x{1F9DB}\x{1F9DC}\x{1F9DD}\x{1F64D}\x{1F64E}\x{1F645}\x{1F646}\x{1F481}\x{1F64B}\x{1F647}\x{1F926}\x{1F937}\x{1F486}\x{1F487}\x{1F6B6}\x{1F3C3}\x{1F9D6}\x{1F9D7}\x{1F9D8}\x{1F3CC}\x{1F3C4}\x{1F6A3}\x{1F3CA}\x{26F9}\x{1F3CB}\x{1F6B4}\x{1F6B5}\x{1F938}\x{1F93D}\x{1F93E}\x{1F939}](?:\x{1F3FD}\x{200D}\x{2642}\x{FE0F})|[\x{1F46E}\x{1F575}\x{1F482}\x{1F477}\x{1F473}\x{1F471}\x{1F9D9}\x{1F9DA}\x{1F9DB}\x{1F9DC}\x{1F9DD}\x{1F64D}\x{1F64E}\x{1F645}\x{1F646}\x{1F481}\x{1F64B}\x{1F647}\x{1F926}\x{1F937}\x{1F486}\x{1F487}\x{1F6B6}\x{1F3C3}\x{1F9D6}\x{1F9D7}\x{1F9D8}\x{1F3CC}\x{1F3C4}\x{1F6A3}\x{1F3CA}\x{26F9}\x{1F3CB}\x{1F6B4}\x{1F6B5}\x{1F938}\x{1F93D}\x{1F93E}\x{1F939}](?:\x{1F3FC}\x{200D}\x{2642}\x{FE0F})|[\x{1F46E}\x{1F575}\x{1F482}\x{1F477}\x{1F473}\x{1F471}\x{1F9D9}\x{1F9DA}\x{1F9DB}\x{1F9DC}\x{1F9DD}\x{1F64D}\x{1F64E}\x{1F645}\x{1F646}\x{1F481}\x{1F64B}\x{1F647}\x{1F926}\x{1F937}\x{1F486}\x{1F487}\x{1F6B6}\x{1F3C3}\x{1F9D6}\x{1F9D7}\x{1F9D8}\x{1F3CC}\x{1F3C4}\x{1F6A3}\x{1F3CA}\x{26F9}\x{1F3CB}\x{1F6B4}\x{1F6B5}\x{1F938}\x{1F93D}\x{1F93E}\x{1F939}](?:\x{1F3FB}\x{200D}\x{2642}\x{FE0F})|[\x{1F46E}\x{1F9B8}\x{1F9B9}\x{1F482}\x{1F477}\x{1F473}\x{1F471}\x{1F9D9}\x{1F9DA}\x{1F9DB}\x{1F9DC}\x{1F9DD}\x{1F9DE}\x{1F9DF}\x{1F64D}\x{1F64E}\x{1F645}\x{1F646}\x{1F481}\x{1F64B}\x{1F647}\x{1F926}\x{1F937}\x{1F486}\x{1F487}\x{1F6B6}\x{1F3C3}\x{1F46F}\x{1F9D6}\x{1F9D7}\x{1F9D8}\x{1F3C4}\x{1F6A3}\x{1F3CA}\x{1F6B4}\x{1F6B5}\x{1F938}\x{1F93C}\x{1F93D}\x{1F93E}\x{1F939}](?:\x{200D}\x{2642}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{1F692})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{1F692})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{1F692})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{1F692})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{1F692})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F692})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{1F680})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{1F680})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{1F680})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{1F680})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{1F680})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F680})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{2708}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{2708}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{2708}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{2708}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{2708}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{200D}\x{2708}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{1F3A8})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{1F3A8})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{1F3A8})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{1F3A8})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{1F3A8})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F3A8})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{1F3A4})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{1F3A4})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{1F3A4})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{1F3A4})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{1F3A4})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F3A4})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{1F4BB})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{1F4BB})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{1F4BB})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{1F4BB})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{1F4BB})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F4BB})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{1F52C})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{1F52C})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{1F52C})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{1F52C})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{1F52C})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F52C})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{1F4BC})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{1F4BC})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{1F4BC})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{1F4BC})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{1F4BC})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F4BC})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{1F3ED})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{1F3ED})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{1F3ED})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{1F3ED})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{1F3ED})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F3ED})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{1F527})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{1F527})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{1F527})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{1F527})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{1F527})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F527})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{1F373})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{1F373})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{1F373})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{1F373})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{1F373})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F373})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{1F33E})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{1F33E})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{1F33E})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{1F33E})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{1F33E})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F33E})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{2696}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{2696}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{2696}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{2696}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{2696}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{200D}\x{2696}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{1F3EB})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{1F3EB})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{1F3EB})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{1F3EB})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{1F3EB})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F3EB})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{1F393})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{1F393})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{1F393})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{1F393})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{1F393})|[\x{1F468}\x{1F469}](?:\x{200D}\x{1F393})|[\x{1F468}\x{1F469}](?:\x{1F3FF}\x{200D}\x{2695}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{1F3FE}\x{200D}\x{2695}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{1F3FD}\x{200D}\x{2695}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{1F3FC}\x{200D}\x{2695}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{1F3FB}\x{200D}\x{2695}\x{FE0F})|[\x{1F468}\x{1F469}](?:\x{200D}\x{2695}\x{FE0F})|[\x{1F476}\x{1F9D2}\x{1F466}\x{1F467}\x{1F9D1}\x{1F468}\x{1F469}\x{1F9D3}\x{1F474}\x{1F475}\x{1F46E}\x{1F575}\x{1F482}\x{1F477}\x{1F934}\x{1F478}\x{1F473}\x{1F472}\x{1F9D5}\x{1F9D4}\x{1F471}\x{1F935}\x{1F470}\x{1F930}\x{1F931}\x{1F47C}\x{1F385}\x{1F936}\x{1F9D9}\x{1F9DA}\x{1F9DB}\x{1F9DC}\x{1F9DD}\x{1F64D}\x{1F64E}\x{1F645}\x{1F646}\x{1F481}\x{1F64B}\x{1F647}\x{1F926}\x{1F937}\x{1F486}\x{1F487}\x{1F6B6}\x{1F3C3}\x{1F483}\x{1F57A}\x{1F9D6}\x{1F9D7}\x{1F9D8}\x{1F6C0}\x{1F6CC}\x{1F574}\x{1F3C7}\x{1F3C2}\x{1F3CC}\x{1F3C4}\x{1F6A3}\x{1F3CA}\x{26F9}\x{1F3CB}\x{1F6B4}\x{1F6B5}\x{1F938}\x{1F93D}\x{1F93E}\x{1F939}\x{1F933}\x{1F4AA}\x{1F9B5}\x{1F9B6}\x{1F448}\x{1F449}\x{261D}\x{1F446}\x{1F595}\x{1F447}\x{270C}\x{1F91E}\x{1F596}\x{1F918}\x{1F919}\x{1F590}\x{270B}\x{1F44C}\x{1F44D}\x{1F44E}\x{270A}\x{1F44A}\x{1F91B}\x{1F91C}\x{1F91A}\x{1F44B}\x{1F91F}\x{270D}\x{1F44F}\x{1F450}\x{1F64C}\x{1F932}\x{1F64F}\x{1F485}\x{1F442}\x{1F443}](?:\x{1F3FF})|[\x{1F476}\x{1F9D2}\x{1F466}\x{1F467}\x{1F9D1}\x{1F468}\x{1F469}\x{1F9D3}\x{1F474}\x{1F475}\x{1F46E}\x{1F575}\x{1F482}\x{1F477}\x{1F934}\x{1F478}\x{1F473}\x{1F472}\x{1F9D5}\x{1F9D4}\x{1F471}\x{1F935}\x{1F470}\x{1F930}\x{1F931}\x{1F47C}\x{1F385}\x{1F936}\x{1F9D9}\x{1F9DA}\x{1F9DB}\x{1F9DC}\x{1F9DD}\x{1F64D}\x{1F64E}\x{1F645}\x{1F646}\x{1F481}\x{1F64B}\x{1F647}\x{1F926}\x{1F937}\x{1F486}\x{1F487}\x{1F6B6}\x{1F3C3}\x{1F483}\x{1F57A}\x{1F9D6}\x{1F9D7}\x{1F9D8}\x{1F6C0}\x{1F6CC}\x{1F574}\x{1F3C7}\x{1F3C2}\x{1F3CC}\x{1F3C4}\x{1F6A3}\x{1F3CA}\x{26F9}\x{1F3CB}\x{1F6B4}\x{1F6B5}\x{1F938}\x{1F93D}\x{1F93E}\x{1F939}\x{1F933}\x{1F4AA}\x{1F9B5}\x{1F9B6}\x{1F448}\x{1F449}\x{261D}\x{1F446}\x{1F595}\x{1F447}\x{270C}\x{1F91E}\x{1F596}\x{1F918}\x{1F919}\x{1F590}\x{270B}\x{1F44C}\x{1F44D}\x{1F44E}\x{270A}\x{1F44A}\x{1F91B}\x{1F91C}\x{1F91A}\x{1F44B}\x{1F91F}\x{270D}\x{1F44F}\x{1F450}\x{1F64C}\x{1F932}\x{1F64F}\x{1F485}\x{1F442}\x{1F443}](?:\x{1F3FE})|[\x{1F476}\x{1F9D2}\x{1F466}\x{1F467}\x{1F9D1}\x{1F468}\x{1F469}\x{1F9D3}\x{1F474}\x{1F475}\x{1F46E}\x{1F575}\x{1F482}\x{1F477}\x{1F934}\x{1F478}\x{1F473}\x{1F472}\x{1F9D5}\x{1F9D4}\x{1F471}\x{1F935}\x{1F470}\x{1F930}\x{1F931}\x{1F47C}\x{1F385}\x{1F936}\x{1F9D9}\x{1F9DA}\x{1F9DB}\x{1F9DC}\x{1F9DD}\x{1F64D}\x{1F64E}\x{1F645}\x{1F646}\x{1F481}\x{1F64B}\x{1F647}\x{1F926}\x{1F937}\x{1F486}\x{1F487}\x{1F6B6}\x{1F3C3}\x{1F483}\x{1F57A}\x{1F9D6}\x{1F9D7}\x{1F9D8}\x{1F6C0}\x{1F6CC}\x{1F574}\x{1F3C7}\x{1F3C2}\x{1F3CC}\x{1F3C4}\x{1F6A3}\x{1F3CA}\x{26F9}\x{1F3CB}\x{1F6B4}\x{1F6B5}\x{1F938}\x{1F93D}\x{1F93E}\x{1F939}\x{1F933}\x{1F4AA}\x{1F9B5}\x{1F9B6}\x{1F448}\x{1F449}\x{261D}\x{1F446}\x{1F595}\x{1F447}\x{270C}\x{1F91E}\x{1F596}\x{1F918}\x{1F919}\x{1F590}\x{270B}\x{1F44C}\x{1F44D}\x{1F44E}\x{270A}\x{1F44A}\x{1F91B}\x{1F91C}\x{1F91A}\x{1F44B}\x{1F91F}\x{270D}\x{1F44F}\x{1F450}\x{1F64C}\x{1F932}\x{1F64F}\x{1F485}\x{1F442}\x{1F443}](?:\x{1F3FD})|[\x{1F476}\x{1F9D2}\x{1F466}\x{1F467}\x{1F9D1}\x{1F468}\x{1F469}\x{1F9D3}\x{1F474}\x{1F475}\x{1F46E}\x{1F575}\x{1F482}\x{1F477}\x{1F934}\x{1F478}\x{1F473}\x{1F472}\x{1F9D5}\x{1F9D4}\x{1F471}\x{1F935}\x{1F470}\x{1F930}\x{1F931}\x{1F47C}\x{1F385}\x{1F936}\x{1F9D9}\x{1F9DA}\x{1F9DB}\x{1F9DC}\x{1F9DD}\x{1F64D}\x{1F64E}\x{1F645}\x{1F646}\x{1F481}\x{1F64B}\x{1F647}\x{1F926}\x{1F937}\x{1F486}\x{1F487}\x{1F6B6}\x{1F3C3}\x{1F483}\x{1F57A}\x{1F9D6}\x{1F9D7}\x{1F9D8}\x{1F6C0}\x{1F6CC}\x{1F574}\x{1F3C7}\x{1F3C2}\x{1F3CC}\x{1F3C4}\x{1F6A3}\x{1F3CA}\x{26F9}\x{1F3CB}\x{1F6B4}\x{1F6B5}\x{1F938}\x{1F93D}\x{1F93E}\x{1F939}\x{1F933}\x{1F4AA}\x{1F9B5}\x{1F9B6}\x{1F448}\x{1F449}\x{261D}\x{1F446}\x{1F595}\x{1F447}\x{270C}\x{1F91E}\x{1F596}\x{1F918}\x{1F919}\x{1F590}\x{270B}\x{1F44C}\x{1F44D}\x{1F44E}\x{270A}\x{1F44A}\x{1F91B}\x{1F91C}\x{1F91A}\x{1F44B}\x{1F91F}\x{270D}\x{1F44F}\x{1F450}\x{1F64C}\x{1F932}\x{1F64F}\x{1F485}\x{1F442}\x{1F443}](?:\x{1F3FC})|[\x{1F476}\x{1F9D2}\x{1F466}\x{1F467}\x{1F9D1}\x{1F468}\x{1F469}\x{1F9D3}\x{1F474}\x{1F475}\x{1F46E}\x{1F575}\x{1F482}\x{1F477}\x{1F934}\x{1F478}\x{1F473}\x{1F472}\x{1F9D5}\x{1F9D4}\x{1F471}\x{1F935}\x{1F470}\x{1F930}\x{1F931}\x{1F47C}\x{1F385}\x{1F936}\x{1F9D9}\x{1F9DA}\x{1F9DB}\x{1F9DC}\x{1F9DD}\x{1F64D}\x{1F64E}\x{1F645}\x{1F646}\x{1F481}\x{1F64B}\x{1F647}\x{1F926}\x{1F937}\x{1F486}\x{1F487}\x{1F6B6}\x{1F3C3}\x{1F483}\x{1F57A}\x{1F9D6}\x{1F9D7}\x{1F9D8}\x{1F6C0}\x{1F6CC}\x{1F574}\x{1F3C7}\x{1F3C2}\x{1F3CC}\x{1F3C4}\x{1F6A3}\x{1F3CA}\x{26F9}\x{1F3CB}\x{1F6B4}\x{1F6B5}\x{1F938}\x{1F93D}\x{1F93E}\x{1F939}\x{1F933}\x{1F4AA}\x{1F9B5}\x{1F9B6}\x{1F448}\x{1F449}\x{261D}\x{1F446}\x{1F595}\x{1F447}\x{270C}\x{1F91E}\x{1F596}\x{1F918}\x{1F919}\x{1F590}\x{270B}\x{1F44C}\x{1F44D}\x{1F44E}\x{270A}\x{1F44A}\x{1F91B}\x{1F91C}\x{1F91A}\x{1F44B}\x{1F91F}\x{270D}\x{1F44F}\x{1F450}\x{1F64C}\x{1F932}\x{1F64F}\x{1F485}\x{1F442}\x{1F443}](?:\x{1F3FB})|[\x{1F1E6}\x{1F1E7}\x{1F1E8}\x{1F1E9}\x{1F1F0}\x{1F1F2}\x{1F1F3}\x{1F1F8}\x{1F1F9}\x{1F1FA}](?:\x{1F1FF})|[\x{1F1E7}\x{1F1E8}\x{1F1EC}\x{1F1F0}\x{1F1F1}\x{1F1F2}\x{1F1F5}\x{1F1F8}\x{1F1FA}](?:\x{1F1FE})|[\x{1F1E6}\x{1F1E8}\x{1F1F2}\x{1F1F8}](?:\x{1F1FD})|[\x{1F1E6}\x{1F1E7}\x{1F1E8}\x{1F1EC}\x{1F1F0}\x{1F1F2}\x{1F1F5}\x{1F1F7}\x{1F1F9}\x{1F1FF}](?:\x{1F1FC})|[\x{1F1E7}\x{1F1E8}\x{1F1F1}\x{1F1F2}\x{1F1F8}\x{1F1F9}](?:\x{1F1FB})|[\x{1F1E6}\x{1F1E8}\x{1F1EA}\x{1F1EC}\x{1F1ED}\x{1F1F1}\x{1F1F2}\x{1F1F3}\x{1F1F7}\x{1F1FB}](?:\x{1F1FA})|[\x{1F1E6}\x{1F1E7}\x{1F1EA}\x{1F1EC}\x{1F1ED}\x{1F1EE}\x{1F1F1}\x{1F1F2}\x{1F1F5}\x{1F1F8}\x{1F1F9}\x{1F1FE}](?:\x{1F1F9})|[\x{1F1E6}\x{1F1E7}\x{1F1EA}\x{1F1EC}\x{1F1EE}\x{1F1F1}\x{1F1F2}\x{1F1F5}\x{1F1F7}\x{1F1F8}\x{1F1FA}\x{1F1FC}](?:\x{1F1F8})|[\x{1F1E6}\x{1F1E7}\x{1F1E8}\x{1F1EA}\x{1F1EB}\x{1F1EC}\x{1F1ED}\x{1F1EE}\x{1F1F0}\x{1F1F1}\x{1F1F2}\x{1F1F3}\x{1F1F5}\x{1F1F8}\x{1F1F9}](?:\x{1F1F7})|[\x{1F1E6}\x{1F1E7}\x{1F1EC}\x{1F1EE}\x{1F1F2}](?:\x{1F1F6})|[\x{1F1E8}\x{1F1EC}\x{1F1EF}\x{1F1F0}\x{1F1F2}\x{1F1F3}](?:\x{1F1F5})|[\x{1F1E6}\x{1F1E7}\x{1F1E8}\x{1F1E9}\x{1F1EB}\x{1F1EE}\x{1F1EF}\x{1F1F2}\x{1F1F3}\x{1F1F7}\x{1F1F8}\x{1F1F9}](?:\x{1F1F4})|[\x{1F1E7}\x{1F1E8}\x{1F1EC}\x{1F1ED}\x{1F1EE}\x{1F1F0}\x{1F1F2}\x{1F1F5}\x{1F1F8}\x{1F1F9}\x{1F1FA}\x{1F1FB}](?:\x{1F1F3})|[\x{1F1E6}\x{1F1E7}\x{1F1E8}\x{1F1E9}\x{1F1EB}\x{1F1EC}\x{1F1ED}\x{1F1EE}\x{1F1EF}\x{1F1F0}\x{1F1F2}\x{1F1F4}\x{1F1F5}\x{1F1F8}\x{1F1F9}\x{1F1FA}\x{1F1FF}](?:\x{1F1F2})|[\x{1F1E6}\x{1F1E7}\x{1F1E8}\x{1F1EC}\x{1F1EE}\x{1F1F2}\x{1F1F3}\x{1F1F5}\x{1F1F8}\x{1F1F9}](?:\x{1F1F1})|[\x{1F1E8}\x{1F1E9}\x{1F1EB}\x{1F1ED}\x{1F1F1}\x{1F1F2}\x{1F1F5}\x{1F1F8}\x{1F1F9}\x{1F1FD}](?:\x{1F1F0})|[\x{1F1E7}\x{1F1E9}\x{1F1EB}\x{1F1F8}\x{1F1F9}](?:\x{1F1EF})|[\x{1F1E6}\x{1F1E7}\x{1F1E8}\x{1F1EB}\x{1F1EC}\x{1F1F0}\x{1F1F1}\x{1F1F3}\x{1F1F8}\x{1F1FB}](?:\x{1F1EE})|[\x{1F1E7}\x{1F1E8}\x{1F1EA}\x{1F1EC}\x{1F1F0}\x{1F1F2}\x{1F1F5}\x{1F1F8}\x{1F1F9}](?:\x{1F1ED})|[\x{1F1E6}\x{1F1E7}\x{1F1E8}\x{1F1E9}\x{1F1EA}\x{1F1EC}\x{1F1F0}\x{1F1F2}\x{1F1F3}\x{1F1F5}\x{1F1F8}\x{1F1F9}\x{1F1FA}\x{1F1FB}](?:\x{1F1EC})|[\x{1F1E6}\x{1F1E7}\x{1F1E8}\x{1F1EC}\x{1F1F2}\x{1F1F3}\x{1F1F5}\x{1F1F9}\x{1F1FC}](?:\x{1F1EB})|[\x{1F1E6}\x{1F1E7}\x{1F1E9}\x{1F1EA}\x{1F1EC}\x{1F1EE}\x{1F1EF}\x{1F1F0}\x{1F1F2}\x{1F1F3}\x{1F1F5}\x{1F1F7}\x{1F1F8}\x{1F1FB}\x{1F1FE}](?:\x{1F1EA})|[\x{1F1E6}\x{1F1E7}\x{1F1E8}\x{1F1EC}\x{1F1EE}\x{1F1F2}\x{1F1F8}\x{1F1F9}](?:\x{1F1E9})|[\x{1F1E6}\x{1F1E8}\x{1F1EA}\x{1F1EE}\x{1F1F1}\x{1F1F2}\x{1F1F3}\x{1F1F8}\x{1F1F9}\x{1F1FB}](?:\x{1F1E8})|[\x{1F1E7}\x{1F1EC}\x{1F1F1}\x{1F1F8}](?:\x{1F1E7})|[\x{1F1E7}\x{1F1E8}\x{1F1EA}\x{1F1EC}\x{1F1F1}\x{1F1F2}\x{1F1F3}\x{1F1F5}\x{1F1F6}\x{1F1F8}\x{1F1F9}\x{1F1FA}\x{1F1FB}\x{1F1FF}](?:\x{1F1E6})|[\x{00A9}\x{00AE}\x{203C}\x{2049}\x{2122}\x{2139}\x{2194}-\x{2199}\x{21A9}-\x{21AA}\x{231A}-\x{231B}\x{2328}\x{23CF}\x{23E9}-\x{23F3}\x{23F8}-\x{23FA}\x{24C2}\x{25AA}-\x{25AB}\x{25B6}\x{25C0}\x{25FB}-\x{25FE}\x{2600}-\x{2604}\x{260E}\x{2611}\x{2614}-\x{2615}\x{2618}\x{261D}\x{2620}\x{2622}-\x{2623}\x{2626}\x{262A}\x{262E}-\x{262F}\x{2638}-\x{263A}\x{2640}\x{2642}\x{2648}-\x{2653}\x{2660}\x{2663}\x{2665}-\x{2666}\x{2668}\x{267B}\x{267E}-\x{267F}\x{2692}-\x{2697}\x{2699}\x{269B}-\x{269C}\x{26A0}-\x{26A1}\x{26AA}-\x{26AB}\x{26B0}-\x{26B1}\x{26BD}-\x{26BE}\x{26C4}-\x{26C5}\x{26C8}\x{26CE}-\x{26CF}\x{26D1}\x{26D3}-\x{26D4}\x{26E9}-\x{26EA}\x{26F0}-\x{26F5}\x{26F7}-\x{26FA}\x{26FD}\x{2702}\x{2705}\x{2708}-\x{270D}\x{270F}\x{2712}\x{2714}\x{2716}\x{271D}\x{2721}\x{2728}\x{2733}-\x{2734}\x{2744}\x{2747}\x{274C}\x{274E}\x{2753}-\x{2755}\x{2757}\x{2763}-\x{2764}\x{2795}-\x{2797}\x{27A1}\x{27B0}\x{27BF}\x{2934}-\x{2935}\x{2B05}-\x{2B07}\x{2B1B}-\x{2B1C}\x{2B50}\x{2B55}\x{3030}\x{303D}\x{3297}\x{3299}\x{1F004}\x{1F0CF}\x{1F170}-\x{1F171}\x{1F17E}-\x{1F17F}\x{1F18E}\x{1F191}-\x{1F19A}\x{1F201}-\x{1F202}\x{1F21A}\x{1F22F}\x{1F232}-\x{1F23A}\x{1F250}-\x{1F251}\x{1F300}-\x{1F321}\x{1F324}-\x{1F393}\x{1F396}-\x{1F397}\x{1F399}-\x{1F39B}\x{1F39E}-\x{1F3F0}\x{1F3F3}-\x{1F3F5}\x{1F3F7}-\x{1F3FA}\x{1F400}-\x{1F4FD}\x{1F4FF}-\x{1F53D}\x{1F549}-\x{1F54E}\x{1F550}-\x{1F567}\x{1F56F}-\x{1F570}\x{1F573}-\x{1F57A}\x{1F587}\x{1F58A}-\x{1F58D}\x{1F590}\x{1F595}-\x{1F596}\x{1F5A4}-\x{1F5A5}\x{1F5A8}\x{1F5B1}-\x{1F5B2}\x{1F5BC}\x{1F5C2}-\x{1F5C4}\x{1F5D1}-\x{1F5D3}\x{1F5DC}-\x{1F5DE}\x{1F5E1}\x{1F5E3}\x{1F5E8}\x{1F5EF}\x{1F5F3}\x{1F5FA}-\x{1F64F}\x{1F680}-\x{1F6C5}\x{1F6CB}-\x{1F6D2}\x{1F6E0}-\x{1F6E5}\x{1F6E9}\x{1F6EB}-\x{1F6EC}\x{1F6F0}\x{1F6F3}-\x{1F6F9}\x{1F910}-\x{1F93A}\x{1F93C}-\x{1F93E}\x{1F940}-\x{1F945}\x{1F947}-\x{1F970}\x{1F973}-\x{1F976}\x{1F97A}\x{1F97C}-\x{1F9A2}\x{1F9B0}-\x{1F9B9}\x{1F9C0}-\x{1F9C2}\x{1F9D0}-\x{1F9FF}]/u', '', $text); } -------------------------------------------------------------------------------- /framework/miscClass.php: -------------------------------------------------------------------------------- 1 | message = "[{$this->prompt}]".(($message=='')?'':": {$message}"); 14 | $this->code = $code; 15 | $this->previous = $previous; 16 | } 17 | } 18 | 19 | class QuitException extends KjBotException{var $prompt = 'ERROR';} 20 | 21 | class PanicException extends KjBotException{var $prompt = 'PANIC';} 22 | 23 | class SilenceModule{public static $silence = false;} 24 | -------------------------------------------------------------------------------- /modules.php.example: -------------------------------------------------------------------------------- 1 | message))$postData->message = kjBot\SDK\CQCode::DecodeCQCode($postData->message); //FIXME 存在转义问题 ref: https://docs.cqp.im/manual/cqcode/ 12 | $event = kjBot\Framework\Event\EventFactory::createEventFrom($postData); 13 | $pluginMethods = event2pluginMethods($event); 14 | 15 | try{ 16 | 17 | foreach($Plugins as $pluginName){ 18 | $plugin = (new ReflectionClass($pluginName))->newInstance(); 19 | if($plugin instanceof kjBot\Framework\Plugin){ 20 | try{ 21 | $methodNeedCQ = (new ReflectionClass($pluginName))->getConstant('cq_'.$pluginMethods[$plugin->handleDepth]); 22 | $methodName = ($methodNeedCQ?'coolq_':'').$pluginMethods[$plugin->handleDepth]; 23 | $method = new ReflectionMethod($plugin, $methodName); 24 | try{ 25 | if($methodNeedCQ){ 26 | _log('NOTICE', "{$pluginName} handle {$pluginMethods[$plugin->handleDepth]} request CoolQ instance."); 27 | $kjBot->addMessage(@$method->invoke($plugin, $event, $kjBot->getCoolQ())); 28 | }else{ 29 | $kjBot->addMessage(@$method->invoke($plugin, $event)); 30 | } 31 | }catch(kjBot\Framework\QuitException $e){ 32 | $kjBot->addMessage($event->sendBack($e->getMessage())); 33 | }catch(\TypeError $e){ 34 | d($e->getMessage()); 35 | } 36 | }catch(ReflectionException $e){ 37 | //silence catch 38 | }catch(\Exception $e){ 39 | _log('ERROR', "{$pluginName}: {$e->getMessage()}"); 40 | } 41 | }else{ 42 | _log('WARNING', "{$pluginName} is not a kjBot plugin."); 43 | } 44 | } 45 | 46 | if(kjBot\Framework\SilenceModule::$silence){} 47 | else{ 48 | if($event instanceof kjBot\Framework\Event\MessageEvent){ 49 | $matches = parseCommand($event->__toString()); 50 | $command = rtrim($matches[0]); 51 | if($matches==NULL){ 52 | $command = $event->getMsg(); 53 | $matches = [$command]; 54 | } 55 | if(isset($Modules[$command])){ 56 | $module = (new ReflectionClass($Modules[$command]))->newInstance(); 57 | if($module instanceof kjBot\Framework\Module){ 58 | d("{$Modules[$command]} handled command: {$command}"); 59 | try{ 60 | if($module->needCQ()){ 61 | d("{$Modules[$command]} request CoolQ instance."); 62 | $kjBot->addMessage($module->processWithCQ($matches, $event, $kjBot->getCoolQ())); 63 | }else{ 64 | $kjBot->addMessage($module->process($matches, $event)); 65 | } 66 | }catch(kjBot\Framework\QuitException $e){ 67 | $kjBot->addMessage($event->sendBack($e->getMessage())); 68 | }catch(\TypeError $e){ 69 | _log('ERROR', $e->getMessage()); 70 | } 71 | } 72 | } 73 | } 74 | } 75 | 76 | }catch(kjBot\Framework\PanicException $e){ 77 | $kjBot->addMessage(notifyMaster(($e->getMessage()))); 78 | } 79 | 80 | foreach($Plugins as $pluginName){ 81 | $plugin = (new ReflectionClass($pluginName))->newInstance(); 82 | if($plugin instanceof kjBot\Framework\Plugin){ 83 | if($plugin->handleQueue){ 84 | d("{$pluginName} handled MessageQueue."); 85 | try{ 86 | $plugin->beforePostMessage($kjBot->getMessageQueue()); 87 | }catch(Exception $e){ 88 | _log('ERROR', "{$pluginName}: {$e->getMessage()}"); 89 | } 90 | } 91 | }else{ 92 | _log('WARNING', "{$pluginName} is not a kjBot plugin."); 93 | } 94 | } 95 | 96 | try{ 97 | $kjBot->postMessage(); 98 | }catch(Exception $e){ 99 | _log('ERROR', $e->getMessage()."({$e->getCode()})"); 100 | } 101 | -------------------------------------------------------------------------------- /public/init.php: -------------------------------------------------------------------------------- 1 |