Selaa lähdekoodia

Parse user counts/bio from modern GraphQL shape

Zed 3 viikkoa sitten
vanhempi
commit
62aa2f2823

+ 22 - 2
src/experimental/parser/graphql.nim

@@ -10,11 +10,16 @@ proc parseUserResult*(userResult: UserResult): User =
     result.verifiedType = blue
     result.verifiedType = blue
 
 
   if result.username.len == 0 and userResult.core.screenName.len > 0:
   if result.username.len == 0 and userResult.core.screenName.len > 0:
+    # Modern UserByRestId/UserByScreenName shape: `legacy` is empty and the data
+    # lives in typed sub-objects (core/relationship_counts/tweet_counts/etc.).
     result.id = userResult.restId
     result.id = userResult.restId
     result.username = userResult.core.screenName
     result.username = userResult.core.screenName
     result.fullname = userResult.core.name
     result.fullname = userResult.core.name
     result.userPic = userResult.avatar.imageUrl.replace("_normal", "")
     result.userPic = userResult.avatar.imageUrl.replace("_normal", "")
 
 
+    if userResult.banner.imageUrl.len > 0:
+      result.banner = userResult.banner.imageUrl & "/1500x500"
+
     if userResult.privacy.isSome:
     if userResult.privacy.isSome:
       result.protected = userResult.privacy.get.protected
       result.protected = userResult.privacy.get.protected
 
 
@@ -29,8 +34,23 @@ proc parseUserResult*(userResult: UserResult): User =
       if v.verifiedType != VerifiedType.none:
       if v.verifiedType != VerifiedType.none:
         result.verifiedType = v.verifiedType
         result.verifiedType = v.verifiedType
 
 
-    if userResult.profileBio.isSome and result.bio.len == 0:
-      result.bio = userResult.profileBio.get.description
+    if userResult.relationshipCounts.isSome:
+      let rc = userResult.relationshipCounts.get
+      result.followers = rc.followers
+      result.following = rc.following
+
+    if userResult.tweetCounts.isSome:
+      let tc = userResult.tweetCounts.get
+      result.tweets = tc.tweets
+      result.media = tc.mediaTweets
+
+    if userResult.actionCounts.isSome:
+      result.likes = userResult.actionCounts.get.favoritesCount
+
+    if userResult.profileBio.isSome:
+      let bio = userResult.profileBio.get
+      result.bio = bio.description
+      result.expandUserEntities(bio.entities)
 
 
 proc parseGraphUser*(json: string): User =
 proc parseGraphUser*(json: string): User =
   if json.len == 0 or json[0] != '{':
   if json.len == 0 or json[0] != '{':

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

@@ -11,10 +11,8 @@ let
   htRegex = nre.re"""(*U)(^|[^\w-_.?])([##$])([\w_]*+)(?!</a>|">|#)"""
   htRegex = nre.re"""(*U)(^|[^\w-_.?])([##$])([\w_]*+)(?!</a>|">|#)"""
   htReplace = "$1<a href=\"/search?f=tweets&q=%23$3\">$2$3</a>"
   htReplace = "$1<a href=\"/search?f=tweets&q=%23$3\">$2$3</a>"
 
 
-proc expandUserEntities(user: var User; raw: RawUser) =
-  let
-    orig = user.bio.toRunes
-    ent = raw.entities
+proc expandUserEntities*(user: var User; ent: Entities) =
+  let orig = user.bio.toRunes
 
 
   if ent.url.urls.len > 0:
   if ent.url.urls.len > 0:
     user.website = ent.url.urls[0].expandedUrl
     user.website = ent.url.urls[0].expandedUrl
@@ -68,7 +66,7 @@ proc toUser*(raw: RawUser): User =
   if raw.pinnedTweetIdsStr.len > 0:
   if raw.pinnedTweetIdsStr.len > 0:
     result.pinnedTweet = parseBiggestInt(raw.pinnedTweetIdsStr[0])
     result.pinnedTweet = parseBiggestInt(raw.pinnedTweetIdsStr[0])
 
 
-  result.expandUserEntities(raw)
+  result.expandUserEntities(raw.entities)
 
 
 proc parseHook*(s: string; i: var int; v: var User) =
 proc parseHook*(s: string; i: var int; v: var User) =
   var u: RawUser
   var u: RawUser

+ 19 - 0
src/experimental/types/graphuser.nim

@@ -1,5 +1,6 @@
 import options, strutils
 import options, strutils
 from ../../types import User, VerifiedType
 from ../../types import User, VerifiedType
+import user as userType  # Entities, for modern profile_bio parsing
 
 
 type
 type
   GraphUser* = object
   GraphUser* = object
@@ -15,6 +16,7 @@ type
 
 
   UserBio* = object
   UserBio* = object
     description*: string
     description*: string
+    entities*: Entities
 
 
   UserAvatar* = object
   UserAvatar* = object
     imageUrl*: string
     imageUrl*: string
@@ -28,18 +30,35 @@ type
   Privacy* = object
   Privacy* = object
     protected*: bool
     protected*: bool
 
 
+  # Modern UserByRestId/UserByScreenName shape moves the counts out of `legacy`
+  # into these typed sub-objects.
+  RelationshipCounts* = object
+    followers*: int
+    following*: int
+
+  TweetCounts* = object
+    tweets*: int
+    mediaTweets*: int
+
+  ActionCounts* = object
+    favoritesCount*: int
+
   UserResult* = object
   UserResult* = object
     legacy*: User
     legacy*: User
     restId*: string
     restId*: string
     isBlueVerified*: bool
     isBlueVerified*: bool
     core*: UserCore
     core*: UserCore
     avatar*: UserAvatar
     avatar*: UserAvatar
+    banner*: UserAvatar
     unavailableReason*: Option[string]
     unavailableReason*: Option[string]
     reason*: Option[string]
     reason*: Option[string]
     privacy*: Option[Privacy]
     privacy*: Option[Privacy]
     profileBio*: Option[UserBio]
     profileBio*: Option[UserBio]
     verification*: Option[Verification]
     verification*: Option[Verification]
     location*: Option[Location]
     location*: Option[Location]
+    relationshipCounts*: Option[RelationshipCounts]
+    tweetCounts*: Option[TweetCounts]
+    actionCounts*: Option[ActionCounts]
 
 
 proc enumHook*(s: string; v: var VerifiedType) =
 proc enumHook*(s: string; v: var VerifiedType) =
   v = try:
   v = try: