mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-22 08:05:34 +08:00
refactor bot action sender, add BotMap to mark bot
This commit is contained in:
31
src/ZM/Context/BotConnectContext.php
Normal file
31
src/ZM/Context/BotConnectContext.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Context;
|
||||
|
||||
use ZM\Context\Trait\BotActionTrait;
|
||||
|
||||
/**
|
||||
* 机器人裸连接的上下文
|
||||
*/
|
||||
class BotConnectContext
|
||||
{
|
||||
use BotActionTrait;
|
||||
|
||||
private ?array $self = null;
|
||||
|
||||
public function __construct(private int $flag, private int $fd)
|
||||
{
|
||||
}
|
||||
|
||||
public function getFd(): int
|
||||
{
|
||||
return $this->fd;
|
||||
}
|
||||
|
||||
public function getFlag(): int
|
||||
{
|
||||
return $this->flag;
|
||||
}
|
||||
}
|
||||
@@ -7,15 +7,13 @@ namespace ZM\Context;
|
||||
use DI\DependencyException;
|
||||
use DI\NotFoundException;
|
||||
use OneBot\Driver\Coroutine\Adaptive;
|
||||
use OneBot\Driver\Event\Http\HttpRequestEvent;
|
||||
use OneBot\Driver\Event\WebSocket\WebSocketMessageEvent;
|
||||
use OneBot\V12\Object\ActionResponse;
|
||||
use OneBot\V12\Object\MessageSegment;
|
||||
use OneBot\V12\Object\OneBotEvent;
|
||||
use ZM\Context\Trait\BotActionTrait;
|
||||
use ZM\Exception\OneBot12Exception;
|
||||
use ZM\Exception\WaitTimeoutException;
|
||||
use ZM\Plugin\OneBot12Adapter;
|
||||
use ZM\Plugin\OneBot\OneBot12Adapter;
|
||||
use ZM\Schedule\Timer;
|
||||
use ZM\Utils\MessageUtil;
|
||||
|
||||
@@ -26,7 +24,7 @@ class BotContext implements ContextInterface
|
||||
/** @var array<string, array<string, BotContext>> 记录机器人的上下文列表 */
|
||||
private static array $bots = [];
|
||||
|
||||
/** @var string[] 记录当前上下文绑定的机器人 */
|
||||
/** @var null|string[] 记录当前上下文绑定的机器人 */
|
||||
private array $self;
|
||||
|
||||
/** @var array 如果是 BotCommand 匹配的上下文,这里会存放匹配到的参数 */
|
||||
@@ -35,11 +33,9 @@ class BotContext implements ContextInterface
|
||||
/** @var bool 用于标记当前上下文会话是否已经调用过 reply() 方法 */
|
||||
private bool $replied = false;
|
||||
|
||||
public function __construct(string $bot_id, string $platform, null|WebSocketMessageEvent|HttpRequestEvent $event = null)
|
||||
public function __construct(string $bot_id, string $platform)
|
||||
{
|
||||
$this->self = ['user_id' => $bot_id, 'platform' => $platform];
|
||||
self::$bots[$bot_id][$platform] = $this;
|
||||
$this->base_event = $event;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,36 +4,28 @@ declare(strict_types=1);
|
||||
|
||||
namespace ZM\Context\Trait;
|
||||
|
||||
use Choir\Http\HttpFactory;
|
||||
use OneBot\Driver\Coroutine\Adaptive;
|
||||
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\Context\BotConnectContext;
|
||||
use ZM\Exception\OneBot12Exception;
|
||||
use ZM\Plugin\OneBot\BotMap;
|
||||
use ZM\Utils\MessageUtil;
|
||||
|
||||
trait BotActionTrait
|
||||
{
|
||||
/**
|
||||
* @var array<string, int> 一个记录 echo 对应协程 ID 的列表,用于恢复协程
|
||||
*/
|
||||
protected static array $coroutine_list = [];
|
||||
|
||||
protected null|WebSocketMessageEvent|HttpRequestEvent $base_event;
|
||||
|
||||
/**
|
||||
* @internal 只允许内部调用
|
||||
* @param ActionResponse $response 尝试调用看看有没有协程等待的
|
||||
*/
|
||||
public static function tryResume(ActionResponse $response): void
|
||||
{
|
||||
if (($co = Adaptive::getCoroutine()) !== null && isset(static::$coroutine_list[$response->echo ?? ''])) {
|
||||
$co->resume(static::$coroutine_list[$response->echo ?? ''], $response);
|
||||
if (($co = Adaptive::getCoroutine()) !== null && isset(BotMap::$bot_coroutines[$response->echo ?? ''])) {
|
||||
$co->resume(BotMap::$bot_coroutines[$response->echo ?? ''], $response);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +40,7 @@ trait BotActionTrait
|
||||
$message = MessageUtil::convertToArr($message);
|
||||
$params['message'] = $message;
|
||||
$params['detail_type'] = $detail_type;
|
||||
return $this->sendAction(Utils::camelToSeparator(__FUNCTION__), $params, $this->self);
|
||||
return $this->sendAction(Utils::camelToSeparator(__FUNCTION__), $params, $this->getSelf());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,6 +50,9 @@ trait BotActionTrait
|
||||
*/
|
||||
public function sendAction(string $action, array $params = [], ?array $self = null): bool|ActionResponse
|
||||
{
|
||||
if ($self === null && $this->self !== null) {
|
||||
$self = $this->self;
|
||||
}
|
||||
// 声明 Action 对象
|
||||
$a = new Action($action, $params, ob_uuidgen(), $self);
|
||||
// 调用事件在回复之前的回调
|
||||
@@ -70,29 +65,29 @@ trait BotActionTrait
|
||||
return false;
|
||||
}
|
||||
|
||||
// 调用机器人连接发送 Action,首先试试看是不是 WebSocket
|
||||
if ($this->base_event instanceof WebSocketMessageEvent) {
|
||||
logger()->debug('使用传入的 base_event 发送消息');
|
||||
$result = $this->base_event->send(json_encode($a->jsonSerialize()));
|
||||
}
|
||||
if (!isset($result) && container()->has('ws.message.event')) {
|
||||
logger()->debug('使用容器的 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;
|
||||
// 获取机器人的 BotMap 对应连接(前提是当前上下文有 self)
|
||||
if ($self !== null) {
|
||||
$fd_map = BotMap::getBotFd($self['user_id'], $self['platform']);
|
||||
if ($fd_map === null) {
|
||||
logger()->error("机器人 [{$self['platform']}:{$self['user_id']}] 没有连接或未就绪,无法发送数据");
|
||||
return false;
|
||||
}
|
||||
$result = ws_socket($fd_map[0])->send(json_encode($a->jsonSerialize()), $fd_map[1]);
|
||||
} elseif ($this instanceof BotConnectContext) {
|
||||
// self 为空,说明可能是发送的元动作,需要通过 fd 来查找对应的 connect 连接
|
||||
$flag = $this->getFlag();
|
||||
$fd = $this->getFd();
|
||||
$result = ws_socket($flag)->send(json_encode($a->jsonSerialize()), $fd);
|
||||
} elseif (method_exists($this, 'emitSendAction')) {
|
||||
$result = $this->emitSendAction($a);
|
||||
} else {
|
||||
logger()->error('未匹配到任何机器人连接');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果开启了协程,并且成功发送,那就进入协程等待,挂起等待结果返回一个 ActionResponse 对象
|
||||
if (($result ?? false) === true && ($co = Adaptive::getCoroutine()) !== null) {
|
||||
static::$coroutine_list[$a->echo] = $co->getCid();
|
||||
BotMap::$bot_coroutines[$a->echo] = $co->getCid();
|
||||
$response = $co->suspend();
|
||||
if ($response instanceof ActionResponse) {
|
||||
return $response;
|
||||
|
||||
Reference in New Issue
Block a user