fix: replace Playwright post() with httpx legacy API, fix session_file Path coercion and comment URL check

smoke test confirmed: post via /api/submit + comment via /api/comment both working with modhash + session cookies
This commit is contained in:
pyr0ball 2026-04-27 14:25:38 -07:00
parent 6cf61663a5
commit 01e5990f58
2 changed files with 30 additions and 37 deletions

View file

@ -134,8 +134,8 @@ class RedditCommentStrategy(PostingStrategy):
client = RedditClient(session_file=session_file) client = RedditClient(session_file=session_file)
comment_url = client.comment(thread_id=thread_id, body=body) comment_url = client.comment(thread_id=thread_id, body=body)
# Reddit comment() may return empty URL; reconstruct from thread_id # Reddit comment() may return bare domain or empty; reconstruct from thread_id
if not comment_url: if not comment_url or comment_url.rstrip("/") in ("https://reddit.com", "https://www.reddit.com"):
comment_url = f"https://www.reddit.com/r/{target}/comments/{thread_id}/" comment_url = f"https://www.reddit.com/r/{target}/comments/{thread_id}/"
return PostResult(url=comment_url, metadata={"thread_id": thread_id}) return PostResult(url=comment_url, metadata={"thread_id": thread_id})

View file

@ -24,7 +24,7 @@ USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) Chrome/124.0.0.0"
class RedditClient: class RedditClient:
def __init__(self, session_file: Path | None = None) -> None: def __init__(self, session_file: Path | None = None) -> None:
settings = get_settings() settings = get_settings()
self._session_file = session_file or Path(settings.reddit_session_file) self._session_file = Path(session_file) if session_file else Path(settings.reddit_session_file)
ensure_valid_session(self._session_file) ensure_valid_session(self._session_file)
self.cookies = load_cookies(self._session_file) self.cookies = load_cookies(self._session_file)
self.headers = {"User-Agent": USER_AGENT} self.headers = {"User-Agent": USER_AGENT}
@ -43,43 +43,36 @@ class RedditClient:
return self._modhash return self._modhash
def post(self, sub: str, title: str, body: str, flair: str | None = None) -> str: def post(self, sub: str, title: str, body: str, flair: str | None = None) -> str:
"""Submit a text post via Playwright (xvfb-run). Returns the permalink.""" """Submit a text post via Reddit legacy API (httpx). Returns the permalink."""
settings = get_settings() data = {
cmd = [ "api_type": "json",
"xvfb-run", "--auto-servernum", "kind": "self",
sys.executable, str(_POST_SCRIPT), "sr": sub,
"--sub", sub, "title": title,
"--title", title, "text": body,
"--body", body, "uh": self.modhash,
"--yes", "sendreplies": "true",
] "nsfw": "false",
if flair: "spoiler": "false",
cmd += ["--flair", flair] }
resp = httpx.post(
result = subprocess.run( "https://www.reddit.com/api/submit",
cmd, cookies=self.cookies,
capture_output=True, headers=self.headers,
text=True, data=data,
timeout=180, timeout=30,
env={
**__import__("os").environ,
"REDDIT_SESSION_FILE": str(self._session_file),
},
) )
if result.returncode != 0: resp.raise_for_status()
result = resp.json()
errors = result.get("json", {}).get("errors", [])
if errors:
raise RuntimeError(f"Post to r/{sub} failed: {errors}")
url = result.get("json", {}).get("data", {}).get("url", "")
if not url:
raise RuntimeError( raise RuntimeError(
f"Post to r/{sub} failed (exit {result.returncode}):\n" f"Post to r/{sub} may have failed — no URL in response:\n{result}"
f"STDOUT:\n{result.stdout}\n"
f"STDERR:\n{result.stderr}"
) )
for line in result.stdout.splitlines(): return url
if line.startswith("Posted:"):
url = line.split("Posted:", 1)[-1].strip()
return url
raise RuntimeError(
f"Post to r/{sub} may have failed — no 'Posted:' line in output.\n"
f"Full stdout:\n{result.stdout}"
)
def comment(self, thread_id: str, body: str) -> str: def comment(self, thread_id: str, body: str) -> str:
"""Post a top-level comment to a thread. Returns the permalink.""" """Post a top-level comment to a thread. Returns the permalink."""