fix(truthsocial): send browser-like headers to fix HTTP 403 from Cloudflare

Author: Bigmoose2000Created Jul 29, 2026Updated Sep 12, 2026

Summary

truthsocial.py sends only the Authorization header. Cloudflare in front of truthsocial.com rejects the skill's default User-Agent, so every Truth Social search returns HTTP 403 regardless of whether the bearer token is valid.

This is the same bug already fixed for Reddit in #199 ("fix(reddit): use browser-like headers to fix HTTP 403 from urllib"). Truth Social never got the same treatment.

Environment

  • engine v3.18.4
  • Windows 11, Python 3.12.10
  • TRUTHSOCIAL_TOKEN set, valid, freshly pulled from browser DevTools

Reproduction

# 1. Default skill User-Agent -> 403, HTML challenge page, not JSON
curl -s -o /dev/null -w '%{http_code}\n' \
  -H "Authorization: Bearer $TRUTHSOCIAL_TOKEN" \
  -H "User-Agent: last30days-skill/3.0 (Assistant Skill)" \
  "https://truthsocial.com/api/v2/search?q=health%20policy&type=statuses&limit=3"
# -> 403

# 2. Same token, browser User-Agent -> 200 with real statuses
curl -s -o /dev/null -w '%{http_code}\n' --compressed \
  -H "Authorization: Bearer $TRUTHSOCIAL_TOKEN" \
  -H "Accept: application/json, text/plain, */*" \
  -H "Accept-Language: en-US,en;q=0.9" \
  -H "Referer: https://truthsocial.com/" \
  -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:153.0) Gecko/20100101 Firefox/153.0" \
  "https://truthsocial.com/api/v2/search?q=health%20policy&type=statuses&limit=3"
# -> 200

The token is identical in both calls. Only the headers differ.

Root cause

scripts/lib/truthsocial.py:90

        response = http.request(
            "GET", url,
            headers={"Authorization": f"Bearer {token}"},
            timeout=30,
        )

http.request() defaults User-Agent to last30days-skill/3.0 (Assistant Skill) (scripts/lib/http.py:37, applied at http.py:552 via setdefault). Cloudflare 403s it.

scripts/lib/http.py:742 already defines BROWSER_USER_AGENT for exactly this situation, and get_text() uses it for the keyless Reddit path. The Truth Social caller just does not opt in.

Why it looks like a token problem

truthsocial.py:108 maps 403 to "Truth Social access denied (Cloudflare)", and doctor reports the source as configured because the token is present. So the user sees a correctly-configured source that returns nothing, and the natural conclusion is an expired token. The 403 branch existing at all suggests this was known but attributed to Cloudflare bot policy rather than to the skill's own UA.

Proposed fix

        response = http.request(
            "GET", url,
            headers={
                "Authorization": f"Bearer {token}",
                "User-Agent": http.BROWSER_USER_AGENT,
                "Accept": "application/json, text/plain, */*",
                "Accept-Language": "en-US,en;q=0.9",
                "Referer": "https://truthsocial.com/",
            },
            timeout=30,
        )

Verification

With the patch applied, through the engine module rather than curl:

[TruthSocial] Searching for 'health policy' (depth=quick, limit=15)
[TruthSocial] Found 15 posts
error: None | statuses: 15 | parsed items: 15

Two separate queries (health policy, medicaid) both returned 15 posts. Before the patch, both returned the Cloudflare 403 error string.

Note for other Windows users

scripts/lib/truthsocial.py ships with CRLF line endings on Windows. Any scripted patch that matches on \n will silently fail to apply.

Source: mvanhorn/last30days-skill