feat: auto_resolved sync + expired card update + DOM step_index
This commit is contained in:
29
bot.py
29
bot.py
@@ -573,6 +573,35 @@ class GravityBot(commands.Bot):
|
|||||||
except (json.JSONDecodeError, OSError):
|
except (json.JSONDecodeError, OSError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# ── Check for expired pendings — update Discord card ──
|
||||||
|
for f in self.bridge.pending_dir.glob("*.json"):
|
||||||
|
try:
|
||||||
|
data = json.loads(f.read_text(encoding="utf-8-sig"))
|
||||||
|
if data.get("status") == "expired":
|
||||||
|
msg_id = data.get("discord_message_id", 0)
|
||||||
|
rid = data.get("request_id", "")
|
||||||
|
project = data.get("project_name", Config.PROJECT_NAME)
|
||||||
|
if msg_id:
|
||||||
|
channel = await self._get_channel(project)
|
||||||
|
if channel:
|
||||||
|
try:
|
||||||
|
msg = await channel.fetch_message(msg_id)
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="⏰ 만료됨",
|
||||||
|
description=f"```\n{data.get('command', '')[:500]}\n```",
|
||||||
|
color=discord.Color.light_grey(),
|
||||||
|
)
|
||||||
|
embed.set_footer(text=f"ID: {rid}")
|
||||||
|
await msg.edit(embed=embed, view=None)
|
||||||
|
except discord.NotFound:
|
||||||
|
pass
|
||||||
|
f.unlink()
|
||||||
|
self._deferred_ids.pop(rid, None)
|
||||||
|
self._sent_commands.pop(rid, None)
|
||||||
|
self._sent_approval_ids.discard(rid)
|
||||||
|
except (json.JSONDecodeError, OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
# ── Check for MERGE updates (step_probe updated command in already-sent pending) ──
|
# ── Check for MERGE updates (step_probe updated command in already-sent pending) ──
|
||||||
for f in self.bridge.pending_dir.glob("*.json"):
|
for f in self.bridge.pending_dir.glob("*.json"):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -498,6 +498,7 @@ function startObserverHttpBridge() {
|
|||||||
project_name: projectName,
|
project_name: projectName,
|
||||||
auto_detected: true,
|
auto_detected: true,
|
||||||
source: 'dom_observer',
|
source: 'dom_observer',
|
||||||
|
step_index: lastPendingStepIndex >= 0 ? lastPendingStepIndex : undefined,
|
||||||
};
|
};
|
||||||
// File permission: inject multi-choice buttons
|
// File permission: inject multi-choice buttons
|
||||||
const cmdLower = (data.command || '').toLowerCase();
|
const cmdLower = (data.command || '').toLowerCase();
|
||||||
@@ -1589,14 +1590,20 @@ function setupMonitor() {
|
|||||||
try {
|
try {
|
||||||
const pendingFiles = fs.readdirSync(path.join(bridgePath, 'pending'))
|
const pendingFiles = fs.readdirSync(path.join(bridgePath, 'pending'))
|
||||||
.filter((f) => f.endsWith('.json'));
|
.filter((f) => f.endsWith('.json'));
|
||||||
|
const nowMs = Date.now();
|
||||||
for (const pf of pendingFiles) {
|
for (const pf of pendingFiles) {
|
||||||
const pfPath = path.join(bridgePath, 'pending', pf);
|
const pfPath = path.join(bridgePath, 'pending', pf);
|
||||||
const pd = JSON.parse(fs.readFileSync(pfPath, 'utf-8'));
|
const pd = JSON.parse(fs.readFileSync(pfPath, 'utf-8'));
|
||||||
if (pd.status === 'pending' && pd.step_index === lastPendingStepIndex) {
|
if (pd.status !== 'pending')
|
||||||
|
continue;
|
||||||
|
// Match by step_index OR by recency (< 60s, any source)
|
||||||
|
const ageMs = nowMs - (pd.timestamp * 1000);
|
||||||
|
const isMatch = pd.step_index === lastPendingStepIndex
|
||||||
|
|| (ageMs < 60_000 && ageMs >= 0);
|
||||||
|
if (isMatch) {
|
||||||
pd.status = 'auto_resolved';
|
pd.status = 'auto_resolved';
|
||||||
fs.writeFileSync(pfPath, JSON.stringify(pd, null, 2), 'utf-8');
|
fs.writeFileSync(pfPath, JSON.stringify(pd, null, 2), 'utf-8');
|
||||||
logToFile(`[AUTO-RESOLVE] step=${lastPendingStepIndex} progressed → marked ${pf}`);
|
logToFile(`[AUTO-RESOLVE] step=${lastPendingStepIndex} progressed → marked ${pf} (age=${Math.round(ageMs / 1000)}s)`);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -477,6 +477,7 @@ function startObserverHttpBridge(): Promise<number> {
|
|||||||
project_name: projectName,
|
project_name: projectName,
|
||||||
auto_detected: true,
|
auto_detected: true,
|
||||||
source: 'dom_observer',
|
source: 'dom_observer',
|
||||||
|
step_index: lastPendingStepIndex >= 0 ? lastPendingStepIndex : undefined,
|
||||||
};
|
};
|
||||||
// File permission: inject multi-choice buttons
|
// File permission: inject multi-choice buttons
|
||||||
const cmdLower = (data.command || '').toLowerCase();
|
const cmdLower = (data.command || '').toLowerCase();
|
||||||
@@ -1572,14 +1573,19 @@ function setupMonitor() {
|
|||||||
try {
|
try {
|
||||||
const pendingFiles = fs.readdirSync(path.join(bridgePath, 'pending'))
|
const pendingFiles = fs.readdirSync(path.join(bridgePath, 'pending'))
|
||||||
.filter((f: string) => f.endsWith('.json'));
|
.filter((f: string) => f.endsWith('.json'));
|
||||||
|
const nowMs = Date.now();
|
||||||
for (const pf of pendingFiles) {
|
for (const pf of pendingFiles) {
|
||||||
const pfPath = path.join(bridgePath, 'pending', pf);
|
const pfPath = path.join(bridgePath, 'pending', pf);
|
||||||
const pd = JSON.parse(fs.readFileSync(pfPath, 'utf-8'));
|
const pd = JSON.parse(fs.readFileSync(pfPath, 'utf-8'));
|
||||||
if (pd.status === 'pending' && pd.step_index === lastPendingStepIndex) {
|
if (pd.status !== 'pending') continue;
|
||||||
|
// Match by step_index OR by recency (< 60s, any source)
|
||||||
|
const ageMs = nowMs - (pd.timestamp * 1000);
|
||||||
|
const isMatch = pd.step_index === lastPendingStepIndex
|
||||||
|
|| (ageMs < 60_000 && ageMs >= 0);
|
||||||
|
if (isMatch) {
|
||||||
pd.status = 'auto_resolved';
|
pd.status = 'auto_resolved';
|
||||||
fs.writeFileSync(pfPath, JSON.stringify(pd, null, 2), 'utf-8');
|
fs.writeFileSync(pfPath, JSON.stringify(pd, null, 2), 'utf-8');
|
||||||
logToFile(`[AUTO-RESOLVE] step=${lastPendingStepIndex} progressed → marked ${pf}`);
|
logToFile(`[AUTO-RESOLVE] step=${lastPendingStepIndex} progressed → marked ${pf} (age=${Math.round(ageMs/1000)}s)`);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: any) { logToFile(`[AUTO-RESOLVE] error: ${e.message}`); }
|
} catch (e: any) { logToFile(`[AUTO-RESOLVE] error: ${e.message}`); }
|
||||||
|
|||||||
Reference in New Issue
Block a user