A Simple Tool To Archive Twitter Threads
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

92 lines
3.0 KiB

import httpClient, base64, uri, json, os, strformat, sequtils, strutils, options
from htmlgen import nil
from xmltree import escape
proc buildAuthHeader() : string =
let consumerKey = "TWITTER_CONSUMER_KEY".getEnv
let secret = "TWITTER_CONSUMER_SECRET".getEnv
"Basic " & (consumerKey.encodeUrl & ":" & secret.encodeUrl).encode
proc getToken*() : string =
var client = newHttpClient()
client.headers = newHttpHeaders(
{
"Content-Type" : "application/x-www-form-urlencoded;charset=UTF-8",
"Authorization" : buildAuthHeader()
}
)
let body = "grant_type=client_credentials"
let response = client.request("https://api.twitter.com/oauth2/token",
httpMethod = HttpPost,
body = body).body.parseJson
let responseType = response["token_type"].getStr
assert(responseType == "bearer")
"Bearer " & response["access_token"].getStr
proc tweetClient() : HttpClient =
var client = newHttpClient()
client.headers = newHttpHeaders(
{
"Authorization" : getToken()
}
)
client
proc listTweets*(user : string) : JsonNode =
let client = tweetClient()
let reqTarget = fmt"/1.1/statuses/user_timeline.json?count=100&screen_name={user}"
let url = fmt"https://api.twitter.com{reqTarget}"
client.request(url, httpMethod = HttpGet).body.parseJson
proc getTweet*(tweetID : string) : string =
let client = tweetClient()
let reqTarget = fmt"/1.1/statuses/show.json?id={tweetID}&tweet_mode=extended"
let url = fmt"https://api.twitter.com{reqTarget}"
client.request(url, httpMethod = HttpGet).body
proc getThread*(tweetStart : string, user : string) : seq[string] =
let parsed = tweetStart.getTweet.parseJson
let nextTweetID = parsed{"in_reply_to_status_id_str"}.getStr()
if nextTweetID == "":
if parsed{"user", "screen_name"}.getStr() == user:
return @[parsed{"full_text"}.getStr()]
else:
return @[]
else:
if parsed{"user", "screen_name"}.getStr() == user:
return nextTweetID.getThread(user) & @[parsed{"full_text"}.getStr()]
else:
return nextTweetID.getThread(user)
proc convertWords(tweet : string) : string =
let words = tweet.split(" ")
var stripped : seq[string]
for chunk in words:
for word in chunk.splitLines:
if word.len > 3 and word[0..3] == "http":
let parsedUri = word.parseUri
let scheme = parsedUri.scheme
let hostname = parsedUri.hostname
let path = parsedUri.path
if (scheme.len > 0 and hostname.len > 0):
let url = xmltree.escape(fmt"{scheme}://{hostname}{path}")
stripped &= htmlgen.a(href=url, url)
elif word.len > 0 and word[0] != '@':
stripped &= xmltree.escape(word)
else:
continue
stripped.join(" ")
proc renderThread*(tweetID : string, user : string) : Option[seq[string]] =
let thread = tweetID.getThread(user).map(convertWords).map(capitalizeAscii)
if thread.len == 0:
return none(seq[string])
some(thread)