Created
July 2, 2026 15:33
-
-
Save cmj/f52b6ad5cd549d51321bdf3e3582029f to your computer and use it in GitHub Desktop.
Trump's truthsocial.com RSS feed generator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| RSS feed generator for @realDonaldTrump's Truth Social posts. | |
| """ | |
| import sys | |
| import io | |
| import re | |
| import html | |
| from datetime import datetime | |
| from email.utils import format_datetime | |
| from xml.sax.saxutils import escape as xml_escape | |
| import requests | |
| ACCOUNT_ID = "107780257626128497" | |
| API_URL = ( | |
| f"https://truthsocial.com/api/v1/accounts/{ACCOUNT_ID}/statuses" | |
| "?exclude_replies=true&with_muted=true" | |
| ) | |
| MAX_LEN = 420 | |
| sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", newline="") | |
| def strip_html(raw: str) -> str: | |
| text = raw | |
| text = text.replace("<br/>", " ").replace("<br />", " ").replace("<br>", " ") | |
| text = re.sub(r'</span><span class="ellipsis">', "", text) | |
| text = re.sub(r'</span><span class="invisible">', "", text) | |
| text = re.sub(r'</span><span class="">', "", text) | |
| text = text.replace("</span>", " ") | |
| text = re.sub(r"<[^>]*>", "", text) # strip any remaining tags | |
| text = html.unescape(text) # & -> &, > -> >, etc. | |
| text = re.sub(r"\s{2,}", " ", text).strip() | |
| return text | |
| def truncate(text: str, limit: int = MAX_LEN) -> str: | |
| if len(text) > limit: | |
| return text[: limit - 1] + "…" | |
| return text | |
| def format_pubdate(created_at: str) -> str: | |
| try: | |
| dt = datetime.fromisoformat(created_at.replace("Z", "+00:00")) | |
| return format_datetime(dt) | |
| except ValueError: | |
| return created_at | |
| HEADERS = { | |
| "User-Agent": ( | |
| "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " | |
| "(KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" | |
| ), | |
| "Accept": "application/json", | |
| } | |
| def fetch_statuses(): | |
| resp = requests.get(API_URL, headers=HEADERS, timeout=15) | |
| resp.encoding = "utf-8" | |
| resp.raise_for_status() | |
| return resp.json() | |
| def build_item(status: dict) -> str: | |
| created = format_pubdate(status.get("created_at", "")) | |
| url = status.get("url", "") | |
| content_html = status.get("content", "") | |
| if content_html and content_html != "<p></p>": | |
| content = strip_html(content_html.replace("*", "٭")) | |
| else: | |
| media = status.get("media_attachments") or [{}] | |
| content = media[0].get("url", "") | |
| content = truncate(content) | |
| title = xml_escape(content) | |
| description = xml_escape(content) | |
| link = xml_escape(url) | |
| return ( | |
| " <item>\n" | |
| f" <title>{title}</title>\n" | |
| " <dc:creator>@realDonaldTrump</dc:creator>\n" | |
| f" <description>{description}</description>\n" | |
| f" <pubDate>{created}</pubDate>\n" | |
| f" <link>{link}</link>\n" | |
| " </item>" | |
| ) | |
| def main(): | |
| print("Content-type: application/rss+xml; charset=utf-8") | |
| print("Cache-control: public, max-age=4000") | |
| print("Access-Control-Allow-Origin: *") | |
| print() | |
| print('<?xml version="1.0" encoding="UTF-8"?>') | |
| print( | |
| '<rss version="2.0" ' | |
| 'xmlns:dc="http://purl.org/dc/elements/1.1/" ' | |
| 'xmlns:webfeeds="http://webfeeds.org/rss/1.0" ' | |
| 'xmlns:media="http://search.yahoo.com/mrss/">' | |
| ) | |
| print(" <channel>") | |
| print(" <title>realDonaldTrump</title>") | |
| print(" <description>Public posts from @realDonaldTrump</description>") | |
| print(" <link>https://truthsocial.com/@realDonaldTrump</link>") | |
| print(" <language>en-us</language>") | |
| try: | |
| statuses = fetch_statuses() | |
| except (requests.RequestException, ValueError) as exc: | |
| sys.stderr.write(f"Failed to fetch/parse statuses: {exc}\n") | |
| statuses = [] | |
| for status in statuses: | |
| print(build_item(status)) | |
| print(" </channel>") | |
| print("</rss>") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment