mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-12 03:01:12 +00:00
perf: 优化机器人Webhook消息
This commit is contained in:
parent
d366cf9885
commit
a49c0aea47
@ -629,12 +629,11 @@ class UsersController extends AbstractController
|
|||||||
User::auth();
|
User::auth();
|
||||||
//
|
//
|
||||||
$type = trim(Request::input('type'));
|
$type = trim(Request::input('type'));
|
||||||
$botName = "ai-{$type}";
|
if (!UserBot::systemBotName($type)) {
|
||||||
if (!UserBot::isAiBot("{$botName}@bot.system")) {
|
|
||||||
return Base::retError('AI机器人不存在');
|
return Base::retError('AI机器人不存在');
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
$botUser = User::botGetOrCreate($botName);
|
$botUser = User::botGetOrCreate("ai-{$type}");
|
||||||
if (empty($botUser)) {
|
if (empty($botUser)) {
|
||||||
return Base::retError('AI机器人不存在');
|
return Base::retError('AI机器人不存在');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -255,6 +255,18 @@ class User extends AbstractModel
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回是否用户机器人
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isUserBot()
|
||||||
|
{
|
||||||
|
if (preg_match('/^user-(.*?)@bot\.system$/', $this->email)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断是否管理员
|
* 判断是否管理员
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -55,16 +55,6 @@ class UserBot extends AbstractModel
|
|||||||
return str_ends_with($email, '@bot.system') && self::systemBotName($email);
|
return str_ends_with($email, '@bot.system') && self::systemBotName($email);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否系统AI机器人
|
|
||||||
* @param $email
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function isAiBot($email)
|
|
||||||
{
|
|
||||||
return str_starts_with($email, 'ai-') && self::isSystemBot($email);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 系统机器人名称
|
* 系统机器人名称
|
||||||
* @param $name string 邮箱 或 邮箱前缀
|
* @param $name string 邮箱 或 邮箱前缀
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use App\Services\RequestContext;
|
|||||||
use Cache;
|
use Cache;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use League\CommonMark\CommonMarkConverter;
|
use League\CommonMark\CommonMarkConverter;
|
||||||
use League\CommonMark\Exception\CommonMarkException;
|
use League\HTMLToMarkdown\HtmlConverter;
|
||||||
use Overtrue\Pinyin\Pinyin;
|
use Overtrue\Pinyin\Pinyin;
|
||||||
use Redirect;
|
use Redirect;
|
||||||
use Request;
|
use Request;
|
||||||
@ -2977,11 +2977,26 @@ class Base
|
|||||||
*/
|
*/
|
||||||
public static function markdown2html($markdown)
|
public static function markdown2html($markdown)
|
||||||
{
|
{
|
||||||
$converter = new CommonMarkConverter();
|
|
||||||
try {
|
try {
|
||||||
|
$converter = new CommonMarkConverter();
|
||||||
return $converter->convert($markdown);
|
return $converter->convert($markdown);
|
||||||
} catch (CommonMarkException $e) {
|
} catch (\League\CommonMark\Exception\CommonMarkException $e) {
|
||||||
return $markdown;
|
return $markdown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* html 转 MD(markdown)
|
||||||
|
* @param $html
|
||||||
|
* @return mixed|string
|
||||||
|
*/
|
||||||
|
public static function html2markdown($html)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$converter = new HtmlConverter();
|
||||||
|
return $converter->convert($html);
|
||||||
|
} catch (\Exception) {
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -92,7 +92,7 @@ class BotReceiveMsgTask extends AbstractTask
|
|||||||
|
|
||||||
// 提取指令
|
// 提取指令
|
||||||
try {
|
try {
|
||||||
$command = $this->extractCommand($msg, $botUser->isAiBot(), $this->mention);
|
$command = $this->extractCommand($msg, $botUser, $this->mention);
|
||||||
if (empty($command)) {
|
if (empty($command)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -406,6 +406,7 @@ class BotReceiveMsgTask extends AbstractTask
|
|||||||
$serverUrl = 'http://nginx';
|
$serverUrl = 'http://nginx';
|
||||||
$userBot = null;
|
$userBot = null;
|
||||||
$extras = [];
|
$extras = [];
|
||||||
|
$replyText = null;
|
||||||
$errorContent = null;
|
$errorContent = null;
|
||||||
if ($botUser->isAiBot($type)) {
|
if ($botUser->isAiBot($type)) {
|
||||||
// AI机器人
|
// AI机器人
|
||||||
@ -456,32 +457,13 @@ class BotReceiveMsgTask extends AbstractTask
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($msg->reply_id > 0) {
|
if ($msg->reply_id > 0) {
|
||||||
$replyMsg = WebSocketDialogMsg::find($msg->reply_id);
|
$replyCommand = $this->extractReplyCommand($msg->reply_id, $botUser);
|
||||||
$replyCommand = null;
|
if (Base::isError($replyCommand)) {
|
||||||
if ($replyMsg) {
|
$errorContent = $replyCommand['msg'];
|
||||||
switch ($replyMsg->type) {
|
} else {
|
||||||
case 'text':
|
|
||||||
try {
|
|
||||||
$replyCommand = $this->extractCommand($replyMsg, true);
|
|
||||||
} catch (Exception) {
|
|
||||||
$errorContent = "引用消息解析失败。";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'file':
|
|
||||||
$msgData = Base::json2array($replyMsg->getRawOriginal('msg'));
|
|
||||||
$fileResult = TextExtractor::extractFile(public_path($msgData['path']));
|
|
||||||
if (Base::isError($fileResult)) {
|
|
||||||
$errorContent = $fileResult['msg'];
|
|
||||||
} else {
|
|
||||||
$replyCommand = $fileResult['data'];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($replyCommand) {
|
|
||||||
$command = <<<EOF
|
$command = <<<EOF
|
||||||
<quoted_content>
|
<quoted_content>
|
||||||
{$replyCommand}
|
{$replyCommand['data']}
|
||||||
</quoted_content>
|
</quoted_content>
|
||||||
|
|
||||||
The content within the above quoted_content tags is a citation.
|
The content within the above quoted_content tags is a citation.
|
||||||
@ -494,9 +476,17 @@ class BotReceiveMsgTask extends AbstractTask
|
|||||||
$webhookUrl = "{$serverUrl}/ai/chat";
|
$webhookUrl = "{$serverUrl}/ai/chat";
|
||||||
} else {
|
} else {
|
||||||
// 用户机器人
|
// 用户机器人
|
||||||
if (str_starts_with($command, '/')) {
|
if ($botUser->isUserBot() && str_starts_with($command, '/')) {
|
||||||
|
// 用户机器人不处理指令类型命令
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($msg->reply_id > 0) {
|
||||||
|
$replyCommand = $this->extractReplyCommand($msg->reply_id, $botUser);
|
||||||
|
if (Base::isSuccess($replyCommand)) {
|
||||||
|
$replyText = $replyCommand['data'] ?: '';
|
||||||
|
}
|
||||||
|
}
|
||||||
$userBot = UserBot::whereBotId($botUser->userid)->first();
|
$userBot = UserBot::whereBotId($botUser->userid)->first();
|
||||||
$webhookUrl = $userBot?->webhook_url;
|
$webhookUrl = $userBot?->webhook_url;
|
||||||
}
|
}
|
||||||
@ -514,6 +504,7 @@ class BotReceiveMsgTask extends AbstractTask
|
|||||||
try {
|
try {
|
||||||
$data = [
|
$data = [
|
||||||
'text' => $command,
|
'text' => $command,
|
||||||
|
'reply_text' => $replyText,
|
||||||
'token' => User::generateToken($botUser),
|
'token' => User::generateToken($botUser),
|
||||||
'dialog_id' => $dialog->id,
|
'dialog_id' => $dialog->id,
|
||||||
'dialog_type' => $dialog->type,
|
'dialog_type' => $dialog->type,
|
||||||
@ -575,12 +566,12 @@ class BotReceiveMsgTask extends AbstractTask
|
|||||||
/**
|
/**
|
||||||
* 提取消息指令(提取消息内容)
|
* 提取消息指令(提取消息内容)
|
||||||
* @param WebSocketDialogMsg $msg
|
* @param WebSocketDialogMsg $msg
|
||||||
* @param bool $isAiBot
|
* @param User $botUser
|
||||||
* @param bool $mention
|
* @param bool $mention
|
||||||
* @return string
|
* @return string
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
private function extractCommand(WebSocketDialogMsg $msg, bool $isAiBot = false, bool $mention = false)
|
private function extractCommand(WebSocketDialogMsg $msg, User $botUser, bool $mention = false)
|
||||||
{
|
{
|
||||||
if ($msg->type !== 'text') {
|
if ($msg->type !== 'text') {
|
||||||
return '';
|
return '';
|
||||||
@ -597,80 +588,113 @@ class BotReceiveMsgTask extends AbstractTask
|
|||||||
}
|
}
|
||||||
return $command;
|
return $command;
|
||||||
}
|
}
|
||||||
if (!$isAiBot) {
|
|
||||||
|
if ($botUser->isAiBot()) {
|
||||||
|
// AI 机器人
|
||||||
|
$contents = [];
|
||||||
|
if (preg_match_all("/<span class=\"mention task\" data-id=\"(\d+)\">(.*?)<\/span>/", $original, $match)) {
|
||||||
|
// 任务
|
||||||
|
$taskIds = Base::newIntval($match[1]);
|
||||||
|
foreach ($taskIds as $index => $taskId) {
|
||||||
|
$taskInfo = ProjectTask::with(['content'])->whereId($taskId)->first();
|
||||||
|
if (!$taskInfo) {
|
||||||
|
throw new Exception("任务不存在或已被删除");
|
||||||
|
}
|
||||||
|
$taskName = addslashes($taskInfo->name) . " (ID:{$taskId})";
|
||||||
|
$taskContext = implode("\n", $taskInfo->AIContext());
|
||||||
|
$contents[] = "<task_content path=\"{$taskName}\">\n{$taskContext}\n</task_content>";
|
||||||
|
$original = str_replace($match[0][$index], "'{$taskName}' (see below for task_content tag)", $original);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (preg_match_all("/<a class=\"mention ([^'\"]*)\" href=\"([^\"']+?)\"[^>]*?>[~%]([^>]*)<\/a>/", $original, $match)) {
|
||||||
|
// 文件、报告
|
||||||
|
$urlPaths = $match[2];
|
||||||
|
foreach ($urlPaths as $index => $urlPath) {
|
||||||
|
$pathTag = null;
|
||||||
|
$pathName = null;
|
||||||
|
$pathContent = null;
|
||||||
|
// 文件
|
||||||
|
if (preg_match("/single\/file\/(.*?)$/", $urlPath, $fileMatch)) {
|
||||||
|
$fileInfo = FileContent::idOrCodeToContent($fileMatch[1]);
|
||||||
|
if (!$fileInfo || !isset($fileInfo->content['url'])) {
|
||||||
|
throw new Exception("文件不存在或已被删除");
|
||||||
|
}
|
||||||
|
$urlPath = public_path($fileInfo->content['url']);
|
||||||
|
if (!file_exists($urlPath)) {
|
||||||
|
throw new Exception("文件不存在或已被删除");
|
||||||
|
}
|
||||||
|
$fileResult = TextExtractor::extractFile($urlPath);
|
||||||
|
if (Base::isError($fileResult)) {
|
||||||
|
throw new Exception("文件读取失败:" . $fileResult['msg']);
|
||||||
|
}
|
||||||
|
$pathTag = "file_content";
|
||||||
|
$pathName = addslashes($match[3][$index]) . " (ID:{$fileInfo->id})";
|
||||||
|
$pathContent = $fileResult['data'];
|
||||||
|
}
|
||||||
|
// 报告
|
||||||
|
elseif (preg_match("/single\/report\/detail\/(.*?)$/", $urlPath, $reportMatch)) {
|
||||||
|
$reportInfo = Report::idOrCodeToContent($reportMatch[1]);
|
||||||
|
if (!$reportInfo) {
|
||||||
|
throw new Exception("报告不存在或已被删除");
|
||||||
|
}
|
||||||
|
$pathTag = "report_content";
|
||||||
|
$pathName = addslashes($match[3][$index]) . " (ID:{$reportInfo->id})";
|
||||||
|
$pathContent = $reportInfo->content;
|
||||||
|
}
|
||||||
|
if ($pathTag) {
|
||||||
|
$contents[] = "<{$pathTag} path=\"{$pathName}\">\n{$pathContent}\n</{$pathTag}>";
|
||||||
|
$original = str_replace($match[0][$index], "'{$pathName}' (see below for {$pathTag} tag)", $original);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$original = Base::html2markdown($original);
|
||||||
|
if ($contents) {
|
||||||
|
// 添加tag内容
|
||||||
|
$original .= "\n\n" . implode("\n\n", $contents);
|
||||||
|
}
|
||||||
|
return $original;
|
||||||
|
} elseif ($botUser->isUserBot()) {
|
||||||
|
// 用户机器人
|
||||||
|
return Base::html2markdown($original);
|
||||||
|
} else {
|
||||||
|
// 其他机器人(系统)
|
||||||
return trim(strip_tags($original));
|
return trim(strip_tags($original));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$contents = [];
|
/**
|
||||||
// 任务
|
* 提取回复消息指令
|
||||||
if (preg_match_all("/<span class=\"mention task\" data-id=\"(\d+)\">(.*?)<\/span>/", $original, $match)) {
|
* @param $id
|
||||||
$taskIds = Base::newIntval($match[1]);
|
* @param User $botUser
|
||||||
foreach ($taskIds as $index => $taskId) {
|
* @return array
|
||||||
$taskInfo = ProjectTask::with(['content'])->whereId($taskId)->first();
|
*/
|
||||||
if (!$taskInfo) {
|
private function extractReplyCommand($id, User $botUser)
|
||||||
throw new Exception("任务不存在或已被删除");
|
{
|
||||||
}
|
$replyMsg = WebSocketDialogMsg::find($id);
|
||||||
$taskName = addslashes($taskInfo->name) . " (ID:{$taskId})";
|
$replyCommand = null;
|
||||||
$taskContext = implode("\n", $taskInfo->AIContext());
|
if ($replyMsg) {
|
||||||
$contents[] = "<task_content path=\"{$taskName}\">\n{$taskContext}\n</task_content>";
|
switch ($replyMsg->type) {
|
||||||
$original = str_replace($match[0][$index], "'{$taskName}' (see below for task_content tag)", $original);
|
case 'text':
|
||||||
|
try {
|
||||||
|
$replyCommand = $this->extractCommand($replyMsg, $botUser);
|
||||||
|
} catch (Exception) {
|
||||||
|
return Base::retError('error', "引用消息解析失败。");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'file':
|
||||||
|
if ($botUser->isAiBot()) {
|
||||||
|
$msgData = Base::json2array($replyMsg->getRawOriginal('msg'));
|
||||||
|
$fileResult = TextExtractor::extractFile(public_path($msgData['path']));
|
||||||
|
if (Base::isError($fileResult)) {
|
||||||
|
return Base::retError('error', $fileResult['msg']);
|
||||||
|
} else {
|
||||||
|
$replyCommand = $fileResult['data'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 文件、报告
|
return Base::retSuccess('success', $replyCommand);
|
||||||
if (preg_match_all("/<a class=\"mention ([^'\"]*)\" href=\"([^\"']+?)\"[^>]*?>[~%]([^>]*)<\/a>/", $original, $match)) {
|
|
||||||
$urlPaths = $match[2];
|
|
||||||
foreach ($urlPaths as $index => $urlPath) {
|
|
||||||
$pathTag = null;
|
|
||||||
$pathName = null;
|
|
||||||
$pathContent = null;
|
|
||||||
// 文件
|
|
||||||
if (preg_match("/single\/file\/(.*?)$/", $urlPath, $fileMatch)) {
|
|
||||||
$fileInfo = FileContent::idOrCodeToContent($fileMatch[1]);
|
|
||||||
if (!$fileInfo || !isset($fileInfo->content['url'])) {
|
|
||||||
throw new Exception("文件不存在或已被删除");
|
|
||||||
}
|
|
||||||
$urlPath = public_path($fileInfo->content['url']);
|
|
||||||
if (!file_exists($urlPath)) {
|
|
||||||
throw new Exception("文件不存在或已被删除");
|
|
||||||
}
|
|
||||||
$fileResult = TextExtractor::extractFile($urlPath);
|
|
||||||
if (Base::isError($fileResult)) {
|
|
||||||
throw new Exception("文件读取失败:" . $fileResult['msg']);
|
|
||||||
}
|
|
||||||
$pathTag = "file_content";
|
|
||||||
$pathName = addslashes($match[3][$index]) . " (ID:{$fileInfo->id})";
|
|
||||||
$pathContent = $fileResult['data'];
|
|
||||||
}
|
|
||||||
// 报告
|
|
||||||
elseif (preg_match("/single\/report\/detail\/(.*?)$/", $urlPath, $reportMatch)) {
|
|
||||||
$reportInfo = Report::idOrCodeToContent($reportMatch[1]);
|
|
||||||
if (!$reportInfo) {
|
|
||||||
throw new Exception("报告不存在或已被删除");
|
|
||||||
}
|
|
||||||
$pathTag = "report_content";
|
|
||||||
$pathName = addslashes($match[3][$index]) . " (ID:{$reportInfo->id})";
|
|
||||||
$pathContent = $reportInfo->content;
|
|
||||||
}
|
|
||||||
if ($pathTag) {
|
|
||||||
$contents[] = "<{$pathTag} path=\"{$pathName}\">\n{$pathContent}\n</{$pathTag}>";
|
|
||||||
$original = str_replace($match[0][$index], "'{$pathName}' (see below for {$pathTag} tag)", $original);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($msg->msg['type'] !== 'md') {
|
|
||||||
// 转换为Markdown
|
|
||||||
try {
|
|
||||||
$converter = new HtmlConverter();
|
|
||||||
$original = $converter->convert($original);
|
|
||||||
} catch (\Exception) {
|
|
||||||
throw new Exception("Failed to convert HTML to Markdown");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($contents) {
|
|
||||||
// 添加tag内容
|
|
||||||
$original .= "\n\n" . implode("\n\n", $contents);
|
|
||||||
}
|
|
||||||
return $original ?: '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
<p><b>{{$L("Webhook说明")}}:</b></p>
|
<p><b>{{$L("Webhook说明")}}:</b></p>
|
||||||
<p>{{$L("机器人收到消息后会将消息POST推送到Webhook地址,请求超时为10秒,请求参数如下")}}:</p>
|
<p>{{$L("机器人收到消息后会将消息POST推送到Webhook地址,请求超时为10秒,请求参数如下")}}:</p>
|
||||||
<p><span class="mark-color">text</span>: {{$L("消息文本")}}</p>
|
<p><span class="mark-color">text</span>: {{$L("消息文本")}}</p>
|
||||||
|
<p><span class="mark-color">reply_text</span>: {{$L("回复/引用消息文本")}}</p>
|
||||||
<p><span class="mark-color">token</span>: {{$L("机器人Token")}}</p>
|
<p><span class="mark-color">token</span>: {{$L("机器人Token")}}</p>
|
||||||
<p><span class="mark-color">dialog_id</span>: {{$L("对话ID")}}</p>
|
<p><span class="mark-color">dialog_id</span>: {{$L("对话ID")}}</p>
|
||||||
<p><span class="mark-color">dialog_type</span>: {{$L("对话类型")}}</p>
|
<p><span class="mark-color">dialog_type</span>: {{$L("对话类型")}}</p>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user