guanghulab/scripts/intent-router.js
Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

238 lines
7.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* ━━━ 意图路由模块 ━━━
* 文件位置scripts/intent-router.js
*
* 解析用户在 Issue 评论中 @铸渊 的意图,路由到对应处理逻辑。
* 用于替换原来的「无脑倒全部数据」行为。
*/
const https = require('https');
const fs = require('fs');
/**
* 解析用户在 Issue 评论中 @铸渊 的意图
* @param {string} commentBody - 评论原文
* @returns {object} 意图对象
*/
function parseIntent(commentBody) {
// 去掉 @铸渊 前缀,提取核心内容
const content = commentBody.replace(/@铸渊[,]?\s*/g, '').trim();
// 意图1查询某个 Issue 的闭环状态
// 匹配:「查询#92闭环状态」「#92进度」「Issue 92 状态」「闭环状态」等
const issueMatch = content.match(/#(\d+)/);
if (issueMatch && /闭环|状态|进度|处理/.test(content)) {
return {
type: 'check_pipeline_status',
issueNumber: parseInt(issueMatch[1]),
raw: content
};
}
// 意图2查询某个开发者的状态
// 匹配「DEV-004状态」「之之进度」「查询肥猫」
const devMatch = content.match(/DEV-(\d+)|页页|肥猫|燕樊|之之|小草莓|花尔|桔子|匆匆那年|Awen|小兴|时雨/);
if (devMatch && /状态|进度|查询/.test(content)) {
return {
type: 'check_dev_status',
devId: devMatch[0],
raw: content
};
}
// 意图3团队总览显式请求
// 匹配:「团队状态」「全部进度」「总览」
if (/团队|全部|总览|所有人/.test(content)) {
return {
type: 'team_overview',
raw: content
};
}
// 意图4未识别 → 返回 unknown
return {
type: 'unknown',
raw: content
};
}
/**
* 查询 Issue 闭环管道状态(通过评论历史)
* @param {number} issueNumber - 要查询的 Issue 编号
* @returns {string} 格式化的状态回复
*/
async function checkPipelineStatus(issueNumber) {
const [owner, repo] = (process.env.GITHUB_REPOSITORY || 'qinfendebingshuo/guanghulab').split('/');
// 获取该 Issue 的评论历史
let comments;
try {
comments = await githubAPI('GET',
`/repos/${owner}/${repo}/issues/${issueNumber}/comments?per_page=100`);
} catch (e) {
return `📋 **Issue #${issueNumber} 闭环状态查询**\n\n` +
`查询失败: ${e.message}\n` +
`请确认 Issue 编号是否正确。`;
}
if (!Array.isArray(comments) || comments.length === 0) {
return `📋 **Issue #${issueNumber} 闭环状态查询**\n\n` +
`未找到该 Issue 的评论记录。\n` +
`可能原因:该 Issue 不存在或没有任何评论。`;
}
// 筛选铸渊的进度评论(包含「步骤 X/8」的
const progressComments = comments.filter(c =>
c.body && c.body.includes('步骤') && c.body.includes('/8') && c.body.includes('铸渊')
);
// 检查是否有闭环完成汇总
const completeComment = comments.find(c =>
c.body && (c.body.includes('闭环处理完成') || c.body.includes('闭环处理异常'))
);
if (progressComments.length === 0 && !completeComment) {
// 检查是否有旧版静态进度表格
const staticTable = comments.find(c =>
c.body && c.body.includes('管道运行') && c.body.includes('等待中')
);
if (staticTable) {
return `📋 **Issue #${issueNumber} 闭环状态查询**\n\n` +
`⏳ 管道已启动但后续步骤未上报进度。\n` +
`可能原因:管道处理脚本未触发,或中间步骤出错。\n\n` +
`> 建议检查 Actions 运行日志。`;
}
return `📋 **Issue #${issueNumber} 闭环状态查询**\n\n` +
`未找到该 Issue 的闭环进度记录。\n` +
`可能原因:闭环尚未启动,或该 Issue 不是 SYSLOG 提交。`;
}
// 有进度评论:提取最新状态
if (completeComment) {
const isSuccess = completeComment.body.includes('闭环处理完成');
const hasFailed = comments.some(c => c.body && c.body.includes('❌'));
return `📋 **Issue #${issueNumber} 闭环状态**\n\n` +
`${isSuccess && !hasFailed ? '✅ 闭环已完成' : '⚠️ 闭环有步骤异常'}\n` +
`进度评论数: ${progressComments.length}\n` +
`最后更新: ${completeComment.created_at}`;
}
// 进行中
const lastProgress = progressComments[progressComments.length - 1];
const stepMatch = lastProgress.body.match(/步骤 (\d+)\/8/);
const lastStep = stepMatch ? parseInt(stepMatch[1]) : 0;
const hasFailed = progressComments.some(c => c.body.includes('❌'));
let statusLine;
if (hasFailed) {
statusLine = '❌ 闭环有步骤失败(详见该 Issue 下各步骤评论)';
} else {
statusLine = `⏳ 闭环进行中 · 已完成到步骤 ${lastStep}/8`;
}
return `📋 **Issue #${issueNumber} 闭环状态**\n\n` +
`${statusLine}\n` +
`进度评论数: ${progressComments.length}\n` +
`最后更新: ${lastProgress.created_at}`;
}
/**
* 查询指定开发者的状态
* @param {string} devIdOrName - DEV-XXX 或开发者昵称
* @param {object} devStatus - dev-status.json 数据
* @returns {string} 格式化的状态回复
*/
function checkDevStatus(devIdOrName, devStatus) {
const team = devStatus.team_status || devStatus.team || [];
const dev = team.find(d =>
d.dev_id === devIdOrName ||
d.name === devIdOrName ||
(d.dev_id && d.dev_id.toUpperCase() === devIdOrName.toUpperCase())
);
if (!dev) {
return `未找到开发者 ${devIdOrName} 的记录。`;
}
const modules = dev.modules
? dev.modules.join('、')
: (dev.module || '未知');
return `📊 **${dev.dev_id} ${dev.name} · 当前状态**\n\n` +
`- 📌 模块: ${modules}\n` +
`- 📊 状态: ${dev.status}\n` +
`- ⏳ 等待中: ${dev.waiting_for || dev.waiting || '无'}\n` +
`- 👉 下一步: ${dev.next_step || dev.current || '无'}\n` +
`- 🔥 连胜: ${dev.streak || 0}`;
}
/**
* 团队总览
* @param {object} devStatus - dev-status.json 数据
* @returns {string} 格式化的总览
*/
function formatTeamOverview(devStatus) {
const team = devStatus.team_status || devStatus.team || [];
let reply = `## ⚒️ 铸渊回复 · 团队进度总览\n\n`;
team.forEach(dev => {
reply += `**${dev.dev_id} ${dev.name}** · ${dev.status}\n`;
});
reply += `\n---\n*最后同步:${devStatus.last_synced || devStatus.last_sync}*`;
return reply;
}
/**
* 生成「未识别意图」的帮助提示
* @returns {string}
*/
function unknownIntentHelp() {
return `🤔 我没理解你的问题。你可以试试:\n\n` +
`- \`@铸渊 查询#92闭环状态\` — 查某个 SYSLOG 提交的处理进度\n` +
`- \`@铸渊 DEV-004状态\` — 查某个开发者的进度\n` +
`- \`@铸渊 团队总览\` — 查所有人的状态`;
}
// ━━━ GitHub API 工具 ━━━
function githubAPI(method, path, data) {
return new Promise((resolve, reject) => {
const body = data ? JSON.stringify(data) : null;
const req = https.request({
hostname: 'api.github.com',
path: path,
method: method,
headers: {
'Authorization': `Bearer ${process.env.GITHUB_TOKEN}`,
'Accept': 'application/vnd.github.v3+json',
'User-Agent': 'Zhuyuan-Intent-Router',
'Content-Type': 'application/json',
...(body ? { 'Content-Length': Buffer.byteLength(body) } : {})
}
}, (res) => {
let responseBody = '';
res.on('data', chunk => { responseBody += chunk; });
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
try { resolve(JSON.parse(responseBody)); }
catch (e) { console.error('⚠️ JSON parse error:', e.message); resolve(responseBody); }
} else {
reject(new Error(`GitHub API ${res.statusCode}: ${responseBody.slice(0, 200)}`));
}
});
});
req.on('error', reject);
if (body) req.write(body);
req.end();
});
}
module.exports = {
parseIntent,
checkPipelineStatus,
checkDevStatus,
formatTeamOverview,
unknownIntentHelp
};