|
52 | 52 | VIDEO_PROCESSING_WAIT_ITERATIONS = 150 |
53 | 53 | VIDEO_PROCESSING_WAIT_INTERVAL_MS = 2000 |
54 | 54 |
|
| 55 | +# How far the post-submit verification step scrolls, and in how many steps, looking for the new |
| 56 | +# post. The pre-submit snapshot (_snapshot_existing_articles) scrolls through exactly the same |
| 57 | +# depth -- Codex-reported: an earlier version only snapshotted the initially-loaded viewport, so |
| 58 | +# an OLDER post sitting below the fold at snapshot time (absent from that snapshot) but revealed |
| 59 | +# by the verification loop's own scrolling would wrongly look "new". Both loops must cover the |
| 60 | +# same ground for the before/after comparison to mean anything. |
| 61 | +VERIFY_SCROLL_ATTEMPTS = 3 |
| 62 | +VERIFY_SCROLL_PIXELS = 4000 |
| 63 | + |
55 | 64 |
|
56 | 65 | def _save_debug_screenshot(page) -> Path | None: |
57 | 66 | """Best-effort screenshot saved BEFORE the calling code raises -- the browser context is |
@@ -111,6 +120,21 @@ def _article_fingerprints(page) -> list[str]: |
111 | 120 | ) |
112 | 121 |
|
113 | 122 |
|
| 123 | +def _snapshot_existing_articles(page) -> set[str]: |
| 124 | + """Fingerprint every article currently reachable by scrolling through |
| 125 | + VERIFY_SCROLL_ATTEMPTS steps of VERIFY_SCROLL_PIXELS each -- the exact same depth the |
| 126 | + post-submit verification loop scrolls through. Accumulates the union seen at every step |
| 127 | + (not just the final one) since Facebook's feed can virtualize/remove off-screen articles from |
| 128 | + the DOM as new ones load in, so an article visible at step 1 might not still be present once |
| 129 | + the page has scrolled to step 3.""" |
| 130 | + fingerprints: set[str] = set(_article_fingerprints(page)) |
| 131 | + for _ in range(VERIFY_SCROLL_ATTEMPTS): |
| 132 | + page.mouse.wheel(0, VERIFY_SCROLL_PIXELS) |
| 133 | + page.wait_for_timeout(1500) |
| 134 | + fingerprints.update(_article_fingerprints(page)) |
| 135 | + return fingerprints |
| 136 | + |
| 137 | + |
114 | 138 | def _open_composer(page) -> bool: |
115 | 139 | """Click the "What's on your mind?" opener on the home feed. The visible text includes the |
116 | 140 | logged-in user's own first name (e.g. "What's on your mind, Alex?"), so this matches on the |
@@ -236,7 +260,7 @@ def publish_facebook( |
236 | 260 | if profile_url: |
237 | 261 | page.goto(profile_url, timeout=STEP_TIMEOUT_MS) |
238 | 262 | page.wait_for_timeout(3000) |
239 | | - existing_article_fingerprints = set(_article_fingerprints(page)) |
| 263 | + existing_article_fingerprints = _snapshot_existing_articles(page) |
240 | 264 | page.goto("https://www.facebook.com/", timeout=STEP_TIMEOUT_MS) |
241 | 265 | page.wait_for_timeout(2000) |
242 | 266 |
|
@@ -338,27 +362,32 @@ def publish_facebook( |
338 | 362 | snippet = text[:40] |
339 | 363 | page.goto(profile_url, timeout=STEP_TIMEOUT_MS) |
340 | 364 | page.wait_for_timeout(4000) |
341 | | - for attempt in range(3): |
342 | | - # Codex-reported, in two parts. First: a bare |
| 365 | + for attempt in range(VERIFY_SCROLL_ATTEMPTS): |
| 366 | + # Codex-reported, in three parts so far. First: a bare |
343 | 367 | # document.body.innerText.includes() check would also match the snippet in |
344 | 368 | # the profile bio or nav text -- fixed by scoping to `[role="article"]` |
345 | 369 | # (Facebook's own ARIA role for a feed post). Second: scoping to articles |
346 | 370 | # alone still doesn't prove the match is the post just made, not an older one |
347 | 371 | # starting with similar text (a repeated caption, or a common generic |
348 | 372 | # opening) -- fixed by comparing against the before-snapshot taken earlier |
349 | | - # and only counting a match in an article that's NEW since then. Still |
350 | | - # unverified against a live account like everything else in this file -- this |
351 | | - # narrows the false-positive surface further, it doesn't eliminate every way |
352 | | - # this could be wrong (e.g. two genuinely new posts with identical leading |
353 | | - # text in the same run). |
| 373 | + # and only counting a match in an article that's NEW since then. Third: the |
| 374 | + # before-snapshot only covered the initially-loaded viewport, so an older post |
| 375 | + # sitting below the fold at snapshot time -- but revealed by THIS loop's own |
| 376 | + # scrolling -- would wrongly look "new" too. Fixed by having |
| 377 | + # _snapshot_existing_articles scroll through the exact same |
| 378 | + # VERIFY_SCROLL_ATTEMPTS x VERIFY_SCROLL_PIXELS depth up front, so both passes |
| 379 | + # cover identical ground. Still unverified against a live account like |
| 380 | + # everything else in this file -- this narrows the false-positive surface |
| 381 | + # further, it doesn't eliminate every way this could be wrong (e.g. two |
| 382 | + # genuinely new posts with identical leading text in the same run). |
354 | 383 | current_fingerprints = _article_fingerprints(page) |
355 | 384 | new_fingerprints = [ |
356 | 385 | fp for fp in current_fingerprints if fp not in existing_article_fingerprints |
357 | 386 | ] |
358 | 387 | if any(snippet in fp for fp in new_fingerprints): |
359 | 388 | verified = True |
360 | 389 | break |
361 | | - page.mouse.wheel(0, 4000) |
| 390 | + page.mouse.wheel(0, VERIFY_SCROLL_PIXELS) |
362 | 391 | page.wait_for_timeout(3000) |
363 | 392 |
|
364 | 393 | result = { |
|
0 commit comments