From 68e90344d6cd07b5655f964f19ce2dbcefb58595 Mon Sep 17 00:00:00 2001 From: Cynthia Foxwell Date: Wed, 9 Apr 2025 00:15:25 -0600 Subject: [PATCH] re-enable redis and rss im too lazy to make another sort of caching system --- public/md/about.md | 1 - src/api.nim | 2 +- src/experimental/parser/user.nim | 2 +- src/formatters.nim | 2 +- src/nitter.nim | 12 +++++----- src/parser.nim | 37 ++++++++++++++--------------- src/parserutils.nim | 4 ++-- src/redis_cache.nim | 40 ++++++++++++++++++++++---------- src/routes/activityspoof.nim | 16 ++++++------- src/routes/home.nim | 4 ++-- src/routes/list.nim | 8 +++---- src/routes/status.nim | 18 +++++++------- src/routes/timeline.nim | 33 ++++++++------------------ src/routes/twitter_api.nim | 4 ++-- src/types.nim | 10 ++++---- src/views/general.nim | 8 +++---- src/views/mastoapi.nim | 2 +- src/views/rss.nimf | 12 +++++----- src/views/status.nim | 2 +- src/views/timeline.nim | 8 +++---- 20 files changed, 113 insertions(+), 112 deletions(-) diff --git a/public/md/about.md b/public/md/about.md index 8a471af..b0d8e50 100644 --- a/public/md/about.md +++ b/public/md/about.md @@ -28,7 +28,6 @@ maintained by the community. * Image zooming/carousel (requires JavaScript) * Up to date Twitter features, e.g. Community Notes * Embeds for chat services on-par with services like [FxTwitter](https://github.com/FixTweet/FxTwitter) and [vxTwitter](https://github.com/dylanpdx/BetterTwitFix) -* No dependency on Redis, as it has caused ratelimiting issues, but also forcably disables RSS ## Why use Nitter? diff --git a/src/api.nim b/src/api.nim index 0a79071..b3e05e1 100644 --- a/src/api.nim +++ b/src/api.nim @@ -95,7 +95,7 @@ proc getGraphTweetResult*(id: string): Future[Tweet] {.async.} = js = await fetch(graphTweetResult ? params, Api.tweetResult) result = parseGraphTweetResult(js) -proc getGraphTweet(id: string; after=""): Future[Conversation] {.async.} = +proc getGraphTweet*(id: string; after=""): Future[Conversation] {.async.} = if id.len == 0: return let cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" diff --git a/src/experimental/parser/user.nim b/src/experimental/parser/user.nim index 07e0477..b52a24c 100644 --- a/src/experimental/parser/user.nim +++ b/src/experimental/parser/user.nim @@ -64,7 +64,7 @@ proc toUser*(raw: RawUser): User = ) if raw.pinnedTweetIdsStr.len > 0: - result.pinnedTweet = parseBiggestInt(raw.pinnedTweetIdsStr[0]) + result.pinnedTweet = raw.pinnedTweetIdsStr[0] result.expandUserEntities(raw) diff --git a/src/formatters.nim b/src/formatters.nim index 29931cf..1da24e8 100644 --- a/src/formatters.nim +++ b/src/formatters.nim @@ -149,7 +149,7 @@ proc getShortTime*(tweet: Tweet): string = result = "now" proc getLink*(tweet: Tweet; focus=true): string = - if tweet.id == 0: return + if tweet.id.len == 0: return var username = tweet.user.username if username.len == 0: username = "i" diff --git a/src/nitter.nim b/src/nitter.nim index 15fa8dd..1f4c7d9 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -9,7 +9,7 @@ import jester import types, config, prefs, formatters, redis_cache, http_pool import views/[general, about] import routes/[ - preferences, timeline, status, media, search, list, #rss, debug, + preferences, timeline, status, media, search, list, rss, #debug, unsupported, embed, resolver, router_utils, home, follow, twitter_api, activityspoof] @@ -36,9 +36,9 @@ setMaxHttpConns(cfg.httpMaxConns) setHttpProxy(cfg.proxy, cfg.proxyAuth) initAboutPage(cfg.staticDir) -#waitFor initRedisPool(cfg) -#stdout.write &"Connected to Redis at {cfg.redisHost}:{cfg.redisPort}\n" -#stdout.flushFile +waitFor initRedisPool(cfg) +stdout.write &"Connected to Redis at {cfg.redisHost}:{cfg.redisPort}\n" +stdout.flushFile createUnsupportedRouter(cfg) createResolverRouter(cfg) @@ -49,7 +49,7 @@ createStatusRouter(cfg) createSearchRouter(cfg) createMediaRouter(cfg) createEmbedRouter(cfg) -#createRssRouter(cfg) +createRssRouter(cfg) #createDebugRouter(cfg) createTwitterApiRouter(cfg) createActivityPubRouter(cfg) @@ -95,7 +95,7 @@ routes: extend home, "" extend follow, "" - #extend rss, "" + extend rss, "" extend status, "" extend search, "" extend timeline, "" diff --git a/src/parser.nim b/src/parser.nim index 584ff7e..13dcfee 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -205,9 +205,9 @@ proc parseCard(js: JsonNode; urls: JsonNode): Card = proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = if js.isNull: return result = Tweet( - id: js{"id_str"}.getId, - threadId: js{"conversation_id_str"}.getId, - replyId: js{"in_reply_to_status_id_str"}.getId, + id: js{"id_str"}.getStr, + threadId: js{"conversation_id_str"}.getStr, + replyId: js{"in_reply_to_status_id_str"}.getStr, replyHandle: js{"in_reply_to_screen_name"}.getStr, text: js{"full_text"}.getStr, time: js{"created_at"}.getTime, @@ -223,17 +223,17 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = ) # fix for pinned threads - if result.hasThread and result.threadId == 0: - result.threadId = js{"self_thread", "id_str"}.getId + if result.hasThread and result.threadId.len == 0: + result.threadId = js{"self_thread", "id_str"}.getStr if "retweeted_status" in js: result.retweet = some Tweet() elif js{"is_quote_status"}.getBool: - result.quote = some Tweet(id: js{"quoted_status_id_str"}.getId) + result.quote = some Tweet(id: js{"quoted_status_id_str"}.getStr) # legacy with rt, js{"retweeted_status_id_str"}: - result.retweet = some Tweet(id: rt.getId) + result.retweet = some Tweet(id: rt.getStr) return # graphql @@ -324,11 +324,10 @@ proc parseTweetSearch*(js: JsonNode; after=""): Timeline = result.content.add @[parsed] if result.content.len > 0: - result.bottom = $(result.content[^1][0].id - 1) + result.bottom = $(result.content[^1][0].id.parseBiggestInt() - 1) proc finalizeTweet(global: GlobalObjects; id: string): Tweet = - let intId = if id.len > 0: parseBiggestInt(id) else: 0 - result = global.tweets.getOrDefault(id, Tweet(id: intId)) + result = global.tweets.getOrDefault(id, Tweet(id: id)) if result.quote.isSome: let quote = get(result.quote).id @@ -463,7 +462,7 @@ proc parseGraphTweet(js: JsonNode; isLegacy=false): Tweet = jsCard["binding_values"] = values result = parseTweet(js{"legacy"}, jsCard) - result.id = js{"rest_id"}.getId + result.id = js{"rest_id"}.getStr result.user = parseGraphUser(js{"core"}) with noteTweet, js{"note_tweet", "note_tweet_results", "result"}: @@ -474,7 +473,7 @@ proc parseGraphTweet(js: JsonNode; isLegacy=false): Tweet = with communityNote, js{"birdwatch_pivot"}: let note = BirdwatchNote( - id: communityNote{"note", "rest_id"}.getId, + id: communityNote{"note", "rest_id"}.getStr, title: communityNote{"title"}.getStr, ) note.expandBirdwatchEntities(communityNote{"subtitle"}) @@ -519,16 +518,16 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = let tweet = parseGraphTweet(tweetResult, true) if not tweet.available: - tweet.id = parseBiggestInt(entryId.getId()) + tweet.id = entryId.getId() - if $tweet.id == tweetId: + if tweet.id == tweetId: result.tweet = tweet else: result.before.content.add tweet elif entryId.startsWith("tombstone"): let id = entryId.getId() let tweet = Tweet( - id: parseBiggestInt(id), + id: id, available: false, text: e{"content", "itemContent", "tombstoneInfo", "richText"}.getTombstone ) @@ -565,7 +564,7 @@ proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile = with tweetResult, e{"content", "content", "tweetResult", "result"}: let tweet = parseGraphTweet(tweetResult, false) if not tweet.available: - tweet.id = parseBiggestInt(entryId.getId()) + tweet.id = entryId.getId() result.tweets.content.add tweet elif "-conversation-" in entryId or entryId.startsWith("homeConversation"): let (thread, self) = parseGraphThread(e) @@ -580,7 +579,7 @@ proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile = with tweetResult, e{"content", "itemContent", "tweet_results", "result"}: let tweet = parseGraphTweet(tweetResult, false) if not tweet.available: - tweet.id = parseBiggestInt(entryId.getId()) + tweet.id = entryId.getId() result.tweets.content.add tweet elif "-conversation-" in entryId or entryId.startsWith("homeConversation"): let (thread, self) = parseGraphThread(e) @@ -594,7 +593,7 @@ proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile = if not tweet.available and tweet.tombstone.len == 0: let entryId = i{"entry", "entryId"}.getEntryId if entryId.len > 0: - tweet.id = parseBiggestInt(entryId) + tweet.id = entryId result.pinned = some tweet proc parseGraphUsersTimeline(timeline: JsonNode; after=""): UsersTimeline = @@ -644,7 +643,7 @@ proc parseGraphSearch*[T: User | Tweets](js: JsonNode; after=""): Result[T] = with tweetRes, e{"content", "itemContent", "tweet_results", "result"}: let tweet = parseGraphTweet(tweetRes) if not tweet.available: - tweet.id = parseBiggestInt(entryId.getId()) + tweet.id = entryId.getId() result.content.add tweet elif T is User: if entryId.startsWith("user"): diff --git a/src/parserutils.nim b/src/parserutils.nim index 0995fee..1d1fa15 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -286,7 +286,7 @@ proc expandTextEntities(tweet: Tweet; entities: JsonNode; text: string; textSlic url: "/" & name, display: mention["name"].getStr) if idx > -1 and name != replyTo: tweet.reply.delete idx - elif idx == -1 and tweet.replyId != 0: + elif idx == -1 and tweet.replyId.len != 0: tweet.reply.add name replacements.deduplicate @@ -303,7 +303,7 @@ proc expandTweetEntities*(tweet: Tweet; js: JsonNode) = hasJobCard = tweet.card.isSome and get(tweet.card).kind == jobDetails var replyTo = "" - if tweet.replyId != 0: + if tweet.replyId.len != 0: with reply, js{"in_reply_to_screen_name"}: replyTo = reply.getStr tweet.reply.add replyTo diff --git a/src/redis_cache.nim b/src/redis_cache.nim index 1d77cca..6c753e7 100644 --- a/src/redis_cache.nim +++ b/src/redis_cache.nim @@ -66,7 +66,8 @@ proc initRedisPool*(cfg: Config) {.async.} = template uidKey(name: string): string = "pid:" & $(hash(name) div 1_000_000) template userKey(name: string): string = "p:" & name template listKey(l: List): string = "l:" & l.id -template tweetKey(id: int64): string = "t:" & $id +template tweetKey(id: string): string = "t:" & id +template convKey(id: string): string = "c:" & id proc get(query: string): Future[string] {.async.} = pool.withAcquire(r): @@ -96,10 +97,15 @@ proc cache*(data: User) {.async.} = dawait r.setEx(name.userKey, baseCacheTime, compress(toFlatty(data))) proc cache*(data: Tweet) {.async.} = - if data.isNil or data.id == 0: return + if data.isNil or data.id.len == 0: return pool.withAcquire(r): dawait r.setEx(data.id.tweetKey, baseCacheTime, compress(toFlatty(data))) +proc cache*(data: Conversation) {.async.} = + if data.isNil or data.tweet.isNil or data.tweet.id.len == 0: return + pool.withAcquire(r): + dawait r.setEx(data.tweet.id.convKey, baseCacheTime, compress(toFlatty(data))) + proc cacheRss*(query: string; rss: Rss) {.async.} = let key = "rss:" & query pool.withAcquire(r): @@ -114,7 +120,13 @@ template deserialize(data, T) = except: echo "Decompression failed($#): '$#'" % [astToStr(T), data] -proc getUserId*(username: string): Future[string] {.async.} = +proc deserializeConversation(data: string): Conversation = + try: + result = fromFlatty(uncompress(data), Conversation) + except: + echo "Decompression failed(Conversation): '$#'" % [data] + +proc getCachedUserId*(username: string): Future[string] {.async.} = let name = toLower(username) pool.withAcquire(r): result = await r.hGet(name.uidKey, name) @@ -148,15 +160,19 @@ proc getCachedUsername*(userId: string): Future[string] {.async.} = if result.len > 0 and user.id.len > 0: await all(cacheUserId(result, user.id), cache(user)) -# proc getCachedTweet*(id: int64): Future[Tweet] {.async.} = -# if id == 0: return -# let tweet = await get(id.tweetKey) -# if tweet != redisNil: -# tweet.deserialize(Tweet) -# else: -# result = await getGraphTweetResult($id) -# if not result.isNil: -# await cache(result) +proc getCachedTweet*(id: string; after=""): Future[Conversation] {.async.} = + if id.len == 0: return + let tweet = await get(id.tweetKey) + + if tweet != redisNil: + result = deserializeConversation(tweet) + else: + result = await getGraphTweet(id) + if not result.isNil: + await cache(result) + + if not result.isNil and after.len > 0: + result.replies = await getReplies(id, after) proc getCachedPhotoRail*(name: string): Future[PhotoRail] {.async.} = if name.len == 0: return diff --git a/src/routes/activityspoof.nim b/src/routes/activityspoof.nim index e8e06d9..a5c6be5 100644 --- a/src/routes/activityspoof.nim +++ b/src/routes/activityspoof.nim @@ -4,7 +4,7 @@ import json, asyncdispatch, strutils, sequtils, uri, options, sugar, strformat, import jester import router_utils -import ".."/[types, formatters, api] +import ".."/[types, formatters, api, redis_cache] import ../views/[mastoapi] export json, uri, sequtils, options, sugar, times @@ -25,11 +25,11 @@ proc createActivityPubRouter*(cfg: Config) = let prefs = cookiePrefs() - let conv = await getTweet(id) + let conv = await getCachedTweet(id) if conv == nil: echo "nil conv" - if conv == nil or conv.tweet == nil or conv.tweet.id == 0: + if conv == nil or conv.tweet == nil or conv.tweet.id.len == 0: var error = "Record not found" if conv != nil and conv.tweet != nil and conv.tweet.tombstone.len > 0: error = conv.tweet.tombstone @@ -112,8 +112,8 @@ proc createActivityPubRouter*(cfg: Config) = postJson["created_at"] = %($tweet.time) postJson["edited_at"] = newJNull() postJson["reblog"] = newJNull() - if tweet.replyId != 0: - let replyUser = await getGraphUser(tweet.replyHandle) + if tweet.replyId.len != 0: + let replyUser = await getCachedUser(tweet.replyHandle) postJson["in_reply_to_id"] = %(&"{tweet.replyId}") postJson["in_reply_to_account_id"] = %replyUser.id else: @@ -170,11 +170,11 @@ proc createActivityPubRouter*(cfg: Config) = let prefs = cookiePrefs() - let conv = await getTweet(id) + let conv = await getCachedTweet(id) if conv == nil: echo "nil conv" - if conv == nil or conv.tweet == nil or conv.tweet.id == 0: + if conv == nil or conv.tweet == nil or conv.tweet.id.len == 0: var error = "Record not found" if conv != nil and conv.tweet != nil and conv.tweet.tombstone.len > 0: error = conv.tweet.tombstone @@ -192,7 +192,7 @@ proc createActivityPubRouter*(cfg: Config) = get "/users/@name": if request.headers.hasKey("Accept") and request.headers["Accept"] == "application/activity+json": - let user = await getGraphUser(@"name") + let user = await getCachedUser(@"name") if user.suspended or user.id.len == 0: resp Http404, {"Content-Type": "application/json"}, """{"error":"User not found"}""" diff --git a/src/routes/home.nim b/src/routes/home.nim index 43ff4e7..dcdbb5e 100644 --- a/src/routes/home.nim +++ b/src/routes/home.nim @@ -1,6 +1,6 @@ import jester import asyncdispatch, strutils, options, router_utils, timeline -import ".."/[prefs, types, utils] +import ".."/[prefs, types, utils, redis_cache] import ../views/[general, home, search] export home @@ -43,7 +43,7 @@ proc createHomeRouter*(cfg: Config) = query.kind = userList for name in names: - let prof = await getGraphUser(name) + let prof = await getCachedUser(name) profs &= @[prof] resp renderMain(renderFollowing(query, profs, prefs), request, cfg, prefs) diff --git a/src/routes/list.nim b/src/routes/list.nim index 0bd7305..d764788 100644 --- a/src/routes/list.nim +++ b/src/routes/list.nim @@ -4,7 +4,7 @@ import strutils, strformat, uri import jester import router_utils -import ".."/[types, api] +import ".."/[types, api, redis_cache] import ../views/[general, timeline, list] template respList*(list, timeline, title, vnode: typed) = @@ -36,7 +36,7 @@ proc createListRouter*(cfg: Config) = cond @"slug" != "memberships" let slug = decodeUrl(@"slug") - list = await getList(@"name", slug) + list = await getCachedList(@"name", slug) if list.id.len == 0: resp Http404, showError(&"""List "{@"slug"}" not found""", cfg) redirect(&"/i/lists/{list.id}") @@ -45,7 +45,7 @@ proc createListRouter*(cfg: Config) = cond '.' notin @"id" let prefs = cookiePrefs() - list = await getList(id=(@"id")) + list = await getCachedList(id=(@"id")) timeline = await getGraphListTweets(list.id, getCursor()) vnode = renderTimelineTweets(timeline, prefs, request.path) respList(list, timeline, list.title, vnode) @@ -54,6 +54,6 @@ proc createListRouter*(cfg: Config) = cond '.' notin @"id" let prefs = cookiePrefs() - list = await getList(id=(@"id")) + list = await getCachedList(id=(@"id")) members = await getGraphListMembers(list, getCursor()) respList(list, members, list.title, renderTimelineUsers(members, prefs, request.path)) diff --git a/src/routes/status.nim b/src/routes/status.nim index 23e09ff..979edf9 100644 --- a/src/routes/status.nim +++ b/src/routes/status.nim @@ -4,7 +4,7 @@ import json, asyncdispatch, strutils, sequtils, uri, options, sugar, strformat, import jester, karax/vdom import router_utils -import ".."/[types, formatters, api] +import ".."/[types, formatters, api, redis_cache] import ../views/[general, status, search, mastoapi] export json, uri, sequtils, options, sugar, times @@ -51,11 +51,11 @@ proc createStatusRouter*(cfg: Config) = let prefs = cookiePrefs() - let conv = await getTweet(id) + let conv = await getCachedTweet(id) if conv == nil: echo "nil conv" - if conv == nil or conv.tweet == nil or conv.tweet.id == 0: + if conv == nil or conv.tweet == nil or conv.tweet.id.len == 0: var error = "Record not found" if conv != nil and conv.tweet != nil and conv.tweet.tombstone.len > 0: error = conv.tweet.tombstone @@ -81,11 +81,11 @@ proc createStatusRouter*(cfg: Config) = resp Http404, "" resp $renderReplies(replies, prefs, getPath()) - let conv = await getTweet(id, getCursor()) + let conv = await getCachedTweet(id, getCursor()) if conv == nil: echo "nil conv" - if conv == nil or conv.tweet == nil or conv.tweet.id == 0: + if conv == nil or conv.tweet == nil or conv.tweet.id.len == 0: var error = "Tweet not found" if conv != nil and conv.tweet != nil and conv.tweet.tombstone.len > 0: error = conv.tweet.tombstone @@ -109,15 +109,15 @@ proc createStatusRouter*(cfg: Config) = let quote = get(tweet.quote) quoteUser = quote.user - if tweet.replyId != 0: - let replyUser = await getGraphUser(tweet.replyHandle) + if tweet.replyId.len != 0: + let replyUser = await getCachedUser(tweet.replyHandle) context = &"↩ {replyUser.fullname} (@{tweet.replyHandle})\nā†˜ {quoteUser.fullname} (@{quoteUser.username})" contextUrl = &"{getUrlPrefix(cfg)}/i/status/{tweet.replyId}" else: context = &"ā†˜ {quoteUser.fullname} (@{quoteUser.username})" contextUrl = &"{getUrlPrefix(cfg)}/i/status/{quote.id}" - elif tweet.replyId != 0: - let replyUser = await getGraphUser(tweet.replyHandle) + elif tweet.replyId.len != 0: + let replyUser = await getCachedUser(tweet.replyHandle) context = &"↩ {replyUser.fullname} (@{tweet.replyHandle})" contextUrl = &"{getUrlPrefix(cfg)}/i/status/{tweet.replyId}" diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index df0d4b2..a8967d9 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -3,13 +3,13 @@ import asyncdispatch, strutils, sequtils, uri, options, times, json import jester, karax/vdom import router_utils -import ".."/[types, formatters, query, api] +import ".."/[types, formatters, query, api, redis_cache] import ../views/[general, profile, timeline, status, search, mastoapi] export vdom export uri, sequtils, json export router_utils -export formatters, query, api +export formatters, query, api, redis_cache export profile, timeline, status, mastoapi proc getQuery*(request: Request; tab, name: string): Query = @@ -28,24 +28,11 @@ template skipIf[T](cond: bool; default; body: Future[T]): Future[T] = else: body -proc getUserId(username: string): Future[string] {.async.} = - let user = await getGraphUser(username) - if user.suspended: - return "suspended" - else: - return user.id - - -proc getUsername*(userId: string): Future[string] {.async.} = - let user = await getGraphUserById(userId) - result = user.username - - proc fetchProfile*(after: string; query: Query; cfg: Config; skipRail=false; skipPinned=false): Future[Profile] {.async.} = let name = query.fromUser[0] - userId = await getUserId(name) + userId = await getCachedUserId(name) if userId.len == 0: return Profile(user: User(username: name)) @@ -61,9 +48,9 @@ proc fetchProfile*(after: string; query: Query; cfg: Config; skipRail=false; let rail = skipIf(skipRail or query.kind == media, @[]): - getPhotoRail(name) + getCachedPhotoRail(name) - user = getGraphUser(name) + user = getCachedUser(name) result = case query.kind @@ -107,7 +94,7 @@ template respTimeline*(timeline: typed) = template respUserId*() = cond @"user_id".len > 0 - let username = await getUsername(@"user_id") + let username = await getCachedUsername(@"user_id") if username.len > 0: redirect("/" & username) else: @@ -133,17 +120,17 @@ proc createTimelineRouter*(cfg: Config) = case tab: of "followers": - resp renderMain(renderUserList(await getGraphFollowers(await getUserId(@"name"), getCursor()), prefs), request, cfg, prefs) + resp renderMain(renderUserList(await getGraphFollowers(await getCachedUserId(@"name"), getCursor()), prefs), request, cfg, prefs) of "following": - resp renderMain(renderUserList(await getGraphFollowing(await getUserId(@"name"), getCursor()), prefs), request, cfg, prefs) + resp renderMain(renderUserList(await getGraphFollowing(await getCachedUserId(@"name"), getCursor()), prefs), request, cfg, prefs) else: if request.headers.hasKey("Accept") and request.headers["Accept"] == "application/activity+json": - let userId = await getUserId(@"name") + let userId = await getCachedUserId(@"name") if userId == "suspended" or userId.len == 0: resp Http404, {"Content-Type": "application/json"}, """{"error":"User not found"}""" - let user = await getGraphUser(@"name") + let user = await getCachedUser(@"name") let userJson = getActivityStream(user, cfg, prefs) diff --git a/src/routes/twitter_api.nim b/src/routes/twitter_api.nim index 666c8a8..092ebd8 100644 --- a/src/routes/twitter_api.nim +++ b/src/routes/twitter_api.nim @@ -4,7 +4,7 @@ import json, asyncdispatch, options, uri import times import jester import router_utils -import ".."/[types, api, apiutils, query, consts] +import ".."/[types, api, apiutils, query, consts, redis_cache] import httpclient, strutils import sequtils @@ -52,7 +52,7 @@ proc tweetToJson*(t: Tweet): JsonNode = result["photos"] = %t.photos proc getUserProfileJson*(username: string): Future[JsonNode] {.async.} = - let user: User = await getGraphUser(username) + let user: User = await getCachedUser(username) let response: JsonNode = %*{ "id": user.id, "username": user.username diff --git a/src/types.nim b/src/types.nim index fdc1eef..3f3b48d 100644 --- a/src/types.nim +++ b/src/types.nim @@ -89,7 +89,7 @@ type bio*: string userPic*: string banner*: string - pinnedTweet*: int64 + pinnedTweet*: string following*: int followers*: int tweets*: int @@ -203,14 +203,14 @@ type quotes*: int BirdwatchNote* = ref object - id*: int64 + id*: string title*: string text*: string Tweet* = ref object - id*: int64 - threadId*: int64 - replyId*: int64 + id*: string + threadId*: string + replyId*: string user*: User text*: string time*: DateTime diff --git a/src/views/general.nim b/src/views/general.nim index 940e300..c577fcd 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -29,8 +29,8 @@ proc renderNavbar(cfg: Config; req: Request; rss, canonical: string): VNode = tdiv(class="nav-item right"): icon "search", title="Search", href="/search" - #if cfg.enableRss and rss.len > 0: - #icon "rss-feed", title="RSS Feed", href=rss + if cfg.enableRss and rss.len > 0: + icon "rss-feed", title="RSS Feed", href=rss icon "bird", title="Open in Twitter", href=canonical a(href="https://liberapay.com/zedeus"): verbatim lp icon "info", title="About", href="/about" @@ -75,8 +75,8 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; if canonical.len > 0: link(rel="canonical", href=canonical) - #if cfg.enableRss and rss.len > 0: - #link(rel="alternate", type="application/rss+xml", href=rss, title="RSS feed") + if cfg.enableRss and rss.len > 0: + link(rel="alternate", type="application/rss+xml", href=rss, title="RSS feed") if prefs.hlsPlayback: script(src="/js/hls.light.min.js", `defer`="") diff --git a/src/views/mastoapi.nim b/src/views/mastoapi.nim index 2198aad..e075de8 100644 --- a/src/views/mastoapi.nim +++ b/src/views/mastoapi.nim @@ -116,7 +116,7 @@ proc getActivityStream*(tweet: Tweet, cfg: Config, prefs: Prefs): JsonNode = postJson["id"] = %tweetUrl postJson["type"] = %"Note" postJson["summary"] = newJNull() - if tweet.replyId != 0: + if tweet.replyId.len != 0: let replyUrl = &"{getUrlPrefix(cfg)}/i/status/{tweet.replyId}" postJson["inReplyTo"] = %replyUrl postJson["inReplyToAtomUri"] = %replyUrl diff --git a/src/views/rss.nimf b/src/views/rss.nimf index 1d40446..cdfcd63 100644 --- a/src/views/rss.nimf +++ b/src/views/rss.nimf @@ -35,7 +35,7 @@ Twitter feed for: ${desc}. Generated by ${cfg.hostname}

${text.replace("\n", "
\n")}

#if tweet.photos.len > 0: # for photo in tweet.photos: - + # end for #elif tweet.video.isSome: @@ -44,11 +44,11 @@ Twitter feed for: ${desc}. Generated by ${cfg.hostname} # let url = &"{urlPrefix}{getPicUrl(get(tweet.gif).url)}" -#elif tweet.card.isSome: -# let card = tweet.card.get() -# if card.image.len > 0: - -# end if +##elif tweet.card.isSome: +## let card = tweet.card.get() +## if card.image.len > 0: +## +## end if #end if #if tweet.quote.isSome and get(tweet.quote).available: # let quoteLink = getLink(get(tweet.quote)) diff --git a/src/views/status.nim b/src/views/status.nim index 71c2c67..c3adb3d 100644 --- a/src/views/status.nim +++ b/src/views/status.nim @@ -45,7 +45,7 @@ proc renderConversation*(conv: Conversation; prefs: Prefs; path: string): VNode if conv.before.content.len > 0: tdiv(class="before-tweet thread-line"): let first = conv.before.content[0] - if threadId != first.id and (first.replyId > 0 or not first.available): + if threadId != first.id and (first.replyId.len > 0 or not first.available): renderEarlier(conv.before) for i, tweet in conv.before.content: renderTweet(tweet, prefs, path, index=i) diff --git a/src/views/timeline.nim b/src/views/timeline.nim index 305129b..34a6e4e 100644 --- a/src/views/timeline.nim +++ b/src/views/timeline.nim @@ -55,7 +55,7 @@ proc renderThread(thread: Tweets; prefs: Prefs; path: string): VNode = renderTweet(tweet, prefs, path, class=(header & "thread"), index=i, last=(i == thread.high), showThread=show) -proc threadFilter(tweets: openArray[Tweet]; threads: openArray[int64]; it: Tweet): seq[Tweet] = +proc threadFilter(tweets: openArray[Tweet]; threads: openArray[string]; it: Tweet): seq[Tweet] = result = @[it] if it.retweet.isSome or it.replyId in threads: return for t in tweets: @@ -112,20 +112,20 @@ proc renderTimelineTweets*(results: Timeline; prefs: Prefs; path: string; else: renderNoneFound() else: - var retweets: seq[int64] + var retweets: seq[string] for thread in results.content: if thread.len == 1: let tweet = thread[0] - retweetId = if tweet.retweet.isSome: get(tweet.retweet).id else: 0 + retweetId = if tweet.retweet.isSome: get(tweet.retweet).id else: "" if retweetId in retweets or tweet.id in retweets or tweet.pinned and prefs.hidePins: continue var hasThread = tweet.hasThread - if retweetId != 0 and tweet.retweet.isSome: + if retweetId.len != 0 and tweet.retweet.isSome: retweets &= retweetId hasThread = get(tweet.retweet).hasThread renderTweet(tweet, prefs, path, showThread=hasThread)