Browse Source

wip new api and 3-legged oauth

oauth
Wesley Kerfoot 3 years ago
parent
commit
50151fcb49
  1. 12
      src/tweetlog.nim
  2. 4
      src/tweetlogpkg/server.nim
  3. 116
      src/tweetlogpkg/twitter.nim

12
src/tweetlog.nim

@ -8,10 +8,14 @@ from xmltree import escape
when isMainModule:
echo "Running"
for tweet in "1355971359168466945".getThread:
echo ""
echo tweet.text
echo ""
echo "https://api.twitter.com/oauth/request_token".requestToken("POST", "")
#echo "weskerfoot".listTweets
#echo 10.getHome
#for tweet in "1355971359168466945".getThread:
#echo ""
#echo tweet.text
#echo ""
#for tweet in "strivev4".listTweets2(){"data"}:
#echo tweet

4
src/tweetlogpkg/server.nim

@ -170,6 +170,10 @@ router twitblog:
let threads = toSeq(threadIDs(author))
resp author.listThreads(threads)
get "/tweetlog/auth":
echo request
resp ""
# Entry points
proc startServer* =

116
src/tweetlogpkg/twitter.nim

@ -1,7 +1,8 @@
import httpClient, base64, uri, json, os, strformat, sequtils, strutils, options
import timezones, times
import types
import httpClient, base64, uri, json, os, strformat, sequtils, strutils, options, sugar, timezones, times, types
import tables, algorithm, base64, math, options
import nimcrypto
from nimcrypto.sysrand import randomBytes
from xmltree import escape
proc parseTwitterTS(ts : string) : DateTime =
@ -44,7 +45,9 @@ proc tweetClient() : HttpClient =
)
client
proc listTweets2*(user : string) : JsonNode =
proc listTweets*(user : string) : JsonNode =
# Lists tweets from a given user
# XXX use Tweet type
let client = tweetClient()
let userIdReq = fmt"/2/users/by?usernames={user}"
var url = fmt"https://api.twitter.com{userIdReq}"
@ -56,6 +59,7 @@ proc listTweets2*(user : string) : JsonNode =
return client.request(url, httpMethod = HttpGet).body.parseJson
proc getTweetConvo*(tweetID : string) : JsonNode =
# Gets the conversation info for a given tweet
let client = tweetClient()
let userIdReq = fmt"/2/tweets?ids={tweetID}&tweet.fields=conversation_id,author_id"
var url = fmt"https://api.twitter.com{userIdReq}"
@ -64,16 +68,19 @@ proc getTweetConvo*(tweetID : string) : JsonNode =
tweetInfo
proc listTweets*(user : string) : JsonNode =
proc getTweet*(tweetID : string) : string =
# Grabs a single tweet
# XXX use Tweet type
let client = tweetClient()
let reqTarget = fmt"/1.1/statuses/user_timeline.json?count=100&screen_name={user}"
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.parseJson
client.request(url, httpMethod = HttpGet).body
proc getTweet*(tweetID : string) : string =
proc getHome*(count: int) : string =
# Gets your home timeline
let client = tweetClient()
let reqTarget = fmt"/1.1/statuses/show.json?id={tweetID}&tweet_mode=extended"
let reqTarget = fmt"/1.1/statuses/user_timeline.json?count={count}&trim_user=1&exclude_replies=1"
let url = fmt"https://api.twitter.com{reqTarget}"
client.request(url, httpMethod = HttpGet).body
@ -133,3 +140,94 @@ proc renderThread*(tweetID : string) : Option[seq[string]] =
if thread.len == 0:
return none(seq[string])
some(thread)
# 3-legged OAuth stuff
type Params = Table[string, string]
type OAuthToken = tuple[token: string, token_secret: string]
proc generateNonce*() : string =
let alphabet = map(toSeq(65..90).concat(
toSeq(97..122)).concat(
toSeq(49..57)), (c) => char(c))
var randBytes : array[50, uint8]
discard randomBytes(randBytes)
return map(randBytes, (c) => alphabet[(c.int %% alphabet.len)]).join
proc constructEncodedString(params : Params, sep : string, include_quotes : bool) : string =
var encodedPairs : seq[string] = @[]
var keyPairs : seq[tuple[key: string, value: string]] = toSeq(params.pairs)
keyPairs.sort((a, b) => cmp(a.key.encodeUrl, b.key.encodeUrl))
for pair in keyPairs:
if include_quotes:
encodedPairs &= pair[0].encodeUrl & "=" & "\"" & pair[1].encodeUrl & "\""
else:
encodedPairs &= pair[0].encodeUrl & "=" & pair[1].encodeUrl
encodedPairs.join(sep)
proc constructParameterString(params : Params) : string =
params.constructEncodedString("&", include_quotes=false)
proc constructHeaderString(params : Params) : string =
"OAuth " & params.constructEncodedString(", ", include_quotes=true)
proc sign(reqMethod : string,
paramString : string,
baseUrl : string,
accessToken : string = "") : string =
let sigBaseString : string = reqMethod.toUpperAscii & "&" & baseUrl.encodeUrl & "&" & paramString.encodeUrl
let signingKey : string = getEnv("TWITTER_CONSUMER_SECRET").encodeUrl & "&" & accessToken
sha256.hmac(signingKey, sigBaseString).data.encode
proc requestToken*(requestUrl : string, requestMethod : string, requestBody : string) : Option[OAuthToken] =
# Obtain a request token for OAuth
# as well as a request token secret
# these are used to authenticate a specific user
let client = tweetClient()
let callback = getEnv("TWITTER_OAUTH_CALLBACK")
let consumerKey = getEnv("TWITTER_CONSUMER_KEY")
var headers = newHttpHeaders([])
let oauth_nonce = generateNonce()
# The twitter documentation uses SHA1, but this works and is future-proof
let oauth_signature_method = "HMAC-SHA256"
let oauth_timestamp : string = $trunc(epochTime()).uint64
var params : Params = {
"oauth_nonce" : oauth_nonce,
"oauth_signature_method" : oauth_signature_method,
"oauth_callback" : callback,
"oauth_timestamp" : oauth_timestamp,
"oauth_consumer_key" : consumerKey,
"oauth_version" : "1.0"
}.toTable
let paramString = params.constructParameterString
let signature = sign(requestMethod, paramString, requestUrl)
params["oauth_signature"] = signature
headers["Authorization"] = @[params.constructHeaderString]
let resp = client.request(requestUrl, httpMethod = HttpPost, headers = headers, body = requestBody)
if resp.status != "200 OK":
echo resp.body
return none(OAuthToken)
let keyPairs : Table[string, string] = toTable(
map(resp.body.split("&"),
proc(pair : string) : tuple[a: string, b: string] =
let split = pair.split("=")
(split[0], split[1])))
if not (keyPairs.hasKey("oauth_token") and keyPairs.hasKey("oauth_token_secret")):
return none(OAuthToken)
some((token: keyPairs["oauth_token"], token_secret: keyPairs["oauth_token_secret"]))

Loading…
Cancel
Save