Просмотр исходного кода

Implement temp fix for cookie sessions

Fixes #1319
Zed 9 месяцев назад
Родитель
Сommit
b83227aaf5

+ 27 - 20
src/api.nim

@@ -13,47 +13,54 @@ proc genParams(variables: string; fieldToggles = ""): seq[(string, string)] =
 
 proc mediaUrl(id: string; cursor: string): SessionAwareUrl =
   let
-    cookieVariables = userMediaVariables % [id, cursor]
-    oauthVariables = restIdVariables % [id, cursor]
+    cookieVars = userMediaVars % [id, cursor]
+    oauthVars = restIdVars % [id, cursor]
   result = SessionAwareUrl(
-    cookieUrl: graphUserMedia ? genParams(cookieVariables),
-    oauthUrl: graphUserMediaV2 ? genParams(oauthVariables)
+    cookieUrl: graphUserMedia ? genParams(cookieVars),
+    oauthUrl: graphUserMediaV2 ? genParams(oauthVars)
   )
 
 proc userTweetsUrl(id: string; cursor: string): SessionAwareUrl =
   let
-    cookieVariables = userTweetsVariables % [id, cursor]
-    oauthVariables = restIdVariables % [id, cursor]
+    cookieVars = userTweetsVars % [id, cursor]
+    oauthVars = restIdVars % [id, cursor]
   result = SessionAwareUrl(
-    # cookieUrl: graphUserTweets ? genParams(cookieVariables, fieldToggles),
-    oauthUrl: graphUserTweetsV2 ? genParams(oauthVariables)
+    # cookieUrl: graphUserTweets ? genParams(cookieVars, userTweetsFieldToggles),
+    oauthUrl: graphUserTweetsV2 ? genParams(oauthVars)
   )
   # might change this in the future pending testing
   result.cookieUrl = result.oauthUrl
 
 proc userTweetsAndRepliesUrl(id: string; cursor: string): SessionAwareUrl =
   let
-    cookieVariables = userTweetsAndRepliesVariables % [id, cursor]
-    oauthVariables = restIdVariables % [id, cursor]
+    cookieVars = userTweetsAndRepliesVars % [id, cursor]
+    oauthVars = restIdVars % [id, cursor]
   result = SessionAwareUrl(
-    cookieUrl: graphUserTweetsAndReplies ? genParams(cookieVariables, fieldToggles),
-    oauthUrl: graphUserTweetsAndRepliesV2 ? genParams(oauthVariables)
+    cookieUrl: graphUserTweetsAndReplies ? genParams(cookieVars, userTweetsFieldToggles),
+    oauthUrl: graphUserTweetsAndRepliesV2 ? genParams(oauthVars)
   )
 
 proc tweetDetailUrl(id: string; cursor: string): SessionAwareUrl =
   let
-    cookieVariables = tweetDetailVariables % [id, cursor]
-    oauthVariables = tweetVariables % [id, cursor]
+    cookieVars = tweetDetailVars % [id, cursor]
+    oauthVars = tweetVars % [id, cursor]
   result = SessionAwareUrl(
-    cookieUrl: graphTweetDetail ? genParams(cookieVariables, tweetDetailFieldToggles),
-    oauthUrl: graphTweet ? genParams(oauthVariables)
+    cookieUrl: graphTweetDetail ? genParams(cookieVars, tweetDetailFieldToggles),
+    oauthUrl: graphTweet ? genParams(oauthVars)
+  )
+
+proc userUrl(username: string): SessionAwareUrl =
+  let
+    cookieVars = """{"screen_name":"$1","withGrokTranslatedBio":false}""" % username
+    oauthVars = """{"screen_name": "$1"}""" % username
+  result = SessionAwareUrl(
+    cookieUrl: graphUser ? genParams(cookieVars, tweetDetailFieldToggles),
+    oauthUrl: graphUserV2 ? genParams(oauthVars)
   )
 
 proc getGraphUser*(username: string): Future[User] {.async.} =
   if username.len == 0: return
-  let
-    url = graphUser ? genParams("""{"screen_name": "$1"}""" % username)
-    js = await fetchRaw(url, Api.userScreenName)
+  let js = await fetchRaw(userUrl(username), Api.userScreenName)
   result = parseGraphUser(js)
 
 proc getGraphUserById*(id: string): Future[User] {.async.} =
@@ -80,7 +87,7 @@ proc getGraphListTweets*(id: string; after=""): Future[Timeline] {.async.} =
   if id.len == 0: return
   let
     cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: ""
-    url = graphListTweets ? genParams(restIdVariables % [id, cursor])
+    url = graphListTweets ? genParams(restIdVars % [id, cursor])
   result = parseGraphTimeline(await fetch(url, Api.listTweets), after).tweets
 
 proc getGraphListBySlug*(name, list: string): Future[List] {.async.} =

+ 1 - 1
src/apiutils.nim

@@ -50,7 +50,7 @@ proc genHeaders*(session: Session, url: string): HttpHeaders =
   of SessionKind.oauth:
     result["authorization"] = getOauthHeader(url, session.oauthToken, session.oauthSecret)
   of SessionKind.cookie:
-    result["authorization"] = "Bearer AAAAAAAAAAAAAAAAAAAAAFQODgEAAAAAVHTp76lzh3rFzcHbmHVvQxYYpTw%3DckAlMINMjmCwxUcaXbAN4XqJVdgMJaHqNOFgPMK0zN1qLqLQCF"
+    result["authorization"] = "Bearer AAAAAAAAAAAAAAAAAAAAAFXzAwAAAAAAMHCxpeSDG1gLNLghVe8d74hl6k4%3DRUMF4xAQLsbeBhTSRrCiQpJtxoGWeyHrDb5te2jpGskWDFW82F"
     result["x-twitter-auth-type"] = "OAuth2Session"
     result["x-csrf-token"] = session.ct0
     result["cookie"] = getCookieHeader(session.authToken, session.ct0)

+ 15 - 9
src/consts.nim

@@ -7,7 +7,8 @@ const
 
   gql = parseUri("https://api.x.com") / "graphql"
 
-  graphUser* = gql / "WEoGnYB0EG1yGwamDCF6zg/UserResultByScreenNameQuery"
+  graphUser* = gql / "-oaLodhGbbnzJBACb1kk2Q/UserByScreenName"
+  graphUserV2* = gql / "WEoGnYB0EG1yGwamDCF6zg/UserResultByScreenNameQuery"
   graphUserById* = gql / "VN33vKXrPT7p35DgNR27aw/UserResultByIdQuery"
   graphUserTweetsV2* = gql / "6QdSuZ5feXxOadEdXa4XZg/UserWithProfileTweetsQueryV2"
   graphUserTweetsAndRepliesV2* = gql / "BDX77Xzqypdt11-mDfgdpQ/UserWithProfileTweetsAndRepliesQueryV2"
@@ -97,10 +98,14 @@ const
   "grok_translations_community_note_auto_translation_is_enabled": false,
   "grok_translations_post_auto_translation_is_enabled": false,
   "grok_translations_community_note_translation_is_enabled": false,
-  "grok_translations_timeline_user_bio_auto_translation_is_enabled": false
+  "grok_translations_timeline_user_bio_auto_translation_is_enabled": false,
+  "subscriptions_feature_can_gift_premium": false,
+  "responsive_web_twitter_article_notes_tab_enabled": false,
+  "subscriptions_verification_info_is_identity_verified_enabled": false,
+  "hidden_profile_subscriptions_enabled": false
 }""".replace(" ", "").replace("\n", "")
 
-  tweetVariables* = """{
+  tweetVars* = """{
   "postId": "$1",
   $2
   "includeHasBirdwatchNotes": false,
@@ -110,7 +115,7 @@ const
   "withV2Timeline": true
 }""".replace(" ", "").replace("\n", "")
 
-  tweetDetailVariables* = """{
+  tweetDetailVars* = """{
   "focalTweetId": "$1",
   $2
   "referrer": "profile",
@@ -123,12 +128,12 @@ const
   "withVoice": true
 }""".replace(" ", "").replace("\n", "")
 
-  restIdVariables* = """{
+  restIdVars* = """{
   "rest_id": "$1", $2
   "count": 20
 }"""
 
-  userMediaVariables* = """{
+  userMediaVars* = """{
   "userId": "$1", $2
   "count": 20,
   "includePromotedContent": false,
@@ -137,7 +142,7 @@ const
   "withVoice": true
 }""".replace(" ", "").replace("\n", "")
 
-  userTweetsVariables* = """{
+  userTweetsVars* = """{
   "userId": "$1", $2
   "count": 20,
   "includePromotedContent": false,
@@ -145,7 +150,7 @@ const
   "withVoice": true
 }""".replace(" ", "").replace("\n", "")
 
-  userTweetsAndRepliesVariables* = """{
+  userTweetsAndRepliesVars* = """{
   "userId": "$1", $2
   "count": 20,
   "includePromotedContent": false,
@@ -153,5 +158,6 @@ const
   "withVoice": true
 }""".replace(" ", "").replace("\n", "")
 
-  fieldToggles* = """{"withArticlePlainText":false}"""
+  userFieldToggles = """{"withPayments":false,"withAuxiliaryUserLabels":true}"""
+  userTweetsFieldToggles* = """{"withArticlePlainText":false}"""
   tweetDetailFieldToggles* = """{"withArticleRichContentState":true,"withArticlePlainText":false,"withGrokAnalyze":false,"withDisallowedReplyControls":false}"""

+ 19 - 5
src/experimental/parser/graphql.nim

@@ -1,6 +1,6 @@
 import options, strutils
 import jsony
-import user, ../types/[graphuser, graphlistmembers]
+import user, utils, ../types/[graphuser, graphlistmembers]
 from ../../types import User, VerifiedType, Result, Query, QueryKind
 
 proc parseUserResult*(userResult: UserResult): User =
@@ -15,22 +15,36 @@ proc parseUserResult*(userResult: UserResult): User =
     result.fullname = userResult.core.name
     result.userPic = userResult.avatar.imageUrl.replace("_normal", "")
 
+    if userResult.privacy.isSome:
+      result.protected = userResult.privacy.get.protected
+
+    if userResult.location.isSome:
+      result.location = userResult.location.get.location
+
+    if userResult.core.createdAt.len > 0:
+      result.joinDate = parseTwitterDate(userResult.core.createdAt)
+
     if userResult.verification.isSome:
       let v = userResult.verification.get
       if v.verifiedType != VerifiedType.none:
         result.verifiedType = v.verifiedType
 
-    if userResult.profileBio.isSome:
+    if userResult.profileBio.isSome and result.bio.len == 0:
       result.bio = userResult.profileBio.get.description
 
 proc parseGraphUser*(json: string): User =
   if json.len == 0 or json[0] != '{':
     return
 
-  let raw = json.fromJson(GraphUser)
-  let userResult = raw.data.userResult.result
+  let 
+    raw = json.fromJson(GraphUser)
+    userResult = 
+      if raw.data.userResult.isSome: raw.data.userResult.get.result
+      elif raw.data.user.isSome: raw.data.user.get.result
+      else: UserResult()
 
-  if userResult.unavailableReason.get("") == "Suspended":
+  if userResult.unavailableReason.get("") == "Suspended" or
+     userResult.reason.get("") == "Suspended":
     return User(suspended: true)
 
   result = parseUserResult(userResult)

+ 3 - 1
src/experimental/parser/user.nim

@@ -58,11 +58,13 @@ proc toUser*(raw: RawUser): User =
     media: raw.mediaCount,
     verifiedType: raw.verifiedType,
     protected: raw.protected,
-    joinDate: parseTwitterDate(raw.createdAt),
     banner: getBanner(raw),
     userPic: getImageUrl(raw.profileImageUrlHttps).replace("_normal", "")
   )
 
+  if raw.createdAt.len > 0:
+    result.joinDate = parseTwitterDate(raw.createdAt)
+
   if raw.pinnedTweetIdsStr.len > 0:
     result.pinnedTweet = parseBiggestInt(raw.pinnedTweetIdsStr[0])
 

+ 11 - 2
src/experimental/types/graphuser.nim

@@ -3,7 +3,7 @@ from ../../types import User, VerifiedType
 
 type
   GraphUser* = object
-    data*: tuple[userResult: UserData]
+    data*: tuple[userResult: Option[UserData], user: Option[UserData]]
 
   UserData* = object
     result*: UserResult
@@ -22,15 +22,24 @@ type
   Verification* = object
     verifiedType*: VerifiedType
 
+  Location* = object
+    location*: string
+
+  Privacy* = object
+    protected*: bool
+
   UserResult* = object
     legacy*: User
     restId*: string
     isBlueVerified*: bool
-    unavailableReason*: Option[string]
     core*: UserCore
     avatar*: UserAvatar
+    unavailableReason*: Option[string]
+    reason*: Option[string]
+    privacy*: Option[Privacy]
     profileBio*: Option[UserBio]
     verification*: Option[Verification]
+    location*: Option[Location]
 
 proc enumHook*(s: string; v: var VerifiedType) =
   v = try:

+ 1 - 1
src/parser.nim

@@ -21,7 +21,7 @@ proc parseUser(js: JsonNode; id=""): User =
     tweets: js{"statuses_count"}.getInt,
     likes: js{"favourites_count"}.getInt,
     media: js{"media_count"}.getInt,
-    protected: js{"protected"}.getBool,
+    protected: js{"protected"}.getBool(js{"privacy", "protected"}.getBool),
     joinDate: js{"created_at"}.getTime
   )