fix headers so UserByRestId works

This commit is contained in:
Cynthia Foxwell 2025-11-13 20:48:34 -07:00
parent 0e74c1e9bd
commit ec019eef72
No known key found for this signature in database
3 changed files with 43 additions and 15 deletions

View File

@ -6,23 +6,41 @@ import experimental/parser as newParser
proc getGraphUser*(username: string): Future[User] {.async.} = proc getGraphUser*(username: string): Future[User] {.async.} =
if username.len == 0: return if username.len == 0: return
let
headers = newHttpHeaders()
headers.add("Referer", """https://x.com/$1""" % username)
let let
variables = """{"screen_name":"$1"}""" % username variables = """{"screen_name":"$1"}""" % username
fieldToggles = """{"withAuxiliaryUserLabels":true}""" fieldToggles = """{"withAuxiliaryUserLabels":true}"""
params = {"variables": variables, "features": gqlFeatures, "fieldToggles": fieldToggles} params = {"variables": variables, "features": gqlFeatures, "fieldToggles": fieldToggles}
js = await fetch(graphUser ? params, Api.userScreenName) js = await fetch(graphUser ? params, Api.userScreenName, headers)
result = parseGraphUser(js) result = parseGraphUser(js)
proc getGraphUserById*(id: string): Future[User] {.async.} = proc getGraphUserById*(id: string): Future[User] {.async.} =
if id.len == 0 or id.any(c => not c.isDigit): return if id.len == 0 or id.any(c => not c.isDigit): return
let
headers = newHttpHeaders()
headers.add("Referer", """https://x.com/i/user/$1""" % id)
let let
variables = """{"userId":"$1"}""" % id variables = """{"userId":"$1"}""" % id
params = {"variables": variables, "features": gqlFeatures} params = {"variables": variables, "features": gqlFeatures}
js = await fetch(graphUserById ? params, Api.userRestId) js = await fetch(graphUserById ? params, Api.userRestId, headers)
result = parseGraphUser(js) result = parseGraphUser(js)
proc getGraphUserTweets*(id: string; kind: TimelineKind; after=""): Future[Profile] {.async.} = proc getGraphUserTweets*(id: string; kind: TimelineKind; after=""): Future[Profile] {.async.} =
if id.len == 0: return if id.len == 0: return
let endpoint = case kind
of TimelineKind.tweets: ""
of TimelineKind.replies: "/with_replies"
of TimelineKind.media: "/media"
let
headers = newHttpHeaders()
headers.add("Referer", """https://x.com/$1$2""" % [id, endpoint])
let let
cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: ""
variables = if kind == TimelineKind.media: userMediaVariables % [id, cursor] else: userTweetsVariables % [id, cursor] variables = if kind == TimelineKind.media: userMediaVariables % [id, cursor] else: userTweetsVariables % [id, cursor]
@ -32,7 +50,7 @@ proc getGraphUserTweets*(id: string; kind: TimelineKind; after=""): Future[Profi
of TimelineKind.tweets: (graphUserTweets, Api.userTweets) of TimelineKind.tweets: (graphUserTweets, Api.userTweets)
of TimelineKind.replies: (graphUserTweetsAndReplies, Api.userTweetsAndReplies) of TimelineKind.replies: (graphUserTweetsAndReplies, Api.userTweetsAndReplies)
of TimelineKind.media: (graphUserMedia, Api.userMedia) of TimelineKind.media: (graphUserMedia, Api.userMedia)
js = await fetch(url ? params, apiId) js = await fetch(url ? params, apiId, headers)
result = parseGraphTimeline(js, if kind == TimelineKind.media: "" else: "user", after) result = parseGraphTimeline(js, if kind == TimelineKind.media: "" else: "user", after)
proc getGraphListTweets*(id: string; after=""): Future[Timeline] {.async.} = proc getGraphListTweets*(id: string; after=""): Future[Timeline] {.async.} =
@ -90,19 +108,27 @@ proc getFavorites*(id: string; cfg: Config; after=""): Future[Profile] {.async.}
proc getGraphTweetResult*(id: string): Future[Tweet] {.async.} = proc getGraphTweetResult*(id: string): Future[Tweet] {.async.} =
if id.len == 0: return if id.len == 0: return
let
headers = newHttpHeaders()
headers.add("Referer", """https://x.com/i/status/$1""" % id)
let let
variables = """{"rest_id":"$1"}""" % id variables = """{"rest_id":"$1"}""" % id
params = {"variables": variables, "features": gqlFeatures} params = {"variables": variables, "features": gqlFeatures}
js = await fetch(graphTweetResult ? params, Api.tweetResult) js = await fetch(graphTweetResult ? params, Api.tweetResult, headers)
result = parseGraphTweetResult(js) result = parseGraphTweetResult(js)
proc getGraphTweet*(id: string; after=""): Future[Conversation] {.async.} = proc getGraphTweet*(id: string; after=""): Future[Conversation] {.async.} =
if id.len == 0: return if id.len == 0: return
let
headers = newHttpHeaders()
headers.add("Referer", """https://x.com/i/status/$1""" % id)
let let
cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: ""
variables = tweetVariables % [id, cursor] variables = tweetVariables % [id, cursor]
params = {"variables": variables, "features": gqlFeatures, "fieldToggles": tweetFieldToggles} params = {"variables": variables, "features": gqlFeatures, "fieldToggles": tweetFieldToggles}
js = await fetch(graphTweet ? params, Api.tweetDetail) js = await fetch(graphTweet ? params, Api.tweetDetail, headers)
result = parseGraphConversation(js, id) result = parseGraphConversation(js, id)
proc getGraphFavoriters*(id: string; after=""): Future[UsersTimeline] {.async.} = proc getGraphFavoriters*(id: string; after=""): Future[UsersTimeline] {.async.} =

View File

@ -1,5 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
import httpclient, asyncdispatch, options, strutils, uri, times, tables import httpclient, asyncdispatch, options, strutils, uri, times, tables, math
import jsony, packedjson, zippy import jsony, packedjson, zippy
import types, tokens, consts, parserutils, http_pool import types, tokens, consts, parserutils, http_pool
import experimental/types/common import experimental/types/common
@ -32,6 +32,9 @@ proc genParams*(pars: openArray[(string, string)] = @[]; cursor="";
#proc genHeaders*(token: Token = nil): HttpHeaders = #proc genHeaders*(token: Token = nil): HttpHeaders =
proc genHeaders*(): HttpHeaders = proc genHeaders*(): HttpHeaders =
let
t = getTime()
ffVersion = floor(124 + ((t.toUnix() / 1000) - 1710892800) / 2419200)
result = newHttpHeaders({ result = newHttpHeaders({
"connection": "keep-alive", "connection": "keep-alive",
"authorization": auth, "authorization": auth,
@ -45,8 +48,12 @@ proc genHeaders*(): HttpHeaders =
"Sec-Fetch-Mode": "cors", "Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin", "Sec-Fetch-Site": "same-origin",
"Sec-GPC": "1", "Sec-GPC": "1",
"TE": "trailers" "TE": "trailers",
}) "User-Agent": """Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:$1.0) Gecko/20100101 Firefox/$1.0""" % $ffVersion,
"x-twitter-active-user": "yes",
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-language": "en"
}, true)
#template updateToken() = #template updateToken() =
# if resp.headers.hasKey(rlRemaining): # if resp.headers.hasKey(rlRemaining):
@ -66,21 +73,16 @@ template fetchImpl(result, additional_headers, fetchBody) {.dirty.} =
if len(cfg.cookieHeader) != 0: if len(cfg.cookieHeader) != 0:
additional_headers.add("Cookie", cfg.cookieHeader) additional_headers.add("Cookie", cfg.cookieHeader)
additional_headers.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0")
if len(cfg.xCsrfToken) != 0: if len(cfg.xCsrfToken) != 0:
additional_headers.add("x-csrf-token", cfg.xCsrfToken) additional_headers.add("x-csrf-token", cfg.xCsrfToken)
additional_headers.add("x-twitter-active-user", "yes")
additional_headers.add("x-twitter-auth-type", "OAuth2Session")
additional_headers.add("x-twitter-client-language", "en")
try: try:
var resp: AsyncResponse var resp: AsyncResponse
#var headers = genHeaders(token) #var headers = genHeaders(token)
var headers = genHeaders() var headers = genHeaders()
for key, value in additional_headers.pairs(): for key, value in additional_headers.pairs():
headers.add(key, value) headers.add(key, value)
pool.use(headers): pool.use(headers):
template getContent = template getContent =
resp = await c.get($url) resp = await c.get($url)

View File

@ -151,7 +151,7 @@ proc getCachedUsername*(userId: string): Future[string] {.async.} =
key = "i:" & userId key = "i:" & userId
username = await get(key) username = await get(key)
if username != redisNil: if username != redisNil and username.len > 0:
result = username result = username
else: else:
let user = await getGraphUserById(userId) let user = await getGraphUserById(userId)