diff --git a/handlers/system.py b/handlers/system.py index ddf7808..533476e 100644 --- a/handlers/system.py +++ b/handlers/system.py @@ -397,69 +397,91 @@ async def incidents_summary(msg: Message): async def incidents_diff(msg: Message): if not is_admin_msg(msg): return - parts = msg.text.split() - hours = 24 - reset = False - if len(parts) >= 2: - if parts[1].lower() in {"reset", "clear"}: - reset = True - else: - try: - hours = max(1, int(parts[1])) - except ValueError: - hours = 24 - if reset: - runtime_state.set_state("incidents_diff_last_ts", None) - await msg.answer("📣 Diff marker reset. Next run will show all within window.", reply_markup=system_logs_audit_kb) - return - last_iso = runtime_state.get("incidents_diff_last_ts") - last_dt = None - if isinstance(last_iso, str): - try: - last_dt = datetime.fromisoformat(last_iso) - except Exception: - last_dt = None - rows = read_raw(cfg, hours=hours, limit=5000, include_old=True) - - def collect(from_dt): - fresh: list[tuple[datetime, str]] = [] - for dt, line in rows: - if from_dt and dt <= from_dt: - continue - fresh.append((dt, line)) - return fresh - - fresh = collect(last_dt) - # auto-reset if marker is ahead of all rows - if not fresh and last_dt and rows and last_dt >= rows[-1][0]: - last_dt = None - runtime_state.set_state("incidents_diff_last_ts", None) - fresh = collect(None) - - if not fresh: - note = f"since {last_dt.astimezone().strftime('%Y-%m-%d %H:%M')}" if last_dt else "for the period" - if rows: - sample = rows[-50:] - body = "\n".join(f"{dt.astimezone().strftime('%m-%d %H:%M')} {line}" for dt, line in sample) - await msg.answer( - f"📣 No new incidents {note} (window {hours}h).\nRecent sample:\n```\n{body}\n```", - reply_markup=system_logs_audit_kb, - parse_mode="Markdown", - ) - else: - await msg.answer(f"📣 No new incidents {note} (window {hours}h)", reply_markup=system_logs_audit_kb) - return - fresh.sort(key=lambda x: x[0]) - body = "\n".join(f"{dt.astimezone().strftime('%m-%d %H:%M')} {line}" for dt, line in fresh[-200:]) - await msg.answer( - f"📣 New incidents (since last mark, window {hours}h): {len(fresh)}\n```\n{body}\n```", - reply_markup=system_logs_audit_kb, - parse_mode="Markdown", - ) try: - runtime_state.set_state("incidents_diff_last_ts", fresh[-1][0].isoformat()) - except Exception: - pass + parts = msg.text.split() + hours = 24 + reset = False + if len(parts) >= 2: + if parts[1].lower() in {"reset", "clear"}: + reset = True + else: + try: + hours = max(1, int(parts[1])) + except ValueError: + hours = 24 + if reset: + runtime_state.set_state("incidents_diff_last_ts", None) + await msg.answer( + "📣 Diff marker reset. Next run will show all within window.", + reply_markup=system_logs_audit_kb, + ) + return + + last_iso = runtime_state.get("incidents_diff_last_ts") + last_dt = None + if isinstance(last_iso, str): + try: + last_dt = datetime.fromisoformat(last_iso) + except Exception: + last_dt = None + rows = read_raw(cfg, hours=hours, limit=5000, include_old=True) + + def collect(from_dt): + fresh: list[tuple[datetime, str]] = [] + for dt, line in rows: + if from_dt and dt <= from_dt: + continue + fresh.append((dt, line)) + return fresh + + fresh = collect(last_dt) + # auto-reset if marker is ahead of all rows + if not fresh and last_dt and rows and last_dt >= rows[-1][0]: + last_dt = None + runtime_state.set_state("incidents_diff_last_ts", None) + fresh = collect(None) + + def fmt_lines(items: list[tuple[datetime, str]], limit_chars: int = 3500) -> str: + buf = [] + total_len = 0 + for dt, line in items: + entry = f"{dt.astimezone().strftime('%m-%d %H:%M')} {line}" + if total_len + len(entry) + 1 > limit_chars: + break + buf.append(entry) + total_len += len(entry) + 1 + return "\n".join(buf) + + if not fresh: + note = f"since {last_dt.astimezone().strftime('%Y-%m-%d %H:%M')}" if last_dt else "for the period" + if rows: + sample = rows[-50:][::-1] # newest first + body = fmt_lines(sample) + await msg.answer( + f"📣 No new incidents {note} (window {hours}h).\nRecent sample:\n```\n{body}\n```", + reply_markup=system_logs_audit_kb, + parse_mode="Markdown", + ) + else: + await msg.answer( + f"📣 No new incidents {note} (window {hours}h)", + reply_markup=system_logs_audit_kb, + ) + return + + fresh.sort(key=lambda x: x[0], reverse=True) + body = fmt_lines(fresh) + await msg.answer( + f"📣 New incidents (since last mark, window {hours}h): {len(fresh)}\n```\n{body}\n```", + reply_markup=system_logs_audit_kb, + parse_mode="Markdown", + ) + try: + runtime_state.set_state("incidents_diff_last_ts", fresh[0][0].isoformat()) + except Exception: + pass + except Exception as e: + await msg.answer(f"⚠️ Diff error: {type(e).__name__}: {e}", reply_markup=system_logs_audit_kb) @dp.message(F.text.startswith("/alerts_heatmap"))