Compare commits

...

8 Commits
2.3.4 ... 2.4.1

Author SHA1 Message Date
jerry
bbe4addd83 update to 2.4.1 version (build 401)
fix startup warning bug
2021-03-25 17:11:35 +08:00
jerry
600829645d fix init command 2021-03-25 16:56:08 +08:00
jerry
68280cfe7e fix init command 2021-03-25 16:50:32 +08:00
jerry
c5523aa95d reset global config 2021-03-25 16:19:09 +08:00
jerry
93a68a5582 update to v2.4.0 (build 400)
add systemd:generate command
add check:config command
init command add `--force|-F` option
add MessageUtil function `addShortCommand()`
clear debug message
2021-03-25 16:18:09 +08:00
jerry
6155236d3c update to v2.4.0 (build 399)
add CheckConfigCommand.php
add config update record docs
adjust swoole version to 4.5.0
fix stop and reload bugs
add $_running_annotation
add remote terminal
update global config
add timer tick exception handler
add zm_xxx global functions
add isAtMe(), splitCommand(), matchCommand() function for MessageUtil
add workerAction(), sendActionToWorker(), resumeAllWorkerCoroutines() functions for ProcessManager
optimize CQCommand match function
add custom TerminalCommand annotation
add TuringAPI
add getReloadableFiles() function for ZMUtil
2021-03-24 23:34:46 +08:00
28f7f20728 update docs 2021-03-23 14:51:55 +08:00
235256d679 rollback and correct to 398(v2.3.5) 2021-03-23 14:49:42 +08:00
28 changed files with 354 additions and 142 deletions

View File

@@ -1,3 +0,0 @@
FROM zmbot/swoole:latest
# TODO: auto-setup entrypoint

View File

@@ -1,14 +0,0 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 2.0 | :white_check_mark: |
| 1.6.x | :white_check_mark: |
| 1.1.x | :x: |
| 1.0.x | :x: |
## Reporting a Vulnerability
If you find a bug which is safety related, you should post a new issue named **Security Issue**, and I will check it as soon as possible.

View File

@@ -1,31 +0,0 @@
#!/usr/bin/env php
<?php /** @since 1.2 */
switch ($argv[1] ?? '') {
case '--generate':
case '':
generate($argv);
break;
case '--help':
case '-h':
default:
echo "\nUsage: " . $argv[0] . " [OPTION]\n";
echo "\nzhamao-framework systemd generator.";
echo "\n\n -h, --help\t\tShow this help menu";
echo "\n --generate\tGenerate a systemd service file\n\n";
break;
}
function generate($argv) {
$s = "[Unit]\nDescription=zhamao-framework Daemon\nAfter=rc-local.service\n\n[Service]\nType=simple";
$s .= "\nUser=" . exec("whoami");
$s .= "\nGroup=" . exec("groups | awk '{print $1}'");
$s .= "\nWorkingDirectory=" . getcwd();
if ($argv[0] == "systemd" && !file_exists(getcwd() . '/systemd'))
$s .= "\nExecStart=" . getcwd() . "/vendor/bin/start server";
else
$s .= "\nExecStart=" . getcwd() . "/bin/start server";
$s .= "\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\n";
@mkdir(getcwd() . "/resources/");
file_put_contents(getcwd() . "/resources/zhamao.service", $s);
echo "File successfully generated. Path: " . getcwd() . "/resources/zhamao.service\n";
}

View File

@@ -29,10 +29,10 @@
"symfony/polyfill-mbstring": "^1.20",
"symfony/console": "^5.1",
"symfony/routing": "^5.1",
"zhamao/connection-manager": "*@dev",
"zhamao/console": "^1.0",
"zhamao/console": "^1.0.10",
"zhamao/config": "^1.0",
"zhamao/request": "*@dev"
"zhamao/request": "^1.1",
"zhamao/connection-manager": "^1.0"
},
"suggest": {
"ext-ctype": "*",

View File

@@ -1,7 +1,6 @@
<?php
/** @noinspection PhpFullyQualifiedNameUsageInspection */
/** @noinspection PhpComposerExtensionStubsInspection */
global $config;
/** bind host */
$config['host'] = '0.0.0.0';
@@ -39,7 +38,7 @@ $config['light_cache'] = [
'size' => 512, //最多允许储存的条数需要2的倍数
'max_strlen' => 32768, //单行字符串最大长度需要2的倍数
'hash_conflict_proportion' => 0.6, //Hash冲突率越大越好但是需要的内存更多
'persistence_path' => $config['zm_data'].'_cache.json',
'persistence_path' => $config['zm_data'] . '_cache.json',
'auto_save_interval' => 900
];
@@ -112,15 +111,15 @@ $config['server_event_handler_class'] = [
// 这里添加例如 \ZM\Event\ServerEventHandler::class 这样的启动注解类
];
/** 服务器启用的外部第三方和内部插件 */
$config['modules']['onebot'] = [
// 机器人解析模块,关闭后无法使用如@CQCommand等注解
/** 机器人解析模块关闭后无法使用如CQCommand等注解(上面的modules即将废弃) */
$config['onebot'] = [
'status' => true,
'single_bot_mode' => false
'single_bot_mode' => false,
'message_level' => 99999
];
$config['modules']['remote_terminal'] = [
// 一个远程简易终端使用nc直接连接即可但是不建议开放host为0.0.0.0(远程连接)
/** 一个远程简易终端使用nc直接连接即可但是不建议开放host为0.0.0.0(远程连接) */
$config['remote_terminal'] = [
'status' => false,
'host' => '127.0.0.1',
'port' => 20002,

View File

@@ -1,3 +1,23 @@
# FAQ
这里会写一些常见的疑难解答。
这里会写一些常见的疑难解答。
## 启动时报错 Address already in use
1. 检查是否开启了两次框架,每个端口只能开启一个框架。
2. 如果是之前已经在 20001 端口或者你设置了别的应用同样占用此端口,更换配置文件 `global.php` 中的 port 即可。
3. 如果是之前框架成功启动,但是使用 Ctrl+C 停止后再次启动导致的报错,请根据下面的步骤来检查是否存在僵尸进程。
- 如果系统内装有 `htop`,可以直接在 `htop` 中开启 Tree 模式并使用 filter 过滤 php检查残留的框架进程。
- 如果系统没有 `htop`,使用 `ps aux | grep vendor/bin/start | grep -v grep` 如果存在进程,请使用以下命令尝试杀掉:
```bash
# 如果确定框架的数据都已保存且没有需要保存的缓存数据,直接杀掉 SIGKILL 即可,输入下面这条
ps aux | grep vendor/bin/start | grep -v grep | awk '{print $2}' | xargs kill -9
# 如果不确定框架是不是还继续运行,想尝试正常关闭(走一遍储存保存数据的事件),使用下面这条
# 首先使用 'ps aux | grep vendor/bin/start | grep -v grep' 找到进程中第二列最小的pid
# 然后使用下面的这条命令假设最小的pid是23643
kill -INT 23643
# 如果使用 ps aux 看不到框架相关进程,证明关闭成功,否则需要使用第一条强行杀死
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 0 B

View File

@@ -271,6 +271,8 @@ zm_dump($pass);
## zm_config()
> v2.4.0 起可用。
同 `ZMConfig::get()`。
定义:`zm_config($name, $key = null)`。
@@ -284,6 +286,8 @@ zm_config("global", "swoole"); //等同于 ZMConfig::get("global", "swoole");
## zm_info()
> v2.4.0 起可用。(下面的 log 类也一样)
同 `Console::info($msg)`。
## zm_debug()

25
docs/update/config.md Normal file
View File

@@ -0,0 +1,25 @@
# 配置文件变更记录
这里将会记录各个主版本的框架升级后,涉及 `global.php` 的更新日志,你可以根据这里描述的内容与你的旧配置文件进行合并。
## v2.4.0 (build 400)
- 调整 `$config['modules']['onebot']` 配置项到 `$config['onebot']`,旧版本的此段会向下兼容,建议更新,
- 新增 `$config['remote_terminal']` 远程终端的配置项,新增此段即可。
更新部分:
```php
/** 机器人解析模块关闭后无法使用如CQCommand等注解(上面的modules即将废弃) */
$config['onebot'] = [
'status' => true,
'single_bot_mode' => false,
'message_level' => 99999
];
/** 一个远程简易终端使用nc直接连接即可但是不建议开放host为0.0.0.0(远程连接) */
$config['remote_terminal'] = [
'status' => false,
'host' => '127.0.0.1',
'port' => 20002,
'token' => ''
];
```

View File

@@ -1,23 +1,53 @@
# 更新日志v2 版本)
## v2.3.4 (build 397)
## v2.4.1 (build 401)
> 更新时间2021.3.25
- 修复:开启框架时导致的报错
## v2.4.0build 400
> 更新时间2021.3.25
- 新增:检查全局配置文件的命令
- 新增:全局配置文件更新记录
- 依赖变更:**Swoole 最低版本需要 4.5.0**
- 优化reload 和 stop 命令重载和停止框架的逻辑
- 新增:`$_running_annotation` 变量,可在注解事件中的类使用
- 新增远程终端Remote Terminal弥补原来删掉的本地终端通过 nc 命令连接即可
- 新增:启动参数 `--worker-num``--task-worker-num``--remote-terminal`
- 更新:全局配置文件结构
- 新增Swoole 计时器报错处理
- 新增:全局方法(`zm_dump()``zm_error()``zm_warning()``zm_info()``zm_success()``zm_verbose()``zm_debug()``zm_config()`
- 新增:示例模块的图灵机器人和 at 机器人的处理函数
- 新增MessageUtil 工具类新增 `isAtMe(), splitCommand(), matchCommand()` 方法
- 新增ProcessManager 进程管理类新增 `workerAction(), sendActionToWorker(), resumeAllWorkerCoroutines()` 方法
- 优化CQCommand 的匹配逻辑
- 新增:支持添加自定义远程终端指令的 `@TerminalCommand` 注解
- 新增:图灵机器人 API 封装函数
- 新增ZMUtil 工具杂项类 `getReloadableFiles()` 函数
- 新增:`vendor/bin/start systemd:generate` 生成 systemd 配置文件的功能
- 新增:`vendor/bin/start check:config` 检查配置文件更新的命令
- 新增:`vendor/bin/init` 新增 `--force` 参数,覆盖现有文件重新生成
- 新增MessageUtil 新增方法:`addShortCommand()`,用于快速添加静态文本问答回复的
以下是需要**手动更新**或**更换新写法**的部分:
- 配置文件 `global.php` 中的 `modules` 字段展开,内置模块的配置一律平铺到外面。详见 [更新日志 - 配置文件变更](/update/config)。
以下是默认机器人直接连接产生的变更:
- 2.4.0 新增了默认回复其他人 at 的消息,如果不需要,请将 `Hello.php` 中的 `changeAt()``turingAPI()` 方法删除。
## v2.3.5 (build 398)
> 更新时间2021.3.23
- 修复:版本号显示
- 修复:MySQL 数据库查询导致的一系列问题
- 修复:内存泄露问题
## v2.3.3 (build 396)
> 更新时间2021.3.23
- 修复Composer 调试时出现加载重复的问题
## v2.3.2 (build 395)
> 更新时间2021.3.23
- 修复:数据库查询部分情况无法正常使用的 bug
- 修复:内存泄漏问题
> 2.3.2-2.3.4 版本由于操作失误导致代码不完整,请直接使用 2.3.5 即可。
## v2.3.1
@@ -25,7 +55,6 @@
- 规范代码,修复一个小报错的 bug
## v2.3.0
> 更新时间2021.3.16

Binary file not shown.

Before

Width:  |  Height:  |  Size: 385 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

View File

@@ -2,14 +2,12 @@
namespace Module\Example;
use ZM\Annotation\Command\TerminalCommand;
use ZM\Annotation\CQ\CQBefore;
use ZM\Annotation\CQ\CQMessage;
use ZM\Annotation\Http\Middleware;
use ZM\Annotation\Swoole\OnCloseEvent;
use ZM\Annotation\Swoole\OnOpenEvent;
use ZM\Annotation\Swoole\OnRequestEvent;
use ZM\Annotation\Swoole\OnStart;
use ZM\API\CQ;
use ZM\API\TuringAPI;
use ZM\ConnectionManager\ConnectionObject;
@@ -18,6 +16,7 @@ use ZM\Annotation\CQ\CQCommand;
use ZM\Annotation\Http\RequestMapping;
use ZM\Event\EventDispatcher;
use ZM\Exception\InterruptException;
use ZM\Module\QQBot;
use ZM\Requests\ZMRequest;
use ZM\Utils\MessageUtil;
use ZM\Utils\ZMUtil;
@@ -29,12 +28,6 @@ use ZM\Utils\ZMUtil;
*/
class Hello
{
/**
* @OnStart()
*/
public function onStart() {
}
/*
* 默认的图片监听路由对应目录,如需要使用可取消下面的注释,把上面的 /* 换成 /**
* @OnStart(-1)
@@ -81,29 +74,37 @@ class Hello
}
/**
* 图灵机器人的内置实现在tuling123.com申请一个apikey填入下方变量即可。
* @CQCommand(start_with="机器人",end_with="机器人",message_type="group")
* @CQMessage(message_type="private",level=1)
*/
public function turingAPI() {
$user_id = ctx()->getUserId();
$api = "83513e3d316f44de9c952cda4c9aed30"; // 请在这里填入你的图灵机器人的apikey
$api = ""; // 请在这里填入你的图灵机器人的apikey
if ($api === "") return false; //如果没有填入apikey则此功能关闭
if (($this->_running_annotation ?? null) instanceof CQCommand) {
$msg = ctx()->getFullArg("我在!有什么事吗?");
} else {
$msg = ctx()->getMessage();
}
zm_dump($msg);
return TuringAPI::getTuringMsg($msg, $user_id, $api);
ctx()->setMessage($msg);
if (MessageUtil::matchCommand($msg, ctx()->getData())->status === false) {
return TuringAPI::getTuringMsg($msg, $user_id, $api);
} else {
QQBot::getInstance()->handle(ctx()->getData(), ctx()->getCache("level") + 1);
return true;
}
}
/**
* 响应at机器人的消息
* @CQBefore("message")
*/
public function changeAt() {
if (MessageUtil::isAtMe(ctx()->getMessage(), ctx()->getRobotId())) {
$msg = str_replace(CQ::at(ctx()->getRobotId()), "", ctx()->getMessage());
ctx()->setMessage("机器人 ".$msg);
ctx()->setMessage("机器人" . $msg);
Console::info(ctx()->getMessage());
}
return true;
}

View File

@@ -0,0 +1,61 @@
<?php
namespace ZM\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class CheckConfigCommand extends Command
{
protected static $defaultName = 'check:config';
private $need_update = false;
protected function configure() {
$this->setDescription("检查配置文件是否和框架当前版本有更新");
}
protected function execute(InputInterface $input, OutputInterface $output): int {
if (LOAD_MODE !== 1) {
$output->writeln("<error>仅限在Composer依赖模式中使用此命令");
return Command::FAILURE;
}
$current_cfg = getcwd() . "/config/";
$remote_cfg = include_once WORKING_DIR . "/config/global.php";
if (file_exists($current_cfg . "global.php")) {
$this->check($remote_cfg, "global.php", $output);
}
if (file_exists($current_cfg . "global.development.php")) {
$this->check($remote_cfg, "global.development.php", $output);
}
if (file_exists($current_cfg . "global.staging.php")) {
$this->check($remote_cfg, "global.staging.php", $output);
}
if (file_exists($current_cfg . "global.production.php")) {
$this->check($remote_cfg, "global.production.php", $output);
}
if ($this->need_update === true) {
$output->writeln("<comment>有配置文件需要更新,详情见文档 https://framework.zhamao.xin/update/config.md</comment>");
} else {
$output->writeln("<info>配置文件暂无更新!</info>");
}
return Command::SUCCESS;
}
/**
* @noinspection PhpIncludeInspection
*/
private function check($remote, $local, OutputInterface $out) {
$local_file = include_once getcwd() . "/config/".$local;
foreach($remote as $k => $v) {
$out->writeln("<comment>正在检查".$k."</comment>");
if (!isset($local_file[$k])) {
$out->writeln("<error>配置文件 ".$local . " 需要更新!(缺少 `$k` 字段配置)</error>");
$this->need_update = true;
}
}
}
}

View File

@@ -6,6 +6,7 @@ namespace ZM\Command;
use Phar;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class InitCommand extends Command
@@ -26,6 +27,9 @@ class InitCommand extends Command
protected function configure() {
$this->setDescription("Initialize framework starter | 初始化框架运行的基础文件");
$this->setDefinition([
new InputOption("force", "F", null, "强制重制,覆盖现有文件")
]);
$this->setHelp("此命令将会解压以下文件到项目的根目录:\n" . implode("\n", $this->getExtractFiles()));
// ...
}
@@ -34,8 +38,9 @@ class InitCommand extends Command
if (LOAD_MODE === 1) { // 从composer依赖而来的项目模式最基本的需要初始化的模式
$output->writeln("<comment>Initializing files</comment>");
$base_path = LOAD_MODE_COMPOSER_PATH;
$args = $input->getOption("force");
foreach ($this->extract_files as $file) {
if (!file_exists($base_path . $file)) {
if (!file_exists($base_path . $file) || $args) {
$info = pathinfo($file);
@mkdir($base_path . $info["dirname"], 0777, true);
echo "Copying " . $file . PHP_EOL;
@@ -67,8 +72,8 @@ class InitCommand extends Command
}
}
file_put_contents($base_path . "/composer.json", json_encode($composer, 64 | 128 | 256));
$output->writeln("<info>Executing composer update command</info>");
exec("composer update");
$output->writeln("<info>Executing composer command: `composer dump-autoload`</info>");
exec("composer dump-autoload");
echo PHP_EOL;
} else {
echo("Error occurred. Please check your updates.\n");

View File

@@ -26,6 +26,8 @@ class RunServerCommand extends Command
new InputOption("remote-terminal", null, null, "启用远程终端配置使用global.php中的"),
new InputOption("disable-coroutine", null, null, "关闭协程Hook"),
new InputOption("daemon", null, null, "以守护进程的方式运行框架"),
new InputOption("worker-num", null, InputOption::VALUE_REQUIRED, "启动框架时运行的 Worker 进程数量"),
new InputOption("task-worker-num", null, InputOption::VALUE_REQUIRED, "启动框架时运行的 TaskWorker 进程数量"),
new InputOption("watch", null, null, "监听 src/ 目录的文件变化并热更新"),
new InputOption("show-php-ver", null, null, "启动时显示PHP和Swoole版本"),
new InputOption("env", null, InputOption::VALUE_REQUIRED, "设置环境类型 (production, development, staging)"),

View File

@@ -13,8 +13,30 @@ class SystemdCommand extends Command
// the name of the command (the part after "bin/console")
protected static $defaultName = 'systemd:generate';
protected function configure() {
$this->setDescription("生成框架的 systemd 配置文件");
}
protected function execute(InputInterface $input, OutputInterface $output): int {
//TODO: 写一个生成systemd配置的功能给2.0
$path = $this->generate();
$output->writeln("<info>成功生成 systemd 文件,位置:".$path."</info>");
$output->writeln("<info>有关如何使用 systemd 配置文件,请访问 `https://github.com/zhamao-robot/zhamao-framework/issues/36`</info>");
return Command::SUCCESS;
}
private function generate() {
$s = "[Unit]\nDescription=zhamao-framework Daemon\nAfter=rc-local.service\n\n[Service]\nType=simple";
$s .= "\nUser=" . exec("whoami");
$s .= "\nGroup=" . exec("groups | awk '{print $1}'");
$s .= "\nWorkingDirectory=" . getcwd();
if (LOAD_MODE == 1) {
$s .= "\nExecStart=" . getcwd() . "/vendor/bin/start server";
} else {
$s .= "\nExecStart=" . getcwd() . "/bin/start server";
}
$s .= "\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\n";
@mkdir(getcwd() . "/resources/");
file_put_contents(getcwd() . "/resources/zhamao.service", $s);
return getcwd() . "/resources/zhamao.service";
}
}

View File

@@ -5,6 +5,7 @@ namespace ZM;
use Exception;
use ZM\Command\CheckConfigCommand;
use ZM\Command\DaemonReloadCommand;
use ZM\Command\DaemonStatusCommand;
use ZM\Command\DaemonStopCommand;
@@ -14,12 +15,13 @@ use ZM\Command\RunServerCommand;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use ZM\Command\SystemdCommand;
use ZM\Utils\DataProvider;
class ConsoleApplication extends Application
{
const VERSION_ID = 397;
const VERSION = "2.3.4";
const VERSION_ID = 401;
const VERSION = "2.4.1";
public function __construct(string $name = 'UNKNOWN') {
define("ZM_VERSION_ID", self::VERSION_ID);
@@ -73,8 +75,12 @@ class ConsoleApplication extends Application
new DaemonStopCommand(),
new RunServerCommand(), //运行主服务的指令控制器
new InitCommand(), //初始化用的用于项目初始化和phar初始化
new PureHttpCommand() //纯HTTP服务器指令
new PureHttpCommand(), //纯HTTP服务器指令
new SystemdCommand()
]);
if (LOAD_MODE === 1) {
$this->add(new CheckConfigCommand());
}
/*
$command_register = ZMConfig::get("global", "command_register_class") ?? [];
foreach ($command_register as $v) {

View File

@@ -216,7 +216,7 @@ class Context implements ContextInterface
* @throws WaitTimeoutException
*/
public function getArgs($mode, $prompt_msg) {
$arg = ctx()->getCache("match");
$arg = ctx()->getCache("match") ?? [];
switch ($mode) {
case ZM_MATCH_ALL:
$p = $arg;

View File

@@ -117,6 +117,17 @@ class EventDispatcher
public function dispatchEvent($v, $rule_func = null, ...$params) {
$q_c = $v->class;
$q_f = $v->method;
if ($q_c === "" && ($q_f instanceof \Closure)) {
if ($this->log) Console::verbose("[事件分发{$this->eid}] 闭包函数的事件触发过程!");
if ($rule_func !== null && !$rule_func($v)) {
if ($this->log) Console::verbose("[事件分发{$this->eid}] 闭包函数下的 ruleFunc 判断为 false, 拒绝执行此方法。");
$this->status = self::STATUS_RULE_FAILED;
return false;
}
$this->store = $q_f(...$params);
$this->status = self::STATUS_NORMAL;
return true;
}
if ($this->log) Console::verbose("[事件分发{$this->eid}] 正在判断 " . $q_c . "::" . $q_f . " 方法下的 ruleFunc ...");
if ($rule_func !== null && !$rule_func($v)) {
if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法下的 ruleFunc 判断为 false, 拒绝执行此方法。");

View File

@@ -143,12 +143,8 @@ class ServerEventHandler
* @throws Exception
*/
public function onWorkerStart(Server $server, $worker_id) {
zm_atomic("_#worker_".$worker_id)->set($server->worker_pid);
//if (ZMBuf::atomic("stop_signal")->get() != 0) return;
Process::signal(SIGUSR1, function() use ($worker_id){
Timer::clearAll();
ProcessManager::resumeAllWorkerCoroutines();
});
Process::signal(SIGINT, function () use ($worker_id, $server) {
Timer::clearAll();
ProcessManager::resumeAllWorkerCoroutines();
@@ -157,6 +153,11 @@ class ServerEventHandler
});
unset(Context::$context[Coroutine::getCid()]);
if ($server->taskworker === false) {
Process::signal(SIGUSR1, function() use ($worker_id){
Timer::clearAll();
ProcessManager::resumeAllWorkerCoroutines();
});
zm_atomic("_#worker_".$worker_id)->set($server->worker_pid);
try {
register_shutdown_function(function () use ($server) {
$error = error_get_last();
@@ -464,8 +465,12 @@ class ServerEventHandler
}
});
try {
if ($conn->getName() === 'qq' && ZMConfig::get("global", "modules")["onebot"]["status"] === true) {
if (ZMConfig::get("global", "modules")["onebot"]["single_bot_mode"]) {
$obb_onebot = ZMConfig::get("global", "onebot") ??
ZMConfig::get("global", "modules")["onebot"] ??
["status" => true, "single_bot_mode" => false, "message_level" => 99999];
$onebot_status = $obb_onebot["status"];
if ($conn->getName() === 'qq' && $onebot_status === true) {
if ($obb_onebot["single_bot_mode"]) {
LightCacheInside::set("connect", "conn_fd", $request->fd);
}
}
@@ -510,8 +515,11 @@ class ServerEventHandler
}
});
try {
if ($conn->getName() === 'qq' && ZMConfig::get("global", "modules")["onebot"]["status"] === true) {
if (ZMConfig::get("global", "modules")["onebot"]["single_bot_mode"]) {
$obb_onebot = ZMConfig::get("global", "onebot") ??
ZMConfig::get("global", "modules")["onebot"] ??
["status" => true, "single_bot_mode" => false, "message_level" => 99999];
if ($conn->getName() === 'qq' && $obb_onebot["status"] === true) {
if ($obb_onebot["single_bot_mode"]) {
LightCacheInside::set("connect", "conn_fd", -1);
}
}
@@ -657,18 +665,19 @@ class ServerEventHandler
}
//加载插件
$plugins = ZMConfig::get("global", "modules") ?? [];
if (!isset($plugins["onebot"])) $plugins["onebot"] = ["status" => true, "single_bot_mode" => false, "message_level" => 99999];
$obb_onebot = ZMConfig::get("global", "onebot") ??
ZMConfig::get("global", "modules")["onebot"] ??
["status" => true, "single_bot_mode" => false, "message_level" => 99999];
if ($plugins["onebot"]) {
if ($obb_onebot["status"]) {
$obj = new OnSwooleEvent();
$obj->class = QQBot::class;
$obj->method = 'handle';
$obj->method = 'handleByEvent';
$obj->type = 'message';
$obj->level = $plugins["onebot"]["message_level"] ?? 99999;
$obj->level = $obb_onebot["message_level"] ?? 99999;
$obj->rule = 'connectIsQQ()';
EventManager::addEvent(OnSwooleEvent::class, $obj);
if ($plugins["onebot"]["single_bot_mode"]) {
if ($obb_onebot["single_bot_mode"]) {
LightCacheInside::set("connect", "conn_fd", -1);
} else {
LightCacheInside::set("connect", "conn_fd", -2);

View File

@@ -89,28 +89,27 @@ class Framework
($o = ZMConfig::get("console_color")) === false ? [] : $o
);
$worker = ZMConfig::get("global", "swoole")["worker_num"] ?? swoole_cpu_num();
define("ZM_WORKER_NUM", $worker);
ZMAtomic::init();
$timezone = ZMConfig::get("global", "timezone") ?? "Asia/Shanghai";
date_default_timezone_set($timezone);
$this->server_set = ZMConfig::get("global", "swoole");
$this->server_set["log_level"] = SWOOLE_LOG_DEBUG;
$add_port = ZMConfig::get("global", "modules")["remote_terminal"]["status"] ?? false;
$add_port = ZMConfig::get("global", "remote_terminal")["status"] ?? false;
$this->parseCliArgs(self::$argv, $add_port);
$worker = $this->server_set["worker_num"] ?? swoole_cpu_num();
define("ZM_WORKER_NUM", $worker);
ZMAtomic::init();
// 打印初始信息
$out["listen"] = ZMConfig::get("global", "host") . ":" . ZMConfig::get("global", "port");
if (!isset(ZMConfig::get("global", "swoole")["worker_num"])) $out["worker"] = swoole_cpu_num() . " (auto)";
else $out["worker"] = ZMConfig::get("global", "swoole")["worker_num"];
if (!isset($this->server_set["worker_num"])) $out["worker"] = swoole_cpu_num() . " (auto)";
else $out["worker"] = $this->server_set["worker_num"];
$out["environment"] = $args["env"] === null ? "default" : $args["env"];
$out["log_level"] = Console::getLevel();
$out["version"] = ZM_VERSION . (LOAD_MODE == 0 ? (" (build " . ZM_VERSION_ID . ")") : "");
if (APP_VERSION !== "unknown") $out["app_version"] = APP_VERSION;
if (isset(ZMConfig::get("global", "swoole")["task_worker_num"])) {
$out["task_worker"] = ZMConfig::get("global", "swoole")["task_worker_num"];
if (isset($this->server_set["task_worker_num"])) {
$out["task_worker"] = $this->server_set["task_worker_num"];
}
if (ZMConfig::get("global", "sql_config")["sql_host"] !== "") {
$conf = ZMConfig::get("global", "sql_config");
@@ -128,7 +127,7 @@ class Framework
$out["swoole_version"] = SWOOLE_VERSION;
}
if ($add_port) {
$conf = ZMConfig::get("global", "modules")["remote_terminal"];
$conf = ZMConfig::get("global", "remote_terminal");
$out["terminal"] = $conf["host"] . ":" . $conf["port"];
}
@@ -138,30 +137,36 @@ class Framework
self::$server = new Server(ZMConfig::get("global", "host"), ZMConfig::get("global", "port"));
if ($add_port) {
$conf = ZMConfig::get("global", "remote_terminal") ?? [
'status' => true,
'host' => '127.0.0.1',
'port' => 20002,
'token' => ''
];
$welcome_msg = Console::setColor("Welcome! You can use `help` for usage.", "green");
/** @var Port $port */
$port = self::$server->listen("127.0.0.1", 20002, SWOOLE_SOCK_TCP);
$port = self::$server->listen($conf["host"], $conf["port"], SWOOLE_SOCK_TCP);
$port->set([
'open_http_protocol' => false
]);
$port->on('connect', function (?\Swoole\Server $serv, $fd) use ($port, $welcome_msg) {
$port->on('connect', function (?\Swoole\Server $serv, $fd) use ($port, $welcome_msg, $conf) {
ManagerGM::pushConnect($fd, "terminal");
$serv->send($fd, file_get_contents(working_dir() . "/config/motd.txt"));
if (!empty(ZMConfig::get("global", "modules")["remote_terminal"]["token"] ?? '')) {
if (!empty($conf["token"])) {
$serv->send($fd, "Please input token: ");
} else {
$serv->send($fd, $welcome_msg . "\n>>> ");
}
});
$port->on('receive', function ($serv, $fd, $reactor_id, $data) use ($welcome_msg) {
$port->on('receive', function ($serv, $fd, $reactor_id, $data) use ($welcome_msg, $conf) {
ob_start();
try {
$arr = LightCacheInside::get("light_array", "input_token") ?? [];
if (empty($arr[$fd] ?? '')) {
if (ZMConfig::get("global", "modules")["remote_terminal"]["token"] != '') {
if ($conf["token"] != '') {
$token = trim($data);
if ($token === ZMConfig::get("global", "modules")["remote_terminal"]["token"]) {
if ($token === $conf["token"]) {
SpinLock::transaction("input_token", function () use ($fd, $token) {
$arr = LightCacheInside::get("light_array", "input_token");
$arr[$fd] = $token;
@@ -334,6 +339,25 @@ class Framework
$terminal_id = uuidgen();
foreach ($args as $x => $y) {
switch ($x) {
case 'worker-num':
if ($y) {
if (intval($y) >= 1 && intval($y) <= 1024) {
$this->server_set["worker_num"] = intval($y);
} else {
Console::warning("Invalid worker num! Turn to default value (".($this->server_set["worker_num"] ?? swoole_cpu_num()).")");
}
}
break;
case 'task-worker-num':
if ($y) {
if (intval($y) >= 1 && intval($y) <= 1024) {
$this->server_set["task_worker_num"] = intval($y);
$this->server_set["task_enable_coroutine"] = true;
} else {
Console::warning("Invalid worker num! Turn to default value (0)");
}
}
break;
case 'disable-coroutine':
if ($y) {
$coroutine_mode = false;

View File

@@ -16,6 +16,7 @@ use ZM\Exception\InterruptException;
use ZM\Exception\WaitTimeoutException;
use ZM\Utils\CoMessage;
use ZM\Utils\MessageUtil;
use ZM\Utils\SingletonTrait;
/**
* Class QQBot
@@ -23,20 +24,28 @@ use ZM\Utils\MessageUtil;
*/
class QQBot
{
use SingletonTrait;
public function handleByEvent() {
$data = json_decode(context()->getFrame()->data, true);
$this->handle($data);
}
/**
* @param $data
* @param int $level
* @throws InterruptException
* @throws Exception
*/
public function handle() {
public function handle($data, $level = 0) {
try {
$data = json_decode(context()->getFrame()->data, true);
if ($level > 10) return;
set_coroutine_params(["data" => $data]);
if (isset($data["post_type"])) {
//echo TermColor::ITALIC.json_encode($data, 128|256).TermColor::RESET.PHP_EOL;
ctx()->setCache("level", 0);
ctx()->setCache("level", $level);
//Console::debug("Calling CQ Event from fd=" . ctx()->getConnection()->getFd());
if ($data["post_type"] != "meta_event") {
$r = $this->dispatchBeforeEvents($data); // before在这里执行元事件不执行before为减少不必要的调试日志
$r = $this->dispatchBeforeEvents($data, "pre"); // before在这里执行元事件不执行before为减少不必要的调试日志
if ($r->store === "block") EventDispatcher::interrupt();
}
//Console::warning("最上数据包:".json_encode($data));
@@ -44,6 +53,10 @@ class QQBot
if (isset($data["echo"]) || isset($data["post_type"])) {
if (CoMessage::resumeByWS()) EventDispatcher::interrupt();
}
if (($data["post_type"] ?? "meta_event") != "meta_event") {
$r = $this->dispatchBeforeEvents($data, "post"); // before在这里执行元事件不执行before为减少不必要的调试日志
if ($r->store === "block") EventDispatcher::interrupt();
}
if (isset($data["post_type"])) $this->dispatchEvents($data);
else $this->dispatchAPIResponse($data);
} /** @noinspection PhpRedundantCatchClauseInspection */ catch (WaitTimeoutException $e) {
@@ -53,14 +66,21 @@ class QQBot
/**
* @param $data
* @param $time
* @return EventDispatcher
* @throws Exception
*/
public function dispatchBeforeEvents($data): EventDispatcher {
public function dispatchBeforeEvents($data, $time): EventDispatcher {
$before = new EventDispatcher(CQBefore::class);
$before->setRuleFunction(function ($v) use ($data) {
return $v->cq_event == $data["post_type"];
});
if ($time === "pre") {
$before->setRuleFunction(function($v) use ($data){
return $v->level >= 200 && $v->cq_event == $data["post_type"];
});
} else {
$before->setRuleFunction(function($v) use ($data){
return $v->level < 200 && $v->cq_event == $data["post_type"];
});
}
$before->setReturnFunction(function ($result) {
if (!$result) EventDispatcher::interrupt("block");
});

View File

@@ -9,7 +9,6 @@ use ZM\API\CQ;
use ZM\Config\ZMConfig;
use ZM\Console\Console;
use ZM\Entity\MatchResult;
use ZM\Event\EventDispatcher;
use ZM\Event\EventManager;
use ZM\Requests\ZMRequest;
@@ -110,7 +109,7 @@ class MessageUtil
* @return MatchResult
*/
public static function matchCommand($msg, $obj) {
$ls = EventManager::$events[CQCommand::class];
$ls = EventManager::$events[CQCommand::class] ?? [];
$word = self::splitCommand($msg);
$matched = new MatchResult();
foreach ($ls as $k => $v) {
@@ -125,12 +124,12 @@ class MessageUtil
$matched->object = $v;
$matched->status = true;
break;
} elseif ($v->start_with != "" && mb_strpos($msg, $v->start_with) === 0) {
} elseif ($v->start_with != "" && mb_substr($msg, 0, mb_strlen($v->start_with)) === $v->start_with) {
$matched->match = [mb_substr($msg, mb_strlen($v->start_with))];
$matched->object = $v;
$matched->status = true;
break;
} elseif ($v->end_with != "" && strlen($msg) == (strripos($msg, $v->end_with) + strlen($v->end_with))) {
} elseif ($v->end_with != "" && mb_substr($msg, 0 - mb_strlen($v->end_with)) === $v->end_with) {
$matched->match = [substr($msg, 0, strripos($msg, $v->end_with))];
$matched->object = $v;
$matched->status = true;
@@ -160,4 +159,10 @@ class MessageUtil
}
return $matched;
}
public static function addShortCommand($command, string $reply) {
for ($i = 0; $i < ZM_WORKER_NUM; ++$i) {
ProcessManager::sendActionToWorker($i, "add_short_command", [$command, $reply]);
}
}
}

View File

@@ -5,9 +5,11 @@ namespace ZM\Utils;
use Co;
use ZM\Annotation\CQ\CQCommand;
use ZM\Annotation\Swoole\OnPipeMessageEvent;
use ZM\Console\Console;
use ZM\Event\EventDispatcher;
use ZM\Event\EventManager;
use ZM\Store\LightCache;
use ZM\Store\LightCacheInside;
use ZM\Store\WorkerCache;
@@ -17,6 +19,14 @@ class ProcessManager
public static function workerAction($src_worker_id, $data) {
$server = server();
switch ($data["action"] ?? '') {
case 'add_short_command':
Console::verbose("Adding short command ".$data["data"][0]);
$obj = new CQCommand();
$obj->method = quick_reply_closure($data["data"][1]);
$obj->match = $data["data"][0];
$obj->class = "";
EventManager::addEvent(CQCommand::class, $obj);
break;
case "eval":
eval($data["data"]);
break;

View File

@@ -33,7 +33,7 @@ class Terminal
$help[] = "bc:\t\teval执行代码但输入必须是将代码base64之后的如 `bc em1faW5mbygn5L2g5aW9Jyk7`";
$help[] = "stop:\t\t停止服务器";
$help[] = "reload | r:\t热重启用户编写的模块代码";
foreach(EventManager::$events[TerminalCommand::class] as $v) {
foreach((EventManager::$events[TerminalCommand::class] ?? []) as $v) {
$help[]=$v->command.":\t\t".(empty($v->description) ? "<用户自定义指令>" : $v->description);
}
echo implode("\n", $help) . PHP_EOL;

View File

@@ -22,8 +22,9 @@ class ZMUtil
Console::warning(Console::setColor("Stopping server...", "red"));
if (Console::getLevel() >= 4) Console::trace();
ZMAtomic::get("stop_signal")->set(1);
for($i = 0; $i < ZM_WORKER_NUM; ++$i) {
Process::kill(zm_atomic("_#worker_".$i)->get(), SIGUSR1);
for ($i = 0; $i < ZM_WORKER_NUM; ++$i) {
if (Process::kill(zm_atomic("_#worker_" . $i)->get(), 0))
Process::kill(zm_atomic("_#worker_" . $i)->get(), SIGUSR1);
}
server()->shutdown();
server()->stop();

View File

@@ -394,3 +394,9 @@ function zm_verbose($obj) { Console::verbose($obj); }
function zm_error($obj) { Console::error($obj); }
function zm_config($name, $key = null) { return ZMConfig::get($name, $key); }
function quick_reply_closure($reply) {
return function() use ($reply){
return $reply;
};
}