fix(truthsocial): send browser-like headers to fix HTTP 403 from Cloudflare
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_TOKENset, 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