feat: guard against a dead Instagram session, show progress, fix missing pings
- check-session.py: preflight in gdl-cron.sh. No unexpired sessionid in the Chrome profile gdl reads -> abort before any request, ping /76 (red check). - stories-streak.py: postflight. Every fetched stories source empty for 3 consecutive days -> exit 77, for a session Instagram stopped honouring while the cookie is still present. The 2026-10-07 session death went unnoticed for a week because every run still exited 0. - gdl-sync.py: [n/N] source headers, a 60s heartbeat with new-file counts, and per-source timing, so a healthy paced run no longer looks hung. - gdl-sync@.service now runs run-%i.sh instead of gdl-cron.sh: HC_URL is set in the wrappers, so scheduled runs had never pinged Healthchecks. - Tests for both guards (offline) and the progress helpers.
This commit is contained in:
Executable
+57
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Detect a silently dead Instagram session from a stories run's output.
|
||||
|
||||
A dead session (killed server-side while the sessionid is still in the cookie jar, so
|
||||
check-session.py passes) shows up as every fetched stories source returning
|
||||
"No results" -- the daily run still exits 0. From 2026-10-07 all six profiles came back
|
||||
empty every day and nothing alerted. One all-empty day is normal (nobody posted a story);
|
||||
several in a row across ALL sources is not.
|
||||
|
||||
Usage: stories-streak.py <this run's output file> [state file]
|
||||
Counts at most one day per calendar date (manual re-runs don't inflate it). Prints a message
|
||||
and exits 1 once the streak reaches GDL_EMPTY_STREAK_FAIL days (default 3); exits 0 otherwise.
|
||||
Runs that fetched nothing (all skipped / no sources) leave the streak untouched.
|
||||
"""
|
||||
import datetime as dt
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
THRESHOLD = int(os.environ.get("GDL_EMPTY_STREAK_FAIL", "3"))
|
||||
|
||||
|
||||
def main() -> int:
|
||||
out = Path(sys.argv[1]).read_text(errors="replace")
|
||||
state = Path(sys.argv[2] if len(sys.argv) > 2
|
||||
else Path.home() / "gdl" / "stories-empty-streak")
|
||||
m = re.search(r"sources\s*:\s*(\d+) to sync", out)
|
||||
to_sync = int(m.group(1)) if m else 0
|
||||
if to_sync == 0:
|
||||
return 0 # nothing fetched: no evidence either way
|
||||
empty = len(re.findall(r"No results for \S*/stories/", out))
|
||||
today = dt.date.today().isoformat()
|
||||
last_date, streak = "", 0
|
||||
try:
|
||||
last_date, s = state.read_text().split()
|
||||
streak = int(s)
|
||||
except (OSError, ValueError):
|
||||
pass
|
||||
if empty >= to_sync: # every fetched source came back empty
|
||||
if last_date != today:
|
||||
streak += 1
|
||||
else:
|
||||
streak = 0
|
||||
state.write_text(f"{today} {streak}\n")
|
||||
if streak >= THRESHOLD:
|
||||
print(f"SESSION LIKELY DEAD: stories came back empty for ALL {to_sync} profile(s) on "
|
||||
f"{streak} consecutive day(s). The cookie is present but Instagram is probably "
|
||||
"no longer honouring it -- log in again in the Chrome window (reload the tab to "
|
||||
"check) and re-run.")
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user