1 ! Copyright (C) 2021 Giftpflanze.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: arrays accessors assocs calendar combinators
4 continuations formatting http http.client io json.reader kernel
5 locals make math math.parser namespaces oauth1 prettyprint
6 sequences strings system threads ;
9 TUPLE: oauth-login consumer-token consumer-secret access-token
11 TUPLE: password-login username password ;
13 C: <oauth-login> oauth-login
14 C: <password-login> password-login
16 SYMBOLS: basetimestamp endpoint botflag contact cookies
17 curtimestamp oauth-login password-login csrf-token ;
23 : prepare ( params -- params' )
25 { [ dup t = ] [ drop "true" ] }
26 { [ dup number? ] [ number>string ] }
27 { [ dup string? ] [ ] }
30 { [ dup number? ] [ number>string ] }
36 : <api-request> ( params -- request )
41 } swap assoc-union prepare
44 contact get vm-version vm-git-id 7 head
45 "%s Factor/%s %s mediawiki.api" sprintf "User-Agent"
48 : oauth-post ( params -- response data )
51 over consumer-secret>> <token> consumer-token set
53 swap access-secret>> <token> access-token set
55 <oauth-request-params> set-oauth
58 : cookie-post* ( params -- assoc )
61 http-request [ cookies>> cookies set-global ] dip json> ;
63 : login-token ( -- token )
69 "query" "tokens" "logintoken" [ of ] tri@ ;
71 : login ( -- cookies )
74 password-login get dup username>> "lgname" ,,
75 password>> "lgpassword" ,,
76 login-token "lgtoken" ,,
77 ] { } make cookie-post* drop cookies get ;
79 : cookie-post ( params -- response data )
81 cookies get [ login ] unless* >>cookies
84 : anon-post ( params -- response data )
85 <api-request> http-request ;
87 : code-200? ( response assoc -- ? )
88 over code>> dup 200 = dup [ 3nip ] [
89 -roll "http status code %d" printf
90 swap header>> [ "=" glue print ] assoc-each
95 : retry-after? ( response -- ? )
96 header>> "retry-after" of dup [ dup seconds sleep ] when ;
98 : nonce-already-used? ( assoc -- ? )
100 [ "code" of "mwoauth-invalid-authorization" = ]
101 [ "info" of "Nonce already used" swap subseq-start ] bi
104 : readonly? ( assoc -- ? )
105 "error" "code" [ of ] bi@ "readonly" = dup
106 [ 5 minutes sleep ] when ;
108 : failed? ( response assoc -- response assoc ? )
109 2dup 2dup code-200? not
111 over nonce-already-used? or
114 : dispatch-call ( params -- response data )
116 { [ oauth-login get ] [ oauth-post ] }
117 { [ password-login get ] [ cookie-post ] }
123 : api-call ( params -- assoc )
127 2drop dup dispatch-call
128 [ json> ] [ swap print rethrow ] recover
129 "warnings" "errors" [ over at [ ... ] when* ] bi@
134 :: (query) ( params -- obj assoc )
135 { { "action" "query" } } params assoc-union api-call dup
136 dup "query" of [ nip ] when*
137 "siprop" params key? [
138 params "prop" "list" "meta" [ of ] tri-curry@ tri or or
144 :: call-continue ( params quot1: ( params -- obj assoc )
145 quot2: ( ... -- ... ) -- seq )
149 params assoc-union quot1 call
150 [ quot2 call >alist append ] dip
151 ] do while drop ; inline
153 : query ( params -- seq )
154 [ (query) ] [ ] call-continue ;
156 :: page-content ( title -- content )
159 { "prop" "revisions" }
160 { "rvprop" { "content" "timestamp" } }
166 [ "curtimestamp" of curtimestamp set-global ]
168 "query" of "pages" "revisions" [ of first ] bi@
169 [ "timestamp" of basetimestamp set-global ]
170 [ "slots" "main" "content" [ of ] tri@ ] bi
175 : get-csrf-token ( -- csrf-token )
180 "csrftoken" of dup csrf-token set-global ;
184 : token-call ( params -- assoc )
187 csrf-token get [ get-csrf-token ] unless* "token" ,,
188 ] { } make api-call ;
190 :: edit-page ( title text summary params -- assoc )
196 curtimestamp get "now" or "starttimestamp" ,,
197 basetimestamp get "now" or "basetimestamp" ,,
199 botflag get { { "bot" t } } { } ?
200 params [ assoc-union ] bi@ token-call ;
202 :: move-page ( from to reason params -- assoc )
208 } params assoc-union token-call ;
210 :: email ( target subject text -- assoc )
212 { "action" "emailuser" }
214 { "subject" subject }