# SPDX-License-Identifier: AGPL-3.0-only import json, asyncdispatch, strutils, sequtils, uri, options, sugar, strformat, times import jester, karax/vdom import router_utils import ".."/[types, formatters, api] import ../views/[general, status, search, mastoapi] export json, uri, sequtils, options, sugar, times export router_utils export api, formatters export status, mastoapi proc createStatusRouter*(cfg: Config) = router status: get "/@name/status/@id/@reactors": cond '.' notin @"name" let id = @"id" if id.len > 19 or id.any(c => not c.isDigit): resp Http404, showError("Invalid tweet ID", cfg) let prefs = cookiePrefs() # used for the infinite scroll feature if @"scroll".len > 0: let replies = await getReplies(id, getCursor()) if replies.content.len == 0: resp Http404, "" resp $renderReplies(replies, prefs, getPath()) if @"reactors" == "favoriters": resp renderMain(renderUserList(await getGraphFavoriters(id, getCursor()), prefs), request, cfg, prefs) elif @"reactors" == "retweeters": resp renderMain(renderUserList(await getGraphRetweeters(id, getCursor()), prefs), request, cfg, prefs) get "/@name/status/@id/?": cond '.' notin @"name" var id = @"id" var rawFile = false if id.endsWith(".mp4"): rawFile = true id.removeSuffix(".mp4") if request.headers.hasKey("Accept") and request.headers["Accept"] == "application/activity+json": if id.len > 19 or id.any(c => not c.isDigit): resp Http404, {"Content-Type": "application/json"}, """{"error":"Invalid record ID"}""" let prefs = cookiePrefs() let conv = await getTweet(id) if conv == nil: echo "nil conv" if conv == nil or conv.tweet == nil or conv.tweet.id == 0: var error = "Record not found" if conv != nil and conv.tweet != nil and conv.tweet.tombstone.len > 0: error = conv.tweet.tombstone var errJson = newJObject() errJson["error"] = %error resp Http404, {"Content-Type": "application/json"}, $errJson let postJson = getActivityStream(conv.tweet, cfg, prefs) resp Http200, {"Content-Type": "application/json"}, $postJson if id.len > 19 or id.any(c => not c.isDigit): resp Http404, showError("Invalid tweet ID", cfg) let prefs = cookiePrefs() # used for the infinite scroll feature if @"scroll".len > 0: let replies = await getReplies(id, getCursor()) if replies.content.len == 0: resp Http404, "" resp $renderReplies(replies, prefs, getPath()) let conv = await getTweet(id, getCursor()) if conv == nil: echo "nil conv" if conv == nil or conv.tweet == nil or conv.tweet.id == 0: var error = "Tweet not found" if conv != nil and conv.tweet != nil and conv.tweet.tombstone.len > 0: error = conv.tweet.tombstone resp Http404, showError(error, cfg) let tweet = conv.tweet title = pageTitle(tweet) ogTitle = pageTitle(tweet.user) desc = tweet.text avatar = tweet.user.userPic time = some(tweet.time) var images = tweet.photos video = "" context = "" contextUrl = "" if tweet.quote.isSome(): let quote = get(tweet.quote) quoteUser = quote.user if tweet.replyId != 0: let replyUser = await getGraphUser(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) context = &"↩ {replyUser.fullname} (@{tweet.replyHandle})" contextUrl = &"{getUrlPrefix(cfg)}/i/status/{tweet.replyId}" if tweet.video.isSome(): let videoObj = get(tweet.video) images = @[videoObj.thumb] let vars = videoObj.variants.filterIt(it.contentType == mp4) # idk why this wont sort when it sorts everywhere else #video = vars.sortedByIt(it.bitrate)[^1].url video = vars[^1].url elif tweet.gif.isSome(): let gif = get(tweet.gif) images = @[gif.thumb] video = getUrlPrefix(cfg) & getPicUrl(gif.url) #elif tweet.card.isSome(): # let card = tweet.card.get() # if card.image.len > 0: # images = @[card.image] # elif card.video.isSome(): # images = @[card.video.get().thumb] if rawFile and video != "": redirect(video) let html = renderConversation(conv, prefs, getPath() & "#m") resp renderMain(html, request, cfg, prefs, title, desc, ogTitle, images=images, video=video, avatar=avatar, time=time, context=context, contextUrl=contextUrl, id=id) get "/@name/@s/@id/@m/?@i?": cond @"s" in ["status", "statuses"] cond @"m" in ["video", "photo"] redirect("/$1/status/$2" % [@"name", @"id"]) get "/@name/statuses/@id/?": redirect("/$1/status/$2" % [@"name", @"id"]) get "/i/web/status/@id": redirect("/i/status/" & @"id") get "/@name/thread/@id/?": redirect("/$1/status/$2" % [@"name", @"id"])