Rearchitect profile, support pins, Profile -> User

This commit is contained in:
Zed
2022-01-23 07:04:50 +01:00
parent 79b98a8081
commit 51ae076ea0
23 changed files with 374 additions and 285 deletions

View File

@@ -4,9 +4,9 @@ import packedjson, packedjson/deserialiser
import types, parserutils, utils
import experimental/parser/unifiedcard
proc parseProfile(js: JsonNode; id=""): Profile =
proc parseUser(js: JsonNode; id=""): User =
if js.isNull: return
result = Profile(
result = User(
id: if id.len > 0: id else: js{"id_str"}.getStr,
username: js{"screen_name"}.getStr,
fullname: js{"name"}.getStr,
@@ -24,7 +24,17 @@ proc parseProfile(js: JsonNode; id=""): Profile =
joinDate: js{"created_at"}.getTime
)
result.expandProfileEntities(js)
result.expandUserEntities(js)
proc parseGraphUser*(js: JsonNode; id: string): User =
if js.isNull: return
with user, js{"data", "user", "result", "legacy"}:
result = parseUser(user, id)
with pinned, user{"pinned_tweet_ids_str"}:
if pinned.kind == JArray and pinned.len > 0:
result.pinnedTweet = parseBiggestInt(pinned[0].getStr)
proc parseGraphList*(js: JsonNode): List =
if js.isNull: return
@@ -45,21 +55,30 @@ proc parseGraphList*(js: JsonNode): List =
banner: list{"custom_banner_media", "media_info", "url"}.getImageStr
)
proc parseListMembers*(js: JsonNode; cursor: string): Result[Profile] =
result = Result[Profile](
proc parseGraphListMembers*(js: JsonNode; cursor: string): Result[User] =
result = Result[User](
beginning: cursor.len == 0,
query: Query(kind: userList)
)
if js.isNull: return
result.top = js{"previous_cursor_str"}.getStr
result.bottom = js{"next_cursor_str"}.getStr
if result.bottom.len == 1:
result.bottom.setLen 0
# result.top = js{"previous_cursor_str"}.getStr
# result.bottom = js{"next_cursor_str"}.getStr
# if result.bottom.len == 1:
# result.bottom.setLen 0
let root = js{"data", "list", "members_timeline", "timeline", "instructions"}
for instruction in root:
if instruction{"type"}.getStr == "TimelineAddEntries":
for entry in instruction{"entries"}:
let content = entry{"content"}
if content{"entryType"}.getStr == "TimelineTimelineItem":
with legacy, content{"itemContent", "user_results", "result", "legacy"}:
result.content.add parseUser(legacy)
elif content{"cursorType"}.getStr == "Bottom":
result.bottom = content{"value"}.getStr
for u in js{"users"}:
result.content.add parseProfile(u)
proc parsePoll(js: JsonNode): Poll =
let vals = js{"binding_values"}
@@ -206,7 +225,7 @@ proc parseTweet(js: JsonNode): Tweet =
time: js{"created_at"}.getTime,
hasThread: js{"self_thread"}.notNull,
available: true,
profile: Profile(id: js{"user_id_str"}.getStr),
user: User(id: js{"user_id_str"}.getStr),
stats: TweetStats(
replies: js{"reply_count"}.getInt,
retweets: js{"retweet_count"}.getInt,
@@ -244,7 +263,7 @@ proc parseTweet(js: JsonNode): Tweet =
of "video":
result.video = some(parseVideo(m))
with user, m{"additional_media_info", "source_user"}:
result.attribution = some(parseProfile(user))
result.attribution = some(parseUser(user))
of "animated_gif":
result.gif = some(parseGif(m))
else: discard
@@ -298,36 +317,32 @@ proc parseGlobalObjects(js: JsonNode): GlobalObjects =
users = ? js{"globalObjects", "users"}
for k, v in users:
result.users[k] = parseProfile(v, k)
result.users[k] = parseUser(v, k)
for k, v in tweets:
var tweet = parseTweet(v)
if tweet.profile.id in result.users:
tweet.profile = result.users[tweet.profile.id]
if tweet.user.id in result.users:
tweet.user = result.users[tweet.user.id]
result.tweets[k] = tweet
proc parseThread(js: JsonNode; global: GlobalObjects): tuple[thread: Chain, self: bool] =
result.thread = Chain()
for t in js{"content", "timelineModule", "items"}:
let content = t{"item", "content"}
if "Self" in content{"tweet", "displayType"}.getStr:
let thread = js{"content", "item", "content", "conversationThread"}
with cursor, thread{"showMoreCursor"}:
result.thread.cursor = cursor{"value"}.getStr
result.thread.hasMore = true
for t in thread{"conversationComponents"}:
let content = t{"conversationTweetComponent", "tweet"}
if content{"displayType"}.getStr == "SelfThread":
result.self = true
let entry = t{"entryId"}.getStr
if "show_more" in entry:
let
cursor = content{"timelineCursor"}
more = cursor{"displayTreatment", "actionText"}.getStr
result.thread.cursor = cursor{"value"}.getStr
if more.len > 0 and more[0].isDigit():
result.thread.more = parseInt(more[0 ..< more.find(" ")])
else:
result.thread.more = -1
else:
var tweet = finalizeTweet(global, t.getEntryId)
if not tweet.available:
tweet.tombstone = getTombstone(content{"tombstone"})
result.thread.content.add tweet
var tweet = finalizeTweet(global, content{"id"}.getStr)
if not tweet.available:
tweet.tombstone = getTombstone(content{"tombstone"})
result.thread.content.add tweet
proc parseConversation*(js: JsonNode; tweetId: string): Conversation =
result = Conversation(replies: Result[Chain](beginning: true))
@@ -373,8 +388,8 @@ proc parseInstructions[T](res: var Result[T]; global: GlobalObjects; js: JsonNod
elif "bottom" in r{"entryId"}.getStr:
res.bottom = r.getCursor
proc parseUsers*(js: JsonNode; after=""): Result[Profile] =
result = Result[Profile](beginning: after.len == 0)
proc parseUsers*(js: JsonNode; after=""): Result[User] =
result = Result[User](beginning: after.len == 0)
let global = parseGlobalObjects(? js)
let instructions = ? js{"timeline", "instructions"}
@@ -404,7 +419,7 @@ proc parseTimeline*(js: JsonNode; after=""): Timeline =
for e in instructions[0]{"addEntries", "entries"}:
let entry = e{"entryId"}.getStr
if "tweet" in entry or "sq-I-t" in entry or "tombstone" in entry:
if "tweet" in entry or entry.startsWith("sq-I-t") or "tombstone" in entry:
let tweet = finalizeTweet(global, e.getEntryId)
if not tweet.available: continue
result.content.add tweet
@@ -412,6 +427,12 @@ proc parseTimeline*(js: JsonNode; after=""): Timeline =
result.top = e.getCursor
elif "cursor-bottom" in entry:
result.bottom = e.getCursor
elif entry.startsWith("sq-C"):
with cursor, e{"content", "operation", "cursor"}:
if cursor{"cursorType"}.getStr == "Bottom":
result.bottom = cursor{"value"}.getStr
else:
result.top = cursor{"value"}.getStr
proc parsePhotoRail*(js: JsonNode): PhotoRail =
for tweet in js: