diff --git a/src/api.nim b/src/api.nim index df6d586..e087673 100644 --- a/src/api.nim +++ b/src/api.nim @@ -1,2 +1,2 @@ -import api/[profile, timeline, tweet, search, media, list] -export profile, timeline, tweet, search, media, list +import api/[profile, timeline, tweet, search, media, list, resolver] +export profile, timeline, tweet, search, media, list, resolver diff --git a/src/api/resolver.nim b/src/api/resolver.nim new file mode 100644 index 0000000..ad3ad10 --- /dev/null +++ b/src/api/resolver.nim @@ -0,0 +1,13 @@ +import asyncdispatch, httpclient + +import ".."/[formatters, types] + +proc resolve*(url: string; prefs: Prefs): Future[string] {.async.} = + let client = newAsyncHttpClient(maxRedirects=0) + try: + let resp = await client.request(url, $HttpHead) + result = resp.headers["location"].replaceUrl(prefs) + except: + discard + finally: + client.close() diff --git a/src/formatters.nim b/src/formatters.nim index f80ac6f..47699c7 100644 --- a/src/formatters.nim +++ b/src/formatters.nim @@ -9,6 +9,8 @@ from unicode import Rune, `$` const ytRegex = re"([A-z.]+\.)?youtu(be.com|.be)" twRegex = re"(www.|mobile.)?twitter.com" + cards = "https://cards.twitter.com/cards" + tco = "https://t.co" nbsp = $Rune(0x000A0) proc stripText*(text: string): string = @@ -33,6 +35,8 @@ proc replaceUrl*(url: string; prefs: Prefs; absolute=""): string = result = result.replace(ytRegex, prefs.replaceYouTube) if prefs.replaceTwitter.len > 0: result = result.replace(twRegex, prefs.replaceTwitter) + result = result.replace(tco, "https://" & prefs.replaceTwitter & "/t.co") + result = result.replace(cards, "https://" & prefs.replaceTwitter & "/cards") if absolute.len > 0: result = result.replace("href=\"/", "href=\"https://" & absolute & "/") diff --git a/src/nitter.nim b/src/nitter.nim index 3fe63cd..054db01 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -6,7 +6,8 @@ import jester import types, config, prefs, formatters import views/[general, about] import routes/[ - preferences, timeline, status, media, search, rss, list, unsupported, embed] + preferences, timeline, status, media, search, rss, list, + unsupported, embed, resolver] const configPath {.strdefine.} = "./nitter.conf" let cfg = getConfig(configPath) @@ -14,6 +15,7 @@ let cfg = getConfig(configPath) setHmacKey(cfg.hmacKey) createUnsupportedRouter(cfg) +createResolverRouter(cfg) createPrefRouter(cfg) createTimelineRouter(cfg) createListRouter(cfg) @@ -51,6 +53,7 @@ routes: extend unsupported, "" extend preferences, "" + extend resolver, "" extend rss, "" extend search, "" extend timeline, "" diff --git a/src/routes/resolver.nim b/src/routes/resolver.nim new file mode 100644 index 0000000..3d2ac5d --- /dev/null +++ b/src/routes/resolver.nim @@ -0,0 +1,23 @@ +import strutils + +import jester + +import router_utils +import ".."/[query, types, api, agents] +import ../views/general + +template respResolved*(url, kind: string): untyped = + if url.len == 0: + resp showError("Invalid $1 link" % kind, cfg) + else: + redirect(url) + +proc createResolverRouter*(cfg: Config) = + router resolver: + get "/cards/@card/@id": + let url = "https://cards.twitter.com/cards/$1/$2" % [@"card", @"id"] + respResolved(await resolve(url, cookiePrefs()), "card") + + get "/t.co/@url": + let url = "https://t.co/" & @"url" + respResolved(await resolve(url, cookiePrefs()), "t.co")