fix(telegram+review): 修復 PPT 按鈕無反應 + Code Review 頁面空白
All checks were successful
CD Pipeline / deploy (push) Successful in 1m28s
All checks were successful
CD Pipeline / deploy (push) Successful in 1m28s
PPT 按鈕: - telegram_bot_service.py 新增 cmd:* handler,透過 Thread 轉發到 OpenClaw Flask 內部 API(/bot/internal/cmd) - openclaw_bot_routes.py 新增 /bot/internal/cmd 端點,背景執行 handle_cmd() Code Review 頁面: - get_history() 補回 findings / openclaw_report 欄位 - code_review.html history 項目可點擊,自動載入詳細內容 - poll() 無 active pipeline 時自動顯示最新歷史記錄 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5897,6 +5897,31 @@ def telegram_webhook():
|
||||
return jsonify({'ok': True})
|
||||
|
||||
|
||||
# ── 內部 CMD 轉發端點(供 momo-telegram-bot 轉發 cmd:* 按鈕用)────────
|
||||
@openclaw_bot_bp.route('/bot/internal/cmd', methods=['POST'])
|
||||
def internal_cmd():
|
||||
"""接受 momo-telegram-bot 轉發的 cmd:* 按鈕指令並執行"""
|
||||
try:
|
||||
token = request.headers.get('X-Internal-Token', '')
|
||||
if not verify_internal_token(token):
|
||||
return jsonify({'ok': False, 'error': 'Unauthorized'}), 401
|
||||
body = request.get_json(silent=True) or {}
|
||||
chat_id = body.get('chat_id')
|
||||
cmd = body.get('cmd', '').strip()
|
||||
arg = body.get('arg', '').strip()
|
||||
if not chat_id or not cmd:
|
||||
return jsonify({'ok': False, 'error': 'missing chat_id or cmd'}), 400
|
||||
threading.Thread(
|
||||
target=handle_cmd,
|
||||
args=(cmd, arg, int(chat_id), None),
|
||||
daemon=True,
|
||||
).start()
|
||||
return jsonify({'ok': True})
|
||||
except Exception as e:
|
||||
sys_log.error(f"[OpenClawBot] /bot/internal/cmd error: {e}", exc_info=True)
|
||||
return jsonify({'ok': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
# ── 管理端點 ──────────────────────────────────────────────────
|
||||
@openclaw_bot_bp.route('/bot/telegram/set_webhook', methods=['POST'])
|
||||
def set_webhook():
|
||||
|
||||
@@ -611,6 +611,8 @@ def get_history(limit: int = 20) -> List[Dict]:
|
||||
"severity_summary": sev,
|
||||
"total_issues": sum(sev.values()),
|
||||
"auto_fix": meta.get("auto_fix_triggered", False),
|
||||
"findings": content.get("findings", []),
|
||||
"openclaw_report": content.get("openclaw_report", ""),
|
||||
"ea_decision": content.get("ea_decision", {}),
|
||||
"created_at": r[4].isoformat() if r[4] else "",
|
||||
"status": r[5] or "active",
|
||||
|
||||
@@ -488,6 +488,34 @@ class TrendTelegramBot:
|
||||
elif data.startswith("momo:ops:"):
|
||||
await self._handle_ops_callback(query, data)
|
||||
|
||||
# ===== OpenClaw 指令按鈕(cmd:<cmd>:<arg>)=====
|
||||
elif data.startswith("cmd:"):
|
||||
parts = data[4:].split(":", 1)
|
||||
cmd = parts[0]
|
||||
arg = parts[1] if len(parts) > 1 else ""
|
||||
chat_id = query.message.chat_id
|
||||
import threading as _t
|
||||
_t.Thread(
|
||||
target=self._forward_cmd_to_openclaw,
|
||||
args=(cmd, arg, chat_id),
|
||||
daemon=True,
|
||||
).start()
|
||||
|
||||
def _forward_cmd_to_openclaw(self, cmd: str, arg: str, chat_id: int):
|
||||
"""轉發 cmd:* 指令到 OpenClaw Flask 內部 API"""
|
||||
import requests as _req
|
||||
try:
|
||||
internal_url = os.getenv("OPENCLAW_INTERNAL_URL", "http://momo-pro-system:80")
|
||||
token = os.getenv("INTERNAL_WEBHOOK_TOKEN", "")
|
||||
_req.post(
|
||||
f"{internal_url}/bot/internal/cmd",
|
||||
json={"chat_id": chat_id, "cmd": cmd, "arg": arg},
|
||||
headers={"X-Internal-Token": token},
|
||||
timeout=10,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"[TelegramBot] forward cmd failed: {e}")
|
||||
|
||||
async def _handle_main_menu_callback(self, query, data: str):
|
||||
"""處理主選單回調 - 完整功能菜單系統"""
|
||||
key = data[5:] # 移除 'menu:' 前綴
|
||||
|
||||
@@ -367,12 +367,15 @@ function renderStatusBar(state) {
|
||||
}
|
||||
|
||||
// ── History ───────────────────────────────────────────────────────
|
||||
let _historyData = [];
|
||||
|
||||
function renderHistory(items) {
|
||||
_historyData = items;
|
||||
const el = document.getElementById('historyList');
|
||||
if (!items.length) { el.innerHTML = '<div class="empty"><div>尚無歷史記錄</div></div>'; return; }
|
||||
el.innerHTML = items.map(h => {
|
||||
el.innerHTML = items.map((h, idx) => {
|
||||
const sev = h.severity_summary || {};
|
||||
return `<div class="hist-item">
|
||||
return `<div class="hist-item" onclick="loadHistoryItem(${idx})" data-idx="${idx}">
|
||||
<div style="display:flex;justify-content:space-between">
|
||||
<span class="hist-sha">${h.commit_sha}</span>
|
||||
<span style="font-size:11px;color:var(--muted)">${h.created_at.slice(0,16).replace('T',' ')}</span>
|
||||
@@ -389,6 +392,34 @@ function renderHistory(items) {
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function loadHistoryItem(idx) {
|
||||
const h = _historyData[idx];
|
||||
if (!h) return;
|
||||
document.querySelectorAll('.hist-item').forEach((el, i) => {
|
||||
el.style.borderColor = i === idx ? 'var(--blue)' : '';
|
||||
});
|
||||
renderSeverity(h.severity_summary);
|
||||
const files = (h.changed_files||[]).slice(0,5).map(f=>`<code>${f.split('/').pop()}</code>`).join(' ');
|
||||
const more = (h.changed_files||[]).length > 5 ? `<span style="color:var(--muted)">+${h.changed_files.length-5}</span>` : '';
|
||||
document.getElementById('commitInfo').innerHTML = `
|
||||
<div><b>Commit</b> <code>${h.commit_sha}</code></div>
|
||||
<div><b>Branch</b> <code>${h.branch||'?'}</code></div>
|
||||
<div><b>時間</b> ${h.created_at.slice(0,16).replace('T',' ')}</div>
|
||||
<div><b>變更</b> ${files} ${more}</div>`;
|
||||
document.getElementById('pipelineId').textContent = (h.pipeline_id||'').slice(-14);
|
||||
const sBar = document.getElementById('statusBar');
|
||||
sBar.style.display = 'block';
|
||||
sBar.className = 'completed';
|
||||
sBar.innerHTML = `✅ <b>歷史記錄</b> — Commit ${h.commit_sha}${h.auto_fix ? ' 🔧 已自動修復' : ''}`;
|
||||
renderFindings(h.findings || []);
|
||||
renderOpenClaw(h.openclaw_report || '');
|
||||
renderEA(h.ea_decision || {}, h.auto_fix || false);
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
const el = document.getElementById('step-' + i);
|
||||
if (el) { el.className = 'step ok'; el.querySelector('.step-num').textContent = '✓'; }
|
||||
}
|
||||
}
|
||||
|
||||
// ── Main polling loop ─────────────────────────────────────────────
|
||||
async function poll() {
|
||||
try {
|
||||
@@ -408,6 +439,11 @@ async function poll() {
|
||||
renderCommitInfo(state);
|
||||
document.getElementById('pipelineId').textContent = (state.pipeline_id||'').slice(-14);
|
||||
|
||||
// 無 active pipeline 時,自動顯示最新歷史記錄
|
||||
if (!state.status && _historyData.length && !_lastPipelineId) {
|
||||
loadHistoryItem(0);
|
||||
}
|
||||
|
||||
// 每 3s 輪詢(running)/ 30s(idle)
|
||||
const interval = state.status === 'running' ? 3000 : 30000;
|
||||
_polling = setTimeout(poll, interval);
|
||||
|
||||
Reference in New Issue
Block a user