Parse user counts/bio from modern GraphQL shape

This commit is contained in:
Zed 2026-08-17 09:05:53 +07:00
commit 62aa2f2823
3 changed files with 44 additions and 7 deletions

View file

@ -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: if userResult.relationshipCounts.isSome:
result.bio = userResult.profileBio.get.description 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] != '{':

View file

@ -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) = proc expandUserEntities*(user: var User; ent: Entities) =
let let orig = user.bio.toRunes
orig = user.bio.toRunes
ent = raw.entities
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

View file

@ -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: