summaryrefslogtreecommitdiff
path: root/desk/ted/twatter.hoon
diff options
context:
space:
mode:
Diffstat (limited to 'desk/ted/twatter.hoon')
-rw-r--r--desk/ted/twatter.hoon158
1 files changed, 158 insertions, 0 deletions
diff --git a/desk/ted/twatter.hoon b/desk/ted/twatter.hoon
new file mode 100644
index 0000000..018fa27
--- /dev/null
+++ b/desk/ted/twatter.hoon
@@ -0,0 +1,158 @@
+/- spider, w=web2-main, tw=web2-twatter, cokis=web2-cookies
+/+ strandio, string, sr=sortug, *web2-twatter
+=, strand=strand:spider
+=, dejs-soft:format
+=, strand-fail=strand-fail:libstrand:spider
+=< run
+|%
+++ cookie-check
+ |= body=@t ^- (unit ?)
+ =/ jon=(unit json) (better-dejson:parsing:sr body)
+ ?~ jon ~
+ =/ error=(unit error-res:tw) (errors:dejs:tw u.jon)
+ ?~ error `.y
+ ?~ u.error ~
+ =/ main (head u.error)
+ `.n
+ :: :- ~ !.=(code.main 32)
+ ::
+ :: :- ~ %+ roll +.u.error
+ :: |= [[msg=@t code=@ud] acc=@t] code
+
+++ parse-user-id
+ |= body=@t ^- (unit @t)
+ =/ jon=(unit json) (better-dejson:parsing:sr body)
+ ?~ jon ~
+ `(profile:dejs:tw u.jon)
+:: ++ get-body
+:: |= res=client-response:iris
+:: =/ m (strand cord)
+:: ^- form:m
+:: ?. ?=(%finished -.res) (strand-fail %no-body ~)
+:: ?~ full-file.res (strand-fail %no-body ~)
+:: =/ body=@t q.data.u.full-file.res
+:: (pure:m body)
+++ get-body
+|= res=client-response:iris ^- @t
+?. ?=(%finished -.res) ''
+?~ full-file.res ''
+q.data.u.full-file.res
+++ coki-to-string
+|= t=twatter-creds:cokis ^- cord
+=/ at (trip auth-token.t)
+=/ ct0 (trip ct0.t)
+=/ kdt (trip kdt.t)
+=/ twid (trip twid.t)
+%- crip
+"auth_token={at};ct0={ct0};kdt={kdt};twid={twid};"
+++ run
+ ^- thread:spider
+ |= arg=vase
+ =/ m (strand vase)
+ ^- form:m
+ |^
+ =/ req !<((unit request:w) arg) :: dojo only?
+ ?~ req (pure:m !>([%fail 'wrong request']))
+ ?. ?=(%twatter -.u.req) (pure:m !>([%fail 'wrong request']))
+ =/ =action:tw +.u.req
+ ?: ?=(%lurk -.action) (lurk thread.action)
+ :: Else is logged in, need cookies
+ ;< =bowl:spider bind:m get-bowl:strandio
+ =/ coki (scry:io:sr %trill-cookies /app/twatter scry:cokis our.bowl now.bowl)
+ ?: ?=(%ng -.coki) (pure:m !>([%twatter %no-coki ~]))
+ ?: ?=(%active -.coki) (pure:m !>([%twatter %no-coki ~]))
+ ?. ?=(%twatter -.p.coki) (pure:m !>([%twatter %no-coki ~]))
+ =/ csrf ct0.p.coki
+ =/ tw=twatter-creds:cokis +.p.coki
+ =/ coki-string (coki-to-string tw)
+ =/ headers (logged-headers coki-string csrf)
+ ?+ -.action (pure:m !>([%fail 'wrong request']))
+ %user
+ =/ vars (build-variables ~[['screen_name' %s username.action]])
+ =/ url %- crip (weld (burl user-by-name:urls) "?variables={vars}&features={features}")
+ =/ req1 [%'GET' url headers ~]
+ ;< ~ bind:m (send-request:strandio req1)
+ ;< res1=client-response:iris bind:m take-client-response:strandio
+ =/ body1=@t (get-body res1)
+ =/ coki-ok (cookie-check body1)
+ ?~ coki-ok (pure:m !>([%fail 'parsing error']))
+ ?. u.coki-ok
+ (pure:m !>([%twatter %no-coki ~]))
+ =/ user-id (parse-user-id body1)
+ ?~ user-id (pure:m !>([%fail 'parsing error']))
+ =/ url2 %- crip (weld (burl user-tweets:urls) (userid-params u.user-id))
+ =/ req2 [%'GET' url2 headers ~]
+ ;< ~ bind:m (send-request:strandio req2)
+ ;< res2=client-response:iris bind:m take-client-response:strandio
+ =/ body2 (get-body res2)
+ (pure:m !>([%twatter %user body1 body2]))
+ :: ?~ user-id (pure:m !>(`response:tw`[%user profile=`user-profile feed=~]))
+ :: =/ tw-data-req ?- replies.u.command
+ :: %| (twatter-request (build-request-url 'userId' u.user-id cursor.u.command user-tweets:endpoints) gt csrf)
+ :: %& (twatter-request (build-request-url 'userId' u.user-id cursor.u.command user-tweets-replies:endpoints) gt csrf) ==
+ %thread
+ =/ vars (build-variables ~[['focalTweetId' %s id.action]])
+ =/ url %- crip (weld (burl tweet-detail:urls) "?variables={vars}&features={features}")
+ :: =/ tw-data-req (embed-request id.u.command)
+ :: =/ req (twatter-request url gt csrf)
+ =/ req [%'GET' url headers ~]
+ :: =/ tw-data-req (twatter-request (build-request-v2-url 'tweetId' id.u.command cursor.u.command tweet-lurk:endpoints) gt csrf)
+ ;< ~ bind:m (send-request:strandio req)
+ ;< res=client-response:iris bind:m take-client-response:strandio
+ =/ body (get-body res)
+ =/ coki-ok (cookie-check body)
+ ?~ coki-ok (pure:m !>([%fail 'parsing error']))
+ ?. u.coki-ok
+ :: (lurk id.action)
+ (pure:m !>([%twatter %no-coki ~]))
+ (pure:m !>([%twatter %thread body]))
+ %search
+ =/ vars (build-variables ~[['rawQuery' %s query.action] ['querySource' %s 'typed_query'] ['product' %s 'Latest']])
+ =/ url %- crip (weld (burl search-timeline:urls) "?variables={vars}&features={features}")
+ :: =/ tw-data-req (twatter-request (build-search-url query.u.command cursor.u.command) gt csrf)
+ =/ req [%'GET' url headers ~]
+ ;< ~ bind:m (send-request:strandio req)
+ ;< res=client-response:iris bind:m take-client-response:strandio
+ =/ body (get-body res)
+ (pure:m !>([%twatter %search query.action body]))
+ %hark
+ =/ vars "?{notes-params}"
+ =/ url %- crip (weld notifications-url vars)
+ =/ req [%'GET' url headers ~]
+ ;< ~ bind:m (send-request:strandio req)
+ ;< res=client-response:iris bind:m take-client-response:strandio
+ =/ body (get-body res)
+ (pure:m !>([%twatter %hark body]))
+ :: posts
+ %post
+ =/ url (crip (burl create-tweet:urls))
+ =/ body=octs (build-post-body +.action)
+ =/ req [%'POST' url headers `body]
+ ;< ~ bind:m (send-request:strandio req)
+ ;< res=client-response:iris bind:m take-client-response:strandio
+ =/ res-body (get-body res)
+ :: TODO error handle. If successful the body returns a {data: {create_tweet: tweet_results...}} object
+ (pure:m !>([%twatter %post-ack ~]))
+ ==
+ ++ lurk
+ |= tweet-id=@t
+ =/ csrf-req csrf-token-request :: TODO could cache gt and crsf for a while, instead of fetching on every request
+ ;< ~ bind:m (send-request:strandio csrf-req)
+ ;< csrf-res=client-response:iris bind:m take-client-response:strandio
+ ;< csrf=@t bind:m (parse-csrf csrf-res)
+ =/ guest-token-req gt-request
+ ;< ~ bind:m (send-request:strandio guest-token-req)
+ ;< gt-res=client-response:iris bind:m take-client-response:strandio
+ ;< gt=@t bind:m (parse-gt gt-res)
+ :: turns out we do need this
+ =/ vars (build-variables ~[['tweetId' %s tweet-id]])
+ =/ url %- crip (weld (burl tweet-lurk:urls) "?variables={vars}&features={features}")
+ =/ req1 (twatter-request url gt csrf)
+ :: =/ req1 [%'GET' url lurk-headers ~]
+ ;< ~ bind:m (send-request:strandio req1)
+ ;< res1=client-response:iris bind:m take-client-response:strandio
+ =/ body1 (get-body res1)
+ (pure:m !>([%twatter %thread-lurk body1]))
+
+ --
+--