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)
|
* Image zooming/carousel (requires JavaScript)
|
||||||
* Up to date Twitter features, e.g. Community Notes
|
* 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)
|
* 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?
|
## Why use Nitter?
|
||||||
|
|
||||||
|
|||||||
@ -95,7 +95,7 @@ proc getGraphTweetResult*(id: string): Future[Tweet] {.async.} =
|
|||||||
js = await fetch(graphTweetResult ? params, Api.tweetResult)
|
js = await fetch(graphTweetResult ? params, Api.tweetResult)
|
||||||
result = parseGraphTweetResult(js)
|
result = parseGraphTweetResult(js)
|
||||||
|
|
||||||
proc getGraphTweet(id: string; after=""): Future[Conversation] {.async.} =
|
proc getGraphTweet*(id: string; after=""): Future[Conversation] {.async.} =
|
||||||
if id.len == 0: return
|
if id.len == 0: return
|
||||||
let
|
let
|
||||||
cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: ""
|
cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: ""
|
||||||
|
|||||||
@ -64,7 +64,7 @@ proc toUser*(raw: RawUser): User =
|
|||||||
)
|
)
|
||||||
|
|
||||||
if raw.pinnedTweetIdsStr.len > 0:
|
if raw.pinnedTweetIdsStr.len > 0:
|
||||||
result.pinnedTweet = parseBiggestInt(raw.pinnedTweetIdsStr[0])
|
result.pinnedTweet = raw.pinnedTweetIdsStr[0]
|
||||||
|
|
||||||
result.expandUserEntities(raw)
|
result.expandUserEntities(raw)
|
||||||
|
|
||||||
|
|||||||
@ -149,7 +149,7 @@ proc getShortTime*(tweet: Tweet): string =
|
|||||||
result = "now"
|
result = "now"
|
||||||
|
|
||||||
proc getLink*(tweet: Tweet; focus=true): string =
|
proc getLink*(tweet: Tweet; focus=true): string =
|
||||||
if tweet.id == 0: return
|
if tweet.id.len == 0: return
|
||||||
var username = tweet.user.username
|
var username = tweet.user.username
|
||||||
if username.len == 0:
|
if username.len == 0:
|
||||||
username = "i"
|
username = "i"
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import jester
|
|||||||
import types, config, prefs, formatters, redis_cache, http_pool
|
import types, config, prefs, formatters, redis_cache, http_pool
|
||||||
import views/[general, about]
|
import views/[general, about]
|
||||||
import routes/[
|
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,
|
unsupported, embed, resolver, router_utils, home, follow, twitter_api,
|
||||||
activityspoof]
|
activityspoof]
|
||||||
|
|
||||||
@ -36,9 +36,9 @@ setMaxHttpConns(cfg.httpMaxConns)
|
|||||||
setHttpProxy(cfg.proxy, cfg.proxyAuth)
|
setHttpProxy(cfg.proxy, cfg.proxyAuth)
|
||||||
initAboutPage(cfg.staticDir)
|
initAboutPage(cfg.staticDir)
|
||||||
|
|
||||||
#waitFor initRedisPool(cfg)
|
waitFor initRedisPool(cfg)
|
||||||
#stdout.write &"Connected to Redis at {cfg.redisHost}:{cfg.redisPort}\n"
|
stdout.write &"Connected to Redis at {cfg.redisHost}:{cfg.redisPort}\n"
|
||||||
#stdout.flushFile
|
stdout.flushFile
|
||||||
|
|
||||||
createUnsupportedRouter(cfg)
|
createUnsupportedRouter(cfg)
|
||||||
createResolverRouter(cfg)
|
createResolverRouter(cfg)
|
||||||
@ -49,7 +49,7 @@ createStatusRouter(cfg)
|
|||||||
createSearchRouter(cfg)
|
createSearchRouter(cfg)
|
||||||
createMediaRouter(cfg)
|
createMediaRouter(cfg)
|
||||||
createEmbedRouter(cfg)
|
createEmbedRouter(cfg)
|
||||||
#createRssRouter(cfg)
|
createRssRouter(cfg)
|
||||||
#createDebugRouter(cfg)
|
#createDebugRouter(cfg)
|
||||||
createTwitterApiRouter(cfg)
|
createTwitterApiRouter(cfg)
|
||||||
createActivityPubRouter(cfg)
|
createActivityPubRouter(cfg)
|
||||||
@ -95,7 +95,7 @@ routes:
|
|||||||
|
|
||||||
extend home, ""
|
extend home, ""
|
||||||
extend follow, ""
|
extend follow, ""
|
||||||
#extend rss, ""
|
extend rss, ""
|
||||||
extend status, ""
|
extend status, ""
|
||||||
extend search, ""
|
extend search, ""
|
||||||
extend timeline, ""
|
extend timeline, ""
|
||||||
|
|||||||
@ -205,9 +205,9 @@ proc parseCard(js: JsonNode; urls: JsonNode): Card =
|
|||||||
proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet =
|
proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet =
|
||||||
if js.isNull: return
|
if js.isNull: return
|
||||||
result = Tweet(
|
result = Tweet(
|
||||||
id: js{"id_str"}.getId,
|
id: js{"id_str"}.getStr,
|
||||||
threadId: js{"conversation_id_str"}.getId,
|
threadId: js{"conversation_id_str"}.getStr,
|
||||||
replyId: js{"in_reply_to_status_id_str"}.getId,
|
replyId: js{"in_reply_to_status_id_str"}.getStr,
|
||||||
replyHandle: js{"in_reply_to_screen_name"}.getStr,
|
replyHandle: js{"in_reply_to_screen_name"}.getStr,
|
||||||
text: js{"full_text"}.getStr,
|
text: js{"full_text"}.getStr,
|
||||||
time: js{"created_at"}.getTime,
|
time: js{"created_at"}.getTime,
|
||||||
@ -223,17 +223,17 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet =
|
|||||||
)
|
)
|
||||||
|
|
||||||
# fix for pinned threads
|
# fix for pinned threads
|
||||||
if result.hasThread and result.threadId == 0:
|
if result.hasThread and result.threadId.len == 0:
|
||||||
result.threadId = js{"self_thread", "id_str"}.getId
|
result.threadId = js{"self_thread", "id_str"}.getStr
|
||||||
|
|
||||||
if "retweeted_status" in js:
|
if "retweeted_status" in js:
|
||||||
result.retweet = some Tweet()
|
result.retweet = some Tweet()
|
||||||
elif js{"is_quote_status"}.getBool:
|
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
|
# legacy
|
||||||
with rt, js{"retweeted_status_id_str"}:
|
with rt, js{"retweeted_status_id_str"}:
|
||||||
result.retweet = some Tweet(id: rt.getId)
|
result.retweet = some Tweet(id: rt.getStr)
|
||||||
return
|
return
|
||||||
|
|
||||||
# graphql
|
# graphql
|
||||||
@ -324,11 +324,10 @@ proc parseTweetSearch*(js: JsonNode; after=""): Timeline =
|
|||||||
result.content.add @[parsed]
|
result.content.add @[parsed]
|
||||||
|
|
||||||
if result.content.len > 0:
|
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 =
|
proc finalizeTweet(global: GlobalObjects; id: string): Tweet =
|
||||||
let intId = if id.len > 0: parseBiggestInt(id) else: 0
|
result = global.tweets.getOrDefault(id, Tweet(id: id))
|
||||||
result = global.tweets.getOrDefault(id, Tweet(id: intId))
|
|
||||||
|
|
||||||
if result.quote.isSome:
|
if result.quote.isSome:
|
||||||
let quote = get(result.quote).id
|
let quote = get(result.quote).id
|
||||||
@ -463,7 +462,7 @@ proc parseGraphTweet(js: JsonNode; isLegacy=false): Tweet =
|
|||||||
jsCard["binding_values"] = values
|
jsCard["binding_values"] = values
|
||||||
|
|
||||||
result = parseTweet(js{"legacy"}, jsCard)
|
result = parseTweet(js{"legacy"}, jsCard)
|
||||||
result.id = js{"rest_id"}.getId
|
result.id = js{"rest_id"}.getStr
|
||||||
result.user = parseGraphUser(js{"core"})
|
result.user = parseGraphUser(js{"core"})
|
||||||
|
|
||||||
with noteTweet, js{"note_tweet", "note_tweet_results", "result"}:
|
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"}:
|
with communityNote, js{"birdwatch_pivot"}:
|
||||||
let note = BirdwatchNote(
|
let note = BirdwatchNote(
|
||||||
id: communityNote{"note", "rest_id"}.getId,
|
id: communityNote{"note", "rest_id"}.getStr,
|
||||||
title: communityNote{"title"}.getStr,
|
title: communityNote{"title"}.getStr,
|
||||||
)
|
)
|
||||||
note.expandBirdwatchEntities(communityNote{"subtitle"})
|
note.expandBirdwatchEntities(communityNote{"subtitle"})
|
||||||
@ -519,16 +518,16 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation =
|
|||||||
let tweet = parseGraphTweet(tweetResult, true)
|
let tweet = parseGraphTweet(tweetResult, true)
|
||||||
|
|
||||||
if not tweet.available:
|
if not tweet.available:
|
||||||
tweet.id = parseBiggestInt(entryId.getId())
|
tweet.id = entryId.getId()
|
||||||
|
|
||||||
if $tweet.id == tweetId:
|
if tweet.id == tweetId:
|
||||||
result.tweet = tweet
|
result.tweet = tweet
|
||||||
else:
|
else:
|
||||||
result.before.content.add tweet
|
result.before.content.add tweet
|
||||||
elif entryId.startsWith("tombstone"):
|
elif entryId.startsWith("tombstone"):
|
||||||
let id = entryId.getId()
|
let id = entryId.getId()
|
||||||
let tweet = Tweet(
|
let tweet = Tweet(
|
||||||
id: parseBiggestInt(id),
|
id: id,
|
||||||
available: false,
|
available: false,
|
||||||
text: e{"content", "itemContent", "tombstoneInfo", "richText"}.getTombstone
|
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"}:
|
with tweetResult, e{"content", "content", "tweetResult", "result"}:
|
||||||
let tweet = parseGraphTweet(tweetResult, false)
|
let tweet = parseGraphTweet(tweetResult, false)
|
||||||
if not tweet.available:
|
if not tweet.available:
|
||||||
tweet.id = parseBiggestInt(entryId.getId())
|
tweet.id = entryId.getId()
|
||||||
result.tweets.content.add tweet
|
result.tweets.content.add tweet
|
||||||
elif "-conversation-" in entryId or entryId.startsWith("homeConversation"):
|
elif "-conversation-" in entryId or entryId.startsWith("homeConversation"):
|
||||||
let (thread, self) = parseGraphThread(e)
|
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"}:
|
with tweetResult, e{"content", "itemContent", "tweet_results", "result"}:
|
||||||
let tweet = parseGraphTweet(tweetResult, false)
|
let tweet = parseGraphTweet(tweetResult, false)
|
||||||
if not tweet.available:
|
if not tweet.available:
|
||||||
tweet.id = parseBiggestInt(entryId.getId())
|
tweet.id = entryId.getId()
|
||||||
result.tweets.content.add tweet
|
result.tweets.content.add tweet
|
||||||
elif "-conversation-" in entryId or entryId.startsWith("homeConversation"):
|
elif "-conversation-" in entryId or entryId.startsWith("homeConversation"):
|
||||||
let (thread, self) = parseGraphThread(e)
|
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:
|
if not tweet.available and tweet.tombstone.len == 0:
|
||||||
let entryId = i{"entry", "entryId"}.getEntryId
|
let entryId = i{"entry", "entryId"}.getEntryId
|
||||||
if entryId.len > 0:
|
if entryId.len > 0:
|
||||||
tweet.id = parseBiggestInt(entryId)
|
tweet.id = entryId
|
||||||
result.pinned = some tweet
|
result.pinned = some tweet
|
||||||
|
|
||||||
proc parseGraphUsersTimeline(timeline: JsonNode; after=""): UsersTimeline =
|
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"}:
|
with tweetRes, e{"content", "itemContent", "tweet_results", "result"}:
|
||||||
let tweet = parseGraphTweet(tweetRes)
|
let tweet = parseGraphTweet(tweetRes)
|
||||||
if not tweet.available:
|
if not tweet.available:
|
||||||
tweet.id = parseBiggestInt(entryId.getId())
|
tweet.id = entryId.getId()
|
||||||
result.content.add tweet
|
result.content.add tweet
|
||||||
elif T is User:
|
elif T is User:
|
||||||
if entryId.startsWith("user"):
|
if entryId.startsWith("user"):
|
||||||
|
|||||||
@ -286,7 +286,7 @@ proc expandTextEntities(tweet: Tweet; entities: JsonNode; text: string; textSlic
|
|||||||
url: "/" & name, display: mention["name"].getStr)
|
url: "/" & name, display: mention["name"].getStr)
|
||||||
if idx > -1 and name != replyTo:
|
if idx > -1 and name != replyTo:
|
||||||
tweet.reply.delete idx
|
tweet.reply.delete idx
|
||||||
elif idx == -1 and tweet.replyId != 0:
|
elif idx == -1 and tweet.replyId.len != 0:
|
||||||
tweet.reply.add name
|
tweet.reply.add name
|
||||||
|
|
||||||
replacements.deduplicate
|
replacements.deduplicate
|
||||||
@ -303,7 +303,7 @@ proc expandTweetEntities*(tweet: Tweet; js: JsonNode) =
|
|||||||
hasJobCard = tweet.card.isSome and get(tweet.card).kind == jobDetails
|
hasJobCard = tweet.card.isSome and get(tweet.card).kind == jobDetails
|
||||||
|
|
||||||
var replyTo = ""
|
var replyTo = ""
|
||||||
if tweet.replyId != 0:
|
if tweet.replyId.len != 0:
|
||||||
with reply, js{"in_reply_to_screen_name"}:
|
with reply, js{"in_reply_to_screen_name"}:
|
||||||
replyTo = reply.getStr
|
replyTo = reply.getStr
|
||||||
tweet.reply.add replyTo
|
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 uidKey(name: string): string = "pid:" & $(hash(name) div 1_000_000)
|
||||||
template userKey(name: string): string = "p:" & name
|
template userKey(name: string): string = "p:" & name
|
||||||
template listKey(l: List): string = "l:" & l.id
|
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.} =
|
proc get(query: string): Future[string] {.async.} =
|
||||||
pool.withAcquire(r):
|
pool.withAcquire(r):
|
||||||
@ -96,10 +97,15 @@ proc cache*(data: User) {.async.} =
|
|||||||
dawait r.setEx(name.userKey, baseCacheTime, compress(toFlatty(data)))
|
dawait r.setEx(name.userKey, baseCacheTime, compress(toFlatty(data)))
|
||||||
|
|
||||||
proc cache*(data: Tweet) {.async.} =
|
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):
|
pool.withAcquire(r):
|
||||||
dawait r.setEx(data.id.tweetKey, baseCacheTime, compress(toFlatty(data)))
|
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.} =
|
proc cacheRss*(query: string; rss: Rss) {.async.} =
|
||||||
let key = "rss:" & query
|
let key = "rss:" & query
|
||||||
pool.withAcquire(r):
|
pool.withAcquire(r):
|
||||||
@ -114,7 +120,13 @@ template deserialize(data, T) =
|
|||||||
except:
|
except:
|
||||||
echo "Decompression failed($#): '$#'" % [astToStr(T), data]
|
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)
|
let name = toLower(username)
|
||||||
pool.withAcquire(r):
|
pool.withAcquire(r):
|
||||||
result = await r.hGet(name.uidKey, name)
|
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:
|
if result.len > 0 and user.id.len > 0:
|
||||||
await all(cacheUserId(result, user.id), cache(user))
|
await all(cacheUserId(result, user.id), cache(user))
|
||||||
|
|
||||||
# proc getCachedTweet*(id: int64): Future[Tweet] {.async.} =
|
proc getCachedTweet*(id: string; after=""): Future[Conversation] {.async.} =
|
||||||
# if id == 0: return
|
if id.len == 0: return
|
||||||
# let tweet = await get(id.tweetKey)
|
let tweet = await get(id.tweetKey)
|
||||||
# if tweet != redisNil:
|
|
||||||
# tweet.deserialize(Tweet)
|
if tweet != redisNil:
|
||||||
# else:
|
result = deserializeConversation(tweet)
|
||||||
# result = await getGraphTweetResult($id)
|
else:
|
||||||
# if not result.isNil:
|
result = await getGraphTweet(id)
|
||||||
# await cache(result)
|
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.} =
|
proc getCachedPhotoRail*(name: string): Future[PhotoRail] {.async.} =
|
||||||
if name.len == 0: return
|
if name.len == 0: return
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import json, asyncdispatch, strutils, sequtils, uri, options, sugar, strformat,
|
|||||||
import jester
|
import jester
|
||||||
|
|
||||||
import router_utils
|
import router_utils
|
||||||
import ".."/[types, formatters, api]
|
import ".."/[types, formatters, api, redis_cache]
|
||||||
import ../views/[mastoapi]
|
import ../views/[mastoapi]
|
||||||
|
|
||||||
export json, uri, sequtils, options, sugar, times
|
export json, uri, sequtils, options, sugar, times
|
||||||
@ -25,11 +25,11 @@ proc createActivityPubRouter*(cfg: Config) =
|
|||||||
|
|
||||||
let prefs = cookiePrefs()
|
let prefs = cookiePrefs()
|
||||||
|
|
||||||
let conv = await getTweet(id)
|
let conv = await getCachedTweet(id)
|
||||||
if conv == nil:
|
if conv == nil:
|
||||||
echo "nil conv"
|
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"
|
var error = "Record not found"
|
||||||
if conv != nil and conv.tweet != nil and conv.tweet.tombstone.len > 0:
|
if conv != nil and conv.tweet != nil and conv.tweet.tombstone.len > 0:
|
||||||
error = conv.tweet.tombstone
|
error = conv.tweet.tombstone
|
||||||
@ -112,8 +112,8 @@ proc createActivityPubRouter*(cfg: Config) =
|
|||||||
postJson["created_at"] = %($tweet.time)
|
postJson["created_at"] = %($tweet.time)
|
||||||
postJson["edited_at"] = newJNull()
|
postJson["edited_at"] = newJNull()
|
||||||
postJson["reblog"] = newJNull()
|
postJson["reblog"] = newJNull()
|
||||||
if tweet.replyId != 0:
|
if tweet.replyId.len != 0:
|
||||||
let replyUser = await getGraphUser(tweet.replyHandle)
|
let replyUser = await getCachedUser(tweet.replyHandle)
|
||||||
postJson["in_reply_to_id"] = %(&"{tweet.replyId}")
|
postJson["in_reply_to_id"] = %(&"{tweet.replyId}")
|
||||||
postJson["in_reply_to_account_id"] = %replyUser.id
|
postJson["in_reply_to_account_id"] = %replyUser.id
|
||||||
else:
|
else:
|
||||||
@ -170,11 +170,11 @@ proc createActivityPubRouter*(cfg: Config) =
|
|||||||
|
|
||||||
let prefs = cookiePrefs()
|
let prefs = cookiePrefs()
|
||||||
|
|
||||||
let conv = await getTweet(id)
|
let conv = await getCachedTweet(id)
|
||||||
if conv == nil:
|
if conv == nil:
|
||||||
echo "nil conv"
|
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"
|
var error = "Record not found"
|
||||||
if conv != nil and conv.tweet != nil and conv.tweet.tombstone.len > 0:
|
if conv != nil and conv.tweet != nil and conv.tweet.tombstone.len > 0:
|
||||||
error = conv.tweet.tombstone
|
error = conv.tweet.tombstone
|
||||||
@ -192,7 +192,7 @@ proc createActivityPubRouter*(cfg: Config) =
|
|||||||
|
|
||||||
get "/users/@name":
|
get "/users/@name":
|
||||||
if request.headers.hasKey("Accept") and request.headers["Accept"] == "application/activity+json":
|
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:
|
if user.suspended or user.id.len == 0:
|
||||||
resp Http404, {"Content-Type": "application/json"}, """{"error":"User not found"}"""
|
resp Http404, {"Content-Type": "application/json"}, """{"error":"User not found"}"""
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import jester
|
import jester
|
||||||
import asyncdispatch, strutils, options, router_utils, timeline
|
import asyncdispatch, strutils, options, router_utils, timeline
|
||||||
import ".."/[prefs, types, utils]
|
import ".."/[prefs, types, utils, redis_cache]
|
||||||
import ../views/[general, home, search]
|
import ../views/[general, home, search]
|
||||||
|
|
||||||
export home
|
export home
|
||||||
@ -43,7 +43,7 @@ proc createHomeRouter*(cfg: Config) =
|
|||||||
query.kind = userList
|
query.kind = userList
|
||||||
|
|
||||||
for name in names:
|
for name in names:
|
||||||
let prof = await getGraphUser(name)
|
let prof = await getCachedUser(name)
|
||||||
profs &= @[prof]
|
profs &= @[prof]
|
||||||
|
|
||||||
resp renderMain(renderFollowing(query, profs, prefs), request, cfg, prefs)
|
resp renderMain(renderFollowing(query, profs, prefs), request, cfg, prefs)
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import strutils, strformat, uri
|
|||||||
import jester
|
import jester
|
||||||
|
|
||||||
import router_utils
|
import router_utils
|
||||||
import ".."/[types, api]
|
import ".."/[types, api, redis_cache]
|
||||||
import ../views/[general, timeline, list]
|
import ../views/[general, timeline, list]
|
||||||
|
|
||||||
template respList*(list, timeline, title, vnode: typed) =
|
template respList*(list, timeline, title, vnode: typed) =
|
||||||
@ -36,7 +36,7 @@ proc createListRouter*(cfg: Config) =
|
|||||||
cond @"slug" != "memberships"
|
cond @"slug" != "memberships"
|
||||||
let
|
let
|
||||||
slug = decodeUrl(@"slug")
|
slug = decodeUrl(@"slug")
|
||||||
list = await getList(@"name", slug)
|
list = await getCachedList(@"name", slug)
|
||||||
if list.id.len == 0:
|
if list.id.len == 0:
|
||||||
resp Http404, showError(&"""List "{@"slug"}" not found""", cfg)
|
resp Http404, showError(&"""List "{@"slug"}" not found""", cfg)
|
||||||
redirect(&"/i/lists/{list.id}")
|
redirect(&"/i/lists/{list.id}")
|
||||||
@ -45,7 +45,7 @@ proc createListRouter*(cfg: Config) =
|
|||||||
cond '.' notin @"id"
|
cond '.' notin @"id"
|
||||||
let
|
let
|
||||||
prefs = cookiePrefs()
|
prefs = cookiePrefs()
|
||||||
list = await getList(id=(@"id"))
|
list = await getCachedList(id=(@"id"))
|
||||||
timeline = await getGraphListTweets(list.id, getCursor())
|
timeline = await getGraphListTweets(list.id, getCursor())
|
||||||
vnode = renderTimelineTweets(timeline, prefs, request.path)
|
vnode = renderTimelineTweets(timeline, prefs, request.path)
|
||||||
respList(list, timeline, list.title, vnode)
|
respList(list, timeline, list.title, vnode)
|
||||||
@ -54,6 +54,6 @@ proc createListRouter*(cfg: Config) =
|
|||||||
cond '.' notin @"id"
|
cond '.' notin @"id"
|
||||||
let
|
let
|
||||||
prefs = cookiePrefs()
|
prefs = cookiePrefs()
|
||||||
list = await getList(id=(@"id"))
|
list = await getCachedList(id=(@"id"))
|
||||||
members = await getGraphListMembers(list, getCursor())
|
members = await getGraphListMembers(list, getCursor())
|
||||||
respList(list, members, list.title, renderTimelineUsers(members, prefs, request.path))
|
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 jester, karax/vdom
|
||||||
|
|
||||||
import router_utils
|
import router_utils
|
||||||
import ".."/[types, formatters, api]
|
import ".."/[types, formatters, api, redis_cache]
|
||||||
import ../views/[general, status, search, mastoapi]
|
import ../views/[general, status, search, mastoapi]
|
||||||
|
|
||||||
export json, uri, sequtils, options, sugar, times
|
export json, uri, sequtils, options, sugar, times
|
||||||
@ -51,11 +51,11 @@ proc createStatusRouter*(cfg: Config) =
|
|||||||
|
|
||||||
let prefs = cookiePrefs()
|
let prefs = cookiePrefs()
|
||||||
|
|
||||||
let conv = await getTweet(id)
|
let conv = await getCachedTweet(id)
|
||||||
if conv == nil:
|
if conv == nil:
|
||||||
echo "nil conv"
|
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"
|
var error = "Record not found"
|
||||||
if conv != nil and conv.tweet != nil and conv.tweet.tombstone.len > 0:
|
if conv != nil and conv.tweet != nil and conv.tweet.tombstone.len > 0:
|
||||||
error = conv.tweet.tombstone
|
error = conv.tweet.tombstone
|
||||||
@ -81,11 +81,11 @@ proc createStatusRouter*(cfg: Config) =
|
|||||||
resp Http404, ""
|
resp Http404, ""
|
||||||
resp $renderReplies(replies, prefs, getPath())
|
resp $renderReplies(replies, prefs, getPath())
|
||||||
|
|
||||||
let conv = await getTweet(id, getCursor())
|
let conv = await getCachedTweet(id, getCursor())
|
||||||
if conv == nil:
|
if conv == nil:
|
||||||
echo "nil conv"
|
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"
|
var error = "Tweet not found"
|
||||||
if conv != nil and conv.tweet != nil and conv.tweet.tombstone.len > 0:
|
if conv != nil and conv.tweet != nil and conv.tweet.tombstone.len > 0:
|
||||||
error = conv.tweet.tombstone
|
error = conv.tweet.tombstone
|
||||||
@ -109,15 +109,15 @@ proc createStatusRouter*(cfg: Config) =
|
|||||||
let
|
let
|
||||||
quote = get(tweet.quote)
|
quote = get(tweet.quote)
|
||||||
quoteUser = quote.user
|
quoteUser = quote.user
|
||||||
if tweet.replyId != 0:
|
if tweet.replyId.len != 0:
|
||||||
let replyUser = await getGraphUser(tweet.replyHandle)
|
let replyUser = await getCachedUser(tweet.replyHandle)
|
||||||
context = &"↩ {replyUser.fullname} (@{tweet.replyHandle})\n↘ {quoteUser.fullname} (@{quoteUser.username})"
|
context = &"↩ {replyUser.fullname} (@{tweet.replyHandle})\n↘ {quoteUser.fullname} (@{quoteUser.username})"
|
||||||
contextUrl = &"{getUrlPrefix(cfg)}/i/status/{tweet.replyId}"
|
contextUrl = &"{getUrlPrefix(cfg)}/i/status/{tweet.replyId}"
|
||||||
else:
|
else:
|
||||||
context = &"↘ {quoteUser.fullname} (@{quoteUser.username})"
|
context = &"↘ {quoteUser.fullname} (@{quoteUser.username})"
|
||||||
contextUrl = &"{getUrlPrefix(cfg)}/i/status/{quote.id}"
|
contextUrl = &"{getUrlPrefix(cfg)}/i/status/{quote.id}"
|
||||||
elif tweet.replyId != 0:
|
elif tweet.replyId.len != 0:
|
||||||
let replyUser = await getGraphUser(tweet.replyHandle)
|
let replyUser = await getCachedUser(tweet.replyHandle)
|
||||||
context = &"↩ {replyUser.fullname} (@{tweet.replyHandle})"
|
context = &"↩ {replyUser.fullname} (@{tweet.replyHandle})"
|
||||||
contextUrl = &"{getUrlPrefix(cfg)}/i/status/{tweet.replyId}"
|
contextUrl = &"{getUrlPrefix(cfg)}/i/status/{tweet.replyId}"
|
||||||
|
|
||||||
|
|||||||
@ -3,13 +3,13 @@ import asyncdispatch, strutils, sequtils, uri, options, times, json
|
|||||||
import jester, karax/vdom
|
import jester, karax/vdom
|
||||||
|
|
||||||
import router_utils
|
import router_utils
|
||||||
import ".."/[types, formatters, query, api]
|
import ".."/[types, formatters, query, api, redis_cache]
|
||||||
import ../views/[general, profile, timeline, status, search, mastoapi]
|
import ../views/[general, profile, timeline, status, search, mastoapi]
|
||||||
|
|
||||||
export vdom
|
export vdom
|
||||||
export uri, sequtils, json
|
export uri, sequtils, json
|
||||||
export router_utils
|
export router_utils
|
||||||
export formatters, query, api
|
export formatters, query, api, redis_cache
|
||||||
export profile, timeline, status, mastoapi
|
export profile, timeline, status, mastoapi
|
||||||
|
|
||||||
proc getQuery*(request: Request; tab, name: string): Query =
|
proc getQuery*(request: Request; tab, name: string): Query =
|
||||||
@ -28,24 +28,11 @@ template skipIf[T](cond: bool; default; body: Future[T]): Future[T] =
|
|||||||
else:
|
else:
|
||||||
body
|
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;
|
proc fetchProfile*(after: string; query: Query; cfg: Config; skipRail=false;
|
||||||
skipPinned=false): Future[Profile] {.async.} =
|
skipPinned=false): Future[Profile] {.async.} =
|
||||||
let
|
let
|
||||||
name = query.fromUser[0]
|
name = query.fromUser[0]
|
||||||
userId = await getUserId(name)
|
userId = await getCachedUserId(name)
|
||||||
|
|
||||||
if userId.len == 0:
|
if userId.len == 0:
|
||||||
return Profile(user: User(username: name))
|
return Profile(user: User(username: name))
|
||||||
@ -61,9 +48,9 @@ proc fetchProfile*(after: string; query: Query; cfg: Config; skipRail=false;
|
|||||||
let
|
let
|
||||||
rail =
|
rail =
|
||||||
skipIf(skipRail or query.kind == media, @[]):
|
skipIf(skipRail or query.kind == media, @[]):
|
||||||
getPhotoRail(name)
|
getCachedPhotoRail(name)
|
||||||
|
|
||||||
user = getGraphUser(name)
|
user = getCachedUser(name)
|
||||||
|
|
||||||
result =
|
result =
|
||||||
case query.kind
|
case query.kind
|
||||||
@ -107,7 +94,7 @@ template respTimeline*(timeline: typed) =
|
|||||||
|
|
||||||
template respUserId*() =
|
template respUserId*() =
|
||||||
cond @"user_id".len > 0
|
cond @"user_id".len > 0
|
||||||
let username = await getUsername(@"user_id")
|
let username = await getCachedUsername(@"user_id")
|
||||||
if username.len > 0:
|
if username.len > 0:
|
||||||
redirect("/" & username)
|
redirect("/" & username)
|
||||||
else:
|
else:
|
||||||
@ -133,17 +120,17 @@ proc createTimelineRouter*(cfg: Config) =
|
|||||||
|
|
||||||
case tab:
|
case tab:
|
||||||
of "followers":
|
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":
|
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:
|
else:
|
||||||
if request.headers.hasKey("Accept") and request.headers["Accept"] == "application/activity+json":
|
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:
|
if userId == "suspended" or userId.len == 0:
|
||||||
resp Http404, {"Content-Type": "application/json"}, """{"error":"User not found"}"""
|
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)
|
let userJson = getActivityStream(user, cfg, prefs)
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import json, asyncdispatch, options, uri
|
|||||||
import times
|
import times
|
||||||
import jester
|
import jester
|
||||||
import router_utils
|
import router_utils
|
||||||
import ".."/[types, api, apiutils, query, consts]
|
import ".."/[types, api, apiutils, query, consts, redis_cache]
|
||||||
import httpclient, strutils
|
import httpclient, strutils
|
||||||
import sequtils
|
import sequtils
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ proc tweetToJson*(t: Tweet): JsonNode =
|
|||||||
result["photos"] = %t.photos
|
result["photos"] = %t.photos
|
||||||
|
|
||||||
proc getUserProfileJson*(username: string): Future[JsonNode] {.async.} =
|
proc getUserProfileJson*(username: string): Future[JsonNode] {.async.} =
|
||||||
let user: User = await getGraphUser(username)
|
let user: User = await getCachedUser(username)
|
||||||
let response: JsonNode = %*{
|
let response: JsonNode = %*{
|
||||||
"id": user.id,
|
"id": user.id,
|
||||||
"username": user.username
|
"username": user.username
|
||||||
|
|||||||
@ -89,7 +89,7 @@ type
|
|||||||
bio*: string
|
bio*: string
|
||||||
userPic*: string
|
userPic*: string
|
||||||
banner*: string
|
banner*: string
|
||||||
pinnedTweet*: int64
|
pinnedTweet*: string
|
||||||
following*: int
|
following*: int
|
||||||
followers*: int
|
followers*: int
|
||||||
tweets*: int
|
tweets*: int
|
||||||
@ -203,14 +203,14 @@ type
|
|||||||
quotes*: int
|
quotes*: int
|
||||||
|
|
||||||
BirdwatchNote* = ref object
|
BirdwatchNote* = ref object
|
||||||
id*: int64
|
id*: string
|
||||||
title*: string
|
title*: string
|
||||||
text*: string
|
text*: string
|
||||||
|
|
||||||
Tweet* = ref object
|
Tweet* = ref object
|
||||||
id*: int64
|
id*: string
|
||||||
threadId*: int64
|
threadId*: string
|
||||||
replyId*: int64
|
replyId*: string
|
||||||
user*: User
|
user*: User
|
||||||
text*: string
|
text*: string
|
||||||
time*: DateTime
|
time*: DateTime
|
||||||
|
|||||||
@ -29,8 +29,8 @@ proc renderNavbar(cfg: Config; req: Request; rss, canonical: string): VNode =
|
|||||||
|
|
||||||
tdiv(class="nav-item right"):
|
tdiv(class="nav-item right"):
|
||||||
icon "search", title="Search", href="/search"
|
icon "search", title="Search", href="/search"
|
||||||
#if cfg.enableRss and rss.len > 0:
|
if cfg.enableRss and rss.len > 0:
|
||||||
#icon "rss-feed", title="RSS Feed", href=rss
|
icon "rss-feed", title="RSS Feed", href=rss
|
||||||
icon "bird", title="Open in Twitter", href=canonical
|
icon "bird", title="Open in Twitter", href=canonical
|
||||||
a(href="https://liberapay.com/zedeus"): verbatim lp
|
a(href="https://liberapay.com/zedeus"): verbatim lp
|
||||||
icon "info", title="About", href="/about"
|
icon "info", title="About", href="/about"
|
||||||
@ -75,8 +75,8 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
|
|||||||
if canonical.len > 0:
|
if canonical.len > 0:
|
||||||
link(rel="canonical", href=canonical)
|
link(rel="canonical", href=canonical)
|
||||||
|
|
||||||
#if cfg.enableRss and rss.len > 0:
|
if cfg.enableRss and rss.len > 0:
|
||||||
#link(rel="alternate", type="application/rss+xml", href=rss, title="RSS feed")
|
link(rel="alternate", type="application/rss+xml", href=rss, title="RSS feed")
|
||||||
|
|
||||||
if prefs.hlsPlayback:
|
if prefs.hlsPlayback:
|
||||||
script(src="/js/hls.light.min.js", `defer`="")
|
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["id"] = %tweetUrl
|
||||||
postJson["type"] = %"Note"
|
postJson["type"] = %"Note"
|
||||||
postJson["summary"] = newJNull()
|
postJson["summary"] = newJNull()
|
||||||
if tweet.replyId != 0:
|
if tweet.replyId.len != 0:
|
||||||
let replyUrl = &"{getUrlPrefix(cfg)}/i/status/{tweet.replyId}"
|
let replyUrl = &"{getUrlPrefix(cfg)}/i/status/{tweet.replyId}"
|
||||||
postJson["inReplyTo"] = %replyUrl
|
postJson["inReplyTo"] = %replyUrl
|
||||||
postJson["inReplyToAtomUri"] = %replyUrl
|
postJson["inReplyToAtomUri"] = %replyUrl
|
||||||
|
|||||||
@ -35,7 +35,7 @@ Twitter feed for: ${desc}. Generated by ${cfg.hostname}
|
|||||||
<p>${text.replace("\n", "<br>\n")}</p>
|
<p>${text.replace("\n", "<br>\n")}</p>
|
||||||
#if tweet.photos.len > 0:
|
#if tweet.photos.len > 0:
|
||||||
# for photo in tweet.photos:
|
# 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
|
# end for
|
||||||
#elif tweet.video.isSome:
|
#elif tweet.video.isSome:
|
||||||
<img src="${urlPrefix}${getPicUrl(get(tweet.video).thumb)}" style="max-width:250px;" />
|
<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)}"
|
# let url = &"{urlPrefix}{getPicUrl(get(tweet.gif).url)}"
|
||||||
<video poster="${thumb}" autoplay muted loop style="max-width:250px;">
|
<video poster="${thumb}" autoplay muted loop style="max-width:250px;">
|
||||||
<source src="${url}" type="video/mp4"></video>
|
<source src="${url}" type="video/mp4"></video>
|
||||||
#elif tweet.card.isSome:
|
##elif tweet.card.isSome:
|
||||||
# let card = tweet.card.get()
|
## let card = tweet.card.get()
|
||||||
# if card.image.len > 0:
|
## if card.image.len > 0:
|
||||||
<img src="${urlPrefix}${getPicUrl(card.image)}" style="max-width:250px;" />
|
##<img src="${urlPrefix}${getPicUrl(card.image)}" style="max-width:250px;" />
|
||||||
# end if
|
## end if
|
||||||
#end if
|
#end if
|
||||||
#if tweet.quote.isSome and get(tweet.quote).available:
|
#if tweet.quote.isSome and get(tweet.quote).available:
|
||||||
# let quoteLink = getLink(get(tweet.quote))
|
# 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:
|
if conv.before.content.len > 0:
|
||||||
tdiv(class="before-tweet thread-line"):
|
tdiv(class="before-tweet thread-line"):
|
||||||
let first = conv.before.content[0]
|
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)
|
renderEarlier(conv.before)
|
||||||
for i, tweet in conv.before.content:
|
for i, tweet in conv.before.content:
|
||||||
renderTweet(tweet, prefs, path, index=i)
|
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"),
|
renderTweet(tweet, prefs, path, class=(header & "thread"),
|
||||||
index=i, last=(i == thread.high), showThread=show)
|
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]
|
result = @[it]
|
||||||
if it.retweet.isSome or it.replyId in threads: return
|
if it.retweet.isSome or it.replyId in threads: return
|
||||||
for t in tweets:
|
for t in tweets:
|
||||||
@ -112,20 +112,20 @@ proc renderTimelineTweets*(results: Timeline; prefs: Prefs; path: string;
|
|||||||
else:
|
else:
|
||||||
renderNoneFound()
|
renderNoneFound()
|
||||||
else:
|
else:
|
||||||
var retweets: seq[int64]
|
var retweets: seq[string]
|
||||||
|
|
||||||
for thread in results.content:
|
for thread in results.content:
|
||||||
if thread.len == 1:
|
if thread.len == 1:
|
||||||
let
|
let
|
||||||
tweet = thread[0]
|
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
|
if retweetId in retweets or tweet.id in retweets or
|
||||||
tweet.pinned and prefs.hidePins:
|
tweet.pinned and prefs.hidePins:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
var hasThread = tweet.hasThread
|
var hasThread = tweet.hasThread
|
||||||
if retweetId != 0 and tweet.retweet.isSome:
|
if retweetId.len != 0 and tweet.retweet.isSome:
|
||||||
retweets &= retweetId
|
retweets &= retweetId
|
||||||
hasThread = get(tweet.retweet).hasThread
|
hasThread = get(tweet.retweet).hasThread
|
||||||
renderTweet(tweet, prefs, path, showThread=hasThread)
|
renderTweet(tweet, prefs, path, showThread=hasThread)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user