re-enable redis and rss im too lazy to make another sort of caching system
This commit is contained in:
parent
91d3f4138c
commit
68e90344d6
@ -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?
|
||||
|
||||
|
||||
@ -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: ""
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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, ""
|
||||
|
||||
@ -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"):
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"}"""
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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}"
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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`="")
|
||||
|
||||
@ -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
|
||||
|
||||
@ -35,7 +35,7 @@ Twitter feed for: ${desc}. Generated by ${cfg.hostname}
|
||||
<p>${text.replace("\n", "<br>\n")}</p>
|
||||
#if tweet.photos.len > 0:
|
||||
# for photo in tweet.photos:
|
||||
<img src="${urlPrefix}${getPicUrl(photo)}" style="max-width:250px;" />
|
||||
<img src="${urlPrefix}${getPicUrl(photo.url)}" style="max-width:250px;" />
|
||||
# end for
|
||||
#elif tweet.video.isSome:
|
||||
<img src="${urlPrefix}${getPicUrl(get(tweet.video).thumb)}" style="max-width:250px;" />
|
||||
@ -44,11 +44,11 @@ Twitter feed for: ${desc}. Generated by ${cfg.hostname}
|
||||
# let url = &"{urlPrefix}{getPicUrl(get(tweet.gif).url)}"
|
||||
<video poster="${thumb}" autoplay muted loop style="max-width:250px;">
|
||||
<source src="${url}" type="video/mp4"></video>
|
||||
#elif tweet.card.isSome:
|
||||
# let card = tweet.card.get()
|
||||
# if card.image.len > 0:
|
||||
<img src="${urlPrefix}${getPicUrl(card.image)}" style="max-width:250px;" />
|
||||
# end if
|
||||
##elif tweet.card.isSome:
|
||||
## let card = tweet.card.get()
|
||||
## if card.image.len > 0:
|
||||
##<img src="${urlPrefix}${getPicUrl(card.image)}" style="max-width:250px;" />
|
||||
## end if
|
||||
#end if
|
||||
#if tweet.quote.isSome and get(tweet.quote).available:
|
||||
# let quoteLink = getLink(get(tweet.quote))
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user