Skip to content

Instagram at /ig/, Facebook at /fb/, and one shared failure rule - #159

Merged
ralyodio merged 1 commit into
mainfrom
social-more
Aug 29, 2026
Merged

Instagram at /ig/, Facebook at /fb/, and one shared failure rule#159
ralyodio merged 1 commit into
mainfrom
social-more

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Two more namespaces, and they cost very different amounts — which is the useful part of this change rather than an aside.

Instagram was cheap

X's shape: no feeds, collected through the RSSHub daemon already running inside the poller, mirrored here. A canonical.js, a fetch.js and a route, because the namespace machinery already existed. /ig/<handle> and /ig/tag/<tag>, four formats each.

It needs far less than X did: X needed a normaliser because a repost has no text of its own and a quote is two posts in one item. An Instagram post is a caption and some pictures, so RSSHub's rendering is the item — the only thing changed is the identity, re-keyed from the bridge's guid onto the post shortcode so swapping bridges doesn't mark every account unread.

Facebook is not possible the way you'd want

Three doors, measured on 2026-08-29 rather than assumed:

Path Result
facebook.com/feeds/page.php?format=rss20 404 — removed, not deprecated
mbasic.facebook.com/<page> unauthenticated 200, and a login wall
RSSHub Facebook namespace none (1,000 namespaces; instagram exists)

The only remaining route is Meta's Graph API, and it returns a Page's posts only to somebody who administers that Page. Reading a stranger's public Page needs Page Public Content Access — App Review plus business verification, granted rarely.

So /fb/ takes no open submissions. A Page appears when its operator connects it via FB_PAGE_TOKENS, and an unconnected Page is told plainly that it is not collectable rather than shown an add button that would quietly do nothing. Nothing here drives a logged-in session against that login wall — it breaks constantly, it is against Meta's terms, and it would risk an account to serve a directory nobody pays for.

The #157 rule is now shared, not per-platform

src/failure.js owns only a source that does not exist may count against the source. X was refactored onto it rather than the new platforms being written against a copy — and its 60 existing tests passed unchanged, which is the evidence the refactor was behaviour-identical.

Two bugs the tests caught, both mine

  • identify.js claimed in a comment that X is tried before Instagram, and had Instagram first — so a bare @handle resolved to Instagram. Exactly the comment-says-one-thing-code-does-another class I flagged in Only a missing account may count against an X source's health #157.
  • The Facebook collector took its display name from the ref, which is lowercased because it is an identity — so every item title read somepage: instead of SomePage:. Display now comes from feed_url, which kept the original casing; identity still comes from the ref.

Also: the title polish

An uncrawled social row now renders its canonical name instead of whatever the OPML import called it. 50,026 subreddits are imported and a few hundred crawled, so /r/programming was headed "reddit.com". A render decision, not a rewrite of stored titles — the crawler owns those, and overwriting would fight markCrawlSuccess on every subsequent crawl.

1,345 tests across 12 packages, all green. Build clean, all four namespaces registering.

Neither new platform collects anything until credentials exist (IG_COOKIE, FB_PAGE_TOKENS) — same as X today.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Q6QEgpuS4MLamogXtr2ZX6

Two more namespaces, and they cost very different amounts — which is the
useful part of this change rather than an aside.

Instagram is X's shape: no feeds, collected through the RSSHub daemon
already running in the poller, mirrored here. It was a canonical.js, a
fetch.js and a route, because the namespace machinery already existed.

Facebook is not. Three doors, measured rather than assumed:

  - facebook.com/feeds/page.php?format=rss20 answers 404 (removed)
  - mbasic.facebook.com/<page> answers 200 with a login wall
  - RSSHub carries a thousand namespaces and no Facebook one

The only remaining route is Meta's Graph API, which returns a Page's
posts only to somebody who ADMINISTERS that Page — reading a stranger's
public Page needs a permission Meta grants rarely and only after review.
So /fb/ takes no open submissions: a Page appears once its operator
connects it via FB_PAGE_TOKENS, and an unconnected Page is told plainly
that it is not collectable rather than shown a button that does nothing.

Nothing here drives a logged-in session against that login wall.

The rule from #157 is now shared rather than per-platform. src/failure.js
owns "only a source that does not exist may count against the source",
and X was refactored onto it rather than Instagram and Facebook being
written against a copy. Its 60 tests passed unchanged, which is the
evidence the refactor was behaviour-identical.

Two bugs the tests caught, both mine:

  - identify.js said in a comment that X is tried before Instagram and
    had Instagram first, so a bare @handle resolved to Instagram
  - the Facebook collector took its display name from the ref, which is
    lowercased because it is an identity — so every item title read
    "somepage:" instead of "SomePage:". Display now comes from feed_url,
    which kept the original casing; identity still comes from the ref

Also: an uncrawled social row now renders its canonical name instead of
whatever the OPML import called it. 50,026 subreddits are imported and a
few hundred crawled, so /r/programming was headed "reddit.com". A render
decision, not a rewrite of stored titles — the crawler owns those.

1,345 tests across 12 packages, all green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q6QEgpuS4MLamogXtr2ZX6
@ralyodio
ralyodio merged commit 4675082 into main Aug 29, 2026
3 checks passed
ralyodio added a commit that referenced this pull request Aug 29, 2026
#159 gave a social row its canonical name in the RSS channel title and in
the page metadata, and left the thing a reader actually looks at — the
<h1> — saying "reddit.com".

The cause was one line away from the change: `FEED_COLS` does not select
`social_ref`, so `feedBySlug` has never returned it and the feed page
could not tell a social row from any other. The RSS path worked only
because it goes through `feedBySocialRef`, which is `select *`.

So the columns join FEED_COLS, and the page uses them. Both addresses for
a row now agree — /r/programming and /r-programming show the same name —
which they did not before, since only one of them knew what it was
holding.

Verified against a seeded database rather than by reading: a row titled
"reddit.com", exactly as the OPML import leaves them, now renders
"r/programming" in the heading, the <title> and the feed.


Claude-Session: https://claude.ai/code/session_01Q6QEgpuS4MLamogXtr2ZX6

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant