guanghulab/scripts/intent-router.js

238 lines
7.9 KiB
JavaScript
Raw Normal View History

/**
* 意图路由模块
* 文件位置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
};