mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-20 23:25:35 +08:00
Merge pull request #214 from zhamao-robot/bot-action-and-some-update
拆分 Bot 动作到 Trait 以及更新一些类型强化的代码
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -9,7 +9,7 @@
|
||||
/site/
|
||||
/plugins/
|
||||
/doxy/
|
||||
|
||||
/walle/
|
||||
# 框架审计文件
|
||||
audit.log
|
||||
|
||||
|
||||
@@ -18,12 +18,6 @@ $config['servers'] = [
|
||||
'type' => 'http',
|
||||
'flag' => 20002,
|
||||
],
|
||||
[
|
||||
'host' => '0.0.0.0',
|
||||
'port' => 20003,
|
||||
'type' => 'http',
|
||||
'flag' => 20003,
|
||||
],
|
||||
];
|
||||
|
||||
/* Workerman 驱动相关配置 */
|
||||
|
||||
@@ -12,6 +12,7 @@ use ZM\Container\ContainerHolder;
|
||||
use ZM\Logger\ConsoleLogger;
|
||||
use ZM\Middleware\MiddlewareHandler;
|
||||
use ZM\Store\Database\DBException;
|
||||
use ZM\Store\Database\DBQueryBuilder;
|
||||
use ZM\Store\Database\DBWrapper;
|
||||
|
||||
// 防止重复引用引发报错
|
||||
@@ -209,7 +210,7 @@ function db(string $name = '')
|
||||
*
|
||||
* @throws DBException
|
||||
*/
|
||||
function sql_builder(string $name = '')
|
||||
function sql_builder(string $name = ''): DBQueryBuilder
|
||||
{
|
||||
return (new DBWrapper($name))->createQueryBuilder();
|
||||
}
|
||||
|
||||
@@ -4,22 +4,21 @@ declare(strict_types=1);
|
||||
|
||||
namespace ZM\Context;
|
||||
|
||||
use Choir\Http\HttpFactory;
|
||||
use OneBot\Driver\Event\Http\HttpRequestEvent;
|
||||
use OneBot\Driver\Event\WebSocket\WebSocketMessageEvent;
|
||||
use OneBot\Util\Utils;
|
||||
use OneBot\V12\Object\Action;
|
||||
use OneBot\V12\Object\MessageSegment;
|
||||
use OneBot\V12\Object\OneBotEvent;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
use ZM\Annotation\AnnotationHandler;
|
||||
use ZM\Annotation\OneBot\BotAction;
|
||||
use ZM\Context\Trait\BotActionTrait;
|
||||
use ZM\Exception\OneBot12Exception;
|
||||
use ZM\Utils\MessageUtil;
|
||||
|
||||
class BotContext implements ContextInterface
|
||||
{
|
||||
use BotActionTrait;
|
||||
|
||||
private static array $echo_id_list = [];
|
||||
|
||||
private array $self;
|
||||
@@ -28,9 +27,10 @@ class BotContext implements ContextInterface
|
||||
|
||||
private bool $replied = false;
|
||||
|
||||
public function __construct(string $bot_id, string $platform)
|
||||
public function __construct(string $bot_id, string $platform, null|WebSocketMessageEvent|HttpRequestEvent $event = null)
|
||||
{
|
||||
$this->self = ['user_id' => $bot_id, 'platform' => $platform];
|
||||
$this->base_event = $event;
|
||||
}
|
||||
|
||||
public function getEvent(): OneBotEvent
|
||||
@@ -82,21 +82,9 @@ class BotContext implements ContextInterface
|
||||
*/
|
||||
public function getBot(string $bot_id, string $platform = ''): BotContext
|
||||
{
|
||||
// TODO: 完善多机器人支持
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function sendMessage(\Stringable|array|MessageSegment|string $message, string $detail_type, array $params = [])
|
||||
{
|
||||
$message = MessageUtil::convertToArr($message);
|
||||
$params['message'] = $message;
|
||||
$params['detail_type'] = $detail_type;
|
||||
return $this->sendAction(Utils::camelToSeparator(__FUNCTION__), $params, $this->self);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置该消息下解析出来的参数列表
|
||||
*
|
||||
@@ -134,39 +122,4 @@ class BotContext implements ContextInterface
|
||||
{
|
||||
return self::$echo_id_list[$echo] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Throwable
|
||||
*/
|
||||
private function sendAction(string $action, array $params = [], ?array $self = null)
|
||||
{
|
||||
// 声明 Action 对象
|
||||
$a = new Action($action, $params, ob_uuidgen(), $self);
|
||||
self::$echo_id_list[$a->echo] = $a;
|
||||
// 调用事件在回复之前的回调
|
||||
$handler = new AnnotationHandler(BotAction::class);
|
||||
container()->set(Action::class, $a);
|
||||
$handler->setRuleCallback(fn (BotAction $act) => $act->action === '' || $act->action === $action && !$act->need_response);
|
||||
$handler->handleAll($a);
|
||||
// 被阻断时候,就不发送了
|
||||
if ($handler->getStatus() === AnnotationHandler::STATUS_INTERRUPTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 调用机器人连接发送 Action
|
||||
if (container()->has('ws.message.event')) {
|
||||
/** @var WebSocketMessageEvent $ws */
|
||||
$ws = container()->get('ws.message.event');
|
||||
return $ws->send(json_encode($a->jsonSerialize()));
|
||||
}
|
||||
// 如果是 HTTP WebHook 的形式,那么直接调用 Response
|
||||
if (container()->has('http.request.event')) {
|
||||
/** @var HttpRequestEvent $event */
|
||||
$event = container()->get('http.request.event');
|
||||
$response = HttpFactory::createResponse(headers: ['Content-Type' => 'application/json'], body: json_encode([$a->jsonSerialize()]));
|
||||
$event->withResponse($response);
|
||||
return true;
|
||||
}
|
||||
throw new OneBot12Exception('No bot connection found.');
|
||||
}
|
||||
}
|
||||
|
||||
81
src/ZM/Context/Trait/BotActionTrait.php
Normal file
81
src/ZM/Context/Trait/BotActionTrait.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Context\Trait;
|
||||
|
||||
use Choir\Http\HttpFactory;
|
||||
use OneBot\Driver\Event\Http\HttpRequestEvent;
|
||||
use OneBot\Driver\Event\WebSocket\WebSocketMessageEvent;
|
||||
use OneBot\Util\Utils;
|
||||
use OneBot\V12\Object\Action;
|
||||
use OneBot\V12\Object\ActionResponse;
|
||||
use OneBot\V12\Object\MessageSegment;
|
||||
use ZM\Annotation\AnnotationHandler;
|
||||
use ZM\Annotation\OneBot\BotAction;
|
||||
use ZM\Exception\OneBot12Exception;
|
||||
use ZM\Utils\MessageUtil;
|
||||
|
||||
trait BotActionTrait
|
||||
{
|
||||
private null|WebSocketMessageEvent|HttpRequestEvent $base_event;
|
||||
|
||||
/**
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function sendMessage(\Stringable|array|MessageSegment|string $message, string $detail_type, array $params = []): ActionResponse|bool
|
||||
{
|
||||
$message = MessageUtil::convertToArr($message);
|
||||
$params['message'] = $message;
|
||||
$params['detail_type'] = $detail_type;
|
||||
return $this->sendAction(Utils::camelToSeparator(__FUNCTION__), $params, $this->self);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送机器人动作
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function sendAction(string $action, array $params = [], ?array $self = null): bool|ActionResponse
|
||||
{
|
||||
// 声明 Action 对象
|
||||
$a = new Action($action, $params, ob_uuidgen(), $self);
|
||||
self::$echo_id_list[$a->echo] = $a;
|
||||
// 调用事件在回复之前的回调
|
||||
$handler = new AnnotationHandler(BotAction::class);
|
||||
container()->set(Action::class, $a);
|
||||
$handler->setRuleCallback(fn (BotAction $act) => $act->action === '' || $act->action === $action && !$act->need_response);
|
||||
$handler->handleAll($a);
|
||||
// 被阻断时候,就不发送了
|
||||
if ($handler->getStatus() === AnnotationHandler::STATUS_INTERRUPTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 调用机器人连接发送 Action
|
||||
if ($this->base_event instanceof WebSocketMessageEvent) {
|
||||
$result = $this->base_event->send(json_encode($a->jsonSerialize()));
|
||||
}
|
||||
if (!isset($result) && container()->has('ws.message.event')) {
|
||||
$result = container()->get('ws.message.event')->send(json_encode($a->jsonSerialize()));
|
||||
}
|
||||
// 如果是 HTTP WebHook 的形式,那么直接调用 Response
|
||||
if (!isset($result) && $this->base_event instanceof HttpRequestEvent) {
|
||||
$response = HttpFactory::createResponse(headers: ['Content-Type' => 'application/json'], body: json_encode([$a->jsonSerialize()]));
|
||||
$this->base_event->withResponse($response);
|
||||
$result = true;
|
||||
}
|
||||
if (!isset($result) && container()->has('http.request.event')) {
|
||||
$response = HttpFactory::createResponse(headers: ['Content-Type' => 'application/json'], body: json_encode([$a->jsonSerialize()]));
|
||||
container()->get('http.request.event')->withResponse($response);
|
||||
$result = true;
|
||||
}
|
||||
if (isset($result)) {
|
||||
return $result;
|
||||
}
|
||||
/* TODO: 协程支持
|
||||
if (($result ?? false) === true && ($co = Adaptive::getCoroutine()) !== null) {
|
||||
return $result ?? false;
|
||||
}*/
|
||||
throw new OneBot12Exception('No bot connection found.');
|
||||
}
|
||||
}
|
||||
@@ -119,10 +119,10 @@ class SignalListener
|
||||
}
|
||||
echo "\r";
|
||||
logger()->notice('请再按 {count} 次 Ctrl+C 以强制杀死进程', ['count' => 5 - self::$manager_kill_time]);
|
||||
return;
|
||||
}
|
||||
++self::$manager_kill_time;
|
||||
if (self::$manager_kill_time === 1) {
|
||||
logger()->notice('Keyboard interrupt, shutting down server...');
|
||||
Framework::getInstance()->stop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ZM\Event\Listener;
|
||||
|
||||
use OneBot\Driver\Coroutine\Adaptive;
|
||||
use OneBot\Driver\Process\ProcessManager;
|
||||
use OneBot\Util\Singleton;
|
||||
use ZM\Annotation\AnnotationHandler;
|
||||
@@ -35,6 +36,8 @@ class WorkerEventListener
|
||||
// 自注册一下,刷新当前进程的logger进程banner
|
||||
ob_logger_register(ob_logger());
|
||||
|
||||
Adaptive::initWithDriver(Framework::getInstance()->getDriver());
|
||||
|
||||
// 如果没有引入参数disable-safe-exit,则监听 Ctrl+C
|
||||
if (!Framework::getInstance()->getArgv()['disable-safe-exit'] && PHP_OS_FAMILY !== 'Windows') {
|
||||
SignalListener::getInstance()->signalWorker();
|
||||
@@ -42,12 +45,10 @@ class WorkerEventListener
|
||||
|
||||
// Windows 环境下,为了监听 Ctrl+C,只能开启终端输入
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
logger()->debug('监听Windows的键盘输入');
|
||||
sapi_windows_set_ctrl_handler([SignalListener::getInstance(), 'signalWindowsCtrlC']);
|
||||
Framework::getInstance()->getDriver()->getEventLoop()->addReadEvent(STDIN, function ($x) {});
|
||||
}
|
||||
|
||||
logger()->debug('Worker #' . ProcessManager::getProcessId() . ' started');
|
||||
|
||||
// 设置 Worker 进程的状态和 ID 等信息
|
||||
if (($name = Framework::getInstance()->getDriver()->getName()) === 'swoole') {
|
||||
/* @phpstan-ignore-next-line */
|
||||
@@ -88,17 +89,23 @@ class WorkerEventListener
|
||||
$this->initUserPlugins();
|
||||
|
||||
// handle @Init annotation
|
||||
$this->dispatchInit();
|
||||
|
||||
Adaptive::getCoroutine()->create(function () {
|
||||
$this->dispatchInit();
|
||||
});
|
||||
// 回显 debug 日志:进程占用的内存
|
||||
$memory_total = memory_get_usage() / 1024 / 1024;
|
||||
logger()->debug('Worker process used ' . round($memory_total, 3) . ' MB');
|
||||
}
|
||||
|
||||
public function onWorkerStart1(): void
|
||||
{
|
||||
logger()->debug('Worker #' . ProcessManager::getProcessId() . ' started');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ZMKnownException
|
||||
*/
|
||||
public function onWorkerStop999()
|
||||
public function onWorkerStop999(): void
|
||||
{
|
||||
logger()->debug('Worker #' . ProcessManager::getProcessId() . ' stopping');
|
||||
if (DIRECTORY_SEPARATOR !== '\\') {
|
||||
@@ -110,11 +117,16 @@ class WorkerEventListener
|
||||
}
|
||||
}
|
||||
|
||||
public function onWorkerStop1(): void
|
||||
{
|
||||
logger()->debug('Worker #' . ProcessManager::getProcessId() . ' stopped');
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载用户代码资源,包括普通插件、单文件插件、Composer 插件等
|
||||
* @throws \Throwable
|
||||
*/
|
||||
private function initUserPlugins()
|
||||
private function initUserPlugins(): void
|
||||
{
|
||||
logger()->debug('Loading user sources');
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class Framework
|
||||
public const VERSION_ID = 651;
|
||||
|
||||
/** @var string 版本名称 */
|
||||
public const VERSION = '3.0.0-beta2';
|
||||
public const VERSION = '3.0.0-beta3';
|
||||
|
||||
/** @var array 传入的参数 */
|
||||
protected array $argv;
|
||||
@@ -228,7 +228,9 @@ class Framework
|
||||
// 添加框架需要监听的顶层事件监听器
|
||||
// worker 事件
|
||||
ob_event_provider()->addEventListener(WorkerStartEvent::getName(), [WorkerEventListener::getInstance(), 'onWorkerStart999'], 999);
|
||||
ob_event_provider()->addEventListener(WorkerStartEvent::getName(), [WorkerEventListener::getInstance(), 'onWorkerStart1'], 1);
|
||||
ob_event_provider()->addEventListener(WorkerStopEvent::getName(), [WorkerEventListener::getInstance(), 'onWorkerStop999'], 999);
|
||||
ob_event_provider()->addEventListener(WorkerStopEvent::getName(), [WorkerEventListener::getInstance(), 'onWorkerStop1'], 1);
|
||||
// Http 事件
|
||||
ob_event_provider()->addEventListener(HttpRequestEvent::getName(), [HttpEventListener::getInstance(), 'onRequest999'], 999);
|
||||
ob_event_provider()->addEventListener(HttpRequestEvent::getName(), [HttpEventListener::getInstance(), 'onRequest1'], 1);
|
||||
|
||||
@@ -8,7 +8,9 @@ class ZMUtil
|
||||
{
|
||||
/**
|
||||
* 获取 composer.json 并转为数组进行读取使用
|
||||
* @param null|string $path 路径
|
||||
*
|
||||
* @param null|string $path 路径
|
||||
* @throws \JsonException
|
||||
*/
|
||||
public static function getComposerMetadata(?string $path = null): ?array
|
||||
{
|
||||
|
||||
@@ -12,11 +12,14 @@ use ZM\Plugin\ZMPlugin;
|
||||
class ZMApplication extends ZMPlugin
|
||||
{
|
||||
/** @var null|ZMApplication 存储单例类的变量 */
|
||||
private static ?ZMApplication $obj;
|
||||
private static ?ZMApplication $obj = null;
|
||||
|
||||
/** @var array 存储要传入的args */
|
||||
private array $args = [];
|
||||
|
||||
/**
|
||||
* @throws SingletonViolationException
|
||||
*/
|
||||
public function __construct(mixed $dir = null)
|
||||
{
|
||||
if (self::$obj !== null) {
|
||||
|
||||
Reference in New Issue
Block a user