fb_posts: past its publish time, a scheduled post reads as published
Facebook does not call back and the reconciliation was never built, so a row that stayed scheduled forever was a lie of omission. Decided by Mike: once the time passes with nothing else reported, the post is published. status keeps what the publisher last said; state is what a reader acts on, and the open-only list and cancel use state. Cancel on a post whose time has passed says so.
This commit is contained in:
+3
-2
@@ -1025,8 +1025,9 @@ def cancel_fb_post(request: Request, pid: str, x_admin_token: str = Header(None)
|
||||
rec = store.get_fb_post(pid)
|
||||
if not rec:
|
||||
raise HTTPException(404, "no such post")
|
||||
if rec["status"] not in ("scheduled",):
|
||||
raise HTTPException(409, "post is %s, not scheduled" % rec["status"])
|
||||
if rec["state"] != "scheduled":
|
||||
raise HTTPException(409, "post is %s, not scheduled" % rec["state"]
|
||||
+ (" (its publish time has passed)" if rec["status"] == "scheduled" else ""))
|
||||
if not rec.get("cancel_url"):
|
||||
raise HTTPException(409, "no cancel link was recorded for this post")
|
||||
import urllib.request as _ur, urllib.error as _ue
|
||||
|
||||
+25
-6
@@ -1191,25 +1191,44 @@ def _image_size(data, mime):
|
||||
return None, None
|
||||
|
||||
|
||||
def fb_state(rec, now=None):
|
||||
"""The effective state. A scheduled post whose publish time has passed is
|
||||
published unless something said otherwise: Facebook does not call back,
|
||||
and a row that stays 'scheduled' forever is a lie of omission. Decided
|
||||
by Mike 2026-09-04. `status` stays what was last reported; `state` is
|
||||
what a reader should act on."""
|
||||
st = rec.get("status")
|
||||
if st == "scheduled" and rec.get("scheduled_for") and rec["scheduled_for"] <= (now or _now()):
|
||||
return "published"
|
||||
return st
|
||||
|
||||
|
||||
def list_fb_posts(limit=100, include_done=True):
|
||||
con = connect()
|
||||
try:
|
||||
sql = "SELECT * FROM fb_posts"
|
||||
if not include_done:
|
||||
sql += " WHERE status IN ('drafted','scheduled','handed_off')"
|
||||
sql += " ORDER BY COALESCE(scheduled_for, updated_at) DESC LIMIT ?"
|
||||
return [dict(r) for r in con.execute(sql, (max(1, min(int(limit), 500)),))]
|
||||
rows = [dict(r) for r in con.execute(
|
||||
"SELECT * FROM fb_posts ORDER BY COALESCE(scheduled_for, updated_at) DESC LIMIT ?",
|
||||
(max(1, min(int(limit), 500)),))]
|
||||
finally:
|
||||
con.close()
|
||||
now = _now()
|
||||
for r in rows:
|
||||
r["state"] = fb_state(r, now)
|
||||
if not include_done:
|
||||
rows = [r for r in rows if r["state"] in ("drafted", "scheduled", "handed_off")]
|
||||
return rows
|
||||
|
||||
|
||||
def get_fb_post(pid):
|
||||
con = connect()
|
||||
try:
|
||||
r = con.execute("SELECT * FROM fb_posts WHERE id=?", (pid,)).fetchone()
|
||||
return dict(r) if r else None
|
||||
finally:
|
||||
con.close()
|
||||
if not r:
|
||||
return None
|
||||
d = dict(r); d["state"] = fb_state(d)
|
||||
return d
|
||||
|
||||
|
||||
def mark_fb_cancelled(pid, by):
|
||||
|
||||
Reference in New Issue
Block a user