fix(ingest): rewrite only the host when normalizing a tweet URL for oEmbed

_fetch_tweet did url.replace("x.com", "twitter.com") over the whole URL,
so "x.com" in the path or query was rewritten too (a ?next= link, or a
handle like dropbox.com_fan becoming dropbotwitter.com_fan), and an
uppercase X.COM host was missed. Match and rewrite the host only, the
same way _detect_url_type classifies it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 3ec4fdcfca)
This commit is contained in:
Daksh authored and safishamsi committed 2026-09-28 09:13:29 +01:00
1 parent 4c21b15e9b
commit c2fb556919
2 files changed
+46 -2

No files matched your search

+9 -2
View File
@@ -109,8 +109,15 @@ def _html_to_markdown(html: str, url: str) -> str:
def _fetch_tweet(url: str, author: str | None, contributor: str | None) -> tuple[str, str]:
"""Fetch a tweet URL. Returns (content, filename)."""
# Normalize to twitter.com for oEmbed
oembed_url = url.replace("x.com", "twitter.com")
# Normalize to twitter.com for oEmbed. Rewrite the host only: a plain
# text replace also rewrote "x.com" wherever it appeared in the path or query.
parts = urllib.parse.urlsplit(url)
host = (parts.hostname or "").rstrip(".")
if _host_is(host, "x.com"):
host = host[: -len("x.com")] + "twitter.com"
netloc = f"{host}:{parts.port}" if parts.port else host
parts = parts._replace(netloc=netloc)
oembed_url = urllib.parse.urlunsplit(parts)
oembed_api = f"https://publish.twitter.com/oembed?url={urllib.parse.quote(oembed_url)}&omit_script=true"
try:
data = json.loads(safe_fetch_text(oembed_api))
+37
View File
@@ -1,6 +1,8 @@
"""Tests for how graphify.ingest classifies and routes a URL."""
from __future__ import annotations
import urllib.parse
import pytest
import graphify.ingest as ingest_mod
@@ -66,3 +68,38 @@ def test_a_dropbox_pdf_is_downloaded_not_saved_as_a_tweet_stub(tmp_path, monkeyp
assert fetched == [url]
assert out.suffix == ".pdf"
assert out.read_bytes() == b"%PDF-1.7 test"
@pytest.mark.parametrize(
("url", "expected"),
[
("https://x.com/user/status/1", "https://twitter.com/user/status/1"),
("https://mobile.x.com/user/status/1", "https://mobile.twitter.com/user/status/1"),
("https://X.COM/user/status/1", "https://twitter.com/user/status/1"),
# "x.com" outside the host must survive untouched.
(
"https://x.com/user/status/1?next=https://x.com/home",
"https://twitter.com/user/status/1?next=https://x.com/home",
),
(
"https://twitter.com/dropbox.com_fan/status/1",
"https://twitter.com/dropbox.com_fan/status/1",
),
],
)
def test_tweet_oembed_rewrites_only_the_host(url, expected, monkeypatch):
"""The x.com -> twitter.com rewrite for oEmbed was a replace over the whole
URL, so "x.com" inside the path or query (e.g. a dropbox.com handle, a
?next= link) was rewritten too, sending oEmbed a different URL."""
requested = []
def fake_fetch_text(u):
requested.append(u)
return '{"html": "<p>hi</p>", "author_name": "someone"}'
monkeypatch.setattr(ingest_mod, "safe_fetch_text", fake_fetch_text)
ingest_mod._fetch_tweet(url, None, None)
query = urllib.parse.parse_qs(urllib.parse.urlsplit(requested[0]).query)
assert query["url"] == [expected]