diff --git a/.gitignore b/.gitignore
old mode 100755
new mode 100644
index d646888b..fa694cc8
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,6 @@
-# Created by .ignore support plugin (hsz.mobi)
-### Example user template template
-### Example user template
-
-# IntelliJ project files
-.idea
-out
-gen
-config/
-cq_data/
-composer.lock
\ No newline at end of file
+.idea/
+/src/test/
+/src/webconsole/config/
+/vendor/
+zm.json
+/zm_data/
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
deleted file mode 100755
index 96f69fec..00000000
--- a/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2018 Blue Whale Network.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/README.md b/README.md
old mode 100755
new mode 100644
index 803c7858..4f355096
--- a/README.md
+++ b/README.md
@@ -1,141 +1,41 @@
-# CQBot-swoole
-
-## 此分支停止维护!
+# zhamao-framework
[]()
[]()
[]()
-一个异步、多平台兼容的**聊天机器人**框架。
+一个异步、多平台兼容的 **聊天机器人** 框架。
-**当前正在重构,框架可能会有很大的变化,旧框架将创建old分支,重构完成后新款框架为master分支。**
-**框架同时将更名为zhamao-framework(炸毛框架),与旧版本兼容性不并存,届时需要根据文档进行模块的升级。**
+ 
-## CQBot-swoole 文档
-本项目的文档正在努力编写中:[https://cqbot.crazywhale.org/](https://cqbot.crazywhale.org/)
+## 简介
+zhamao-framework 是一个基于 酷Q 的 PHP 机器人框架,它会对 QQ 机器人收到的消息进行解析处理,并以模块化的形式进行开发,来完成机器人的自然语言对话等功能。
-## 什么是Swoole
-PHP原生对多线程、多进程、异步等特性支持不是很好,有了Swoole,你可以非常简单自由地写出优雅的高性能服务器。
+除了起到解析消息的作用,炸毛框架 还提供了完整的 WebSocket + HTTP 服务器,你还能用此框架构建出高性能的 API 接口服务器。
-本项目原生支持多机器人连接,故选择了反向Websocket连接方式。同时更适用于高并发、多机器人同时连接以及对接**微信公众号**和**web前端**等场景。
-[Swoole官网](https://www.swoole.com/)
-
-## 框架简介
-cqbot-swoole是一个聊天机器人框架,同时兼容酷Q(需安装[cqhttp插件](https://cqhttp.cc)),微信公众号,支持多QQ账号对接。
-
-## 说在前面
-本框架目前还有一些缺陷,说在前面的原因是如果你有更好的想法或发现的问题,可以提issue或联系作者。
-
-> 如果你对swoole、PHP研究较深,推荐尝试学习框架的源码和运行原理后自己动手写一个。也欢迎star本项目给予支持!
+## 文档
+本项目文档正在努力编写中:[https://framework.zhamao.xin/](https://framework.zhamao.xin/)
## 特点
-- 多账号单后端式框架
-- 采用模块式编写,功能之间独立性高,可分别开关各个模块和设置响应优先级
-- 全局缓存,随处使用
-- 协程开发,传统同步写法实现高并发
-- 除swoole外不依赖composer其他项目
-- 自带HTTP、websocket服务器,可对接其他服务
-- 支持协程MySQL
+- 支持多账号
+- 灵活的注解事件绑定机制
+- 采用模块化编写,功能之间高内聚低耦合
+- 常驻内存,全局缓存变量随处使用
+- 自带 MySQL 查询器、数据库连接池等数据库连接方案
+- 自带 HTTP 服务器、WebSocket 服务器可复用,可以构建属于自己的 HTTP API 接口
-## 环境部署
+## 从 cqbot-swoole 升级
+目前新的框架采用了全新的注解机制,所以旧版的框架上写的模块到新框架需要重新编写。当然为了减少工作量,新的框架也最大限度地保留了旧版框架编写的风格,一般情况下根据新版框架的文档仅需修改少量地方即可完成重写。
-### 酷Q和HTTP API插件
-由于框架是独立于酷Q运行的,故你可以在多台主机上部署酷Q的docker。
+旧版框架并入了 `old` 分支,如果想继续使用旧版框架请移步分支。升级过程中如果遇到问题可以找作者。
-如果你是新用户或重新安装含有HTTPAPI插件的**酷Q-Docker**的话,可以在你需要部署酷Q的Linux主机下使用下面的脚本快速构建酷Q环境,此脚本会引导进行相关的cqhttp插件设置。每台部署酷Q的主机均可直接使用下方的命令(服务器需要提前安装Docker)
-
-```shell
-#第一次部署酷Q-httpapi docker运行下面的代码
-bash -c "$(wget https://raw.githubusercontent.com/crazywhalecc/cqbot-swoole/master/start-coolq.sh -O -)"
-
-#以后每次启动/停止/重启酷Q容器执行的命令
-docker start coolq
-docker stop coolq
-docker restart coolq
-
-#以上指令非root用户可能需要sudo
-```
-### 微信公众号
-很快将兼容微信公众平台,敬请期待。
-
-
-## 框架部署
-### 手动安装到Linux主机上
-``` shell
-# 安装PHP(ubuntu/debian)
-apt-get install software-properties-common
-add-apt-repository ppa:ondrej/php
-apt-get update
-apt-get install php7.2 php7.2-dev php7.2 php7.2-mbstring php7.2-json php7.2 php-pear
-
-#安装PHP(CentOS)
-rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
-rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm
-yum makecache fast
-yum install php72w-devel.x86_64 php72w-mbstring.x86_64 php72w-pear.noarch gcc gcc-c++ -y
-
-
-# 安装Swoole
-pecl install swoole
-echo "extension=swoole.so" >> $(php -i | grep "Loaded Configuration File" | awk '{print $5}')
-
-# 部署框架
-git clone https://github.com/crazywhalecc/cqbot-swoole.git
-
-# 以上指令可能需要sudo执行
-```
-
-
-### 使用Docker快速构建并启动
-``` shell
-sudo docker run -it --rm --net=host --name cqbot -v $(pwd)/cqbot/:/root/ jesse2061/cqbot-swoole
-
-# 可以将命令添加为alias方便以后快速启动
-echo "alias cqbot='sudo docker run -it --rm --net=host --name cqbot -v $(pwd)/cqbot/:/root/ jesse2061/cqbot-swoole'" >> ~/.bash_profile
-source ~/.bash_profile
-cqbot
-```
-
-
-## 启动
-#### 直接安装后启动框架
-
-```shell
-cd cqbot-swoole/
-php start.php
-```
-
-#### 在screen中运行框架
-
-```shell
-screen -R cqbot
-cd cqbot-swoole/
-php start.php
-# Ctrl A + D 将screen放到后台运行
-```
-
-#### 使用Docker在screen中运行框架
-
-```shell
-screen -R cqbot
-sudo docker run -it --rm --net=host --name cqbot -v $(pwd)/cqbot/:/root/ jesse2061/cqbot-swoole
-# Ctrl A + D 将screen放到后台运行
-```
-
-## MacOS与Windows兼容性
-#### MacOS下运行cqbot-swoole
-mac下运行和Linux整体相同,使用brew安装好PHP后通过源码编译`swoole`组件安装,或使用docker。
-> Docker for mac 运行需要手动指定端口`-p 20000:20000`,不能使用`--net=host`网络模式。
-
-#### Windows下运行cqbot-swoole
-因为swoole使用了Linux的特性,故**不推荐**在Windows电脑或服务器使用,Windows可以使用Docker运行,使用 `cygwin` 环境或 `WSL` 环境。
-> 不推荐原因有不能使用`reload`指令进行重启服务和不能使用全部的swoole特性。
+## 贡献
+如果你在使用过程中发现任何问题,可以提交 Issue 或自行 Fork 后修改并提交 Pull Request。目前项目仅一人维护,耗费精力较大,所以非常欢迎对框架的贡献。
## 关于
+框架和 SDK 是 炸毛机器人 项目的核心框架开源部分。炸毛机器人(3276124472)是作者写的一个高性能机器人,曾获全国计算机设计大赛一等奖。
-框架和SDK部分代码直接从 [炸毛机器人](https://github.com/zhamao-robot/) 中移植而来,炸毛机器人(3290004669)是作者写的一个高性能的机器人,曾获全国计算机设计大赛一等奖。
+欢迎随时在 HTTP-API 插件群里提问,当然更好的话可以加作者 QQ(627577391)或提交 Issue 进行疑难解答。
-欢迎随时在HTTP-API插件群提问,当然更好的话可以加作者QQ(627577391)或提交issue进行疑难解答。
-
-本项目在有更新内容时,请及时关注GitHub的动态,更新前请将自己的**模块**代码做好备份。
+本项目在更行内容时,请及时关注 GitHub 动态,更新前请将自己的模块代码做好备份。
\ No newline at end of file
diff --git a/bin/start b/bin/start
new file mode 100755
index 00000000..b141b3f2
--- /dev/null
+++ b/bin/start
@@ -0,0 +1,44 @@
+#!/usr/bin/env php
+ 30000,
+]);
+
+date_default_timezone_set("Asia/Shanghai");
+
+switch ($argv[1] ?? '') {
+ case 'scheduler':
+ case 'timer':
+ go(function () {
+ try {
+ new Scheduler(Scheduler::REMOTE);
+ } catch (Exception $e) {
+ die($e->getMessage());
+ }
+ });
+ break;
+ case '':
+ case 'framework':
+ case 'server':
+ if(!is_dir(__DIR__.'/../vendor/')){
+ echo "Warning: you have not update composer!\n";
+ echo "You need to run \"composer update\" at root of zhamao-framework!\n";
+ echo "Or if you are using docker or composer installed, just run \"sh bin/update-composer\"\n";
+ echo "In China, if your composer downloading slowly, you can get latest vendor.tar.gz from HERE:\n";
+ echo "https://dl2.zhamao.xin/zhamao-framework/vendor.tar.gz\n";
+ die;
+ }
+ $loader = new FrameworkLoader($argv);
+ break;
+ default:
+ echo "Unknown option \"{$argv[1]}\"!\n";
+ break;
+}
+
diff --git a/bin/update-composer b/bin/update-composer
new file mode 100644
index 00000000..454e504a
--- /dev/null
+++ b/bin/update-composer
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+composer update
\ No newline at end of file
diff --git a/composer.json b/composer.json
index 6e6abe7b..cead0423 100644
--- a/composer.json
+++ b/composer.json
@@ -1,19 +1,31 @@
{
+ "name": "zhamao/framework",
"description": "high-performance intelligent assistant",
"minimum-stability": "stable",
- "license": "MIT",
+ "license": "proprietary",
+ "authors": [
+ {
+ "name": "whale",
+ "email": "crazysnowcc@gmail.com"
+ },
+ {
+ "name": "swift",
+ "email": "hugo_swift@yahoo.com"
+ }
+ ],
"require": {
- "php": ">=7.0.0",
- "ext-mbstring": "^7.1",
+ "swoole/ide-helper": "@dev",
+ "ext-mbstring": "*",
+ "swlib/saber": "^1.0",
+ "doctrine/annotations": "^1.8",
"ext-json": "*",
- "ext-iconv": "*",
- "ext-mysqli": "*",
- "ext-dom": "20031129",
- "ext-gd": "^7.1",
- "ext-curl": "^7.1",
- "ext-openssl": "^7.1"
+ "ext-posix": "*",
+ "ext-ctype": "*"
},
- "require-dev": {
- "eaglewu/swoole-ide-helper": "dev-master"
+ "repositories": {
+ "packagist": {
+ "type": "composer",
+ "url": "https://mirrors.aliyun.com/composer/"
+ }
}
}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 00000000..3e192231
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,395 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "fb2cdfc2c51cc4f5c2758916f7a3b628",
+ "packages": [
+ {
+ "name": "doctrine/annotations",
+ "version": "v1.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/annotations.git",
+ "reference": "904dca4eb10715b92569fbcd79e201d5c349b6bc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/annotations/zipball/904dca4eb10715b92569fbcd79e201d5c349b6bc",
+ "reference": "904dca4eb10715b92569fbcd79e201d5c349b6bc",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/lexer": "1.*",
+ "php": "^7.1"
+ },
+ "require-dev": {
+ "doctrine/cache": "1.*",
+ "phpunit/phpunit": "^7.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.7.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "Docblock Annotations Parser",
+ "homepage": "http://www.doctrine-project.org",
+ "keywords": [
+ "annotations",
+ "docblock",
+ "parser"
+ ],
+ "time": "2019-10-01T18:55:10+00:00"
+ },
+ {
+ "name": "doctrine/lexer",
+ "version": "1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/lexer.git",
+ "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/lexer/zipball/5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6",
+ "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^6.0",
+ "phpstan/phpstan": "^0.11.8",
+ "phpunit/phpunit": "^8.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
+ "homepage": "https://www.doctrine-project.org/projects/lexer.html",
+ "keywords": [
+ "annotations",
+ "docblock",
+ "lexer",
+ "parser",
+ "php"
+ ],
+ "time": "2019-10-30T14:39:59+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "time": "2016-08-06T14:39:51+00:00"
+ },
+ {
+ "name": "swlib/http",
+ "version": "v1.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/swlib/http.git",
+ "reference": "d8ad0b0aed67ab9d1b18a6ce87c40813c0e00060"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/swlib/http/zipball/d8ad0b0aed67ab9d1b18a6ce87c40813c0e00060",
+ "reference": "d8ad0b0aed67ab9d1b18a6ce87c40813c0e00060",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0",
+ "psr/http-message": "~1.0"
+ },
+ "require-dev": {
+ "eaglewu/swoole-ide-helper": "dev-master",
+ "phpunit/phpunit": "~7"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Swlib\\Http\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "twosee",
+ "email": "twose@qq.com"
+ }
+ ],
+ "description": "Swlib-HTTP base class repository, PSR implementation",
+ "keywords": [
+ "http",
+ "php",
+ "psr7",
+ "swoole"
+ ],
+ "time": "2019-09-24T06:38:56+00:00"
+ },
+ {
+ "name": "swlib/saber",
+ "version": "1.0.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/swlib/saber.git",
+ "reference": "10b3a1b4e30edce6de583c44cb7d2185f33a8a1f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/swlib/saber/zipball/10b3a1b4e30edce6de583c44cb7d2185f33a8a1f",
+ "reference": "10b3a1b4e30edce6de583c44cb7d2185f33a8a1f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1",
+ "swlib/http": "1.0.5",
+ "swlib/util": "1.0.0"
+ },
+ "require-dev": {
+ "eaglewu/swoole-ide-helper": "dev-master",
+ "phpunit/phpunit": "~7"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/Saber.php",
+ "src/SaberGM.php"
+ ],
+ "psr-4": {
+ "Swlib\\Saber\\": "src"
+ },
+ "files": [
+ "src/include/functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "twosee",
+ "email": "twose@qq.com"
+ }
+ ],
+ "description": "Swoole coroutine HTTP client",
+ "keywords": [
+ "ajax",
+ "axios",
+ "client",
+ "coroutine",
+ "curl",
+ "http",
+ "php",
+ "psr7",
+ "requests",
+ "swoole"
+ ],
+ "time": "2019-09-26T08:29:29+00:00"
+ },
+ {
+ "name": "swlib/util",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/swlib/util.git",
+ "reference": "f9aaa9028ea16a489a46b9ad09f0e844997cffe7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/swlib/util/zipball/f9aaa9028ea16a489a46b9ad09f0e844997cffe7",
+ "reference": "f9aaa9028ea16a489a46b9ad09f0e844997cffe7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "eaglewu/swoole-ide-helper": "dev-master",
+ "phpunit/phpunit": "~7"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Swlib\\Util\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "twosee",
+ "email": "twose@qq.com"
+ }
+ ],
+ "description": "Swlib Toolkit",
+ "keywords": [
+ "php",
+ "swlib",
+ "swoole",
+ "util"
+ ],
+ "time": "2018-07-23T04:36:19+00:00"
+ },
+ {
+ "name": "swoole/ide-helper",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/swoole/ide-helper.git",
+ "reference": "a7bae643171ff4176ff37868a40276819e5789aa"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/swoole/ide-helper/zipball/a7bae643171ff4176ff37868a40276819e5789aa",
+ "reference": "a7bae643171ff4176ff37868a40276819e5789aa",
+ "shasum": ""
+ },
+ "require-dev": {
+ "guzzlehttp/guzzle": "~6.5.0",
+ "squizlabs/php_codesniffer": "~3.5.0",
+ "symfony/filesystem": "~4.3.0",
+ "zendframework/zend-code": "~3.3.0"
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Team Swoole",
+ "email": "team@swoole.com"
+ }
+ ],
+ "description": "IDE help files for Swoole.",
+ "time": "2020-01-15T08:00:05+00:00"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {
+ "swoole/ide-helper": 20
+ },
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "ext-mbstring": "*",
+ "ext-json": "*",
+ "ext-posix": "*"
+ },
+ "platform-dev": []
+}
diff --git a/config/global.php b/config/global.php
new file mode 100644
index 00000000..1e991c5b
--- /dev/null
+++ b/config/global.php
@@ -0,0 +1,64 @@
+set参数 */
+$config['swoole'] = [
+ 'log_file' => $config['crash_dir'].'swoole_error.log',
+ 'worker_num' => 1,
+ 'dispatch_mode' => 2,
+ 'task_worker_num' => 0
+];
+
+/** MySQL数据库连接信息,host留空则启动时不创建sql连接池 */
+$config['sql_config'] = [
+ 'sql_host' => '',
+ 'sql_port' => 3306,
+ 'sql_username' => 'name',
+ 'sql_database' => 'db_name',
+ 'sql_password' => '',
+ 'sql_enable_cache' => true,
+ 'sql_reset_cache' => '0300'
+];
+
+/** CQHTTP连接约定的token */
+$config["access_token"] = "";
+
+/** HTTP服务器固定请求头的返回 */
+$config['http_header'] = [
+ 'X-Powered-By' => 'zhamao-framework',
+ 'Content-Type' => 'text/html; charset=utf-8'
+];
+
+/** HTTP服务器在指定状态码下回复的页面(默认) */
+$config['http_default_code_page'] = [
+ '404' => '404.html'
+];
+
+/** zhamao-framework在框架启动时初始化的atomic们 */
+$config['init_atomics'] = [
+ 'in_count' => 0, //消息接收message的统计数量
+ 'out_count' => 0, //消息发送(调用send_*_msg的统计数量)
+ 'reload_time' => 0, //调用reload功能统计数量
+ 'wait_msg_id' => 0, //协程挂起id自增
+ 'info_level' => 0, //终端显示的log等级
+];
+
+/** 自动保存的缓存保存时间(秒) */
+$config['auto_save_interval'] = 900;
+
+return $config;
\ No newline at end of file
diff --git a/cqbot.json b/cqbot.json
deleted file mode 100644
index 3b4a5f42..00000000
--- a/cqbot.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "robot_name": "CQBot",
- "cqbot_version": "1.0.0",
- "config_dir": "cq_data\/config\/",
- "crash_dir": "cq_data\/crash\/",
- "user_dir": "cq_data\/users\/",
- "cq_data": "cq_data\/",
- "admin_group": "",
- "access_token": "",
- "info_level": 0,
- "admin": [],
- "save_user_data": true,
- "swoole_host": "",
- "swoole_port": "",
- "swoole_worker_num": 1,
- "swoole_dispatch_mode": 2,
- "swoole_log_file": "cq_data\/crash\/swoole.log",
- "swoole_use_tick": true,
- "swoole_tick_interval": 1000
-}
\ No newline at end of file
diff --git a/docker/Dockerfile b/docker/Dockerfile
deleted file mode 100644
index f9dbe4f4..00000000
--- a/docker/Dockerfile
+++ /dev/null
@@ -1,19 +0,0 @@
-FROM php:7.2
-WORKDIR /root
-RUN echo "Asia/Shanghai" > /etc/timezone
-RUN dpkg-reconfigure -f noninteractive tzdata
-ENV LANG C.UTF-8
-RUN apt-get update && apt-get install curl libxml2 libzip-dev openssl git -y
-
-# Install php extensions
-RUN docker-php-ext-install zip
-RUN docker-php-ext-install mysqli
-RUN docker-php-ext-install iconv
-RUN docker-php-ext-install mbstring
-
-RUN pecl install swoole
-RUN docker-php-ext-enable swoole
-
-VOLUME ["/root/"]
-
-CMD if [ ! -d "CQBot-swoole" ]; then git clone https://github.com/crazywhalecc/CQBot-swoole.git; fi && cd CQBot-swoole/ && bash -c "php start.php"
\ No newline at end of file
diff --git a/resources/html/404.html b/resources/html/404.html
new file mode 100644
index 00000000..a0538554
--- /dev/null
+++ b/resources/html/404.html
@@ -0,0 +1,11 @@
+
+
+
+
+ Sorry
+
+
+Nothing here.
+Please check your url. (404)
+
+
\ No newline at end of file
diff --git a/resources/images/logo.png b/resources/images/logo.png
new file mode 100644
index 00000000..c7e36850
Binary files /dev/null and b/resources/images/logo.png differ
diff --git a/src/Custom/Annotation/Example.php b/src/Custom/Annotation/Example.php
new file mode 100644
index 00000000..8d41fd51
--- /dev/null
+++ b/src/Custom/Annotation/Example.php
@@ -0,0 +1,19 @@
+get(), [1, 2])) {
+ $trace = debug_backtrace()[1] ?? ['file' => '', 'function' => ''];
+ $trace = "[" . basename($trace["file"], ".php") . ":" . $trace["function"] . "] ";
+ }
+ if (!is_string($obj)) {
+ if (isset($trace)) {
+ var_dump($obj);
+ return;
+ } else $obj = "{Object}";
+ }
+ echo(self::setColor($head . ($trace ?? "") . $obj, "red") . "\n");
+ }
+
+ static function warning($obj, $head = null) {
+ if ($head === null) $head = date("[H:i:s") . " WARN] ";
+ if (ZMBuf::$info_level !== null && in_array(ZMBuf::$info_level->get(), [1, 2])) {
+ $trace = debug_backtrace()[1] ?? ['file' => '', 'function' => ''];
+ $trace = "[" . basename($trace["file"], ".php") . ":" . $trace["function"] . "] ";
+ }
+ if (!is_string($obj)) {
+ if (isset($trace)) {
+ var_dump($obj);
+ return;
+ } else $obj = "{Object}";
+ }
+ echo(self::setColor($head . ($trace ?? "") . $obj, "yellow") . "\n");
+ }
+
+ static function info($obj, $head = null) {
+ if ($head === null) $head = date("[H:i:s ") . "INFO] ";
+ if (ZMBuf::$info_level !== null && in_array(ZMBuf::$info_level->get(), [1, 2])) {
+ $trace = debug_backtrace()[1] ?? ['file' => '', 'function' => ''];
+ $trace = "[" . basename($trace["file"], ".php") . ":" . $trace["function"] . "] ";
+ }
+ if (!is_string($obj)) {
+ if (isset($trace)) {
+ var_dump($obj);
+ return;
+ } else $obj = "{Object}";
+ }
+ echo(self::setColor($head . ($trace ?? "") . $obj, "lightblue") . "\n");
+ }
+
+ static function log($obj, $color = "") {
+ if (!is_string($obj)) var_dump($obj);
+ else echo(self::setColor($obj, $color) . "\n");
+ }
+
+ static function msg($obj, $self_id = "") {
+ if (ZMBuf::$info_level !== null && ZMBuf::$info_level->get() == 3) {
+ if (!isset($obj["post_type"])) {
+ switch ($obj["action"]) {
+ case "send_private_msg":
+ $msg = Console::setColor(date("H:i:s ") . "[" . (ZMBuf::globals("robot_alias")[$self_id] ?? "Null") . "] ", "lightlightblue");
+ $msg .= Console::setColor("私聊↑(" . $obj["params"]["user_id"] . ")", "lightlightblue");
+ $msg .= Console::setColor(" > ", "gray");
+ $msg .= $obj["params"]["message"];
+ Console::log($msg);
+ break;
+ case "send_group_msg":
+ //TODO: 写新的控制台消息(API消息处理)
+ Console::log(Console::setColor("[" . date("H:i:s") . " GROUP:" . $obj["params"]["group_id"] . "] ", "blue") . Console::setColor($obj["params"]["user_id"] ?? "", "yellow") . Console::setColor(" > ", "gray") . ($obj["params"]["message"] ?? ""));
+ break;
+ case "send_discuss_msg":
+ Console::log(Console::setColor("[" . date("H:i:s") . " DISCUSS:" . $obj["params"]["discuss_id"] . "] ", "blue") . Console::setColor($obj["params"]["user_id"] ?? "", "yellow") . Console::setColor(" > ", "gray") . ($obj["params"]["message"] ?? ""));
+ break;
+ case "send_msg":
+ $obj["action"] = "send_" . $obj["message_type"] . "_msg";
+ self::msg($obj);
+ break;
+ case "send_wechat_msg":
+ Console::log(Console::setColor("[" . date("H:i:s") . " WECHAT] ", "blue") . Console::setColor($obj["params"]["user_id"] ?? "", "yellow") . Console::setColor(" > ", "gray") . ($obj["params"]["message"] ?? ""));
+ break;
+ default:
+ break;
+ }
+ } else {
+ if ($obj["post_type"] == "message") {
+ switch ($obj["message_type"]) {
+ case "group":
+ //
+ //TODO: 写新的控制台消息(event处理)
+ case "private":
+ case "discuss":
+ case "wechat":
+ }
+ }
+ }
+ }
+ }
+
+ static function stackTrace(){
+ $log = "Stack trace:\n";
+ $trace = debug_backtrace();
+ //array_shift($trace);
+ foreach ($trace as $i => $t) {
+ if (!isset($t['file'])) {
+ $t['file'] = 'unknown';
+ }
+ if (!isset($t['line'])) {
+ $t['line'] = 0;
+ }
+ if (!isset($t['function'])) {
+ $t['function'] = 'unknown';
+ }
+ $log .= "#$i {$t['file']}({$t['line']}): ";
+ if (isset($t['object']) and is_object($t['object'])) {
+ $log .= get_class($t['object']) . '->';
+ }
+ $log .= "{$t['function']}()\n";
+ }
+ $log = Console::setColor($log, "gray");
+ echo $log;
+ }
+
+ static function listenConsole(){
+ go(function () {
+ while (true) {
+ $cmd = trim(co::fread(STDIN));
+ if (self::executeCommand($cmd) === false) break;
+ }
+ });
+ }
+
+ /**
+ * @param string $cmd
+ * @return bool
+ */
+ private static function executeCommand(string $cmd) {
+ $it = explodeMsg($cmd);
+ switch ($it[0] ?? '') {
+ case 'call':
+ $class_name = $it[1];
+ $function_name = $it[2];
+ $class = new $class_name([]);
+ call_user_func_array([$class, $function_name],[]);
+ return true;
+ case 'bc':
+ $code = base64_decode($it[1] ?? '', true);
+ try {
+ eval($code);
+ } catch (Exception $e) {
+ }
+ return true;
+ case 'echo':
+ Console::info($it[1]);
+ return true;
+ case 'stop':
+ ZMUtil::stop();
+ return false;
+ case 'reload':
+ case 'r':
+ ZMUtil::reload();
+ return false;
+ case '':
+ return true;
+ default:
+ Console::info("Command not found: " . $it[0]);
+ return true;
+ }
+ }
+
+ public static function withSleep(string $string, int $int) {
+ self::info($string);
+ sleep($int);
+ }
+}
\ No newline at end of file
diff --git a/src/Framework/FrameworkLoader.php b/src/Framework/FrameworkLoader.php
new file mode 100644
index 00000000..b1e49884
--- /dev/null
+++ b/src/Framework/FrameworkLoader.php
@@ -0,0 +1,106 @@
+requireGlobalFunctions();
+ $this->registerAutoloader('classLoader');
+ self::$settings = new GlobalConfig();
+ ZMBuf::$globals = self::$settings;
+ if (!self::$settings->success) die("Failed to load global config. Please check config/global.php file");
+ $this->defineProperties();
+
+ //start swoole Framework
+ $this->selfCheck();
+ try {
+ $this->server = new Server(self::$settings->get("host"), self::$settings->get("port"));
+ if (in_array("--remote-shell", $args)) RemoteShell::listen($this->server, "127.0.0.1");
+ $this->server->set(self::$settings->get("swoole"));
+ $this->server->on("WorkerStart", [$this, "onWorkerStart"]);
+ $this->server->on("message", function ($server, $frame) { EventHandler::callSwooleEvent("message", $server, $frame); });
+ $this->server->on("request", function ($request, $response) {
+ $response = new Response($response);
+ EventHandler::callSwooleEvent("request", $request, $response);
+ });
+ $this->server->on("open", function ($server, $request) { EventHandler::callSwooleEvent("open", $server, $request); });
+ $this->server->on("close", function ($server, $fd) { EventHandler::callSwooleEvent("close", $server, $fd); });
+ ZMBuf::initAtomic();
+ Console::info("host: ".self::$settings->get("host").", port: ".self::$settings->get("port"));
+ $this->server->start();
+ } catch (Exception $e) {
+ Console::error("Framework初始化出现错误,请检查!");
+ Console::error($e->getMessage());
+ die;
+ }
+ }
+
+ private function requireGlobalFunctions() {
+ require __DIR__ . '/global_functions.php';
+ }
+
+ private function registerAutoloader(string $string) {
+ if (!spl_autoload_register($string)) die("Failed to register autoloader named \"$string\" !");
+ }
+
+ private function defineProperties() {
+ define("ZM_START_TIME", microtime(true));
+ define("ZM_DATA", self::$settings->get("zm_data"));
+ define("CONFIG_DIR", self::$settings->get("config_dir"));
+ define("CRASH_DIR", self::$settings->get("crash_dir"));
+ @mkdir(ZM_DATA);
+ @mkdir(CONFIG_DIR);
+ @mkdir(CRASH_DIR);
+ define("ZM_MATCH_ALL", 0);
+ define("ZM_MATCH_FIRST", 1);
+ define("ZM_MATCH_NUMBER", 2);
+ define("ZM_MATCH_SECOND", 3);
+ }
+
+ private function selfCheck() {
+ if (!extension_loaded("swoole")) die("Can not find swoole extension.\n");
+ //if (!extension_loaded("gd")) die("Can not find gd extension.\n");
+ if (!extension_loaded("sockets")) die("Can not find sockets extension.\n");
+ if (!function_exists("mb_substr")) die("Can not find mbstring extension.\n");
+ if (substr(PHP_VERSION, 0, 1) != "7") die("PHP >=7 required.\n");
+ //if (!function_exists("curl_exec")) die("Can not find curl extension.\n");
+ //if (!class_exists("ZipArchive")) die("Can not find Zip extension.\n");
+ //if (!file_exists(CRASH_DIR . "last_error.log")) die("Can not find log file.\n");
+ return true;
+ }
+
+ public function onWorkerStart(\Swoole\Server $server, $worker_id) {
+ self::$instance = $this;
+ self::$run_time = microtime(true);
+ EventHandler::callSwooleEvent("WorkerStart", $server, $worker_id);
+ }
+}
\ No newline at end of file
diff --git a/src/Framework/GlobalConfig.php b/src/Framework/GlobalConfig.php
new file mode 100644
index 00000000..c6daaf1b
--- /dev/null
+++ b/src/Framework/GlobalConfig.php
@@ -0,0 +1,33 @@
+success = true;
+ $this->config = $config;
+ }
+
+ public function get($key) {
+ $r = $this->config[$key] ?? null;
+ if ($r === null) return null;
+ return $r;
+ }
+}
\ No newline at end of file
diff --git a/src/Framework/InfoLevel.php b/src/Framework/InfoLevel.php
new file mode 100644
index 00000000..58de3ab9
--- /dev/null
+++ b/src/Framework/InfoLevel.php
@@ -0,0 +1,10 @@
+listen($host, $port, SWOOLE_SOCK_TCP);
+ if (!$port) {
+ throw new Exception("listen fail.");
+ }
+ $port->set(array(
+ "open_eof_split" => true,
+ 'package_eof' => "\r\n",
+ ));
+ $port->on("Connect", array(__CLASS__, 'onConnect'));
+ $port->on("Close", array(__CLASS__, 'onClose'));
+ $port->on("Receive", array(__CLASS__, 'onReceive'));
+ if (method_exists($serv, 'getCallback')) {
+ self::$oriPipeMessageCallback = $serv->getCallback('PipeMessage');
+ }
+ $serv->on("PipeMessage", array(__CLASS__, 'onPipeMessage'));
+ self::$serv = $serv;
+ }
+
+ static function onConnect($serv, $fd, $reactor_id) {
+ self::$contexts[$fd]['worker_id'] = $serv->worker_id;
+ self::output($fd, implode("\r\n", self::$menu));
+ }
+
+ static function output($fd, $msg) {
+ if (!isset(self::$contexts[$fd]['worker_id'])) {
+ $msg .= "\r\nworker#" . self::$serv->worker_id . "$ ";
+ } else {
+ $msg .= "\r\nworker#" . self::$contexts[$fd]['worker_id'] . "$ ";
+ }
+ self::$serv->send($fd, $msg);
+ }
+
+ static function onClose($serv, $fd, $reactor_id) {
+ unset(self::$contexts[$fd]);
+ }
+
+ static function onPipeMessage($serv, $src_worker_id, $message) {
+ //不是 debug 消息
+ if (!is_string($message) or substr($message, 0, strlen(self::STX)) != self::STX) {
+ if (self::$oriPipeMessageCallback == null) {
+ trigger_error("require swoole-4.3.0 or later.", E_USER_WARNING);
+ return true;
+ }
+ return call_user_func(self::$oriPipeMessageCallback, $serv, $src_worker_id, $message);
+ } else {
+ $request = unserialize(substr($message, strlen(self::STX)));
+ self::call($request['fd'], $request['func'], $request['args']);
+ }
+ return true ;
+ }
+
+ static protected function call($fd, $func, $args) {
+ ob_start();
+ call_user_func_array($func, $args);
+ self::output($fd, ob_get_clean());
+ }
+
+ static protected function exec($fd, $func, $args) {
+ //不在当前Worker进程
+ if (self::$contexts[$fd]['worker_id'] != self::$serv->worker_id) {
+ self::$serv->sendMessage(self::STX . serialize(['fd' => $fd, 'func' => $func, 'args' => $args]), self::$contexts[$fd]['worker_id']);
+ } else {
+ self::call($fd, $func, $args);
+ }
+ }
+
+ static function getCoros() {
+ var_export(iterator_to_array(Coroutine::listCoroutines()));
+ }
+
+ static function getBackTrace($_cid) {
+ $info = Co::getBackTrace($_cid);
+ if (!$info) {
+ echo "coroutine $_cid not found.";
+ } else {
+ echo get_debug_print_backtrace($info);
+ }
+ }
+
+ static function printVariant($var) {
+ $var = ltrim($var, '$ ');
+ var_dump($var);
+ var_dump($$var);
+ }
+
+ static function evalCode($code) {
+ eval($code . ';');
+ }
+
+ /**
+ * @param $serv server
+ * @param $fd
+ * @param $reactor_id
+ * @param $data
+ */
+ static function onReceive($serv, $fd, $reactor_id, $data) {
+ $args = explode(" ", $data, 2);
+ $cmd = trim($args[0]);
+ unset($args[0]);
+ switch ($cmd) {
+ case 'w':
+ case 'worker':
+ if (!isset($args[1])) {
+ self::output($fd, "invalid command.");
+ break;
+ }
+ $dstWorkerId = intval($args[1]);
+ self::$contexts[$fd]['worker_id'] = $dstWorkerId;
+ self::output($fd, "[switching to worker " . self::$contexts[$fd]['worker_id'] . "]");
+ break;
+ case 'e':
+ case 'exec':
+ if (!isset($args[1])) {
+ self::output($fd, "invalid command.");
+ break;
+ }
+ $var = trim($args[1]);
+ self::exec($fd, 'self::evalCode', [$var]);
+ break;
+ case 'p':
+ case 'print':
+ $var = trim($args[1]);
+ self::exec($fd, 'self::printVariant', [$var]);
+ break;
+ case 'h':
+ case 'help':
+ self::output($fd, implode("\r\n", self::$menu));
+ break;
+ case 's':
+ case 'stats':
+ $stats = $serv->stats();
+ self::output($fd, var_export($stats, true));
+ break;
+ case 'c':
+ case 'coros':
+ self::exec($fd, 'self::getCoros', []);
+ break;
+ /**
+ * 查看协程堆栈
+ */
+ case 'bt':
+ case 'b':
+ case 'backtrace':
+ if (empty($args[1])) {
+ self::output($fd, "invalid command [" . trim($args[1]) . "].");
+ break;
+ }
+ $_cid = intval($args[1]);
+ self::exec($fd, 'self::getBackTrace', [$_cid]);
+ break;
+ case 'i':
+ case 'info':
+ if (empty($args[1])) {
+ self::output($fd, "invalid command [" . trim($args[1]) . "].");
+ break;
+ }
+ $_fd = intval($args[1]);
+ $info = $serv->getClientInfo($_fd);
+ if (!$info) {
+ self::output($fd, "connection $_fd not found.");
+ } else {
+ self::output($fd, var_export($info, true));
+ }
+ break;
+ case 'l':
+ case 'list':
+ $tmp = array();
+ foreach ($serv->connections as $fd) {
+ $tmp[] = $fd;
+ if (count($tmp) > self::PAGESIZE) {
+ self::output($fd, json_encode($tmp));
+ $tmp = array();
+ }
+ }
+ if (count($tmp) > 0) {
+ self::output($fd, json_encode($tmp));
+ }
+ break;
+ case 'q':
+ case 'quit':
+ $serv->close($fd);
+ break;
+ default:
+ self::output($fd, "unknow command[$cmd]");
+ break;
+ }
+ }
+}
+
+function get_debug_print_backtrace($traces) {
+ $ret = array();
+ foreach ($traces as $i => $call) {
+ $object = '';
+ if (isset($call['class'])) {
+ $object = $call['class'] . $call['type'];
+ if (is_array($call['args'])) {
+ foreach ($call['args'] as &$arg) {
+ get_arg($arg);
+ }
+ }
+ }
+ $ret[] = '#' . str_pad($i, 3, ' ')
+ . $object . $call['function'] . '(' . implode(', ', $call['args'])
+ . ') called at [' . $call['file'] . ':' . $call['line'] . ']';
+ }
+ return implode("\n", $ret);
+}
+
+function get_arg(&$arg) {
+ if (is_object($arg)) {
+ $arr = (array)$arg;
+ $args = array();
+ foreach ($arr as $key => $value) {
+ if (strpos($key, chr(0)) !== false) {
+ $key = ''; // Private variable found
+ }
+ $args[] = '[' . $key . '] => ' . get_arg($value);
+ }
+ $arg = get_class($arg) . ' Object (' . implode(',', $args) . ')';
+ }
+}
\ No newline at end of file
diff --git a/src/Framework/ZMBuf.php b/src/Framework/ZMBuf.php
new file mode 100755
index 00000000..e60c840f
--- /dev/null
+++ b/src/Framework/ZMBuf.php
@@ -0,0 +1,95 @@
+get($key); }
+
+ public static function resetCache() {
+ self::$cache = [];
+ self::$connect = [];
+ self::$time_nlp = null;
+ }
+
+ /**
+ * 初始化atomic计数器
+ */
+ public static function initAtomic() {
+ foreach(ZMBuf::globals("init_atomics") as $k => $v) {
+ self::$atomics[$k] = new Atomic($v);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Framework/global_functions.php b/src/Framework/global_functions.php
new file mode 100644
index 00000000..2a4d0026
--- /dev/null
+++ b/src/Framework/global_functions.php
@@ -0,0 +1,146 @@
+ $v) {
+ if (trim($v) == "") continue;
+ $ls[] = trim($v);
+ }
+ return $ls;
+}
+
+function unicode_decode($str) {
+ return preg_replace_callback('/\\\\u([0-9a-f]{4})/i', function ($matches) {
+ return mb_convert_encoding(pack("H*", $matches[1]), "UTF-8", "UCS-2BE");
+ }, $str);
+}
+
+/**
+ * 获取模块文件夹下的每个类文件的类名称
+ * @param $dir
+ * @param string $indoor_name
+ * @return array
+ */
+function getAllClasses($dir, $indoor_name) {
+ $list = scandir($dir);
+ $classes = [];
+ unset($list[0], $list[1]);
+ foreach ($list as $v) {
+ //echo "Finding " . $dir . $v . PHP_EOL;
+ //echo "At " . $indoor_name . PHP_EOL;
+ if (is_dir($dir . $v)) $classes = array_merge($classes, getAllClasses($dir . $v . "/", $indoor_name . "\\" . $v));
+ elseif (mb_substr($v, -4) == ".php") {
+ $class_name = $indoor_name . "\\" . mb_substr($v, 0, -4);
+ $classes [] = $class_name;
+ }
+ }
+ return $classes;
+}
+
+function matchPattern($pattern, $context) {
+ if (mb_substr($pattern, 0, 1) == "" && mb_substr($context, 0, 1) == "")
+ return true;
+ if ('*' == mb_substr($pattern, 0, 1) && "" != mb_substr($pattern, 1, 1) && "" == mb_substr($context, 0, 1))
+ return false;
+ if (mb_substr($pattern, 0, 1) == mb_substr($context, 0, 1))
+ return matchPattern(mb_substr($pattern, 1), mb_substr($context, 1));
+ if (mb_substr($pattern, 0, 1) == "*")
+ return matchPattern(mb_substr($pattern, 1), $context) || matchPattern($pattern, mb_substr($context, 1));
+ return false;
+}
+
+function split_explode($del, $str, $divide_en = false) {
+ $str = explode($del, $str);
+ for ($i = 0; $i < mb_strlen($str[0]); $i++) {
+ if (
+ is_numeric(mb_substr($str[0], $i, 1)) &&
+ (
+ !is_numeric(mb_substr($str[0], $i - 1, 1)) &&
+ mb_substr($str[0], $i - 1, 1) != ' ' &&
+ ctype_alpha(mb_substr($str[0], $i - 1, 1)) === false
+ )
+ ) {
+ $str[0] = mb_substr($str[0], 0, $i) . " " . mb_substr($str[0], $i);
+ } elseif (
+ $divide_en &&
+ ctype_alnum(mb_substr($str[0], $i, 1)) &&
+ !ctype_alnum(mb_substr($str[0], $i - 1, 1)) &&
+ mb_substr($str[0], $i - 1, 1) != ' '
+ ) {
+ $str[0] = mb_substr($str[0], 0, $i) . ' ' . mb_substr($str[0], $i);
+ }
+ }
+ $str = implode($del, $str);
+ //echo $str."\n";
+ $ls = [];
+ foreach (explode($del, $str) as $k => $v) {
+ if (trim($v) == "") continue;
+ $ls[] = $v;
+ }
+ //var_dump($ls);
+ return $ls == [] ? [""] : $ls;
+}
+
+function matchArgs($pattern, $context) {
+ $result = [];
+ if (matchPattern($pattern, $context)) {
+ if (mb_strpos($pattern, "*") === false) return [];
+ $exp = explode("*", $pattern);
+ $i = 0;
+ foreach ($exp as $k => $v) {
+ //echo "[MATCH$k] " . $v . PHP_EOL;
+ if ($v == "" && $k == 0) continue;
+ elseif ($v == "" && $k == count($exp) - 1) {
+ $context = $context . "^EOL";
+ $v = "^EOL";
+ }
+ $cur_var = "";
+ //echo mb_substr($context, $i) . "|" . $v . PHP_EOL;
+ $ori = $i;
+ while (($a = mb_substr($context, $i, mb_strlen($v))) != $v && $a != "") {
+ $cur_var .= mb_substr($context, $i, 1);
+ ++$i;
+ }
+ if ($i != $ori || $k == 1 || $k == count($exp) - 1) {
+ //echo $cur_var . PHP_EOL;
+ $result[] = $cur_var;
+ }
+ $i += mb_strlen($v);
+ }
+ return $result;
+ } else return false;
+}
\ No newline at end of file
diff --git a/src/Module/Example/Hello.php b/src/Module/Example/Hello.php
new file mode 100644
index 00000000..d8dc46e8
--- /dev/null
+++ b/src/Module/Example/Hello.php
@@ -0,0 +1,34 @@
+getQQ()." 已连接!");
+ }
+ /**
+ * @CQCommand("你好")
+ */
+ public function hello(){
+ return "你好啊,我是由炸毛框架构建的机器人!";
+ }
+}
\ No newline at end of file
diff --git a/src/Module/TestMod/CQTest.php b/src/Module/TestMod/CQTest.php
new file mode 100644
index 00000000..e352d4d2
--- /dev/null
+++ b/src/Module/TestMod/CQTest.php
@@ -0,0 +1,45 @@
+getQQ()." connected.");
+ }
+
+ /**
+ * @CQCommand("多命令a")
+ * @CQCommand(regexMatch="*是什么")
+ * @param $arg
+ * @return string
+ * @throws \ZM\Exception\DbException
+ */
+ public function hello($arg) {
+ return "我也不知道".$arg[0]."是什么呀";
+ }
+
+ /**
+ * @CQNotice(notice_type="group_admin")
+ */
+ public function onNotice(){
+
+ }
+}
\ No newline at end of file
diff --git a/src/Module/TestMod/Hola.php b/src/Module/TestMod/Hola.php
new file mode 100644
index 00000000..f1a0e615
--- /dev/null
+++ b/src/Module/TestMod/Hola.php
@@ -0,0 +1,28 @@
+connection->close();
+ }
+}
\ No newline at end of file
diff --git a/src/Scheduler/MessageEvent.php b/src/Scheduler/MessageEvent.php
new file mode 100644
index 00000000..b3e67f6e
--- /dev/null
+++ b/src/Scheduler/MessageEvent.php
@@ -0,0 +1,29 @@
+client = $client;
+ $this->frame = $frame;
+ }
+
+ public function onActivate() {
+ //TODO: 写Scheduler计时器内的处理逻辑
+ }
+}
\ No newline at end of file
diff --git a/src/Scheduler/Scheduler.php b/src/Scheduler/Scheduler.php
new file mode 100644
index 00000000..3d8a1deb
--- /dev/null
+++ b/src/Scheduler/Scheduler.php
@@ -0,0 +1,140 @@
+initProcess();
+ elseif ($method == self::REMOTE) $this->initRemote();
+ }
+
+ private function initProcess() { //TODO: 完成Process模式的代码
+ $m_pid = posix_getpid();
+ $this->process = new Process(function (Process $worker) use ($m_pid) { self::onWork($worker, $m_pid); }, false, 2, true);
+ $this->pid = $this->process->start();
+ while (1) {
+ $ret = Process::wait();
+ if ($ret) {
+ $this->process = new Process(function (Process $worker) use ($m_pid) { self::onWork($worker, $m_pid); }, false, 2, true);
+ $this->pid = $this->process->start();
+ echo "Reboot done.\n";
+ }
+ }
+ }
+
+ private function initRemote() {
+ define('WORKING_DIR', __DIR__ . '../..');
+ $this->requireGlobalFunctions();
+ $this->registerAutoloader('classLoader');
+ $this->settings = new GlobalConfig();
+ if (!$this->settings->success) die("Failed to load global config. Please check config/global.php file");
+ $this->defineProperties();
+
+ //start swoole Framework
+ $this->selfCheck();
+ try {
+ $host = $this->settings->get("scheduler")["host"];
+ $port = $this->settings->get("scheduler")["port"];
+ $token = $this->settings->get("scheduler")["token"];
+ $this->client = new Client($host, $port);
+ $path = "/" . ($token != "" ? ("?token=" . urlencode($token)) : "");
+ while (true) {
+ if ($this->client->upgrade($path)) {
+ while (true) {
+ $recv = $this->client->recv();
+ if ($recv instanceof Frame) {
+ (new MessageEvent($this->client, $recv))->onActivate();
+ } else {
+ break;
+ }
+ }
+ } else {
+ Console::warning("无法连接Framework,将在5秒后重连...");
+ Coroutine::sleep(5);
+ }
+ }
+ } catch (Exception $e) {
+ Console::error($e);
+ }
+ }
+
+ private function requireGlobalFunctions() {
+ /** @noinspection PhpIncludeInspection */
+ require WORKING_DIR . '/src/Framework/global_functions.php';
+ }
+
+ private function registerAutoloader(string $string) {
+ if (!spl_autoload_register($string)) die("Failed to register autoloader named \"$string\" !");
+ }
+
+ private function defineProperties() {
+ define("ZM_START_TIME", microtime(true));
+ define("ZM_DATA", $this->settings->get("zm_data"));
+ //define("CONFIG_DIR", $this->settings->get("config_dir"));
+ define("CRASH_DIR", $this->settings->get("crash_dir"));
+ }
+
+ private function selfCheck() {
+ if (!extension_loaded("swoole")) die("Can not find swoole extension.\n");
+ if (!extension_loaded("sockets")) die("Can not find sockets extension.\n");
+ if (!function_exists("mb_substr")) die("Can not find mbstring extension.\n");
+ if (substr(PHP_VERSION, 0, 1) != "7") die("PHP >=7 required.\n");
+ //if (!class_exists("ZipArchive")) die("Can not find Zip extension.\n");
+ if (!file_exists(CRASH_DIR . "last_error.log")) die("Can not find log file.\n");
+ return true;
+ }
+
+ private static function onWork(Process $worker, $m_pid) {
+ swoole_set_process_name('php-scheduler');
+ for ($j = 0; $j < 16000; $j++) {
+ self::checkMpid($worker, $m_pid);
+ echo "msg: {$j}\n";
+ sleep(1);
+ }
+ }
+
+ private static function checkMpid(Process $worker, $m_pid) {
+ if (!Process::kill($m_pid, 0)) {
+ $worker->exit(); //主进程死了我也死
+ // 这句提示,实际是看不到的.需要写到日志中
+ echo "Master process exited, I [{$worker['pid']}] also quit\n";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/cqbot/api/CQ.php b/src/ZM/API/CQ.php
similarity index 83%
rename from src/cqbot/api/CQ.php
rename to src/ZM/API/CQ.php
index 58b99f23..b62a6338 100644
--- a/src/cqbot/api/CQ.php
+++ b/src/ZM/API/CQ.php
@@ -1,10 +1,11 @@
$v) {
- $ss = explode("=", $v);
- $sk = array_shift($ss);
- $array[$sk] = implode("=", $ss);
- }
- return ["type" => $type, "params" => $array, "start" => $start, "end" => $end];
+ /**
+ * 转义CQ码
+ * @param $msg
+ * @return mixed
+ */
+ public static function escape($msg) {
+ $msg = str_replace("&", "&", $msg);
+ $msg = str_replace("[", "[", $msg);
+ $msg = str_replace("]", "]", $msg);
+ return $msg;
}
+ public static function removeCQ($msg) {
+ while (($cq = ZMUtil::getCQ($msg)) !== null) {
+ $msg = str_replace(mb_substr($msg, $cq["start"], $cq["end"] - $cq["start"] + 1), "", $msg);
+ }
+ return $msg;
+ }
}
\ No newline at end of file
diff --git a/src/ZM/API/CQAPI.php b/src/ZM/API/CQAPI.php
new file mode 100644
index 00000000..cd6edb56
--- /dev/null
+++ b/src/ZM/API/CQAPI.php
@@ -0,0 +1,249 @@
+ $data["group_id"], "message" => $msg], $yield);
+ case "private":
+ return self::send_private_msg($conn, ["user_id" => $data["user_id"], "message" => $msg], $yield);
+ case "discuss":
+ return self::send_discuss_msg($conn, ["discuss_id" => $data["discuss_id"], "message" => $msg], $yield);
+ }
+ return null;
+ }
+
+ public static function __callStatic($name, $arg) {
+ $all = self::getSupportedAPIs();
+ $find = null;
+ if (in_array($name, $all)) $find = $name;
+ else {
+ foreach ($all as $v) {
+ if (strtolower($name) == strtolower(str_replace("_", "", $v))) {
+ $find = $v;
+ break;
+ }
+ }
+ }
+ if ($find === null) {
+ Console::error("Unknown API " . $name);
+ return false;
+ }
+ $reply = ["action" => $find];
+ if (!is_array($arg[1])) {
+ Console::error("Error when parsing params. Please make sure your params is an array.");
+ return false;
+ }
+ if ($arg[1] != []) {
+ $reply["params"] = $arg[1];
+ }
+ if (!($arg[0] instanceof CQConnection)) {
+ $robot = ConnectionManager::getByType("qq", ["self_id" => $arg[0]]);
+ if ($robot == []) {
+ Console::error("发送错误,机器人连接不存在!");
+ return false;
+ }
+ $arg[0] = $robot[0];
+ }
+ return self::processAPI($arg[0], $reply, $arg[2] ?? null);
+ }
+
+ /********************** non-API Part **********************/
+
+ private static function getSupportedAPIs() {
+ return [
+ "send_private_msg",
+ "send_group_msg",
+ "send_discuss_msg",
+ "send_msg",
+ "delete_msg",
+ "send_like",
+ "set_group_kick",
+ "set_group_ban",
+ "set_group_anonymous_ban",
+ "set_group_whole_ban",
+ "set_group_admin",
+ "set_group_anonymous",
+ "set_group_card",
+ "set_group_leave",
+ "set_group_special_title",
+ "set_discuss_leave",
+ "set_friend_add_request",
+ "set_group_add_request",
+ "get_login_info",
+ "get_stranger_info",
+ "get_group_list",
+ "get_group_member_info",
+ "get_group_member_list",
+ "get_cookies",
+ "get_csrf_token",
+ "get_credentials",
+ "get_record",
+ "get_status",
+ "get_version_info",
+ "set_restart",
+ "set_restart_plugin",
+ "clean_data_dir",
+ "clean_plugin_log",
+ "_get_friend_list",
+ "_get_group_info",
+ "_get_vip_info",
+ //异步API
+ "send_private_msg_async",
+ "send_group_msg_async",
+ "send_discuss_msg_async",
+ "send_msg_async",
+ "delete_msg_async",
+ "set_group_kick_async",
+ "set_group_ban_async",
+ "set_group_anonymous_ban_async",
+ "set_group_whole_ban_async",
+ "set_group_admin_async",
+ "set_group_anonymous_async",
+ "set_group_card_async",
+ "set_group_leave_async",
+ "set_group_special_title_async",
+ "set_discuss_leave_async",
+ "set_friend_add_request_async",
+ "set_group_add_request_async"
+ ];
+ }
+
+ public static function getLoggedAPIs() {
+ return [
+ "send_private_msg",
+ "send_group_msg",
+ "send_discuss_msg",
+ "send_msg",
+ "send_private_msg_async",
+ "send_group_msg_async",
+ "send_discuss_msg_async",
+ "send_msg_async"
+ ];
+ }
+
+ /**
+ * @param WSConnection $connection
+ * @param $reply
+ * @param |null $function
+ * @return bool
+ */
+ private static function processAPI($connection, $reply, $function = null) {
+ $api_id = ZMBuf::$atomics["wait_msg_id"]->get();
+ $reply["echo"] = $api_id;
+ ZMBuf::$atomics["wait_msg_id"]->add(1);
+
+ if (is_callable($function)) {
+ ZMBuf::appendKey("sent_api", $api_id, [
+ "data" => $reply,
+ "time" => microtime(true),
+ "func" => $function,
+ "self_id" => $connection->getQQ()
+ ]);
+ } elseif ($function === true) {
+ ZMBuf::appendKey("sent_api", $api_id, [
+ "data" => $reply,
+ "time" => microtime(true),
+ "coroutine" => Co::getuid(),
+ "self_id" => $connection->getQQ()
+ ]);
+ } else {
+ ZMBuf::appendKey("sent_api", $api_id, [
+ "data" => $reply,
+ "time" => microtime(true),
+ "self_id" => $connection->getQQ()
+ ]);
+ }
+ if ($connection->push(json_encode($reply))) {
+ Console::msg($reply, $connection->getQQ());
+ ZMBuf::$atomics["out_count"]->add(1);
+ if ($function === true) {
+ Co::suspend();
+ $data = ZMBuf::get("sent_api")[$api_id];
+ ZMBuf::unsetByValue("sent_api", $reply["echo"]);
+ return isset($data['result']) ? $data['result'] : null;
+ }
+ return true;
+ } else {
+ $response = [
+ "status" => "failed",
+ "retcode" => 999,
+ "data" => null,
+ "self_id" => $connection->getQQ()
+ ];
+ $s = ZMBuf::get("sent_api")[$reply["echo"]];
+ if (($s["func"] ?? null) !== null)
+ call_user_func($s["func"], $response, $reply);
+ ZMBuf::unsetByValue("sent_api", $reply["echo"]);
+ if ($function === true) return null;
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Annotation/AnnotationBase.php b/src/ZM/Annotation/AnnotationBase.php
new file mode 100644
index 00000000..3c6ab548
--- /dev/null
+++ b/src/ZM/Annotation/AnnotationBase.php
@@ -0,0 +1,29 @@
+ $v) {
+ $str .= "\n\t" . $k . " => ";
+ if (is_string($v)) $str .= "\"$v\"";
+ elseif (is_numeric($v)) $str .= $v;
+ elseif (is_bool($v)) $str .= ($v ? "TRUE" : "FALSE");
+ elseif (is_array($v)) $str .= json_encode($v, JSON_UNESCAPED_UNICODE);
+ elseif ($v instanceof Closure) $str .= "@AnonymousFunction";
+ elseif (is_null($v)) $str .= "NULL";
+ else $str .= "@Unknown";
+ }
+ return $str;
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Annotation/AnnotationParser.php b/src/ZM/Annotation/AnnotationParser.php
new file mode 100644
index 00000000..428d6d17
--- /dev/null
+++ b/src/ZM/Annotation/AnnotationParser.php
@@ -0,0 +1,223 @@
+getMethods(ReflectionMethod::IS_PUBLIC);
+ $class_annotations = $reader->getClassAnnotations($reflection_class);
+ foreach ($class_annotations as $vs) {
+ if ($vs instanceof Closed) {
+ continue 2;
+ } elseif ($vs instanceof Controller) {
+ $class_prefix = $vs->prefix;
+ } elseif ($vs instanceof SaveBuffer) {
+ DataProvider::addSaveBuffer($vs->buf_name, $vs->sub_folder);
+ }
+ }
+ foreach ($methods as $vs) {
+ $method_annotations = $reader->getMethodAnnotations($vs);
+ foreach ($method_annotations as $vss) {
+ if ($vss instanceof Rule) $vss = self::registerRuleEvent($vss, $vs, $reflection_class);
+ else $vss = self::registerMethod($vss, $vs, $reflection_class);
+
+ if ($vss instanceof SwooleEventAt) ZMBuf::$events[SwooleEventAt::class][] = $vss;
+ elseif ($vss instanceof SwooleEventAfter) ZMBuf::$events[SwooleEventAfter::class][] = $vss;
+ elseif ($vss instanceof CQMessage) ZMBuf::$events[CQMessage::class][] = $vss;
+ elseif ($vss instanceof CQNotice) ZMBuf::$events[CQNotice::class][] = $vss;
+ elseif ($vss instanceof CQRequest) ZMBuf::$events[CQRequest::class][] = $vss;
+ elseif ($vss instanceof CQMetaEvent) ZMBuf::$events[CQMetaEvent::class][] = $vss;
+ elseif ($vss instanceof CQCommand) ZMBuf::$events[CQCommand::class][] = $vss;
+ elseif ($vss instanceof RequestMapping) self::registerRequestMapping($vss, $vs, $reflection_class, $class_prefix);
+ elseif ($vss instanceof CustomAnnotation) ZMBuf::$events[get_class($vss)][] = $vss;
+ elseif ($vss instanceof CQBefore) ZMBuf::$events[CQBefore::class][$vss->cq_event][] = $vss;
+ elseif ($vss instanceof CQAfter) ZMBuf::$events[CQAfter::class][$vss->cq_event][] = $vss;
+ }
+ }
+ }
+
+ //给支持level的排个序
+ foreach (ZMBuf::$events as $class_name => $v) {
+ if ((new $class_name()) instanceof Level) {
+ for ($i = 0; $i < count(ZMBuf::$events[$class_name]) - 1; ++$i) {
+ for ($j = 0; $j < count(ZMBuf::$events[$class_name]) - $i - 1; ++$j) {
+ $l1 = ZMBuf::$events[$class_name][$j]->level;
+ $l2 = ZMBuf::$events[$class_name][$j + 1]->level;
+ if ($l1 < $l2) {
+ $t = ZMBuf::$events[$class_name][$j + 1];
+ ZMBuf::$events[$class_name][$j + 1] = ZMBuf::$events[$class_name][$j];
+ ZMBuf::$events[$class_name][$j] = $t;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static function getRuleCallback($rule_str) {
+ $func = null;
+ $rule = $rule_str;
+ if ($rule != "") {
+ $asp = explode(":", $rule);
+ $asp_name = array_shift($asp);
+ $rest = implode(":", $asp);
+ //Swoole 事件时走此switch
+ switch ($asp_name) {
+ case "connectType": //websocket连接类型
+ $func = function (WSConnection $connection) use ($rest) {
+ return $connection->getType() == $rest ? true : false;
+ };
+ break;
+ case "containsGet": //handle http request事件时才能用
+ case "containsPost":
+ $get_list = explode(",", $rest);
+ if ($asp_name == "containsGet")
+ $func = function ($request) use ($get_list) {
+ foreach ($get_list as $v) if (!isset($request->get[$v])) return false;
+ return true;
+ };
+ else
+ $func = function ($request) use ($get_list) {
+ foreach ($get_list as $v) if (!isset($request->post[$v])) return false;
+ return true;
+ };
+ /*
+ if ($controller_prefix != '') {
+ $p = ZMBuf::$req_mapping_node;
+ $prefix_exp = explode("/", $controller_prefix);
+ foreach ($prefix_exp as $k => $v) {
+ if ($v == "" || $v == ".." || $v == ".") {
+ unset($prefix_exp[$k]);
+ }
+ }
+ while (($shift = array_shift($prefix_exp)) !== null) {
+ $p->addRoute($shift, new MappingNode($shift));
+ $p = $p->getRoute($shift);
+ }
+ if ($p->getNodeName() != "/") {
+ $p->setMethod($method->getName());
+ $p->setClass($class->getName());
+ $p->setRule($func);
+ return "mapped";
+ }
+ }*/
+ break;
+ case "containsJson": //handle http request事件时才能用
+ $json_list = explode(",", $rest);
+ $func = function ($json) use ($json_list) {
+ foreach ($json_list as $v) if (!isset($json[$v])) return false;
+ return true;
+ };
+ break;
+ case "dataEqual": //handle websocket message事件时才能用
+ $func = function ($data) use ($rest) { return $data == $rest; };
+ break;
+ }
+ switch ($asp_name) {
+ case "msgMatch": //handle cq message事件时才能用
+ $func = function ($msg) use ($rest) { return matchPattern($rest, $msg); };
+ break;
+ case "msgEqual": //handle cq message事件时才能用
+ $func = function ($msg) use ($rest) { return trim($msg) == $rest; };
+ break;
+
+ }
+ }
+ return $func;
+ }
+
+ private static function registerRuleEvent(?AnnotationBase $vss, ReflectionMethod $method, ReflectionClass $class) {
+ $vss->callback = self::getRuleCallback($vss->getRule());
+ $vss->method = $method->getName();
+ $vss->class = $class->getName();
+ return $vss;
+ }
+
+ private static function registerMethod(?AnnotationBase $vss, ReflectionMethod $method, ReflectionClass $class) {
+ $vss->method = $method->getName();
+ $vss->class = $class->getName();
+ return $vss;
+ }
+
+ private static function registerRequestMapping(RequestMapping $vss, ReflectionMethod $method, ReflectionClass $class, string $prefix) {
+ $prefix_exp = explode("/", $prefix);
+ $route_exp = explode("/", $vss->route);
+ foreach ($prefix_exp as $k => $v) {
+ if ($v == "" || $v == ".." || $v == ".") {
+ unset($prefix_exp[$k]);
+ }
+ }
+ foreach ($route_exp as $k => $v) {
+ if ($v == "" || $v == ".." || $v == ".") {
+ unset($route_exp[$k]);
+ }
+ }
+ $a = ZMBuf::$req_mapping_node;
+ $p = $a;
+ if ($prefix_exp == [] && $route_exp == []) {
+ $p->setMethod($method->getName());
+ $p->setClass($class->getName());
+ $p->setRequestMethod($vss->request_method);
+ return;
+ }
+ while (($shift = array_shift($prefix_exp)) !== null) {
+ $p->addRoute($shift, new MappingNode($shift));
+ $p = $p->getRoute($shift);
+ }
+ while (($shift = array_shift($route_exp)) !== null) {
+ if (mb_substr($shift, 0, 1) == "{" && mb_substr($shift, -1, 1) == "}") {
+ $p->removeAllRoute();
+ }
+ $p->addRoute($shift, new MappingNode($shift));
+ $p = $p->getRoute($shift);
+ }
+ $p->setMethod($method->getName());
+ $p->setClass($class->getName());
+ $p->setRequestMethod($vss->request_method);
+ }
+
+ private static function loadAnnotationClasses() {
+ $class = getAllClasses(WORKING_DIR . "/src/ZM/Annotation/", "ZM\\Annotation");
+ foreach ($class as $v) {
+ $s = WORKING_DIR . '/src/' . str_replace("\\", "/", $v) . ".php";
+ require_once $s;
+ }
+ $class = getAllClasses(WORKING_DIR . "/src/Custom/Annotation/", "Custom\\Annotation");
+ foreach ($class as $v) {
+ $s = WORKING_DIR . '/src/' . str_replace("\\", "/", $v) . ".php";
+ require_once $s;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Annotation/CQ/CQAfter.php b/src/ZM/Annotation/CQ/CQAfter.php
new file mode 100644
index 00000000..3bfd3c35
--- /dev/null
+++ b/src/ZM/Annotation/CQ/CQAfter.php
@@ -0,0 +1,22 @@
+level;
+ }
+
+ /**
+ * @param mixed $level
+ */
+ public function setLevel($level): void {
+ $this->level = $level;
+ }
+
+}
\ No newline at end of file
diff --git a/src/ZM/Annotation/CQ/CQCommand.php b/src/ZM/Annotation/CQ/CQCommand.php
new file mode 100644
index 00000000..55848d13
--- /dev/null
+++ b/src/ZM/Annotation/CQ/CQCommand.php
@@ -0,0 +1,35 @@
+level; }
+
+ /**
+ * @param int $level
+ */
+ public function setLevel(int $level) { $this->level = $level; }
+
+}
\ No newline at end of file
diff --git a/src/ZM/Annotation/CQ/CQMessage.php b/src/ZM/Annotation/CQ/CQMessage.php
new file mode 100644
index 00000000..70917033
--- /dev/null
+++ b/src/ZM/Annotation/CQ/CQMessage.php
@@ -0,0 +1,41 @@
+level; }
+
+ public function setLevel(int $level) {
+ $this->level = $level;
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Annotation/CQ/CQMetaEvent.php b/src/ZM/Annotation/CQ/CQMetaEvent.php
new file mode 100644
index 00000000..000f922b
--- /dev/null
+++ b/src/ZM/Annotation/CQ/CQMetaEvent.php
@@ -0,0 +1,40 @@
+level; }
+
+ /**
+ * @param int $level
+ */
+ public function setLevel(int $level): void {
+ $this->level = $level;
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Annotation/CQ/CQNotice.php b/src/ZM/Annotation/CQ/CQNotice.php
new file mode 100644
index 00000000..b24fcc88
--- /dev/null
+++ b/src/ZM/Annotation/CQ/CQNotice.php
@@ -0,0 +1,42 @@
+level;
+ }
+
+ /**
+ * @param int $level
+ */
+ public function setLevel(int $level): void {
+ $this->level = $level;
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Annotation/CQ/CQRequest.php b/src/ZM/Annotation/CQ/CQRequest.php
new file mode 100644
index 00000000..bdf6cd91
--- /dev/null
+++ b/src/ZM/Annotation/CQ/CQRequest.php
@@ -0,0 +1,42 @@
+level;
+ }
+
+ /**
+ * @param int $level
+ */
+ public function setLevel(int $level): void {
+ $this->level = $level;
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Annotation/Http/Controller.php b/src/ZM/Annotation/Http/Controller.php
new file mode 100644
index 00000000..9556a6bd
--- /dev/null
+++ b/src/ZM/Annotation/Http/Controller.php
@@ -0,0 +1,23 @@
+node = $node_name; }
+
+ public function addRoute(string $route_name, MappingNode $route_node) { $this->route[$route_name] = $route_node; }
+
+ /**
+ * @param string $shift
+ * @return MappingNode|null
+ */
+ public function getRoute(string $shift) {
+ return $this->route[$shift] ?? null;
+ }
+
+ public function getRealRoute(string $shift, array &$bind_params) {
+ if (mb_substr(key($this->route), 0, 1) == "{" && mb_substr(key($this->route), -1, 1) == "}") {
+ $param_name = mb_substr(current($this->route)->getNodeName(), 1, -1);
+ $bind_params[$param_name] = $shift;
+ return current($this->route);
+ } else return $this->route[$shift] ?? null;
+ }
+
+ public function setMethod($method) { $this->method = $method; }
+
+ public function setClass($class) { $this->class = $class; }
+
+ public function setRequestMethod($method) {
+ if (is_string($method)) $this->request_method = [$method];
+ else $this->request_method = $method;
+ }
+
+ public function getNodeName() { return $this->node; }
+
+ public function getRule() { return $this->rule; }
+
+ public function setRule(Closure $rule): void { $this->rule = $rule; }
+
+ public function removeAllRoute() { $this->route = []; }
+
+ /**
+ * @return null
+ */
+ public function getMethod() {
+ return $this->method;
+ }
+
+ /**
+ * @return array
+ */
+ public function getRequestMethod(): array {
+ return $this->request_method;
+ }
+
+ /**
+ * @return null
+ */
+ public function getClass() {
+ return $this->class;
+ }
+
+ /**
+ * @return string
+ */
+ public function getNode(): string {
+ return $this->node;
+ }
+
+ public function __toString() {
+ $str = "[" . $this->node . "] => ";
+ if ($this->class != "" && $this->class != null)
+ $str .= "\n\t" . $this->class . "->" . $this->method . ": " . implode(", ", $this->request_method);
+ $str .= "\n\t[Route] => [";
+ foreach ($this->route as $k => $v) {
+ $r = $v;
+ $r = explode("\n", $r);
+ foreach ($r as $ks => $vs) {
+ $r[$ks] = "\t" . $r[$ks];
+ }
+ $r = implode("\n", $r);
+ $str .= "\n\t" . $r;
+ }
+ $str .= "\n]";
+ return $str;
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Annotation/Module/Closed.php b/src/ZM/Annotation/Module/Closed.php
new file mode 100644
index 00000000..d4e6a143
--- /dev/null
+++ b/src/ZM/Annotation/Module/Closed.php
@@ -0,0 +1,18 @@
+type;
+ }
+
+ /**
+ * @param string $type
+ */
+ public function setType(string $type): void {
+ $this->type = $type;
+ }
+
+ /**
+ * @return string
+ */
+ public function getRule(): string {
+ return $this->rule;
+ }
+
+ /**
+ * @param string $rule
+ */
+ public function setRule(string $rule): void {
+ $this->rule = $rule;
+ }
+
+ /**
+ * @return int
+ */
+ public function getLevel(): int {
+ return $this->level;
+ }
+
+ /**
+ * @param int $level
+ */
+ public function setLevel(int $level): void {
+ $this->level = $level;
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/ZM/Annotation/Swoole/SwooleEventAt.php b/src/ZM/Annotation/Swoole/SwooleEventAt.php
new file mode 100644
index 00000000..7f91df13
--- /dev/null
+++ b/src/ZM/Annotation/Swoole/SwooleEventAt.php
@@ -0,0 +1,76 @@
+type;
+ }
+
+ /**
+ * @param string $type
+ */
+ public function setType(string $type): void {
+ $this->type = $type;
+ }
+
+ /**
+ * @return string
+ */
+ public function getRule(): string {
+ return $this->rule;
+ }
+
+ /**
+ * @param string $rule
+ */
+ public function setRule(string $rule): void {
+ $this->rule = $rule;
+ }
+
+ /**
+ * @return int
+ */
+ public function getLevel(): int {
+ return $this->level;
+ }
+
+ /**
+ * @param int $level
+ */
+ public function setLevel(int $level): void {
+ $this->level = $level;
+ }
+
+}
\ No newline at end of file
diff --git a/src/ZM/Connection/CQConnection.php b/src/ZM/Connection/CQConnection.php
new file mode 100644
index 00000000..a3f2a483
--- /dev/null
+++ b/src/ZM/Connection/CQConnection.php
@@ -0,0 +1,23 @@
+self_id = $self_id;
+ }
+
+ public function getQQ(){
+ return $this->self_id;
+ }
+
+ public function getType() {
+ return "qq";
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Connection/ConnectionManager.php b/src/ZM/Connection/ConnectionManager.php
new file mode 100644
index 00000000..d4290c29
--- /dev/null
+++ b/src/ZM/Connection/ConnectionManager.php
@@ -0,0 +1,73 @@
+fd == $fd) return $v;
+ }
+ return null;
+ }
+
+ /**
+ * @param string $type
+ * @param array $option
+ * @return WSConnection[]
+ */
+ public static function getByType(string $type, $option = []) {
+ $conn = [];
+ foreach (ZMBuf::$connect as $v) {
+ foreach ($option as $ks => $vs) {
+ if (($v->$ks ?? "") == $vs) continue;
+ else continue 2;
+ }
+ if ($v->getType() == $type) $conn[] = $v;
+ }
+ return $conn;
+ }
+
+ public static function getTypeClassName(string $type) {
+ switch (strtolower($type)) {
+ case "qq":
+ case "universal":
+ return CQConnection::class;
+ case "webconsole":
+ return WCConnection::class;
+ case "proxy":
+ return ProxyConnection::class;
+ default:
+ foreach (ZMBuf::$custom_connection_class as $v) {
+ /** @var WSConnection $r */
+ $r = new $v(ZMBuf::$server, -1);
+ if ($r->getType() == strtolower($type)) return $v;
+ }
+ return UnknownConnection::class;
+ }
+ }
+
+ public static function close($fd) {
+ foreach (ZMBuf::$connect as $k => $v) {
+ if ($v->fd == $fd) {
+ ZMBuf::$server->close($fd);
+ unset(ZMBuf::$connect[$k]);
+ break;
+ }
+ }
+ }
+
+ public static function registerCustomClass() {
+ $classes = getAllClasses(WORKING_DIR . "/src/Custom/Connection/", "Custom\\Connection");
+ ZMBuf::$custom_connection_class = $classes;
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Connection/ProxyConnection.php b/src/ZM/Connection/ProxyConnection.php
new file mode 100644
index 00000000..f43083f0
--- /dev/null
+++ b/src/ZM/Connection/ProxyConnection.php
@@ -0,0 +1,13 @@
+server = $server;
+ $this->fd = $fd;
+ }
+
+ public abstract function getType();
+
+ public function exists() {
+ return $this->available = $this->server->exist($this->fd);
+ }
+
+ public function close() {
+ ConnectionManager::close($this->fd);
+ }
+
+ public function push($data, $push_error_record = true) {
+ if ($data === null || $data == "") {
+ Console::warning("推送了空消息");
+ return false;
+ }
+ if (!$this->server->exist($this->fd)) {
+ Console::warning("Swoole 原生 websocket连接池中无此连接");
+ return false;
+ }
+ if ($this->server->push($this->fd, $data) === false) {
+ $data = unicode_decode($data);
+ if ($push_error_record) Logger::writeSwooleLog("API push failed. Data: " . $data);
+ Console::error("websocket数据未成功推送,长度:" . strlen($data));
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/DB/DB.php b/src/ZM/DB/DB.php
new file mode 100644
index 00000000..65c11aef
--- /dev/null
+++ b/src/ZM/DB/DB.php
@@ -0,0 +1,120 @@
+get();
+ if ($conn === false) {
+ throw new DbException("无法连接SQL!" . $line);
+ }
+ $result = $conn->query($line) === false ? false : ($conn->errno != 0 ? false : true);
+ ZMBuf::$sql_pool->put($conn);
+ return $result;
+ } catch (DBException $e) {
+ if (ZMBuf::get("sql_log") === true) {
+ $log =
+ "[" . date("Y-m-d H:i:s") .
+ " " . round(microtime(true) - $starttime, 5) .
+ "] " . $line . " (Error:" . $e->getMessage() . ")\n";
+ Coroutine::writeFile(CRASH_DIR . "sql.log", $log, FILE_APPEND);
+ }
+ Console::error($e->getMessage());
+ return false;
+ }
+ }
+
+ public static function rawQuery(string $line, $params) {
+ if (ZMBuf::get("sql_log") === true) {
+ $starttime = microtime(true);
+ }
+ try {
+ $conn = ZMBuf::$sql_pool->get();
+ if ($conn === false) {
+ throw new DbException("无法连接SQL!" . $line);
+ }
+ $ps = $conn->prepare($line);
+ if ($ps === false) {
+ $conn->close();
+ ZMBuf::$sql_pool->connect_cnt -= 1;
+ throw new DbException("SQL语句查询错误," . $line . ",错误信息:" . $conn->error);
+ } else {
+ if (!($ps instanceof Statement)) {
+ throw new DbException("语句查询错误!" . $line);
+ }
+ if ($params == []) $result = $ps->execute();
+ elseif (!is_array($params)) {
+ $result = $ps->execute([$params]);
+ } else $result = $ps->execute($params);
+ ZMBuf::$sql_pool->put($conn);
+ if ($ps->errno != 0) {
+ throw new DBException("语句[$line]错误!" . $ps->error);
+ //echo json_encode(debug_backtrace(), 128 | 256);
+ }
+ if (ZMBuf::get("sql_log") === true) {
+ $log =
+ "[" . date("Y-m-d H:i:s") .
+ " " . round(microtime(true) - $starttime, 4) .
+ "] " . $line . " " . json_encode($params, JSON_UNESCAPED_UNICODE) . "\n";
+ Coroutine::writeFile(CRASH_DIR . "sql.log", $log, FILE_APPEND);
+ }
+ return $result;
+ }
+ } catch (DBException $e) {
+ if (ZMBuf::get("sql_log") === true) {
+ $log =
+ "[" . date("Y-m-d H:i:s") .
+ " " . round(microtime(true) - $starttime, 4) .
+ "] " . $line . " " . json_encode($params, JSON_UNESCAPED_UNICODE) . " (Error:" . $e->getMessage() . ")\n";
+ Coroutine::writeFile(CRASH_DIR . "sql.log", $log, FILE_APPEND);
+ }
+ Console::error($e->getMessage());
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/DB/DeleteBody.php b/src/ZM/DB/DeleteBody.php
new file mode 100644
index 00000000..bc6e590b
--- /dev/null
+++ b/src/ZM/DB/DeleteBody.php
@@ -0,0 +1,28 @@
+table = $table;
+ }
+
+ public function save() {
+ list($sql, $param) = $this->getWhereSQL();
+ return DB::rawQuery("DELETE FROM " . $this->table->getTableName() . " WHERE " . $sql, $param);
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/DB/InsertBody.php b/src/ZM/DB/InsertBody.php
new file mode 100644
index 00000000..a8a130d4
--- /dev/null
+++ b/src/ZM/DB/InsertBody.php
@@ -0,0 +1,28 @@
+table = $table;
+ $this->row = $row;
+ }
+
+ public function save() {
+ DB::rawQuery('INSERT INTO ' . $this->table->getTableName() . ' VALUES ('.implode(',', array_fill(0, 5, '?')).')', $this->row);
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/DB/SelectBody.php b/src/ZM/DB/SelectBody.php
new file mode 100644
index 00000000..d4daab19
--- /dev/null
+++ b/src/ZM/DB/SelectBody.php
@@ -0,0 +1,88 @@
+table = $table;
+ $this->select_thing = $select_thing;
+ }
+
+ public function get() { return $this->fetchAll(); }
+
+ public function fetchAll() {
+ if ($this->table->isCacheEnabled()) {
+ $rr = md5(implode(",", $this->select_thing) . serialize($this->where_thing));
+ if (array_key_exists($rr, $this->table->cache)) {
+ Console::info('SQL query cached: ' . $rr, date("[H:i:s ") . 'DB] ');
+ return $this->table->cache[$rr]->getResult();
+ }
+ }
+ $this->execute();
+ if ($this->table->isCacheEnabled() && !in_array($rr, $this->table->cache)) {
+ $this->table->cache[$rr] = $this;
+ }
+ return $this->getResult();
+ }
+
+ public function fetchFirst() {
+ return $this->fetchAll()[0] ?? null;
+ }
+
+ public function value() {
+ $r = $this->fetchFirst();
+ if ($r === null) return null;
+ return current($r);
+ }
+
+ public function execute() {
+ $str = $this->queryPrepare();
+ $this->result = DB::rawQuery($str[0], $str[1]);
+ }
+
+ public function getResult() { return $this->result; }
+
+ public function equals(SelectBody $body) {
+ if ($this->select_thing != $body->getSelectThing()) return false;
+ elseif ($this->where_thing == $body->getWhereThing()) return false;
+ else return true;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getSelectThing() { return $this->select_thing; }
+
+ /**
+ * @return array
+ */
+ public function getWhereThing() { return $this->where_thing; }
+
+ private function queryPrepare() {
+ $msg = "SELECT " . implode(", ", $this->select_thing) . " FROM " . $this->table->getTableName();
+ $sql = $this->table->paintWhereSQL($this->where_thing['='] ?? [], '=');
+ if ($sql[0] != '') {
+ $msg .= " WHERE " . $sql[0];
+ $array = $sql[1];
+ $sql = $this->table->paintWhereSQL($this->where_thing['!='] ?? [], '!=');
+ if ($sql[0] != '') $msg .= " AND " . $sql[0];
+ $array = array_merge($array, $sql[1]);
+ }
+ return [$msg, $array ?? []];
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/DB/Table.php b/src/ZM/DB/Table.php
new file mode 100644
index 00000000..cf85d4cd
--- /dev/null
+++ b/src/ZM/DB/Table.php
@@ -0,0 +1,79 @@
+enable_cache = $enable_cache;
+ $this->table_name = $table_name;
+ self::$table_instance[$table_name] = $this;
+ }
+
+ public static function getTableInstance($table_name) {
+ if (isset(self::$table_instance[$table_name])) return self::$table_instance[$table_name];
+ else return null;
+ }
+
+ public function select($what = []) {
+ return new SelectBody($this, $what == [] ? ["*"] : $what);
+ }
+
+ public function where($column, $operation_or_value, $value = null){
+ return (new SelectBody($this, ["*"]))->where($column, $operation_or_value, $value);
+ }
+
+ public function insert($row) {
+ $this->cache = [];
+ return new InsertBody($this, $row);
+ }
+
+ public function update(array $set_value) {
+ $this->cache = [];
+ return new UpdateBody($this, $set_value);
+ }
+
+ public function delete() {
+ $this->cache = [];
+ return new DeleteBody($this);
+ }
+
+ public function statement($line){
+ $this->cache = [];
+ //TODO: 无返回的statement语句
+ }
+
+ public function paintWhereSQL($rule, $operator) {
+ if ($rule == []) return ["", []];
+ $msg = "";
+ $param = [];
+ foreach ($rule as $k => $v) {
+ if ($msg == "") {
+ $msg .= $k . " $operator ? ";
+ } else {
+ $msg .= "AND " . $k . " $operator ?";
+ }
+ $param[] = $v;
+ }
+ return [$msg, $param];
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getTableName() { return $this->table_name; }
+
+ public function isCacheEnabled() { return $this->enable_cache; }
+}
\ No newline at end of file
diff --git a/src/ZM/DB/UpdateBody.php b/src/ZM/DB/UpdateBody.php
new file mode 100644
index 00000000..f9681758
--- /dev/null
+++ b/src/ZM/DB/UpdateBody.php
@@ -0,0 +1,50 @@
+table = $table;
+ $this->set_value = $set_value;
+ }
+
+ /**
+ * @throws DbException
+ */
+ public function save(){
+ $arr = [];
+ $msg = [];
+ foreach($this->set_value as $k => $v) {
+ $msg []= $k .' = ?';
+ $arr[]=$v;
+ }
+ if(($msg ?? []) == []) throw new DbException('update value sets can not be empty!');
+ $line = 'UPDATE '.$this->table->getTableName().' SET '.implode(', ', $msg);
+ if($this->where_thing != []) {
+ list($sql, $param) = $this->getWhereSQL();
+ $arr = array_merge($arr, $param);
+ $line .= ' WHERE '.$sql;
+ }
+ return DB::rawQuery($line, $arr);
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/DB/WhereBody.php b/src/ZM/DB/WhereBody.php
new file mode 100644
index 00000000..eb2b651f
--- /dev/null
+++ b/src/ZM/DB/WhereBody.php
@@ -0,0 +1,33 @@
+where_thing['='][$column] = $operation_or_value;
+ elseif ($value !== null) $this->where_thing[$operation_or_value][$column] = $value;
+ else $this->where_thing['='][$column] = $operation_or_value;
+ return $this;
+ }
+
+ protected function getWhereSQL(){
+ $param = [];
+ $msg = '';
+ foreach($this->where_thing as $k => $v) {
+ foreach($v as $ks => $vs) {
+ if($param != []) {
+ $msg .= ' AND '.$ks ." $k ?";
+ } else {
+ $msg .= "$ks $k ?";
+ }
+ $param []=$vs;
+ }
+ }
+ return [$msg, $param];
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/DBCache/CourseCache.php b/src/ZM/DBCache/CourseCache.php
new file mode 100644
index 00000000..82681c3d
--- /dev/null
+++ b/src/ZM/DBCache/CourseCache.php
@@ -0,0 +1,18 @@
+data = $data;
+ $this->swoole_event = $event;
+ $this->circle = $circle;
+ }
+
+ public function onBefore() {
+ foreach (ZMBuf::$events[CQBefore::class][CQMessage::class] ?? [] as $v) {
+ $c = $v->class;
+ $class = new $c([
+ "data" => $this->data,
+ "frame" => $this->swoole_event->frame,
+ "server" => $this->swoole_event->server,
+ "connection" => ConnectionManager::get($this->swoole_event->frame->fd)
+ ], ModHandleType::CQ_MESSAGE);
+ $r = call_user_func_array([$class, $v->method], []);
+ if (!$r || $class->block_continue) return false;
+ }
+ foreach (ZMBuf::get("wait_api", []) as $k => $v) {
+ if($this->data["user_id"] == $v["user_id"] &&
+ $this->data["self_id"] == $v["self_id"] &&
+ $this->data["message_type"] == $v["message_type"] &&
+ ($this->data[$this->data["message_type"]."_id"] ?? $this->data["user_id"]) ==
+ ($v[$v["message_type"]."_id"] ?? $v["user_id"])){
+ $v["result"] = $this->data["message"];
+ ZMBuf::appendKey("wait_api", $k, $v);
+ Co::resume($v["coroutine"]);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /** @noinspection PhpRedundantCatchClauseInspection */
+ public function onActivate() {
+ try {
+ $word = split_explode(" ", str_replace("\r", "", $this->data["message"]));
+ if (count(explode("\n", $word[0])) >= 2) {
+ $enter = explode("\n", $this->data["message"]);
+ $first = split_explode(" ", array_shift($enter));
+ $word = array_merge($first, $enter);
+ foreach ($word as $k => $v) {
+ $word[$k] = trim($word[$k]);
+ }
+ }
+ /** @var ModBase[] $obj */
+ $obj = [];
+ foreach (ZMBuf::$events[CQCommand::class] ?? [] as $v) {
+ /** @var CQCommand $v */
+ if ($v->match == "" && $v->regexMatch == "") continue;
+ else {
+ $c = $v->class;
+ if (!isset($obj[$c]))
+ $obj[$c] = new $c([
+ "data" => $this->data,
+ "frame" => $this->swoole_event->frame,
+ "server" => $this->swoole_event->server,
+ "connection" => ConnectionManager::get($this->swoole_event->frame->fd)
+ ], ModHandleType::CQ_MESSAGE);
+ if ($word[0] != "" && $v->match == $word[0]) {
+ $r = call_user_func([$obj[$c], $v->method], $word);
+ if (is_string($r)) $obj[$c]->reply($r);
+ $this->function_call = true;
+ return;
+ } elseif (($args = matchArgs($v->regexMatch, $this->data["message"])) !== false) {
+ $r = call_user_func([$obj[$c], $v->method], $args);
+ if (is_string($r)) $obj[$c]->reply($r);
+ $this->function_call = true;
+ return;
+ }
+ }
+ }
+ foreach (ZMBuf::$events[CQMessage::class] ?? [] as $v) {
+ /** @var CQMessage $v */
+ if (
+ ($v->message == '' || ($v->message != '' && $v->message == $this->data["message"])) &&
+ ($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == $this->data["user_id"])) &&
+ ($v->group_id == 0 || ($v->group_id != 0 && $v->group_id == ($this->data["group_id"] ?? 0))) &&
+ ($v->discuss_id == 0 || ($v->discuss_id != 0 && $v->discuss_id == ($this->data["discuss_id"] ?? 0))) &&
+ ($v->message_type == '' || ($v->message_type != '' && $v->message_type == $this->data["message_type"])) &&
+ ($v->raw_message == '' || ($v->raw_message != '' && $v->raw_message == $this->data["raw_message"]))) {
+ $c = $v->class;
+ if (!isset($obj[$c]))
+ $obj[$c] = new $c([
+ "data" => $this->data,
+ "frame" => $this->swoole_event->frame,
+ "server" => $this->swoole_event->server,
+ "connection" => ConnectionManager::get($this->swoole_event->frame->fd)
+ ], ModHandleType::CQ_MESSAGE);
+ $r = call_user_func([$obj[$c], $v->method], $this->data["message"]);
+ if (is_string($r)) $obj[$c]->reply($r);
+ if ($obj[$c]->block_continue) return;
+ }
+ }
+ } catch (WaitTimeoutException $e) {
+
+ $e->module->finalReply($e->getMessage());
+ }
+ }
+
+ /**
+ * 在调用完事件后执行的
+ */
+ public function onAfter() {
+ foreach (ZMBuf::$events[CQAfter::class][CQMessage::class] ?? [] as $v) {
+ $c = $v->class;
+ $class = new $c([
+ "data" => $this->data,
+ "frame" => $this->swoole_event->frame,
+ "server" => $this->swoole_event->server,
+ "connection" => ConnectionManager::get($this->swoole_event->frame->fd)
+ ], ModHandleType::CQ_MESSAGE);
+ $r = call_user_func_array([$class, $v->method], []);
+ if (!$r || $class->block_continue) return false;
+ }
+ return true;
+ }
+
+ public function hasReply() {
+ return $this->function_call;
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Event/CQ/MetaEvent.php b/src/ZM/Event/CQ/MetaEvent.php
new file mode 100644
index 00000000..1460464e
--- /dev/null
+++ b/src/ZM/Event/CQ/MetaEvent.php
@@ -0,0 +1,71 @@
+data = $data;
+ $this->swoole_event = $event;
+ $this->circle = $circle;
+ }
+
+ public function onBefore() {
+ foreach (ZMBuf::$events[CQBefore::class][CQMetaEvent::class] ?? [] as $v) {
+ $c = $v->class;
+ /** @var CQMetaEvent $v */
+ $class = new $c([
+ "data" => $this->data,
+ "frame" => $this->swoole_event->frame,
+ "server" => $this->swoole_event->server,
+ "connection" => ConnectionManager::get($this->swoole_event->frame->fd)
+ ], ModHandleType::CQ_META_EVENT);
+ $r = call_user_func_array([$class, $v->method], []);
+ if (!$r || $class->block_continue) return false;
+ }
+ return true;
+ }
+
+ /** @noinspection PhpRedundantCatchClauseInspection */
+ public function onActivate() {
+ try {
+ /** @var ModBase[] $obj */
+ $obj = [];
+ foreach (ZMBuf::$events[CQMetaEvent::class] ?? [] as $v) {
+ /** @var CQMetaEvent $v */
+ if (
+ ($v->meta_event_type == '' || ($v->meta_event_type != '' && $v->meta_event_type == $this->data["meta_event_type"])) &&
+ ($v->sub_type == 0 || ($v->sub_type != 0 && $v->sub_type == $this->data["sub_type"]))) {
+ $c = $v->class;
+ if (!isset($obj[$c]))
+ $obj[$c] = new $c([
+ "data" => $this->data,
+ "frame" => $this->swoole_event->frame,
+ "server" => $this->swoole_event->server,
+ "connection" => ConnectionManager::get($this->swoole_event->frame->fd)
+ ], ModHandleType::CQ_META_EVENT);
+ $r = call_user_func([$obj[$c], $v->method]);
+ if (is_string($r)) $obj[$c]->reply($r);
+ if ($obj[$c]->block_continue) return;
+ }
+ }
+ } catch (WaitTimeoutException $e) {
+ $e->module->finalReply($e->getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Event/CQ/NoticeEvent.php b/src/ZM/Event/CQ/NoticeEvent.php
new file mode 100644
index 00000000..fe7c62fb
--- /dev/null
+++ b/src/ZM/Event/CQ/NoticeEvent.php
@@ -0,0 +1,88 @@
+data = $data;
+ $this->swoole_event = $event;
+ $this->circle = $circle;
+ }
+
+ public function onBefore() {
+ foreach (ZMBuf::$events[CQBefore::class][CQNotice::class] ?? [] as $v) {
+ $c = $v->class;
+ /** @var CQNotice $v */
+ $class = new $c([
+ "data" => $this->data,
+ "frame" => $this->swoole_event->frame,
+ "server" => $this->swoole_event->server,
+ "connection" => ConnectionManager::get($this->swoole_event->frame->fd)
+ ], ModHandleType::CQ_NOTICE);
+ $r = call_user_func_array([$class, $v->method], []);
+ if (!$r || $class->block_continue) return false;
+ }
+ return true;
+ }
+
+ public function onActivate() {
+ try {
+ /** @var ModBase[] $obj */
+ $obj = [];
+ foreach (ZMBuf::$events[CQNotice::class] ?? [] as $v) {
+ /** @var CQNotice $v */
+ if (
+ ($v->notice_type == '' || ($v->notice_type != '' && $v->notice_type == $this->data["notice_type"])) &&
+ ($v->sub_type == 0 || ($v->sub_type != 0 && $v->sub_type == $this->data["sub_type"])) &&
+ ($v->group_id == 0 || ($v->group_id != 0 && $v->group_id == ($this->data["group_id"] ?? 0))) &&
+ ($v->operator_id == 0 || ($v->operator_id != 0 && $v->operator_id == ($this->data["operator_id"] ?? 0)))) {
+ $c = $v->class;
+ if (!isset($obj[$c]))
+ $obj[$c] = new $c([
+ "data" => $this->data,
+ "frame" => $this->swoole_event->frame,
+ "server" => $this->swoole_event->server,
+ "connection" => ConnectionManager::get($this->swoole_event->frame->fd)
+ ], ModHandleType::CQ_NOTICE);
+ $r = call_user_func([$obj[$c], $v->method]);
+ if (is_string($r)) $obj[$c]->reply($r);
+ if ($obj[$c]->block_continue) return;
+ }
+ }
+ } /** @noinspection PhpRedundantCatchClauseInspection */ catch (WaitTimeoutException $e) {
+ $e->module->finalReply($e->getMessage());
+ }
+ }
+
+ public function onAfter() {
+ foreach (ZMBuf::$events[CQAfter::class][CQNotice::class] ?? [] as $v) {
+ $c = $v->class;
+ $class = new $c([
+ "data" => $this->data,
+ "frame" => $this->swoole_event->frame,
+ "server" => $this->swoole_event->server,
+ "connection" => ConnectionManager::get($this->swoole_event->frame->fd)
+ ], ModHandleType::CQ_NOTICE);
+ $r = call_user_func_array([$class, $v->method], []);
+ if (!$r || $class->block_continue) return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Event/CQ/RequestEvent.php b/src/ZM/Event/CQ/RequestEvent.php
new file mode 100644
index 00000000..ad99b7c3
--- /dev/null
+++ b/src/ZM/Event/CQ/RequestEvent.php
@@ -0,0 +1,89 @@
+data = $data;
+ $this->swoole_event = $event;
+ $this->circle = $circle;
+ }
+
+ public function onBefore() {
+ foreach (ZMBuf::$events[CQBefore::class][CQRequest::class] ?? [] as $v) {
+ $c = $v->class;
+ /** @var CQRequest $v */
+ $class = new $c([
+ "data" => $this->data,
+ "frame" => $this->swoole_event->frame,
+ "server" => $this->swoole_event->server,
+ "connection" => ConnectionManager::get($this->swoole_event->frame->fd)
+ ], ModHandleType::CQ_REQUEST);
+ $r = call_user_func_array([$class, $v->method], []);
+ if (!$r || $class->block_continue) return false;
+ }
+ return true;
+ }
+
+ /** @noinspection PhpRedundantCatchClauseInspection */
+ public function onActivate() {
+ try {
+ /** @var ModBase[] $obj */
+ $obj = [];
+ foreach (ZMBuf::$events[CQRequest::class] ?? [] as $v) {
+ /** @var CQRequest $v */
+ if (
+ ($v->request_type == '' || ($v->request_type != '' && $v->request_type == $this->data["request_type"])) &&
+ ($v->sub_type == 0 || ($v->sub_type != 0 && $v->sub_type == $this->data["sub_type"])) &&
+ ($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == ($this->data["user_id"] ?? 0))) &&
+ ($v->comment == 0 || ($v->comment != 0 && $v->comment == ($this->data["comment"] ?? 0)))) {
+ $c = $v->class;
+ if (!isset($obj[$c]))
+ $obj[$c] = new $c([
+ "data" => $this->data,
+ "frame" => $this->swoole_event->frame,
+ "server" => $this->swoole_event->server,
+ "connection" => ConnectionManager::get($this->swoole_event->frame->fd)
+ ], ModHandleType::CQ_REQUEST);
+ $r = call_user_func([$obj[$c], $v->method]);
+ if (is_string($r)) $obj[$c]->reply($r);
+ if ($obj[$c]->block_continue) return;
+ }
+ }
+ } catch (WaitTimeoutException $e) {
+ $e->module->finalReply($e->getMessage());
+ }
+ }
+
+ public function onAfter() {
+ foreach (ZMBuf::$events[CQAfter::class][CQRequest::class] ?? [] as $v) {
+ $c = $v->class;
+ $class = new $c([
+ "data" => $this->data,
+ "frame" => $this->swoole_event->frame,
+ "server" => $this->swoole_event->server,
+ "connection" => ConnectionManager::get($this->swoole_event->frame->fd)
+ ], ModHandleType::CQ_REQUEST);
+ $r = call_user_func_array([$class, $v->method], []);
+ if (!$r || $class->block_continue) return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Event/Event.php b/src/ZM/Event/Event.php
new file mode 100644
index 00000000..f78aff33
--- /dev/null
+++ b/src/ZM/Event/Event.php
@@ -0,0 +1,11 @@
+onActivate()->onAfter();
+ Console::log("\n=== Worker #" . $param0->worker_id . " 已启动 ===\n", "gold");
+ } catch (Exception $e) {
+ Console::error("Worker加载出错!停止服务!");
+ Console::error($e->getMessage() . "\n" . $e->getTraceAsString());
+
+ ZMUtil::stop();
+ return;
+ }
+ break;
+ case "message":
+ (new MessageEvent($param0, $param1))->onActivate()->onAfter();
+ break;
+ case "request":
+ try {
+ (new RequestEvent($param0, $param1))->onActivate()->onAfter();
+ } catch (Exception $e) {
+ /** @var Response $param1 */
+ $param1->status(500);
+ if (!$param1->isEnd()) $param1->end("Internal server error: " . $e->getMessage());
+ Console::error("Internal server error (500), caused by uncaught exception.");
+ Console::log($e->getTraceAsString(), "gray");
+ }
+ break;
+ case "open":
+ (new WSOpenEvent($param0, $param1))->onActivate()->onAfter();
+ break;
+ case "close":
+ (new WSCloseEvent($param0, $param1))->onActivate()->onAfter();
+ break;
+ }
+ Console::info(Console::setColor("Event: " . $event_name . " 运行了 " . round(microtime(true) - $starttime, 5) . " 秒", "gold"));
+ }
+
+ public static function callCQEvent($event_data, MessageEvent $event, $level = 0) {
+ if ($level >= 5) {
+ Console::warning("Recursive call reached " . $level . " times");
+ Console::stackTrace();
+ return false;
+ }
+ $starttime = microtime(true);
+ switch ($event_data["post_type"]) {
+ case "message":
+ $event = new CQ\MessageEvent($event_data, $event, $level);
+ if ($event->onBefore()) $event->onActivate();
+ $event->onAfter();
+ return $event->hasReply();
+ break;
+ case "notice":
+ $event = new CQ\NoticeEvent($event_data, $event, $level);
+ if($event->onBefore()) $event->onActivate();
+ $event->onAfter();
+ return true;
+ case "request":
+ $event = new CQ\RequestEvent($event_data, $event, $level);
+ if($event->onBefore()) $event->onActivate();
+ $event->onAfter();
+ return true;
+ case "meta_event":
+ $event = new CQ\MetaEvent($event_data, $event, $level);
+ if($event->onBefore()) $event->onActivate();
+ return true;
+ }
+ unset($starttime);
+ return false;
+ }
+
+ public static function callCQResponse($req) {
+ //Console::info("收到来自API连接的回复:".json_encode($req, 128|256));
+ if(isset($req["echo"]) && ZMBuf::array_key_exists("sent_api", $req["echo"])) {
+ $status = $req["status"];
+ $retcode = $req["retcode"];
+ $data = $req["data"];
+ $origin = ZMBuf::get("sent_api")[$req["echo"]];
+ $self_id = $origin["self_id"];
+ $response = [
+ "status" => $status,
+ "retcode" => $retcode,
+ "data" => $data,
+ "self_id" => $self_id
+ ];
+ if (($origin["func"] ?? null) !== null) {
+ call_user_func($origin["func"], $response, $origin["data"]);
+ } elseif (($origin["coroutine"] ?? false) !== false) {
+ $p = ZMBuf::get("sent_api");
+ $p[$req["echo"]]["result"] = $response;
+ ZMBuf::set("sent_api", $p);
+ Co::resume($origin['coroutine']);
+ }
+ ZMBuf::unsetByValue("sent_api", $req["echo"]);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Event/Swoole/MessageEvent.php b/src/ZM/Event/Swoole/MessageEvent.php
new file mode 100644
index 00000000..c30279af
--- /dev/null
+++ b/src/ZM/Event/Swoole/MessageEvent.php
@@ -0,0 +1,94 @@
+server = $server;
+ $this->frame = $frame;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function onActivate() {
+ ZMUtil::checkWait();
+ try {
+ if (ConnectionManager::get($this->frame->fd)->getType() == "qq") {
+ $data = json_decode($this->frame->data, true);
+ if (isset($data["post_type"]))
+ EventHandler::callCQEvent($data, $this, 0);
+ else
+ EventHandler::callCQResponse($data);
+ }
+ foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
+ if (strtolower($v->type) == "message" && $this->parseSwooleRule($v)) {
+ $conn = ConnectionManager::get($this->frame->fd);
+ $c = $v->class;
+ /** @var ModBase $class */
+ $class = new $c(["server" => $this->server, "frame" => $this->frame, "connection" => $conn], ModHandleType::SWOOLE_MESSAGE);
+ call_user_func_array([$class, $v->method], [$conn]);
+ if ($class->block_continue) break;
+ }
+ }
+ } catch (Exception $e) {
+ Console::error("出现错误: " . $e->getMessage());
+ }
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function onAfter() {
+ foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
+ if (strtolower($v->type) == "message" && $this->parseSwooleRule($v) === true) {
+ $conn = ConnectionManager::get($this->frame->fd);
+ $c = $v->class;
+ /** @var ModBase $class */
+ $class = new $c(["server" => $this->server, "frame" => $this->frame, "connection" => $conn], ModHandleType::SWOOLE_MESSAGE);
+ call_user_func_array([$class, $v->method], []);
+ if ($class->block_continue) break;
+ }
+ }
+ return $this;
+ }
+
+ private function parseSwooleRule($v) {
+ switch (explode(":", $v->rule)[0]) {
+ case "connectType": //websocket连接类型
+ if ($v->callback instanceof Closure) return call_user_func($v->callback, ConnectionManager::get($this->frame->fd));
+ break;
+ case "dataEqual": //handle websocket message事件时才能用
+ if ($v->callback instanceof Closure) return call_user_func($v->callback, $this->frame->data);
+ break;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Event/Swoole/RequestEvent.php b/src/ZM/Event/Swoole/RequestEvent.php
new file mode 100644
index 00000000..b91eb3d2
--- /dev/null
+++ b/src/ZM/Event/Swoole/RequestEvent.php
@@ -0,0 +1,132 @@
+request = $request;
+ $this->response = $response;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function onActivate() {
+ ZMUtil::checkWait();
+ foreach (ZMBuf::globals("http_header") as $k => $v) {
+ $this->response->setHeader($k, $v);
+ }
+ $uri = $this->request->server["request_uri"];
+ if ($uri != "/") {
+ $uri = explode("/", $uri);
+ $uri = array_diff($uri, ["..", "", "."]);
+ $node = ZMBuf::$req_mapping_node;
+ $params = [];
+ while (true) {
+ $r = array_shift($uri);
+ if ($r === null) break;
+ if (($node2 = $node->getRealRoute($r, $params)) !== null) {
+ $node = $node2;
+ continue;
+ } else {
+ $this->responseStatus(404);
+ return $this;
+ }
+ }
+ if ($node->getRule() === null || call_user_func($node->getRule(), $this->request) === true) { //判断规则是否存在,如果有规则则走一遍规则
+ if (in_array(strtoupper($this->request->server["request_method"]), $node->getRequestMethod())) { //判断目标方法在不在里面
+ $c_name = $node->getClass();
+ /** @var ModBase $class */
+ $class = new $c_name(["request" => $this->request, "response" => $this->response, "params" => $params], ModHandleType::SWOOLE_REQUEST);
+ $r = call_user_func_array([$class, $node->getMethod()], [$params]);
+ if (is_string($r) && !$this->response->isEnd()) $this->response->end($r);
+ if ($class->block_continue) return $this;
+ if ($this->response->isEnd()) return $this;
+ }
+ }
+ } else {
+ if (($node = ZMBuf::$req_mapping_node)->getMethod() !== null) {
+ if (in_array(strtoupper($this->request->server["request_method"]), $node->getRequestMethod())) { //判断目标方法在不在里面
+ $c_name = $node->getClass();
+ /** @var ModBase $class */
+ $class = new $c_name(["request" => $this->request, "response" => $this->response], ModHandleType::SWOOLE_REQUEST);
+ $r = call_user_func_array([$class, $node->getMethod()], []);
+ if (is_string($r) && !$this->response->isEnd()) $this->response->end($r);
+ if ($class->block_continue) return $this;
+ if ($this->response->isEnd()) return $this;
+ }
+ }
+ foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
+ if (strtolower($v->type) == "request" && $this->parseSwooleRule($v)) {
+ $c = $v->class;
+ $class = new $c(["request" => $this->request, "response" => $this->response]);
+ $r = call_user_func_array([$class, $v->method], []);
+ if ($class->block_continue) break;
+ }
+ }
+ }
+ if (!$this->response->isEnd()) {
+ $this->response->status(404);
+ $this->response->end(ZMUtil::getHttpCodePage(404));
+ }
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function onAfter() {
+ foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
+ if (strtolower($v->type) == "request" && $this->parseSwooleRule($v)) {
+ $c = $v->class;
+ $class = new $c(["request" => $this->request, "response" => $this->response]);
+ call_user_func_array([$class, $v->method], []);
+ if ($class->block_continue) break;
+ }
+ }
+ return $this;
+ }
+
+ private function responseStatus(int $int) {
+ $this->response->status($int);
+ $this->response->end();
+ }
+
+ private function parseSwooleRule($v) {
+ switch ($v->rule) {
+ case "containsGet":
+ case "containsPost":
+ if ($v->callback instanceof Closure) return call_user_func($v->callback, $this->request);
+ break;
+ case "containsJson":
+ $content = $this->request->rawContent();
+ $content = json_decode($content, true);
+ if ($content === null) return false;
+ if ($v->callback instanceof Closure) return call_user_func($v->callback, $content);
+ break;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Event/Swoole/SwooleEvent.php b/src/ZM/Event/Swoole/SwooleEvent.php
new file mode 100644
index 00000000..73b1d54e
--- /dev/null
+++ b/src/ZM/Event/Swoole/SwooleEvent.php
@@ -0,0 +1,20 @@
+server = $server;
+ $this->fd = $fd;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function onActivate() {
+ ZMUtil::checkWait();
+ foreach(ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
+ if(strtolower($v->type) == "close" && $this->parseSwooleRule($v)) {
+ $c = $v->class;
+ $class = new $c(["server" => $this->server, "fd" => $this->fd], ModHandleType::SWOOLE_CLOSE);
+ call_user_func_array([$class, $v->method], []);
+ if($class->block_continue) break;
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function onAfter() {
+ foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
+ if (strtolower($v->type) == "close" && $this->parseSwooleRule($v) === true) {
+ $c = $v->class;
+ /** @var ModBase $class */
+ $class = new $c(["server" => $this->server, "fd" => $this->fd], ModHandleType::SWOOLE_CLOSE);
+ call_user_func_array([$class, $v->method], []);
+ if($class->block_continue) break;
+ }
+ }
+ return $this;
+ }
+
+ private function parseSwooleRule($v) {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Event/Swoole/WSOpenEvent.php b/src/ZM/Event/Swoole/WSOpenEvent.php
new file mode 100644
index 00000000..af6ef95b
--- /dev/null
+++ b/src/ZM/Event/Swoole/WSOpenEvent.php
@@ -0,0 +1,93 @@
+server = $server;
+ $this->request = $request;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function onActivate() {
+ ZMUtil::checkWait();
+ $type = strtolower($this->request->get["type"] ?? $this->request->header["x-client-role"] ?? "");
+ $type_conn = ConnectionManager::getTypeClassName($type);
+ if ($type_conn == CQConnection::class) {
+ $qq = $this->request->get["qq"] ?? $this->request->header["x-self-id"] ?? "";
+ $self_token = ZMBuf::globals("access_token") ?? "";
+ $remote_token = $this->request->get["token"] ?? (isset($header["authorization"]) ? explode(" ", $this->request->header["authorization"])[1] : "");
+ if ($qq != "" && ($self_token == $remote_token)) $this->conn = new CQConnection($this->server, $this->request->fd, $qq);
+ else $this->conn = new UnknownConnection($this->server, $this->request->fd);
+ } else {
+ $this->conn = new $type_conn($this->server, $this->request->fd);
+ }
+ ZMBuf::$connect[$this->request->fd] = $this->conn;
+ foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
+ if (strtolower($v->type) == "open" && $this->parseSwooleRule($v) === true) {
+ $c = $v->class;
+ $class = new $c(["server" => $this->server, "request" => $this->request, "connection" => $this->conn], ModHandleType::SWOOLE_OPEN);
+ call_user_func_array([$class, $v->method], [$this->conn]);
+ if ($class->block_continue) break;
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function onAfter() {
+ if (!$this->conn->exists()) return $this;
+ foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
+ if (strtolower($v->type) == "open" && $this->parseSwooleRule($v) === true) {
+ /** @var ModBase $class */
+ $class = new $v["class"](["server" => $this->server, "request" => $this->request, "connection" => $this->conn], ModHandleType::SWOOLE_OPEN);
+ call_user_func_array([$class, $v["method"]], [$this->conn]);
+ if ($class->block_continue) break;
+ }
+ }
+ return $this;
+ }
+
+ private function parseSwooleRule($v) {
+ switch (explode(":", $v->rule)[0]) {
+ case "connectType": //websocket连接类型
+ if ($v->callback instanceof Closure) return call_user_func($v->callback, $this->conn);
+ break;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Event/Swoole/WorkerStartEvent.php b/src/ZM/Event/Swoole/WorkerStartEvent.php
new file mode 100644
index 00000000..01736459
--- /dev/null
+++ b/src/ZM/Event/Swoole/WorkerStartEvent.php
@@ -0,0 +1,137 @@
+server = $server;
+ $this->worker_id = $worker_id;
+ }
+
+ /**
+ * @return WorkerStartEvent
+ * @throws AnnotationException
+ * @throws ReflectionException
+ */
+ public function onActivate(): WorkerStartEvent {
+ Console::info("Worker启动中");
+ ZMBuf::resetCache(); //清空变量缓存
+ ZMBuf::set("wait_start", []); //添加队列,在workerStart运行完成前先让其他协程等待执行
+ DBCacheManager::freeAllCache(); // 清空数据库缓存
+ $this->resetConnections();//释放所有与framework的连接
+
+ //设置炸毛buf中储存的对象
+ ZMBuf::$globals = new GlobalConfig();
+ if (ZMBuf::globals("sql_config")["sql_host"] != "") {
+ Console::info("新建SQL连接池中");
+ ZMBuf::$sql_pool = new SQLPool();
+ DB::initTableList();
+ }
+ ZMBuf::$server = $this->server;
+ ZMBuf::$atomics['reload_time']->add(1);
+ ZMBuf::$req_mapping_node = new MappingNode("/");
+
+ Console::info("监听console输入");
+ Console::listenConsole(); //这个方法只能在这里调用,且如果worker_num不为1的话,此功能不可用
+
+ $this->loadAllClass(); //加载composer资源、phar外置包、注解解析注册等
+
+ $this->setAutosaveTimer(ZMBuf::globals("auto_save_interval"));
+
+ return $this;
+ }
+
+ public function onAfter(): WorkerStartEvent {
+ foreach (ZMBuf::get("wait_start") as $v) {
+ Coroutine::resume($v);
+ }
+ ZMBuf::unsetCache("wait_start");
+ foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
+ /** @var AnnotationBase $v */
+ if (strtolower($v->type) == "workerstart") {
+ $class_name = $v->class;
+ /** @var ModBase $class */
+ $class = new $class_name(["server" => $this->server, "worker_id" => $this->worker_id], ModHandleType::SWOOLE_WORKER_START);
+ call_user_func_array([$class, $v->method], []);
+ if ($class->block_continue) break;
+ }
+ }
+ return $this;
+ }
+
+ private function resetConnections() {
+ foreach ($this->server->connections as $v) {
+ $this->server->close($v);
+ }
+ if (ZMBuf::$sql_pool instanceof SqlPool) {
+ ZMBuf::$sql_pool->destruct();
+ ZMBuf::$sql_pool = null;
+ }
+ }
+
+ /**
+ * @throws AnnotationException
+ * @throws ReflectionException
+ */
+ private function loadAllClass() {
+ //加载phar包
+ Console::info("加载外部phar包中");
+ $dir = WORKING_DIR . "/resources/package/";
+ if (is_dir($dir)) {
+ $list = scandir($dir);
+ unset($list[0], $list[1]);
+ foreach ($list as $v) {
+ if (is_dir($dir . $v)) continue;
+ if (pathinfo($dir . $v, 4) == "phar") require_once($dir . $v);
+ }
+ }
+
+ //加载composer类
+ Console::info("加载composer资源中");
+ /** @noinspection PhpIncludeInspection */
+ require_once WORKING_DIR . "/vendor/autoload.php";
+
+ //加载各个模块的注解类,以及反射
+ Console::info("检索Module中");
+ AnnotationParser::registerMods();
+
+ //加载Custom目录下的自定义的内部类
+ ConnectionManager::registerCustomClass();
+ }
+
+ private function setAutosaveTimer($globals) {
+ DataProvider::$buffer_list = [];
+ Timer::tick($globals * 1000, function() {
+ DataProvider::saveBuffer();
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Exception/DbException.php b/src/ZM/Exception/DbException.php
new file mode 100644
index 00000000..f5575ce2
--- /dev/null
+++ b/src/ZM/Exception/DbException.php
@@ -0,0 +1,12 @@
+module = $module;
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Http/Response.php b/src/ZM/Http/Response.php
new file mode 100644
index 00000000..7eae9d97
--- /dev/null
+++ b/src/ZM/Http/Response.php
@@ -0,0 +1,229 @@
+response = $response;
+ $this->fd = $response->fd;
+ $this->socket = $response->socket;
+ $this->header = $response->header;
+ $this->cookie = $response->cookie;
+ if (isset($response->trailer))
+ $this->trailer = $response->trailer;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function initHeader() {
+ return $this->response->initHeader();
+ }
+
+ /**
+ * @param $name
+ * @param $value
+ * @param $expires
+ * @param $path
+ * @param $domain
+ * @param $secure
+ * @param $httponly
+ * @param $samesite
+ * @return mixed
+ */
+ public function cookie($name, $value = null, $expires = null, $path = null, $domain = null, $secure = null, $httponly = null, $samesite = null) {
+ return $this->response->rawcookie($name, $value, $expires, $path, $domain, $secure, $httponly, $samesite);
+ }
+
+ /**
+ * @param $name
+ * @param $value
+ * @param $expires
+ * @param $path
+ * @param $domain
+ * @param $secure
+ * @param $httponly
+ * @param $samesite
+ * @return mixed
+ */
+ public function setCookie($name, $value = null, $expires = null, $path = null, $domain = null, $secure = null, $httponly = null, $samesite = null) {
+ return $this->response->setCookie($name, $value, $expires, $path, $domain, $secure, $httponly, $samesite);
+ }
+
+ /**
+ * @param $name
+ * @param $value
+ * @param $expires
+ * @param $path
+ * @param $domain
+ * @param $secure
+ * @param $httponly
+ * @param $samesite
+ * @return mixed
+ */
+ public function rawcookie($name, $value = null, $expires = null, $path = null, $domain = null, $secure = null, $httponly = null, $samesite = null) {
+ return $this->response->rawcookie($name, $value, $expires, $path, $domain, $secure, $httponly, $samesite);
+ }
+
+ /**
+ * @param $http_code
+ * @param $reason
+ * @return mixed
+ */
+ public function status($http_code, $reason = null) {
+ return $this->response->status($http_code, $reason);
+ }
+
+ /**
+ * @param $http_code
+ * @param $reason
+ * @return mixed
+ */
+ public function setStatusCode($http_code, $reason = null) {
+ return $this->response->setStatusCode($http_code, $reason);
+ }
+
+ /**
+ * @param $key
+ * @param $value
+ * @param $ucwords
+ * @return mixed
+ */
+ public function header($key, $value, $ucwords = null) {
+ return $this->response->header($key, $value, $ucwords);
+ }
+
+ /**
+ * @param $key
+ * @param $value
+ * @param $ucwords
+ * @return mixed
+ */
+ public function setHeader($key, $value, $ucwords = null) {
+ return $this->response->setHeader($key, $value, $ucwords);
+ }
+
+ /**
+ * @param $key
+ * @param $value
+ * @return mixed
+ */
+ public function trailer($key, $value) {
+ return $this->response->trailer($key, $value);
+ }
+
+ /**
+ * @return mixed
+ */
+ public function ping() {
+ return $this->response->ping();
+ }
+
+ /**
+ * @param $content
+ * @return mixed
+ */
+ public function write($content) {
+ return $this->response->write($content);
+ }
+
+ /**
+ * @param $content
+ * @return mixed
+ */
+ public function end($content = null) {
+ $this->is_end = true;
+ return $this->response->end($content);
+ }
+
+ public function isEnd() { return $this->is_end; }
+
+ /**
+ * @param $filename
+ * @param $offset
+ * @param $length
+ * @return mixed
+ */
+ public function sendfile($filename, $offset = null, $length = null) {
+ return $this->response->sendfile($filename, $offset, $length);
+ }
+
+ /**
+ * @param $location
+ * @param $http_code
+ * @return mixed
+ */
+ public function redirect($location, $http_code = null) {
+ return $this->redirect($location, $http_code);
+ }
+
+ /**
+ * @return mixed
+ */
+ public function detach() {
+ return $this->response->detach();
+ }
+
+ /**
+ * @param $fd
+ * @return mixed
+ */
+ public static function create($fd) {
+ return \Swoole\Http\Response::create($fd);
+ }
+
+ /**
+ * @return mixed
+ */
+ public function upgrade() {
+ return $this->response->upgrade();
+ }
+
+ /**
+ * @param $data
+ * @param null $opcode
+ * @param null $flags
+ * @return mixed
+ */
+ public function push($data, $opcode = null, $flags = null) {
+ return $this->response->push($data, $opcode, $flags);
+ }
+
+ /**
+ * @return mixed
+ */
+ public function recv() {
+ return $this->response->recv();
+ }
+
+ /**
+ * @return mixed
+ */
+ public function close() {
+ return $this->response->close();
+ }
+
+ public function __destruct() {
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/ZM/ModBase.php b/src/ZM/ModBase.php
new file mode 100644
index 00000000..e14649f6
--- /dev/null
+++ b/src/ZM/ModBase.php
@@ -0,0 +1,165 @@
+server = $param0["server"];
+ if (isset($param0["frame"])) $this->frame = $param0["frame"];
+ if (isset($param0["data"])) $this->data = $param0["data"];
+ if (isset($param0["request"])) $this->request = $param0["request"];
+ if (isset($param0["response"])) $this->response = $param0["response"];
+ if (isset($param0["fd"])) $this->fd = $param0["fd"];
+ if (isset($param0["worker_id"])) $this->worker_id = $param0["worker_id"];
+ if (isset($param0["connection"])) $this->connection = $param0["connection"];
+ $this->handle_type = $handle_type;
+ }
+
+ /**
+ * only can used by cq->message event function
+ * @param $msg
+ * @param bool $yield
+ * @return mixed
+ */
+ public function reply($msg, $yield = false) {
+ switch ($this->data["message_type"]) {
+ case "group":
+ case "private":
+ case "discuss":
+ return CQAPI::quick_reply($this->connection, $this->data, $msg, $yield);
+ }
+ return false;
+ }
+
+ public function finalReply($msg, $yield = false) {
+ $this->block_continue = true;
+ if ($msg == "") return true;
+ return $this->reply($msg, $yield);
+ }
+
+ /**
+ * @param string $prompt
+ * @param int $timeout
+ * @param string $timeout_prompt
+ * @return string
+ * @throws InvalidArgumentException
+ * @throws WaitTimeoutException
+ */
+ public function waitMessage($prompt = "", $timeout = 600, $timeout_prompt = "") {
+ if ($prompt != "") $this->reply($prompt);
+ if (!isset($this->data["user_id"], $this->data["message"], $this->data["self_id"]))
+ throw new InvalidArgumentException("协程等待参数缺失");
+ $cid = Co::getuid();
+ $api_id = ZMBuf::$atomics["wait_msg_id"]->get();
+ ZMBuf::$atomics["wait_msg_id"]->add(1);
+ $hang = [
+ "coroutine" => $cid,
+ "user_id" => $this->data["user_id"],
+ "message" => $this->data["message"],
+ "self_id" => $this->data["self_id"],
+ "message_type" => $this->data["message_type"],
+ "result" => null
+ ];
+ if ($hang["message_type"] == "group" || $hang["message_type"] == "discuss") {
+ $hang[$hang["message_type"] . "_id"] = $this->data[$this->data["message_type"] . "_id"];
+ }
+ ZMBuf::appendKey("wait_api", $api_id, $hang);
+ $id = swoole_timer_after($timeout * 1000, function () use ($api_id, $timeout_prompt) {
+ $r = ZMBuf::get("wait_api")[$api_id] ?? null;
+ if ($r !== null) {
+ Co::resume($r["coroutine"]);
+ }
+ });
+
+ Co::suspend();
+ $sess = ZMBuf::get("wait_api")[$api_id];
+ ZMBuf::unsetByValue("wait_api", $api_id);
+ $result = $sess["result"];
+ if (isset($id)) swoole_timer_clear($id);
+ if ($result === null) throw new WaitTimeoutException($this, $timeout_prompt);
+ return $result;
+ }
+
+ /**
+ * @param $arg
+ * @param $mode
+ * @param $prompt_msg
+ * @return mixed|string
+ * @throws InvalidArgumentException
+ * @throws WaitTimeoutException
+ */
+ public function getArgs(&$arg, $mode, $prompt_msg) {
+ switch ($mode) {
+ case ZM_MATCH_ALL:
+ $p = $arg;
+ array_shift($p);
+ return trim(implode(" ", $p)) == "" ? $this->waitMessage($prompt_msg) : trim(implode(" ", $p));
+ case ZM_MATCH_NUMBER:
+ foreach ($arg as $k => $v) {
+ if (is_numeric($v)) {
+ array_splice($arg, $k, 1);
+ return $v;
+ }
+ }
+ return $this->waitMessage($prompt_msg);
+ case ZM_MATCH_FIRST:
+ if (isset($arg[1])) {
+ $a = $arg[1];
+ array_splice($arg, 1, 1);
+ return $a;
+ } else {
+ return $this->waitMessage($prompt_msg);
+ }
+ }
+ throw new InvalidArgumentException();
+ }
+
+ public function getMessage() { return $this->data["message"] ?? null; }
+
+ public function getUserId() { return $this->data["user_id"] ?? null; }
+
+ public function getGroupId() { return $this->data["group_id"] ?? null; }
+
+ public function getMessageType() { return $this->data["message_type"] ?? null; }
+
+ public function getRobotId() { return $this->data["self_id"]; }
+
+ public function getConnection() { return $this->connection; }
+
+ public function setBlock($result = true) { $this->block_continue = $result; }
+}
\ No newline at end of file
diff --git a/src/ZM/ModHandleType.php b/src/ZM/ModHandleType.php
new file mode 100644
index 00000000..1082dfc7
--- /dev/null
+++ b/src/ZM/ModHandleType.php
@@ -0,0 +1,20 @@
+ $v) {
+ self::setJsonData($v, ZMBuf::get($k));
+ }
+ echo Console::setColor("saved", "lightblue").PHP_EOL;
+ }
+
+ private static function getJsonData(string $string) {
+ if(!file_exists(self::getDataFolder().$string)) return [];
+ return json_decode(Co::readFile(self::getDataFolder().$string), true);
+ }
+
+ private static function setJsonData($filename, array $args) {
+ Co::writeFile(self::getDataFolder() . $filename, json_encode($args, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_BIGINT_AS_STRING));
+ }
+
+ private static function getDataFolder() {
+ return CONFIG_DIR;
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Utils/SQLPool.php b/src/ZM/Utils/SQLPool.php
new file mode 100644
index 00000000..db5805fa
--- /dev/null
+++ b/src/ZM/Utils/SQLPool.php
@@ -0,0 +1,101 @@
+pool = new SplQueue;
+ $this->info = [
+ "host" => ZMBuf::globals("sql_config")["sql_host"],
+ "port" => ZMBuf::globals("sql_config")["sql_port"],
+ "user" => ZMBuf::globals("sql_config")["sql_username"],
+ "password" => ZMBuf::globals("sql_config")["sql_password"],
+ "database" => ZMBuf::globals("sql_config")["sql_database"]
+ ];
+ }
+
+ /**
+ * 将利用过的连接入队
+ * @param $mysql
+ */
+ public function put($mysql) {
+ $this->pool->push($mysql);
+ if (($a = array_shift($this->co_list)) !== null) {
+ Coroutine::resume($a);
+ }
+ }
+
+ /**
+ * 获取队中的连接,如果不存在则创建新的
+ * @param bool $no_new_conn
+ * @return bool|mixed|Mysql
+ */
+ public function get($no_new_conn = false) {
+ if (count($this->pool) == 0 && $this->connect_cnt <= 70) {
+ if($no_new_conn) return false;
+ $this->connect_cnt += 1;
+ $r = $this->newConnect();
+ if ($r !== false) {
+ return $r;
+ } else {
+ $this->connect_cnt -= 1;
+ return false;
+ }
+ } elseif (count($this->pool) > 0) {
+ $con = $this->pool->pop();
+ if ($con->connected !== false) return $con;
+ } elseif ($this->connect_cnt > 70) {
+ $this->co_list[]=Coroutine::getuid();
+ Console::warning("数据库连接过多,协程等待重复利用中...当前协程数 ".Coroutine::stats()["coroutine_num"]);
+ Coroutine::suspend();
+ return $this->get($no_new_conn);
+ }
+ return false;
+ }
+
+ public function getCount() {
+ return $this->pool->count();
+ }
+
+ public function destruct() {
+ // 连接池销毁, 置不可用状态, 防止新的客户端进入常驻连接池, 导致服务器无法平滑退出
+ $this->available = false;
+ while (!$this->pool->isEmpty()) {
+ $this->pool->pop();
+ }
+ }
+
+ private function newConnect() {
+ //无空闲连接,创建新连接
+ $mysql = new Mysql();
+
+ Console::info("创建SQL连接中,当前有" . $this->connect_cnt . "个连接");
+ $res = $mysql->connect($this->info);
+ if ($res == false) {
+ echo $mysql->error . PHP_EOL;
+ return false;
+ } else {
+ return $mysql;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ZM/Utils/ScheduleManager.php b/src/ZM/Utils/ScheduleManager.php
new file mode 100644
index 00000000..e87e15ce
--- /dev/null
+++ b/src/ZM/Utils/ScheduleManager.php
@@ -0,0 +1,10 @@
+connections as $v) {
+ ZMBuf::$server->close($v);
+ }
+ DataProvider::saveBuffer();
+ ZMBuf::$server->shutdown();
+ ZMBuf::$server->stop();
+ }
+
+ public static function getHttpCodePage(int $http_code) {
+ if(isset(ZMBuf::globals("http_default_code_page")[$http_code])) {
+ return Co::readFile(DataProvider::getResourceFolder()."html/".ZMBuf::globals("http_default_code_page")[$http_code]);
+ } else return null;
+ }
+
+ public static function reload() {
+ Console::info(Console::setColor("Reloading server...", "gold"));
+ foreach (ZMBuf::$server->connections as $v) {
+ ZMBuf::$server->close($v);
+ }
+ DataProvider::saveBuffer();
+ ZMBuf::$server->reload();
+ }
+
+ /**
+ * 解析CQ码
+ * @param $msg
+ * @return array|null
+ * 0123456
+ * [CQ:at]
+ */
+ static function getCQ($msg) {
+ if (($start = mb_strpos($msg, '[')) === false) return null;
+ if (($end = mb_strpos($msg, ']')) === false) return null;
+ $msg = mb_substr($msg, $start + 1, $end - $start - 1);
+ if (mb_substr($msg, 0, 3) != "CQ:") return null;
+ $msg = mb_substr($msg, 3);
+ $msg2 = explode(",", $msg);
+ $type = array_shift($msg2);
+ $array = [];
+ foreach ($msg2 as $k => $v) {
+ $ss = explode("=", $v);
+ $sk = array_shift($ss);
+ $array[$sk] = implode("=", $ss);
+ }
+ return ["type" => $type, "params" => $array, "start" => $start, "end" => $end];
+ }
+}
\ No newline at end of file
diff --git a/src/cqbot/CQBot.php b/src/cqbot/CQBot.php
deleted file mode 100755
index fa2eaa7c..00000000
--- a/src/cqbot/CQBot.php
+++ /dev/null
@@ -1,142 +0,0 @@
-circle = $circle;
- $this->starttime = microtime(true);
- $this->framework = $framework;
- $this->data = $package;
- $this->self_id = $this->data["self_id"];
- }
-
- public function execute() {
- if ($this->circle >= 5) return false;
- if ($this->data === null) return false;
- if (isset($it["user_id"]) && CQUtil::isRobot($this->data["user_id"])) return false;
- if (isset($it["group_id"]) && $this->data["group_id"] == Cache::get("admin_group")) {
- if ($this->getRobotId() != Cache::get("admin_active")) {
- return false;
- }
- }
- if ($this->data["message"] == "")
- return false;
- foreach (Cache::get("mods") as $v) {
- /** @var ModBase $r */
- $r = new $v($this, $this->data);
- if ($r->split_execute === true) {
- $msg = trim($this->data["message"]);
- $msg = explodeMsg($msg);
- $r->execute($msg);
- }
- }
- $this->endtime = microtime(true);
- return $this->function_called;
- }
-
- /**
- * 快速回复消息
- * @param $msg
- * @param callable|null $callback
- * @param bool $async
- * @return bool
- */
- public function reply($msg, callable $callback = null, $async = false) {
- $this->function_called = true;
- switch ($this->data["message_type"]) {
- case "group":
- $this->function_called = true;
- if (!$async) return CQAPI::send_group_msg($this->getRobotId(), ["group_id" => $this->data["group_id"], "message" => $msg], $callback);
- else return CQAPI::send_group_msg_async($this->getRobotId(), ["group_id" => $this->data["group_id"], "message" => $msg], $callback);
- case "private":
- $this->function_called = true;
- if (!$async) return CQAPI::send_private_msg($this->getRobotId(), ["user_id" => $this->data["user_id"], "message" => $msg], $callback);
- else return CQAPI::send_private_msg_async($this->getRobotId(), ["user_id" => $this->data["user_id"], "message" => $msg], $callback);
- case "discuss":
- $this->function_called = true;
- if (!$async) return CQAPI::send_discuss_msg($this->getRobotId(), ["discuss_id" => $this->data["discuss_id"], "message" => $msg], $callback);
- else return CQAPI::send_discuss_msg_async($this->getRobotId(), ["discuss_id" => $this->data["discuss_id"], "message" => $msg], $callback);
- case "wechat":
- //TODO: add wechat account support in the future
- break;
- }
- return false;
- }
-
- public function isAdmin($user) {
- if (in_array($user, Cache::get("admin"))) return true;
- else return false;
- }
-
- public function replace($msg, $dat) {
- $msg = str_replace("{at}", '[CQ:at,qq=' . $dat["user_id"] . ']', $msg);
- $msg = str_replace("{and}", '&', $msg);
- while (strpos($msg, '{') !== false && strpos($msg, '}') !== false) {
- if (strpos($msg, '{') > strpos($msg, '}')) return $msg;
- $start = strpos($msg, '{');
- $end = strpos($msg, '}');
- $sub = explode("=", substr($msg, $start + 1, $end - $start - 1));
- switch ($sub[0]) {
- case "at":
- $qq = $sub[1];
- $msg = str_replace(substr($msg, $start, $end - $start + 1), '[CQ:at,qq=' . $qq . ']', $msg);
- break;
- case "image":
- case "record":
- $pictFile = $sub[1];
- $msg = str_replace(substr($msg, $start, $end - $start + 1), '[CQ:' . $sub[0] . ',file=' . $pictFile . ']', $msg);
- break;
- case "dice":
- $file = $sub[1];
- $msg = str_replace(substr($msg, $start, $end - $start + 1), '[CQ:dice,type=' . $file . ']', $msg);
- break;
- case "shake":
- $msg = str_replace(substr($msg, $start, $end - $start + 1), '[CQ:shake]', $msg);
- break;
- case "music":
- $id = $sub[1];
- $msg = str_replace(substr($msg, $start, $end - $start + 1), '[CQ:music,type=163,id=' . $id . ']', $msg);
- break;
- case "internet":
- array_shift($sub);
- $id = implode("=", $sub);
- if (substr($id, 0, 7) != "http://") $id = "http://" . $id;
- $is = file_get_contents($id, false, NULL, 0, 1024);
- if ($is == false) $is = "[请求时发生了错误] 如有疑问,请联系管理员";
- $msg = str_replace(substr($msg, $start, $end - $start + 1), $is, $msg);
- break 2;
- default:
- break 2;
- }
- }
- return $msg;
- }
-
- /**
- * 返回当前机器人的id
- * @return string|null
- */
- public function getRobotId() {
- return $this->data["self_id"] ?? null;
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/api/CQAPI.php b/src/cqbot/api/CQAPI.php
deleted file mode 100644
index 00155a3d..00000000
--- a/src/cqbot/api/CQAPI.php
+++ /dev/null
@@ -1,232 +0,0 @@
- $msg, "group_id" => Cache::get("admin_group")]);
- }
- return false;
- }
-
- public static function __callStatic($name, $arg) {
- if(mb_substr($name, -6) == "_after"){
- $all = self::getSupportedAPIs();
- $find = null;
- $true_name = mb_substr($name, 0, -6);
- if(!in_array($true_name, $all)){
- Console::error("Unknown API " . $name);
- return false;
- }
- $ms = array_shift($arg);
- Scheduler::after($ms, function() use ($true_name, $arg){
- $reply = ["action" => $true_name];
- if (!is_array($arg[1])) {
- Console::error("Error when parsing params. Please make sure your params is an array.");
- return false;
- }
- if ($arg[1] != []) {
- $reply["params"] = $arg[1];
- }
- return self::processAPI($arg[0], $reply, $arg[2]);
- });
- return true;
- }
- $all = self::getSupportedAPIs();
- $find = null;
- if (in_array($name, $all)) $find = $name;
- else {
- foreach ($all as $v) {
- if (strtolower($name) == strtolower(str_replace("_", "", $v))) {
- $find = $v;
- break;
- }
- }
- }
- if ($find === null) {
- Console::error("Unknown API " . $name);
- return false;
- }
- $reply = ["action" => $find];
- if (!is_array($arg[1])) {
- Console::error("Error when parsing params. Please make sure your params is an array.");
- return false;
- }
- if ($arg[1] != []) {
- $reply["params"] = $arg[1];
- }
- return self::processAPI($arg[0], $reply, $arg[2]);
- }
-
- /********************** non-API Part **********************/
-
- private static function getSupportedAPIs() {
- return [
- "send_private_msg",
- "send_group_msg",
- "send_discuss_msg",
- "send_msg",
- "delete_msg",
- "send_like",
- "set_group_kick",
- "set_group_ban",
- "set_group_anonymous_ban",
- "set_group_whole_ban",
- "set_group_admin",
- "set_group_anonymous",
- "set_group_card",
- "set_group_leave",
- "set_group_special_title",
- "set_discuss_leave",
- "set_friend_add_request",
- "set_group_add_request",
- "get_login_info",
- "get_stranger_info",
- "get_group_list",
- "get_group_member_info",
- "get_group_member_list",
- "get_cookies",
- "get_csrf_token",
- "get_credentials",
- "get_record",
- "get_status",
- "get_version_info",
- "set_restart",
- "set_restart_plugin",
- "clean_data_dir",
- "clean_plugin_log",
- "_get_friend_list",
- "_get_group_info",
- "_get_vip_info",
- //异步API
- "send_private_msg_async",
- "send_group_msg_async",
- "send_discuss_msg_async",
- "send_msg_async",
- "delete_msg_async",
- "set_group_kick_async",
- "set_group_ban_async",
- "set_group_anonymous_ban_async",
- "set_group_whole_ban_async",
- "set_group_admin_async",
- "set_group_anonymous_async",
- "set_group_card_async",
- "set_group_leave_async",
- "set_group_special_title_async",
- "set_discuss_leave_async",
- "set_friend_add_request_async",
- "set_group_add_request_async"
- ];
- }
-
- public static function getLoggedAPIs() {
- return [
- "send_private_msg",
- "send_group_msg",
- "send_discuss_msg",
- "send_msg",
- "send_private_msg_async",
- "send_group_msg_async",
- "send_discuss_msg_async",
- "send_msg_async"
- ];
- }
-
- /**
- * @param $self_id
- * @param $reply
- * @param callable|null $function
- * @return bool
- */
- private static function processAPI($self_id, $reply, callable $function = null) {
- $api_id = Cache::$api_id->get();
- $reply["echo"] = $api_id;
- Cache::$api_id->add(1);
- if ($self_id instanceof RobotWSConnection) {
- $connection = $self_id;
- $self_id = $connection->getQQ();
- } else $connection = ConnectionManager::getRobotConnection($self_id);
- if ($connection instanceof NullConnection) {
- Console::error("未找到qq号:" . $self_id . "的API连接");
- return false;
- }
- if ($connection->push(json_encode($reply))) {
- Cache::appendKey("sent_api", $api_id, [
- "data" => $reply,
- "time" => microtime(true),
- "func" => $function,
- "self_id" => $self_id
- ]);
- if (in_array($reply["action"], self::getLoggedAPIs())) {
- Console::msg($reply);
- Cache::$out_count->add(1);
- }
- return true;
- } else return false;
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/connection/CustomWSConnection.php b/src/cqbot/connection/CustomWSConnection.php
deleted file mode 100644
index 954fcb81..00000000
--- a/src/cqbot/connection/CustomWSConnection.php
+++ /dev/null
@@ -1,16 +0,0 @@
-server["remote_addr"]);
- $this->create_success = true;
- // Here to put your custom other websocket connection to manage.
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/connection/NullConnection.php b/src/cqbot/connection/NullConnection.php
deleted file mode 100644
index 59be835e..00000000
--- a/src/cqbot/connection/NullConnection.php
+++ /dev/null
@@ -1,37 +0,0 @@
-qq = $qq;
- }
-
- public function push($data, $push_error_record = true) {
- $data = unicodeDecode($data);
- CQUtil::errorlog("API推送失败,未发送的消息: \n" . $data, "API ERROR", false);
- if ($push_error_record) Cache::append("bug_msg_list", json_decode($data, true));
- return false;
- }
-
- public function sendAPI($api, $params = [], $echo = []){
- $data["action"] = $api;
- if($params != []) $data["params"] = $params;
- $echo_result["self_id"] = $this->qq;
- if($echo != []){
- if(count($echo) >= 1) $echo_result['type'] = array_shift($echo);
- if(!empty($echo)) $echo_result["params"] = $echo;
- }
- $data["echo"] = $echo_result;
- Console::debug("将要发送的API包:" . json_encode($data, 128 | 256));
- return $this->push(json_encode($data));
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/connection/RobotWSConnection.php b/src/cqbot/connection/RobotWSConnection.php
deleted file mode 100644
index 36ed514c..00000000
--- a/src/cqbot/connection/RobotWSConnection.php
+++ /dev/null
@@ -1,133 +0,0 @@
- "小马哥",
- "838714432" => "鲸鱼的test机器人"
- ];
-
- private $qq;
- private $alias;//别名
- private $sub_type;
-
- public function __construct(swoole_websocket_server $server, $fd, $qq, swoole_http_request $request, $sub_type) {
- parent::__construct($server, $fd, $request->server["remote_addr"]);
- $this->qq = $qq;
- $this->alias = self::ALIAS_LIST[$qq] ?? "机器人" . $qq;
- $this->sub_type = $sub_type;
- foreach (ConnectionManager::getAll("robot") as $k => $v) {
- if ($v->getQQ() == $this->getQQ() && $k != $this->fd && $v->getSubType() == $this->getSubType()) {
- $v->close();
- }
- }
- if ($sub_type != "event") {
- $obj = $this;
- $r = $this->sendAPI("get_version_info", [], function ($response) use ($obj) {
- Cache::set("version_info", $response["data"]);
- });
- if ($r)
- $this->create_success = $this->sendAPI("send_group_msg", ["message" => "[CQBot] 机器人 " . $this->getAlias() . " 已连接,链接fd:" . $this->fd, "group_id" => Cache::get("admin_group")]);
- } else $this->create_success = true;
- }
-
- /**
- * 返回连接的QQ
- * @return mixed
- */
- public function getQQ() {
- return $this->qq;
- }
-
- /**
- * @return mixed|string
- */
- public function getAlias() {
- return $this->alias;
- }
-
- public function sendAPI($api, $params = [], callable $callback = null) {
- $data["action"] = $api;
- if ($params != []) $data["params"] = $params;
-
- if ($this->sub_type == "event") {
- $conns = ConnectionManager::getAll("robot");
- foreach ($conns as $k => $v) {
- if ($v->getSubType() == "api" && $v->getQQ() == $this->getQQ()) {
- $api_id = Cache::$api_id->get();
- $data["echo"] = $api_id;
- Cache::$api_id->add(1);
- Cache::appendKey("sent_api", $api_id, [
- "data" => $data,
- "time" => microtime(true),
- "func" => $callback,
- "self_id" => $this->getQQ()
- ]);
- if ($v->push(json_encode($data))) {
- if (in_array($data["action"], CQAPI::getLoggedAPIs())) {
- Console::msg($data);
- Cache::$out_count->add(1);
- }
- return true;
- } else {
- $response = [
- "status" => "failed",
- "retcode" => 998,
- "data" => null,
- "self_id" => $this->getQQ()
- ];
- $s = Cache::get("sent_api")[$data["echo"]];
- StatusParser::parse($response, $data);
- if ($s["func"] !== null)
- call_user_func($s["func"], $response, $data);
- Cache::removeKey("sent_api", $data["echo"]);
- return false;
- }
- }
- }
- return false;
- }
- $api_id = Cache::$api_id->get();
- $data["echo"] = $api_id;
- Cache::$api_id->add(1);
- Cache::appendKey("sent_api", $api_id, [
- "data" => $data,
- "time" => microtime(true),
- "func" => $callback,
- "self_id" => $this->getQQ()
- ]);
- if ($this->push(json_encode($data))) {
- if (in_array($data["action"], CQAPI::getLoggedAPIs())) {
- Console::msg($data);
- Cache::$out_count->add(1);
- }
- return true;
- } else {
- $response = [
- "status" => "failed",
- "retcode" => 999,
- "data" => null,
- "self_id" => $this->getQQ()
- ];
- $s = Cache::get("sent_api")[$data["echo"]];
- StatusParser::parse($response, $data);
- if ($s["func"] !== null)
- call_user_func($s["func"], $response, $data);
- Cache::removeKey("sent_api", $data["echo"]);
- return false;
- }
- }
-
- /**
- * @return mixed
- */
- public function getSubType() {
- return $this->sub_type;
- }
-}
diff --git a/src/cqbot/connection/WSConnection.php b/src/cqbot/connection/WSConnection.php
deleted file mode 100644
index 95e76a25..00000000
--- a/src/cqbot/connection/WSConnection.php
+++ /dev/null
@@ -1,64 +0,0 @@
-server = $server;
- $this->fd = $fd;
- $this->remote_address = $remote;
- }
-
- /**
- * 返回swoole server
- * @return swoole_websocket_server
- */
- public function getServer() {
- return $this->server;
- }
-
- public function getType(){
- if($this instanceof RobotWSConnection) return "robot";
- elseif($this instanceof CustomWSConnection) return "custom";
- else return "unknown";
- }
-
- public function push($data, $push_error_record = true) {
- if ($data === null || $data == "") {
- Console::error("Empty data pushed.");
- return false;
- }
- if ($this->server->push($this->fd, $data) === false) {
- $data = unicodeDecode($data);
- CQUtil::errorlog("API推送失败,未发送的消息: \n" . $data, "API ERROR", false);
- if ($push_error_record) Cache::append("bug_msg_list", json_decode($data, true));
-
- return false;
- }
- return true;
- }
-
- public function close() {
- $this->server->close($this->fd);
- ConnectionManager::remove($this->fd);
- }
-
- /**
- * @return mixed
- */
- public function getRemoteAddress() {
- return $this->remote_address;
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/event/Event.php b/src/cqbot/event/Event.php
deleted file mode 100644
index 05063d22..00000000
--- a/src/cqbot/event/Event.php
+++ /dev/null
@@ -1,17 +0,0 @@
-get();
- Cache::$in_count->add(1);
- if (Cache::$data["info_level"] >= 1) {
- $num = CQUtil::getRobotAlias($req["self_id"]);
- $type = $req["post_type"] == "message" ? ($req["message_type"] == "group" ? "GROUP_MSG" . $num . ":" . $req["group_id"] : ($req["message_type"] == "private" ? "PRIVATE_MSG" . $num : "DISCUSS_MSG" . $num . ":" . $req["discuss_id"])) : strtoupper($req["post_type"]);
- Console::put(Console::setColor(date("H:i:s"), "green") . Console::setColor(" [$in_count]" . $type, "lightlightblue") . Console::setColor(" " . $req["user_id"], "yellow") . Console::setColor(" > ", "gray") . $req["message"]);
- }
-
- CQUtil::updateMsg();//更新消息速度
-
- //传入消息处理的逻辑
- $c = new CQBot($this->getFramework(), 0, $req);
- $c->execute();
- $value = $c->endtime - $c->starttime;
- Console::debug("Using time: " . $value);
-
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/event/post/MetaEvent.php b/src/cqbot/event/post/MetaEvent.php
deleted file mode 100644
index 05e8fe69..00000000
--- a/src/cqbot/event/post/MetaEvent.php
+++ /dev/null
@@ -1,29 +0,0 @@
- $v){
- if(in_array("onNotice", get_class_methods($v))){
- /** @var ModBase $v */
- $v::onNotice($req);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/event/post/RequestEvent.php b/src/cqbot/event/post/RequestEvent.php
deleted file mode 100644
index 6da270c1..00000000
--- a/src/cqbot/event/post/RequestEvent.php
+++ /dev/null
@@ -1,19 +0,0 @@
- $v){
- if(in_array("onRequest", get_class_methods($v))){
- /** @var ModBase $v */
- $v::onRequest($req);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/event/post/UnknownEvent.php b/src/cqbot/event/post/UnknownEvent.php
deleted file mode 100644
index d7b29966..00000000
--- a/src/cqbot/event/post/UnknownEvent.php
+++ /dev/null
@@ -1,14 +0,0 @@
-end("Hello world!");
- //此为HTTP请求的回复,更多设置回复头、传送文件、POST、GET请求解析等内容请查阅文档https://wiki.swoole.com
- }
-
- /**
- * 此函数为一个示例的检测有效的函数,为此预留
- * 作用是判断传入的请求数据是合法的
- * @param $param
- * @return bool
- */
- public function isValidParam($param) {
- if ($param === null) return false;
- if (!isset($param["event"])) return false;
- if (!isset($param["timestamp"])) return false;
- if (!isset($param[$param["event"]])) return false;
- if(($param["timestamp"] > (time() + 10)) || ($param["timestamp"] < (time() - 10))) return false;
- return true;
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/event/server/ServerEvent.php b/src/cqbot/event/server/ServerEvent.php
deleted file mode 100644
index 4b7b4d63..00000000
--- a/src/cqbot/event/server/ServerEvent.php
+++ /dev/null
@@ -1,16 +0,0 @@
-server = $server;
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/event/server/WSCloseEvent.php b/src/cqbot/event/server/WSCloseEvent.php
deleted file mode 100644
index 744dd770..00000000
--- a/src/cqbot/event/server/WSCloseEvent.php
+++ /dev/null
@@ -1,21 +0,0 @@
-fd;
- $req = json_decode($frame->data, true);
- $conn = ConnectionManager::get($fd);
- if ($conn === null) {
- Console::info("收到一个未知链接发来的消息。" . $fd);
- return;
- }
- switch ($conn->getType()) {
- case "robot":
- //处理酷Q HTTP事件接口消息(Event)
- if (isset($req["post_type"])) {
- switch ($req["post_type"]) {
- case "message":
- new MessageEvent($req);
- break 2;
- case "notice":
- new NoticeEvent($req);
- break 2;
- case "request":
- new RequestEvent($req);
- break 2;
- case "meta_event":
- new MetaEvent($req);
- break 2;
- default:
- new UnknownEvent($req);
- break 2;
- }
- } else {
- Console::debug("收到来自API[" . $fd . "]连接的回复:" . json_encode($req, 128 | 256));
- if (isset($req["echo"]) && Cache::array_key_exists("sent_api", $req["echo"])) {
- $status = $req["status"];
- $retcode = $req["retcode"];
- $data = $req["data"];
- $origin = Cache::get("sent_api")[$req["echo"]];
- $self_id = $origin["self_id"];
- $response = [
- "status" => $status,
- "retcode" => $retcode,
- "data" => $data,
- "self_id" => $self_id
- ];
- StatusParser::parse($response, $origin);
- if ($origin["func"] !== null)
- call_user_func($origin["func"], $response, $origin, $conn);
- Cache::removeKey("sent_api", $req["echo"]);
- }
- }
- break;
- default:
- Console::info("收到未知链接的消息, 来自: " . $fd);
- break;
- }
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/event/server/WSOpenEvent.php b/src/cqbot/event/server/WSOpenEvent.php
deleted file mode 100644
index 2946673f..00000000
--- a/src/cqbot/event/server/WSOpenEvent.php
+++ /dev/null
@@ -1,56 +0,0 @@
-fd;
- $get = $request->get;
- $header = $request->header;
- //echo json_encode($header, 128|256);
- $connect_type = $get["type"] ?? (isset($header["x-client-role"]) ? strtolower($header["x-client-role"]) : "");
- $access_token = $get["token"] ?? (isset($header["authorization"]) ? explode(" ", $header["authorization"])[1] : "");
- //Console::info("链接类型:".$connect_type."\n链接token:".$access_token);
- if ($connect_type == "") {
- Console::info("未指定连接类型,关闭连接.");
- $server->close($fd);
- return;
- }
- //if (isset($request->header["authorization"])) {
- //$tokens = explode(" ", $request->header["authorization"]);
- //$tokens = trim($tokens[1]);
- if ($access_token !== Cache::get("access_token") && Cache::get("access_token") != "") {
- Console::info("监测到WS连接,但是token不对,无法匹配。");
- $server->close($fd);
- return;
- }
- switch ($connect_type) {
- case "event":
- case "api":
- case "universal":
- $self_id = $get["qq"] ?? ($header["x-self-id"] ?? "");
- Console::info("收到 " . $connect_type . " 连接,来自机器人:" . $self_id . ",fd:" . $fd);
- $conn = new RobotWSConnection($server, $fd, $self_id, $request, $connect_type);
- if($conn->create_success) ConnectionManager::set($fd, $conn);
- else {
- Console::error("初始化WS连接失败!fd:".$fd.",QQ:".$self_id);
- $server->close($fd);
- return;
- }
- break;
- case "custom":
- $conn = new CustomWSConnection($server, $fd, $request);
- if($conn->create_success) ConnectionManager::set($fd, $conn);
- break;
- default:
- Console::info("Unknown WS Connection connected. I will close it.");
- $server->close($fd);
- }
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/event/server/WorkerStartEvent.php b/src/cqbot/event/server/WorkerStartEvent.php
deleted file mode 100644
index 0e17b3a2..00000000
--- a/src/cqbot/event/server/WorkerStartEvent.php
+++ /dev/null
@@ -1,30 +0,0 @@
-add(1);
- CQUtil::loadAllFiles();
- $set = settings();
- foreach (get_included_files() as $file)
- Console::debug("Loaded " . $file);
- //计时器(ms)
- if ($set["swoole_use_tick"] === true) {
- Cache::$scheduler = new Scheduler($this->getFramework(), time());
- //$timer_id = $server->tick($set["swoole_tick_interval"], [Cache::$scheduler, "tick"]);
- Console::info("已在worker #" . $worker_id . " 中成功启动了计时器!计时器间隔:" . $set["swoole_tick_interval"]);
- }
- Console::debug("master_pid = " . $server->master_pid);
- Console::debug("worker_id = " . $worker_id);
- Console::put("===== Worker " . Console::setColor("#" . $worker_id, "gold") . " 已启动 =====");
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/item/User.php b/src/cqbot/item/User.php
deleted file mode 100755
index 5b359f6c..00000000
--- a/src/cqbot/item/User.php
+++ /dev/null
@@ -1,136 +0,0 @@
-id = $qid;
- if (strlen($qid) >= 15) $this->user_type = 1;
- $this->permission = DP::getJsonData("permissions.json")[$qid] ?? 0;
- $this->friend = [];
- }
-
- /**
- * 获取用户QQ号
- * @return mixed
- */
- public function getId() { return $this->id; }
-
- /**
- * 获取用户权限值
- * @return int
- */
- public function getPermission() { return $this->permission; }
-
- /**
- * @param int $permission
- * @return User
- */
- public function setPermission(int $permission) {
- $this->permission = $permission;
- return $this;
- }
-
- public function getBuffer() {
- return $this->buffer;
- }
-
- public function setBuffer($buffer) {
- $this->buffer = $buffer;
- return $this;
- }
-
- /**
- * @return string
- */
- public function getNickname() {
- return $this->nickname;
- }
-
- /**
- * @return array
- */
- public function getFriend() {
- return $this->friend;
- }
-
- /**
- * @param array $friend
- */
- public function setFriend(array $friend) {
- $this->friend = $friend;
- }
-
- /**
- * @param string $nickname
- */
- public function setNickname(string $nickname) {
- $this->nickname = $nickname;
- }
-
- /**
- * @return array
- */
- public function getLexicon() {
- return $this->lexicon;
- }
-
- /**
- * @param array $lexicon
- */
- public function setLexicon(array $lexicon) {
- $this->lexicon = $lexicon;
- }
-
- /**
- * @return array
- */
- public function getFunction() {
- return $this->function;
- }
-
- /**
- * @param array $function
- */
- public function setFunction(array $function) {
- $this->function = $function;
- }
-
- public function closeFunction($name) {
- unset($this->function[$name]);
- }
-
- /**
- * @return int
- */
- public function getUserType() {
- return $this->user_type;
- }
-
- public function toJson() {
- $ls = [];
- foreach ($this as $k => $v) {
- $ls[$k] = $v;
- }
- return json_encode($ls, 128 | 256);
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/mods/Admin.php b/src/cqbot/mods/Admin.php
deleted file mode 100755
index 007cdae2..00000000
--- a/src/cqbot/mods/Admin.php
+++ /dev/null
@@ -1,71 +0,0 @@
-split_execute = true;
- }
-
- public static function initValues() {
- Cache::set("msg_speed", []);//消息速度列表(存的是时间戳)
- Cache::set("admin_active", "");
- Cache::set("admin", settings()["admin"]);
- }
-
- public static function onTick($tick) {
- if ($tick % 900 == 0) CQUtil::saveAllFiles();//900秒储存一次数据
- if (settings()["save_user_data"]) {
- if ($tick % 21600 == 0) { //21600秒刷新一次好友列表
- GroupManager::updateGroupList();
- FriendManager::updateFriendList();
- }
- }
- }
-
- public static function onRequest($req) {
- switch ($req["request_type"]) {
- case "friend":
- //好友添加请求处理
- $comment = $req["comment"];//验证信息
- $flag = $req["flag"];//添加好友用的flag,用于处理好友
- $user_id = $req["user_id"];
-
- //如果不需要将好友添加信息发到群里,请注释下面这行
- CQAPI::debug("有用户请求添加好友啦!\n用户QQ:" . $user_id . "\n验证信息:" . $comment . "\n添加flag:" . $flag, "");
-
- //如果不需要要自动同意,请注释下面这几行
- $params = ["flag" => $flag, "approve" => true];
- CQAPI::set_friend_add_request($req["self_id"], $params, function ($response) use ($user_id) {
- CQAPI::debug("已自动同意 " . $user_id, "", $response["self_id"]);
- CQAPI::send_private_msg($user_id, ["message" => "你好,第一次见面请多关照!"]);
- });
- break;
- }
- }
-
- public function execute($it) {
- if (!$this->main->isAdmin($this->getUserId())) return false;
- switch ($it[0]) {
- case "reload": //管理员重载代码
- $this->reply("正在重新启动...");
- CQUtil::reload();
- return true;
- case "stop": //管理员停止server
- $this->reply("正在停止服务器...");
- CQUtil::stop();
- return true;
- }
- return false;
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/mods/Example.php b/src/cqbot/mods/Example.php
deleted file mode 100644
index 5f714bc4..00000000
--- a/src/cqbot/mods/Example.php
+++ /dev/null
@@ -1,52 +0,0 @@
-split_execute = true;
- * 默认不会执行execute函数
- */
-class Example extends ModBase
-{
- public function __construct(CQBot $main, $data) {
- parent::__construct($main, $data);
- //$data为CQHTTP插件上报的消息事件数组
- //这里编写你的内容
- $this->split_execute = true;
- }
-
- /**
- * 分词函数,如果开启分词模式的话将调用此数组。
- * 如果将一句话使用空格、换行和Tab进行分割,用来处理多项参数的功能指令
- * 例如:"随机数 1 100" 将被分割成数组$it ["随机数","1","100"]
- * @param $it
- * @return bool
- */
- public function execute($it) {
- switch ($it[0]) {
- case "ping":
- $this->reply("pong");
- return true;
- case "你好":
- $this->reply("你好,我是CQBot!");
- return true;
- case "随机数":
- if (!isset($it[1]) || !isset($it[2])) {
- $this->reply("用法: 随机数 开始整数 结束整数");
- return true;
- }
- $c1 = intval($it[1]);
- $c2 = intval($it[2]);
- if ($c1 > $c2) {
- $this->reply("随机数范围错误!应该从小的一方到大的一方!例如:\n随机数 1 99");
- return true;
- }
- $this->reply("生成的随机数是 " . mt_rand($c1, $c2));
- return true;
- }
- return false;
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/mods/Help.php b/src/cqbot/mods/Help.php
deleted file mode 100644
index 7af0d85e..00000000
--- a/src/cqbot/mods/Help.php
+++ /dev/null
@@ -1,35 +0,0 @@
-split_execute = true;
- }
-
- public function execute($it) {
- switch ($it[0]) {
- case "帮助":
- $msg = "「机器人帮助」";
- $msg .= "\n随机数:生成一个随机数";
- $this->reply($msg);
- return true;
- case "如何增加机器人功能":
- $msg = "机器人功能是在框架中src/cqbot/mods/xxx.php文件中编写的。";
- $msg .= "\nCQBot采用关键词系统,你可以直接像现有源码一样添加case在switch里面,";
- $msg .= "\n也可以自己新建一个任意名称的Mod名称,例如Entertain.php,你可以在里面编写娱乐功能。";
- $msg .= "\n你可以直接复制框架中Example.php文件的内容进行编辑。";
- $msg .= "\n预先封装好的机器人函数均在CQUtil类中,只需直接使用静态方法调用即可!";
- $msg .= "\n更多示例功能会逐渐添加到框架中,记得更新哦~";
- $this->reply($msg);
- return true;
- }
- return false;
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/mods/ModBase.php b/src/cqbot/mods/ModBase.php
deleted file mode 100755
index aca924cd..00000000
--- a/src/cqbot/mods/ModBase.php
+++ /dev/null
@@ -1,43 +0,0 @@
-main = $main;
- $this->data = $data;
- }
-
- public function execute($it) { }
-
- public function getUser($data = null) { return CQUtil::getUser($data === null ? $this->data["user_id"] : $data["user_id"]); }
-
- public function getUserId($data = null) { return $data === null ? strval($this->data["user_id"]) : strval($data["user_id"]); }
-
- public function reply($msg, callable $callback = null) { return $this->main->reply($msg, $callback); }
-
- public function getMessageType() { return $this->data["message_type"]; }
-
- public function getRobotId() { return $this->data["self_id"]; }
-}
\ No newline at end of file
diff --git a/src/cqbot/utils/CQUtil.php b/src/cqbot/utils/CQUtil.php
deleted file mode 100755
index ba23e752..00000000
--- a/src/cqbot/utils/CQUtil.php
+++ /dev/null
@@ -1,375 +0,0 @@
- $v) {
- $serial = serialize($v);
- file_put_contents(DP::getUserFolder() . $k . ".dat", $serial);
- }
- }
-
- Console::put("Saved files.");
- }
-
- /**
- * 生成报错日志
- * @param $log
- * @param string $head
- * @param int $send_debug_message
- */
- public static function errorLog($log, $head = "ERROR", $send_debug_message = 1) {
- Console::error($log, ($head === "ERROR") ? null : "[" . $head . "] ");
- $time = date("Y-m-d H:i:s");
- $msg = "[$head @ $time]: $log\n";
- file_put_contents(DP::getDataFolder() . "log_error.txt", $msg, FILE_APPEND);
- if ($send_debug_message) CQAPI::debug($msg);
- }
-
- static function findRobot() {
- foreach (ConnectionManager::getAll("robot") as $v) {
- return $v->getQQ();
- }
- return null;
- }
-
- /**
- * 获取运行时间
- * @param $time
- * @return array
- */
- static function getRunTime($time) {
- $time_len = time() - $time;
- $run_time = [];
- if (intval($time_len / 86400) > 0) {
- $run_time[0] = intval($time_len / 86400);
- $time_len = $time_len % 86400;
- } else {
- $run_time[0] = 0;
- }
- if (intval($time_len / 3600) > 0) {
- $run_time[1] = intval($time_len / 3600);
- $time_len = $time_len % 3600;
- } else {
- $run_time[1] = 0;
- }
- if (intval($time_len / 60) > 0) {
- $run_time[2] = intval($time_len / 60);
- $time_len = $time_len % 60;
- } else {
- $run_time[2] = 0;
- }
- $run_time[3] = $time_len;
- return $run_time;
- }
-
- /**
- * 获取格式化的运行时间
- * @param $time
- * @return string
- */
- static function getRunTimeFormat($time) {
- $time_len = time() - $time;
- $msg = "";
- if (intval($time_len / 86400) > 0) {
- $msg .= intval($time_len / 86400) . "天";
- $time_len = $time_len % 86400;
- }
- if (intval($time_len / 3600) > 0) {
- $msg .= intval($time_len / 3600) . "小时";
- $time_len = $time_len % 3600;
- }
- if (intval($time_len / 60) > 0) {
- $msg .= intval($time_len / 60) . "分";
- $time_len = $time_len % 60;
- }
- $msg .= $time_len . "秒";
- return $msg;
- }
-
- /**
- * 获取所有已经加载到内存的用户。
- * read_all为true时,会加载所有User.dat到内存中,false时仅会读取已经加载到内存的用户
- * @param bool $real_all
- * @return User[]
- */
- static function getAllUsers($real_all = false): array {
- if ($real_all === true) {
- $dir = scandir(DP::getUserFolder());
- unset($dir[0], $dir[1]);
- foreach ($dir as $d => $v) {
- $vs = explode(".", $v);
- if (array_pop($vs) == "dat") {
- $class = unserialize(file_get_contents(DP::getUserFolder() . $v));
- if (!Cache::array_key_exists("user", $vs[0])) {
- Cache::appendKey("user", $vs[0], $class);
- }
- }
- }
- }
- return Cache::get("user");
- }
-
- /**
- * 获取用户实例
- * @param $id
- * @param bool $enable_init
- * @return User
- */
- static function getUser($id, $enable_init = true) {
- $d = Cache::get("user");
- if (!isset($d[$id])) {
- $r = self::initUser($id, $enable_init);
- if (!$r) return null;
- $d = Cache::get("user");
- }
- /** @var User $class */
- $class = $d[$id];
- return $class;
- }
-
- /**
- * 初始化用户实例。如果没有此用户的实例数据,会创建
- * @param $id
- * @param bool $enable_init
- * @return bool
- */
- static function initUser($id, $enable_init = true) {
- if (file_exists(DP::getUserFolder() . $id . ".dat")) $class = unserialize(file_get_contents(DP::getUserFolder() . $id . ".dat"));
- else {
- if ($enable_init) {
- Console::info("无法找到用户 " . $id . " 的数据,正在创建...");
- $class = new User($id);
- } else return false;
- }
- Cache::appendKey("user", $id, $class);
- return true;
- }
-
- /**
- * 获取模块列表的通用方法
- * @return array
- */
- static function getMods() {
- $dir = WORKING_DIR . "src/cqbot/mods/";
- $dirs = scandir($dir);
- $ls = [];
- unset($dirs[0], $dirs[1]);
- foreach ($dirs as $v) {
- if ($v != "ModBase.php" && (strstr($v, ".php") !== false)) {
- $name = substr($v, 0, -4);
- $ls[] = $name;
- Console::debug("loading mod: " . $name);
- }
- }
- /** @var ModBase[] $ls */
- for ($i = 0; $i < count($ls) - 1; $i++) {
- for ($j = 0; $j < count($ls) - $i - 1; $j++) {
- $s = defined($ls[$j] . "::mod_level") ? $ls[$j]::mod_level : 10;
- $s1 = defined($ls[$j + 1] . "::mod_level") ? $ls[$j + 1]::mod_level : 10;
- //Console::info("Comparing mod " . $ls[$j] . " with " . $ls[$j + 1] . ", level are " . $s . ", " . $s1);
- if ($s < $s1) {
- $t = $ls[$j + 1];
- $ls[$j + 1] = $ls[$j];
- $ls[$j] = $t;
- }
- }
- }
- for ($i = count($ls) - 1; $i >= 0; $i--) {
- $s = defined($ls[$i] . "::mod_level") ? $ls[$i]::mod_level : 10;
- if ($s === 0) unset($ls[$i]);
- }
- return $ls;
- }
-
- /**
- * 重启框架,此服务重启为全自动的
- */
- static function reload() {
- Console::info("Reloading server");
- self::saveAllFiles();
- Cache::$data = [];
- foreach (ConnectionManager::getAll() as $v) {
- $v->close();
- }
- Cache::$server->reload();
- }
-
- /**
- * 停止运行框架,需要用shell再次开启才能启动
- */
- static function stop() {
- Console::info("Stopping server...");
- self::saveAllFiles();
- Cache::$server->shutdown();
- }
-
- /**
- * 此函数用于解析其他非消息类型事件,显示在log里
- * @param $req
- * @return string
- */
- static function executeType($req) {
- switch ($req["post_type"]) {
- case "message":
- return "消息";
- case "event"://兼容3.x
- switch ($req["event"]) {
- case "group_upload":
- return "群[" . $req["group_id"] . "] 文件上传:" . $req["file"]["name"] . "(" . intval($req["file"]["size"] / 1024) . "kb)";
- case "group_admin":
- switch ($req["sub_type"]) {
- case "set":
- return "群[" . $req["group_id"] . "] 设置管理员:" . $req["user_id"];
- case "unset":
- return "群[" . $req["group_id"] . "] 取消管理员:" . $req["user_id"];
- default:
- return "unknown_group_admin_type";
- }
- case "group_decrease":
- switch ($req["sub_type"]) {
- case "leave":
- return "群[" . $req["group_id"] . "] 成员主动退群:" . $req["user_id"];
- case "kick":
- return "群[" . $req["group_id"] . "] 管理员[" . $req["operator_id"] . "]踢出了:" . $req["user_id"];
- case "kick_me":
- return "群[" . $req["group_id"] . "] 本账号被踢出";
- default:
- return "unknown_group_decrease_type";
- }
- case "group_increase":
- return "群[" . $req["group_id"] . "] " . $req["operator_id"] . " 同意 " . $req["user_id"] . " 加入了群";
- default:
- return "unknown_event";
- }
- case "notice":
- switch ($req["notice_type"]) {
- case "group_upload":
- return "群[" . $req["group_id"] . "] 文件上传:" . $req["file"]["name"] . "(" . intval($req["file"]["size"] / 1024) . "kb)";
- case "group_admin":
- switch ($req["sub_type"]) {
- case "set":
- return "群[" . $req["group_id"] . "] 设置管理员:" . $req["user_id"];
- case "unset":
- return "群[" . $req["group_id"] . "] 取消管理员:" . $req["user_id"];
- default:
- return "unknown_group_admin_type";
- }
- case "group_decrease":
- switch ($req["sub_type"]) {
- case "leave":
- return "群[" . $req["group_id"] . "] 成员主动退群:" . $req["user_id"];
- case "kick":
- return "群[" . $req["group_id"] . "] 管理员[" . $req["operator_id"] . "]踢出了:" . $req["user_id"];
- case "kick_me":
- return "群[" . $req["group_id"] . "] 本账号被踢出";
- default:
- return "unknown_group_decrease_type";
- }
- case "group_increase":
- return "群[" . $req["group_id"] . "] " . $req["operator_id"] . " 同意 " . $req["user_id"] . " 加入了群";
- default:
- return "unknown_event";
- }
- case "request":
- switch ($req["request_type"]) {
- case "friend":
- return "加好友请求:" . $req["user_id"] . ",验证信息:" . ($req["message"] ?? $req["comment"]);//兼容3.x
- case "group":
- switch ($req["sub_type"]) {
- case "add":
- return "加群[" . $req["group_id"] . "] 请求:" . $req["user_id"] . ",请求信息:" . ($req["message"] ?? $req["comment"]);//兼容3.x
- case "invite":
- return "用户" . $req["user_id"] . "邀请机器人进入群:" . $req["group_id"];
- default:
- return "unknown_group_type";
- }
- default:
- return "unknown_request_type";
- }
- default:
- return "unknown_post_type";
- }
- }
-
- static function isRobot($user_id) {
- $robots = [];
- foreach (ConnectionManager::getAll("robot") as $v) {
- if (!in_array($v->getQQ(), $robots))
- $robots[] = $v->getQQ();
- }
- if ((Cache::get("bots") ?? []) != []) {
- foreach (Cache::get("bots") as $v) {
- $robots[] = $v;
- }
- }
- return in_array($user_id, $robots);
- }
-
- static function getRobotAlias($qq) {
- return RobotWSConnection::ALIAS_LIST[$qq] ?? "机器人";
- }
-
- /**
- * 刷新消息速率(每分钟)
- * 当 $insert 为 true 时,表明运行此函数时收到了一条消息
- * @param bool $insert
- */
- static function updateMsg($insert = true) {
- $ls = Cache::get("msg_speed");
- if ($insert === true)
- $ls [] = time();
- foreach ($ls as $k => $v) {
- if ((time() - $v) > 60) {
- array_splice($ls, $k, 1);
- }
- }
- Cache::set("msg_speed", $ls);
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/utils/ConnectionManager.php b/src/cqbot/utils/ConnectionManager.php
deleted file mode 100644
index 2bf938ba..00000000
--- a/src/cqbot/utils/ConnectionManager.php
+++ /dev/null
@@ -1,45 +0,0 @@
- $v) {
- switch ($type) {
- case "robot":
- /** @var RobotWSConnection[] $ls */
- if ($v instanceof RobotWSConnection) $ls[] = $v;
- break;
- default:
- break;
- }
- }
- return $ls;
- }
-
- public static function get($fd) { return Cache::$connect[$fd] ?? null; }
-
- public static function set($fd, WSConnection $connection) { Cache::$connect[$fd] = $connection; }
-
- public static function remove($fd) { unset(Cache::$connect[$fd]); }
-
- public static function isConnectExists($fd) { return array_key_exists($fd, Cache::$connect); }
-
- /**
- * @param $qq
- * @return null|RobotWSConnection|NullConnection
- */
- public static function getRobotConnection($qq) {
- foreach (Cache::$connect as $v) {
- if ($v instanceof RobotWSConnection && $v->getQQ() == $qq && $v->getSubType() != "event") return $v;
- }
- return new NullConnection(Cache::$server, -1, "0.0.0.0", $qq);
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/utils/DataProvider.php b/src/cqbot/utils/DataProvider.php
deleted file mode 100755
index bdd639c8..00000000
--- a/src/cqbot/utils/DataProvider.php
+++ /dev/null
@@ -1,64 +0,0 @@
- $v) {
- $v->setFriend([]);
- }
- foreach (ConnectionManager::getAll("robot") as $k => $v) {
- if ($v->getSubType() != "event") {
- $robot_id = $v->getQQ();
- Console::put("正在获取机器人 " . $robot_id . " 的好友列表...");
- $v->sendAPI("_get_friend_list", ["flat" => "true"], function ($response) use ($robot_id) {
- foreach ($response["data"]["friends"] as $k => $v) {
- $user = CQUtil::getUser($v["user_id"]);
- $ls = $user->getFriend();
- if (!in_array($robot_id, $ls)) {
- $ls[] = $robot_id;
- }
- $user->setFriend($ls);
- $user->setNickname($v["nickname"]);
- }
- foreach (CQUtil::getAllUsers() as $k => $v) {
- $serial = serialize($v);
- file_put_contents(DataProvider::getUserFolder() . $k . ".dat", $serial);
- }
- Cache::set("user", []);
- });
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/utils/GroupManager.php b/src/cqbot/utils/GroupManager.php
deleted file mode 100644
index 95bffcc4..00000000
--- a/src/cqbot/utils/GroupManager.php
+++ /dev/null
@@ -1,39 +0,0 @@
- $v) {
- if ($v->getSubType() != "event") {
- $robot_id = $v->getQQ();
- Console::put("正在获取机器人 " . $robot_id . " 的群组列表...");
- $v->sendAPI("get_group_list", [], function ($response) use ($robot_id) {
- $list = Cache::get("group_list");
- foreach ($response["data"] as $k => $v) {
- if (!isset($list[$v["group_id"]])) {
- $list[$v["group_id"]] = [
- "group_id" => $v["group_id"],
- "group_name" => $v["group_name"],
- "fetch_members" => false,
- "joiner" => [$robot_id]
- ];
- } elseif (!in_array($robot_id, $list[$v["group_id"]]["joiner"])) {
- $list[$v["group_id"]]["joiner"][] = $robot_id;
- }
- }
- Cache::set("group_list", $list);
- });
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/utils/Scheduler.php b/src/cqbot/utils/Scheduler.php
deleted file mode 100644
index 5c3133aa..00000000
--- a/src/cqbot/utils/Scheduler.php
+++ /dev/null
@@ -1,47 +0,0 @@
-framework = $framework;
- self::$obj = $this;
- $this->start_time = $start_time;
- }
-
- public static function getInstance() {
- return self::$obj;
- }
-
- public function tick($id) {
- //Console::info("Timer ".Console::setColor("#".$id, "gold").":".Cache::$server->worker_id." ticking at ".time());
- /** @var array $ls */
- $ls = Cache::get("mods");
- foreach ($ls as $v) {
- if (in_array("onTick", get_class_methods($v))) {
- $v::onTick(time() - $this->start_time, $id);
- }
- }
- }
-
- public static function after($ms, callable $callback){
- return swoole_timer_after($ms, $callback);
- }
-}
\ No newline at end of file
diff --git a/src/cqbot/utils/StatusParser.php b/src/cqbot/utils/StatusParser.php
deleted file mode 100644
index 27ce2088..00000000
--- a/src/cqbot/utils/StatusParser.php
+++ /dev/null
@@ -1,30 +0,0 @@
-= 1) {
- switch ($obj["action"]) {
- case "send_private_msg":
- Console::put(Console::setColor("[".date("H:i:s")." PRIVATE] ", "blue").Console::setColor($obj["params"]["user_id"] ?? "", "yellow") . Console::setColor(" > ", "gray").($obj["params"]["message"] ?? ""));
- break;
- case "send_group_msg":
- Console::put(Console::setColor("[".date("H:i:s")." GROUP:".$obj["params"]["group_id"]."] ", "blue").Console::setColor($obj["params"]["user_id"] ?? "", "yellow") . Console::setColor(" > ", "gray").($obj["params"]["message"] ?? ""));
- break;
- case "send_discuss_msg":
- Console::put(Console::setColor("[".date("H:i:s")." DISCUSS:".$obj["params"]["discuss_id"]."] ", "blue").Console::setColor($obj["params"]["user_id"] ?? "", "yellow") . Console::setColor(" > ", "gray").($obj["params"]["message"] ?? ""));
- break;
- case "send_msg":
- $obj["action"] = "send_".$obj["message_type"]."_msg";
- self::msg($obj);
- break;
- default:
- break;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/framework/ErrorStatus.php b/src/framework/ErrorStatus.php
deleted file mode 100755
index 0e57eef1..00000000
--- a/src/framework/ErrorStatus.php
+++ /dev/null
@@ -1,47 +0,0 @@
- "POST 请求的正文格式不正确",
- 1404 => "API 不存在",
- 100 => "参数缺失或参数无效",
- 102 => "酷q操作权限不足",
- 103 => "用户权限不足或文件系统异常",
- 201 => "工作线程池未正确初始化",
- -1 => "请求发送失败",
- -2 => "未收到服务器回复,可能未发送成功",
- -3 => "消息过长或为空",
- -4 => "消息解析过程异常",
- -5 => "日志功能未启用",
- -6 => "日志优先级错误",
- -7 => "数据入库失败",
- -8 => "不支持对系统帐号操作",
- -9 => "帐号不在该群内,消息无法发送",
- -10 => "该用户不存在/不在群内",
- -11 => "数据错误,无法请求发送",
- -12 => "不支持对匿名成员解除禁言",
- -13 => "无法解析要禁言的匿名成员数据",
- -14 => "由于未知原因,操作失败",
- -15 => "群未开启匿名发言功能,或匿名帐号被禁言",
- -16 => "帐号不在群内或网络错误,无法退出/解散该群",
- -17 => "帐号为群主,无法退出该群",
- -18 => "帐号非群主,无法解散该群",
- -19 => "临时消息已失效或未建立",
- -20 => "参数错误",
- -21 => "临时消息已失效或未建立",
- -22 => "获取QQ信息失败",
- -23 => "找不到与目标QQ的关系,消息无法发送",
- -26 => "消息过长"
- ];
-
- static function getMessage($retcode){
- return self::$error[$retcode] ?? "未知错误";
- }
-}
\ No newline at end of file
diff --git a/src/framework/Framework.php b/src/framework/Framework.php
deleted file mode 100755
index 00fdf9e7..00000000
--- a/src/framework/Framework.php
+++ /dev/null
@@ -1,92 +0,0 @@
-host = $config["swoole_host"];
- $this->port = $config["swoole_port"];
- //Buffer::set("access_token", $config["access_token"] ?? "");
- //Buffer::set("info_level", $config["info_level"]);
- //Buffer::set("admin_group", $config["admin_group"]);
-
- $this->selfCheck();
-
- Console::info("CQBot Framework starting on " . $this->host . ":" . $this->port);
- $this->event = new swoole_websocket_server($this->host, $this->port);
-
- $this->event->set([
- "log_file" => $config["swoole_log_file"],
- "worker_num" => $config["swoole_worker_num"],
- "dispatch_mode" => $config["swoole_dispatch_mode"]
- ]);
-
- //swoole服务器启动时运行的函数
- $this->event->on('WorkerStart', [$this, 'onWorkerStart']);
-
- //swoole服务端收到WebSocket信息时运行的函数
- $this->event->on('message', function ($server, $frame) { new WSMessageEvent($server, $frame); });
-
- //收到ws连接和断开连接回调的函数
- $this->event->on('open', function (\swoole_websocket_server $server, \swoole_http_request $request) { new WSOpenEvent($server, $request); });
- $this->event->on('close', function (\swoole_server $server, int $fd) { new WSCloseEvent($server, $fd); });
-
- //设置接收HTTP接口接收的内容,兼容微信公众号和其他服务用
- $this->event->on("request", function (\swoole_http_request $request, \swoole_http_response $response) { new HTTPEvent($request, $response); });
-
- //设置原子计数器
- Cache::$in_count = new \swoole_atomic(0);
- Cache::$out_count = new \swoole_atomic(0);
- Cache::$reload_time = new \swoole_atomic(0);
- Cache::$api_id = new \swoole_atomic(0);
- }
-
- public function start() { $this->event->start(); }
-
- public static function getInstance() { return self::$obj; }
-
- /* Callback function down here */
-
- /**
- * This is async function in EventLoop
- * When it reload, it will run this function again.
- * @param \swoole_server $server
- * @param $worker_id
- */
- public function onWorkerStart(\swoole_server $server, $worker_id) {
- self::$obj = $this;
- new WorkerStartEvent($server, $worker_id);
- }
-
- /**
- * 开启时候的自检模块
- * 检测项目在下面列举
- */
- public function selfCheck() {
- if (!extension_loaded("swoole")) die("无法找到swoole扩展,请先安装.\n");
- if (!function_exists("mb_substr")) die("无法找到mbstring扩展,请先安装.\n");
- if (substr(PHP_VERSION, 0, 1) != "7") die("PHP >=7 required.\n");
- if (!function_exists("curl_exec")) die("无法找到curl扩展,请先安装.\n");
- //if (!class_exists("ZipArchive")) die("无法找到zip扩展,请先安装.(如果不需要zip功能可以删除此条自检)\n");
- if (!is_file(settings()["swoole_log_file"])) file_put_contents(settings()["swoole_log_file"], "");
- return true;
- }
-}
\ No newline at end of file
diff --git a/src/framework/global_functions.php b/src/framework/global_functions.php
deleted file mode 100644
index 8d490c4b..00000000
--- a/src/framework/global_functions.php
+++ /dev/null
@@ -1,157 +0,0 @@
- $t) {
- if (!isset($t['file'])) {
- $t['file'] = 'unknown';
- }
- if (!isset($t['line'])) {
- $t['line'] = 0;
- }
- if (!isset($t['function'])) {
- $t['function'] = 'unknown';
- }
- $log .= "#$i {$t['file']}({$t['line']}): ";
- if (isset($t['object']) and is_object($t['object'])) {
- $log .= get_class($t['object']) . '->';
- }
- $log .= "{$t['function']}()\n";
- }
-
- file_put_contents(CRASH_DIR . "last_error.log", $log);
- break;
- default:
- break;
- }
- }
-});
-
-
-function CQMsg($msg, $type, $id) {
- if ($type === "group") {
- $reply = ["action" => "send_group_msg", "params" => ["group_id" => $id, "message" => $msg]];
- $reply["echo"] = $reply;
- $reply["echo"]["time"] = time();
- $reply = json_encode($reply);
- } else if ($type === "private") {
- $reply = ["action" => "send_private_msg", "params" => ["user_id" => $id, "message" => $msg]];
- $reply["echo"] = $reply;
- $reply["echo"]["time"] = time();
- $reply = json_encode($reply);
- } else if ($type === "discuss") {
- $reply = ["action" => "send_discuss_msg", "params" => ["discuss_id" => $id, "message" => $msg]];
- $reply["echo"] = $reply;
- $reply["echo"]["time"] = time();
- $reply = json_encode($reply);
- } else {
- $reply = false;
- }
- return $reply;
-}
-
-function getClassPath($dir, $class_name) {
- $list = scandir($dir);
- unset($list[0], $list[1]);
- foreach ($list as $v) {
- $taskFileName = explode(".", $v);
- if (is_dir($dir . $v)) {
- if (($find = getClassPath($dir . $v . "/", $class_name)) !== null) return $find;
- else continue;
- } else {
- if (array_pop($taskFileName) == "php" && $taskFileName[0] == $class_name) return $dir . $v;
- }
- }
- return null;
-}
-
-function class_loader($p) {
- $dir = WORKING_DIR . "src/";
- $filepath = getClassPath($dir, $p);
- require_once $filepath;
-}
-
-function load_extensions() {
- $dir = WORKING_DIR . "src/extension/";
- $ls = scandir($dir);
- unset($ls[0], $ls[1]);
- foreach ($ls as $k => $v) {
- if (mb_substr($v, -4) == "phar") {
- require_once $dir . $v;
- }
- }
-}
-
-function color($str, $end = "\n") {
- $str = str_replace("{red}", "\e[38;5;203m", $str);
- $str = str_replace("{green}", "\e[38;5;83m", $str);
- $str = str_replace("{yellow}", "\e[38;5;227m", $str);
- $str = str_replace("{lightpurple}", "\e[38;5;207m", $str);
- $str = str_replace("{lightblue}", "\e[38;5;87m", $str);
- $str = str_replace("{gold}", "\e[38;5;214m", $str);
- $str = str_replace("{gray}", "\e[38;5;59m", $str);
- $str = str_replace("{pink}", "\e[38;5;207m", $str);
- $str = str_replace("{lightlightblue}", "\e[38;5;63m", $str);
- $str = str_replace("{r}", "\e[m", $str);
- $str .= "\e[m" . $end;
- return $str;
-}
-
-/**
- * 使用自己定义的万(san)能分割函数
- * @param $msg
- * @param bool $ban_comma
- * @return array
- */
-function explodeMsg($msg, $ban_comma = false) {
- $msg = str_replace(" ", "\n", trim($msg));
- if (!$ban_comma) {
- $msg = str_replace(",", "\n", $msg);
- $msg = str_replace("\t", "\n", $msg);
- }
- $msgs = explode("\n", $msg);
- $ls = [];
- foreach ($msgs as $k => $v) {
- if (trim($v) == "") continue;
- $ls[] = trim($v);
- }
- return $ls;
-}
-
-/**
- * Unicode解析
- * @param $str
- * @return null|string|string[]
- */
-function unicodeDecode($str) {
- return preg_replace_callback('/\\\\u([0-9a-f]{4})/i', function ($matches) {
- return mb_convert_encoding(pack("H*", $matches[1]), "UTF-8", "UCS-2BE");
- }, $str);
-}
diff --git a/start-coolq.sh b/start-coolq.sh
deleted file mode 100644
index 4471a072..00000000
--- a/start-coolq.sh
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/bin/bash
-
-if [[ ! -d "coolq-data" ]]; then
- mkdir coolq-data
-fi
-sudo docker start coolq >/dev/null 2>&1
-
-if [[ ! $? -eq 0 ]]; then
- echo -n "请输入你的VNC登陆密码(无回显): "
- read -s vnc_pwd
- echo -n "请输入你的反向ws连接地址(默认ws://127.0.0.1:20000/): "
- read reverse_url
- if [[ ${reverse_url} = "" ]]; then
- reverse_url="ws://127.0.0.1:20000/"
- echo "使用默认ws地址。"
- fi
- echo -n "请输入连接CQBot-swoole框架的token(没有请回车,无回显):"
- read access_token
- while :
- do
- echo -n "请输入你的酷Q下载版本 [1(CQA,默认) / 2(CQP)] : "
- read cqp_ver
- if [[ ${cqp_ver} = "" ]]; then
- cqp_ver="1"
- fi
- link="http://dlsec.cqp.me/cqa-tuling"
- if [[ ${cqp_ver} = "2" ]]; then
- link="-e COOLQ_URL=http://dlsec.cqp.me/cqp-tuling"
- break
- elif [[ ${cqp_ver} = "1" ]]; then
- link=""
- break
- else
- echo "你输入的数字有误!"
- continue
- fi
- done
- if [[ ${access_token} = "" ]]; then
- access_token2=" "
- else
- access_token2="-e CQHTTP_ACCESS_TOKEN="${access_token}
- fi
- host_mode_line="--net=host"
- sudo docker run --name coolq -d -v $(pwd)/coolq-data:/home/user/coolq \
- ${host_mode_line} \
- -e VNC_PASSWD=${vnc_pwd} \
- -e CQHTTP_USE_WS_REVERSE=true \
- ${link} \
- ${access_token2} \
- -e CQHTTP_WS_REVERSE_USE_UNIVERSAL_CLIENT=true \
- -e CQHTTP_WS_REVERSE_URL=${reverse_url} \
- -e FORCE_ENV=false \
- richardchien/cqhttp:latest
- echo -n "成功启动docker!正在等待酷Q下载完成... "
- while [[ ! -f "coolq-data/conf/CQP.cfg" ]]
- do
- sleep 1s
- done
- echo ""
- echo "下载完成,请登陆VNC进行登陆QQ!"
-else
- sudo docker start coolq
- echo "已启动酷Q docker!"
-fi
diff --git a/start-docker-screen.sh b/start-docker-screen.sh
deleted file mode 100755
index 13275bff..00000000
--- a/start-docker-screen.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-
-cq=$(screen -list | grep "cq")
-if [[ "$cq" = "" ]]; then
- screen -dmS cq
-fi
-sleep 1s
-
-screen -x -S cq -p 0 -X stuff "sudo docker run -it --rm --net=host --name cqbot -v "$(pwd)"/cqbot/:/root/ jesse2061/cqbot-swoole"
-screen -x -S cq -p 0 -X stuff "\n"
-screen -x -S cq -p 0 -X stuff "php start.php"
-screen -x -S cq -p 0 -X stuff "\n"
diff --git a/start-docker.sh b/start-docker.sh
deleted file mode 100755
index b5638972..00000000
--- a/start-docker.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-sudo docker run -it --rm --net=host --name cqbot -v $(pwd)/cqbot/:/root/ jesse2061/cqbot-swoole
diff --git a/start-screen.sh b/start-screen.sh
deleted file mode 100755
index 46ed6070..00000000
--- a/start-screen.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-
-cq=$(screen -list | grep "cq")
-if [[ "$cq" = "" ]]; then
- screen -dmS cq
-fi
-sleep 1s
-
-#ls="cd "$(pwd)"/CQBot-swoole/"
-#screen -x -S cq -p 0 -X stuff ${ls}
-#screen -x -S cq -p 0 -X stuff "\n"
-screen -x -S cq -p 0 -X stuff "php start.php"
-screen -x -S cq -p 0 -X stuff "\n"
\ No newline at end of file
diff --git a/start.php b/start.php
deleted file mode 100755
index 9e994ed3..00000000
--- a/start.php
+++ /dev/null
@@ -1,30 +0,0 @@
-start();
-
diff --git a/wizard.php b/wizard.php
deleted file mode 100644
index a01d8827..00000000
--- a/wizard.php
+++ /dev/null
@@ -1,105 +0,0 @@
-