From 44a5ae8f7a9aad88fb9a212e5762689131cf2213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Luka=20=C5=A0ijanec?= Date: Tue, 9 Aug 2022 17:32:21 +0200 Subject: =?UTF-8?q?ws=20se=20pove=C5=BEe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- discord.tcl | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 83 insertions(+), 16 deletions(-) diff --git a/discord.tcl b/discord.tcl index 0548b9b..5cc8b80 100755 --- a/discord.tcl +++ b/discord.tcl @@ -38,6 +38,13 @@ namespace eval discord { return $r } + proc every {ms body} { + if {[catch $body] != 0} { + return + } + after $ms [namespace code [info level 0]] + } + set html_mapping { "\"" " ' ' & & < < > > } proc login {email password callback {captcha {}}} { @@ -108,13 +115,49 @@ namespace eval discord { proc authorize_ip {click_url callback} { proc authorize_ip_command {callback http_token} { upvar #0 $http_token state - puts $state(body) - puts $state(meta) + dict for {key value} $state(meta) { + dict append headers [string tolower $key] $value + } + proc authorize_ip_post_command {callback http_token} { + upvar #0 $http_token state + if {[lindex [::http::code $http_token] 1] <= 299 && [lindex [::http::code $http_token] 1] >= 200} { + {*}$callback ok + } else { + set code {} + if {[catch { + set message [dict get [::json::json2dict $state(body)] message] + set code [dict get [::json::json2dict $state(body)] code] + }] != 0} { + {*}$callback error $state(body) + } else { + if {$code == 50014} { + # expired token + {*}$callback invalid_token $message + } else { + {*}$callback error $state(body) + } + } + } + ::http::cleanup $http_token + } + ::http::geturl https://discord.com/api/v9/auth/authorize-ip -query "{\"token\":[::json::write string [lindex [split [dict get $headers location] "="] 1]]}" -timeout 10000 -type application/json -command [list [namespace which authorize_ip_post_command] $callback] + ::http::cleanup $http_token } - puts $click_url ::http::geturl $click_url -timeout 10000 -command "[namespace which authorize_ip_command] {$callback}" } - + proc authorize_ip_example_callback {type {arg1 {}}} { + switch $type { + ok { + puts "ip authorized" + } + invalid_token { + puts "invalid, possibly expired, token. message from server: $arg1" + } + error { + puts "unable to parse response from server: $arg1" + } + } + } oo::class create discord { constructor {{stor {login {} password {} token {} user_id {}}}} { my variable log storage @@ -122,10 +165,7 @@ namespace eval discord { set log [logger::init discord] } destructor { - my variable log storage sockets - foreach socket $sockets { - close $socket - } + my variable log storage if {[my is_connected] != -1} { my disconnect } @@ -150,10 +190,12 @@ namespace eval discord { method login {callback {captcha {}}} { my variable storage log proc login_callback {self_discordobj callback type {arg1 ""} {arg2 ""}} { - my variable log log sockets sockets + my variable storage log switch $type { ok { ${log}::notice "login ok: token is $arg1, user_id is $arg2" + dict set storage token $arg1 + dict set storage user_id $arg2 {*}$callback ok [list $arg1 $arg2] } captcha { @@ -195,7 +237,7 @@ namespace eval discord { $client send {200 ok} {content-type text/plain} "http server resources were freed. please close this browser tab. " } - ::www::server create s 0 [list /captcha.html [list [namespace which captcha.html] $arg1] /submit [list [namespace which submit] $self_discordobj $callback [namespace current]::s] /stop.txt [list [namespace which stop.txt] [namespace which s]]] + ::www::server create s 0 [list /captcha.html [list [namespace which captcha.html] $arg1] /submit [list [namespace which submit] $self_discordobj $callback [namespace current]::s] /stop.txt [list [namespace which stop.txt] [namespace current]::s]] ${log}::notice "please solve captcha at http://127.0.0.1:[s ports]/captcha.html" {*}$callback captcha "http://127.0.0.1:[s ports]/captcha.html" } @@ -209,18 +251,25 @@ namespace eval discord { } } } - ::discord::login [dict get $storage login] [dict get $storage password] "[namespace which login_callback] [self] $callback" $captcha + ::discord::login [dict get $storage login] [dict get $storage password] [list [namespace which login_callback] [self] $callback] $captcha } + # websocket complains that it couldn't remove socket from socketmap in ::http, but future connections to same host and port are working regardless. if something doesn't work in this direction, this is likely the cause -- 2022-08-09 method connect {} { my variable sock log storage if {[my is_connected] != -1} { my disconnect } + ${log}::notice "trying to connect" proc handler { sock type msg } { - my variable log + my variable log last_packet switch $type { text { ${log}::debug "received a message: $msg" + set p [::json::json2dict $msg] + set last_packet [dict get $p s] + if [dict exists $p d heartbeat_interval] { + set heartbeat_interval [dict get $p d heartbeat_interval] + } } connect { ${log}::notice "connected" @@ -237,7 +286,8 @@ namespace eval discord { } } } - set sock [::websocket::open "wss://gateway.discord.gg/?encoding=json&v=9" [namespace which handler]] + # in order to overwrite the user-agent header + set sock [::websocket::open "wss://gateway.discord.gg/?encoding=json&v=9" [namespace which handler] -headers {Origin https://discord.com User-Agent "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36"}] ${log}::debug "created sock, $sock" } method is_connected {} { @@ -253,11 +303,28 @@ namespace eval discord { } } if [string match *discord.tcl* $argv0] { - ::discord::authorize_ip https://click.discord.com/ls/click?upn=qDOo8cnwIoKzt0aLL1cBeJWc43CAiLlKYSQGUErhKV7fF2lroxEuEaMS14HcVQRWKEsXxCWfVqMYFnAqiyFKlyc60qLVSfrR2BpIhn60wAL4y7X8dEY5UhD7n-2BEIulILGfHWzhi03YqYAqwN1dzNDsL7BrPVj5dWSRz43qNCKZs2Mre7Chd4IbpPox9Y-2F4ktYY4N_PdlvdP47mdorwIWlvGoY-2Fnv9MARx98jl0olgQff-2FSKZtFfa9W0dHpN7isUf-2BBQiGTEplWgkKV5AWO17KWsssOLh7AWnsZoy5YvVHlKWck92RQ5vrqN5LMcYUfucbVfjTrOdjPoOJLpa-2F6uIpp4HgFjgpPQjxA-2B3Mm9UmJJTSAUKfWdzMYiWkDqft72DZnIyAHKkjDaEO8wSn3CcCTqm-2FzMMTi-2BVKGxWIQb2p-2F6LNSxtos3YXgYUOtHh5pLu6WeUIvmdhDYWgvG9534NpmGXELQ-3D-3D {} - vwait forever ::discord::discord create d d set_login_password $env(DC_E) $env(DC_P) - d login ::discord::login_example_callback + proc login_callback {dobj type {arg1 {}}} { + switch $type { + ok { + puts "ok, login successful" + $dobj connect + } + captcha { + puts "solve the captcha at address $arg1" + } + error_message { + puts "the server sent a human-readable error message: $arg1" + } + error { + puts "error: $arg1" + } + } + } + d login [list [namespace which login_callback] [namespace which d]] + after 10000 set end 1 + vwait end vwait forever ::discord::login env(DC_E) env(DC_P) login_example_callback d connect -- cgit v1.2.3