Skip to content

fix facebook feed decoy junk extraction - #3

Merged
lawmight merged 1 commit into
lawmight/fb-chrome-stackfrom
cursor/facebook-feed-decoy-junk-b9a7
Sep 10, 2026
Merged

lawmight merged 1 commit into
lawmight/fb-chrome-stackfrom
cursor/facebook-feed-decoy-junk-b9a7

Conversation

@lawmight

@lawmight lawmight commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Description

Live smoke (opencli facebook feed --limit 5) was exiting 0 with a single anti-scrape decoy row: author Adolph L'héritage and scrambled/unreadable content. This PR tightens decoy filtering so the extractor either returns ≥1 readable post with a plausible author or fails with a clear diagnostic — never exit 0 with only poisoned rows.

Related issue: live smoke 2026-09-11 (tom.coustols, OpenCLI 1.8.8)

Type of Change

  • 🐛 Bug fix

Changes

  • isScrambledOrGarbledText (in-page) + isScrambledFeedText (Node mirror): detect alphanumeric soup, scrambled timestamp prefixes (Mk7sPrtt9B23f81:), low vowel-ratio garbage, and mixed letter-digit tokens
  • Lower opaque-token threshold in isDecoyText from 24→16 chars to catch shorter FB poison tokens
  • rowLooksLikeDecoyPost post-extraction filter (parallel to existing messenger bleed filter)
  • getFacebookFeed fail-closed: when all rows are decoys → CommandExecutionError with sample author in hint; mixed decoy+real → keep readable rows only
  • Fixture + tests: feed-adolph-decoy.html and 5 new unit tests covering Adolph-style decoy rejection, no-rows extraction, row heuristic, fail-closed error, and mixed filtering

Checklist

  • I ran the checks relevant to this PR (npm test -- clis/facebook/ — 107 passed)
  • I updated tests or docs if needed
  • I included output or screenshots when useful (live smoke pending post-merge install)

Messenger bleed regression

PR #1/#2 behavior preserved — all existing messenger bleed tests pass unchanged.

Post-merge verification (KIWI-BACK-TICKET)

After merge + install, run live opencli facebook feed --limit 5 on tom.coustols desk and confirm either readable posts (e.g. LVLUP with Lani) or a typed decoy error — not exit 0 with scrambled junk.

Open in Web Open in Cursor 

Summary by Sourcery

Reject poisoned Facebook feed rows and fail clearly when no readable posts are available.

Bug Fixes:

  • Prevent Facebook feed extraction from returning anti-scrape decoy posts as successful results.
  • Fail with a typed diagnostic when all extracted feed rows are unreadable decoys while retaining readable rows from mixed results.

Enhancements:

  • Strengthen feed text and row readability checks to identify scrambled, garbled, and opaque decoy content.

Tests:

  • Add decoy fixtures and coverage for decoy rejection, mixed-row filtering, empty extraction, and fail-closed errors.

Summary by cubic

Fixes opencli facebook feed silently exiting 0 when Facebook serves only anti-scrape decoy rows (e.g., author "Adolph L'héritage" with scrambled content), so the extractor now returns readable posts or fails with a clear diagnostic. Messenger bleed filtering behavior from earlier PRs is unchanged.

Changes

  • Detects scrambled decoy text (alphanumeric soup, scrambled timestamp prefixes, low vowel ratio, mixed letter-digit tokens) both in-page and as a Node mirror; lowers the opaque-token threshold from 24 to 16 characters.
  • Adds a rowLooksLikeDecoyPost post-extraction filter alongside the existing messenger bleed filter.
  • getFacebookFeed now fail-closes: all-decoy rows throw a typed CommandExecutionError with a sample author; mixed decoy+real rows keep only readable posts.
  • Adds the feed-adolph-decoy.html fixture and five unit tests covering decoy rejection, no-row extraction, row heuristics, fail-closed errors, and mixed filtering.

Written for commit 8b4a87f. Summary will update on new commits.

Review in cubic

…tent

Filter Adolph-style decoy rows where Facebook injects plausible author
names but scrambled alphanumeric soup for post bodies. Adds in-page
isScrambledOrGarbledText heuristics, rowLooksLikeDecoyPost post-filter,
and fail-closed CommandExecutionError when only decoy rows survive.

Includes Adolph decoy fixture and unit tests; messenger bleed behavior
from PR #1/#2 unchanged.

Co-authored-by: Tom Coustols <tom.coustols@tcdynamics.fr>
@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai

sourcery-ai Bot commented Sep 10, 2026

Copy link
Copy Markdown

Reviewer's Guide

The PR hardens Facebook feed extraction against anti-scrape decoy rows by mirroring expanded readability heuristics in the browser and Node paths, filtering poisoned rows while retaining real posts, and failing with a typed diagnostic when no readable feed content remains; fixture and unit tests cover the new behavior and preserve Messenger bleed handling.

Sequence diagram for fail-closed Facebook feed extraction

sequenceDiagram
    participant CLI
    participant Feed as getFacebookFeed
    participant Browser as Feed extraction script
    participant Filter as Decoy/readability checks

    CLI->>Feed: command.func(page, limit)
    Feed->>Browser: Extract feed rows
    Browser->>Filter: isScrambledOrGarbledText(content)
    Filter-->>Browser: Keep readable rows only
    Browser-->>Feed: payload.rows
    Feed->>Filter: rowLooksLikeDecoyPost(row)
    alt readable rows remain
        Filter-->>Feed: Return readable rows
        Feed-->>CLI: Feed results
    else all rows are decoys
        Filter-->>Feed: All rows rejected
        Feed-->>CLI: CommandExecutionError
    end
Loading

Flow diagram for Facebook feed decoy filtering

flowchart TD
    A[Facebook feed DOM] --> B[Browser extraction]
    B --> C{Readable post rows?}
    C -->|Mixed decoy and real| D[Filter decoy rows]
    D --> E[Return readable rows]
    C -->|No rows| F[Existing empty-feed handling]
    C -->|Decoy rows only| G[Node rowLooksLikeDecoyPost]
    G --> H[Throw CommandExecutionError]
    H --> I[Retry with diagnostic]
Loading

File-Level Changes

Change Details Files
Strengthen detection of Facebook anti-scrape and scrambled feed text across browser extraction and Node-side validation.
  • Add shared in-page and Node mirror heuristics for alphanumeric soup, scrambled prefixes, mixed letter-digit tokens, low vowel ratios, and opaque tokens.
  • Lower the opaque-token threshold to catch shorter poison values.
  • Filter unreadable content and decoy-looking authors during DOM extraction and post-extraction validation.
clis/facebook/feed.js
Fail closed for decoy-only feeds while preserving valid posts from mixed results.
  • Classify extracted rows independently from Messenger bleed rows.
  • Raise a typed CommandExecutionError with a sample author when every row is a decoy.
  • Return only readable, non-Messenger rows when valid and decoy rows are mixed.
clis/facebook/feed.js
Add fixture coverage for Adolph-style poisoned markup and extraction outcomes.
  • Add a mixed decoy/real feed HTML fixture.
  • Cover decoy rejection, empty extraction, row classification, typed failure, and mixed-row filtering.
clis/facebook/__fixtures__/feed-adolph-decoy.html
clis/facebook/feed.test.js

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@lawmight
lawmight marked this pull request as ready for review September 10, 2026 20:41
@lawmight
lawmight merged commit ae8e790 into lawmight/fb-chrome-stack Sep 10, 2026
1 check passed

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="clis/facebook/feed.js" line_range="89" />
<code_context>
+  return false;
+}
+
+function isScrambledFeedText(text) {
+  const t = cleanFeedText(text);
+  if (!t) return true;
+  if (isDecoyFeedText(t)) return true;
+  if (t.length < 12) return false;
+
+  if (/^[A-Za-z0-9]{10,}:\s/.test(t)) return true;
+
+  const tokens = t.split(/\s+/).filter((word) => word.length > 1);
+  if (tokens.length === 0) return true;
+
+  let scrambled = 0;
+  for (const word of tokens) {
+    const core = word.replace(/[^\w]/g, '');
+    if (!core) continue;
+    if (/\d/.test(core) && /[a-zA-Z]/.test(core) && !/^(19|20)\d{2}$/.test(core)) {
+      if (/[a-zA-Z]\d|\d[a-zA-Z]/.test(core) || /(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(core)) {
+        scrambled += 1;
+        continue;
+      }
+    }
</code_context>
<issue_to_address>
**issue (bug_risk):** A normal two-token post containing one mixed alphanumeric word is always classified as scrambled because one scrambled token reaches the 0.25 ratio threshold; for example, `COVID19 update` has a scrambled ratio of 1/2 and is rejected. This removes legitimate posts containing common product names, model identifiers, or years joined to words.

**Triggers:** When a legitimate post has at least 12 characters, two or more tokens, and one token containing adjacent letters and digits.

**Suggested fix:** Require multiple suspicious mixed tokens or additional soup/entropy evidence before rejecting short content, rather than treating a single mixed alphanumeric token as sufficient.

```suggestion
  if (scrambled >= 2 && scrambled / tokens.length >= 0.25) return true;
```
</issue_to_address>

Sourcery assessment

Approval pending. 1 finding to address first.

Blocking findings: clis/facebook/feed.js:89


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread clis/facebook/feed.js
}
}

if (tokens.length >= 2 && scrambled / tokens.length >= 0.25) return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): A normal two-token post containing one mixed alphanumeric word is always classified as scrambled because one scrambled token reaches the 0.25 ratio threshold; for example, COVID19 update has a scrambled ratio of 1/2 and is rejected. This removes legitimate posts containing common product names, model identifiers, or years joined to words.

Triggers: When a legitimate post has at least 12 characters, two or more tokens, and one token containing adjacent letters and digits.

Suggested fix: Require multiple suspicious mixed tokens or additional soup/entropy evidence before rejecting short content, rather than treating a single mixed alphanumeric token as sufficient.

Suggested change
if (tokens.length >= 2 && scrambled / tokens.length >= 0.25) return true;
if (scrambled >= 2 && scrambled / tokens.length >= 0.25) return true;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants