############################################################ # # # Author: Kim "kimpact" Slavinjak # # E-Mail: kim@kimpact.info # # IRC: #kimpact @ quakenet # # Date: 08.06.2018 # # Version: 0.2 # # Info: This script uses the OpenDota API to call REST # # services and fetch json data to display over # # your Eggdrop Bot. This script will create a # # a file called recentmatch in your home # # directory to remember the last game you played. # # If you have questions, need help or have # # suggestions feel free to email me or find me # # on quakenet # # # # Installation: # # # # You need these lines in your eggdrop.conf # # -) set dotaapi(owner_steamid) "10694300" # # -> change the number to your steamid32 # # -) set dotaapi(post_recent_matches) 1 # # -> set this param to 1 and the bot will # # look if your steamid has played a new # # match every 40 minutes and post the stats # # in the channel below # # -) set dotaapi(channel) "#kimpact" # # -> change to the channel you want the bot # # to post stats in # # -) source scripts/dotaapi.tcl # # # # History: # # # # -) 08.06.18 - Init of the tcl script # # -) 09.06.18 - Added proc to periodically check # # recent matches of designated player # # in eggdrop conf defined player # # and write it publicly to channel # # -) 13.06.18 - Added call to return latest match # # stats per privmsg and alot of # # coloring to the dota stats # # # ############################################################ #Required Packages ############################################################ package require http package require rest package require tls package require json #Global variables ########################################################### global dotaapi #Inits for packages ############################################################ http::register https 443 tls:socket #Bindings ############################################################ bind msg - "wl" msg:restwl bind msg - "last_match" msg:last_match bind msg - "httpswl" msg:httpswl #Automation of procdedures ############################################################ if { ![info exists recentmatch_running] && $dotaapi(post_recent_matches) } { timer 0 opendota:recent_match set recentmatch_running 1 } #Procedures ############################################################ #This procedure is needed to initialize TLS #If this isn't used, all https connections will be rejected #Therefore if you're just planning on calling http you #can delete it proc tls:socket args { set opts [lrange $args 0 end-2] set host [lindex $args end-1] set port [lindex $args end] ::tls::socket -servername $host {*}$opts $host $port ::tls::socket -ssl3 false -ssl2 false -tls1 true -servername $host {*}$opts $host $port } #Checks if the steamid32 is set and only numbers proc steam:checkid args { set steamid [lindex $args 0] if { $steamid != "" && [regexp {^[0-9]*$} $steamid] } { return 1 } else { putlog "SteamId '$steamid' rejected" return 0 } } #returns statistics of last match of received steamid #per privmasg proc msg:last_match { nick host hand args } { set steam_id [lindex $args 0] putlog "msg:last_match - received call from $nick for steam id $steam_id" if { [steam:checkid $steam_id] } { #get latest match into dict set d_result [opendota:get_last_match $steam_id] set lane [dict get $d_result "lane_role"] set lane [opendota:get_lane_string $lane] set player_slot [dict get $d_result "player_slot"] set player_slot [opendota:get_radiant_dire $player_slot] set radiant_win [dict get $d_result "radiant_win"] set game_mode [dict get $d_result "game_mode"] set lobby_type [dict get $d_result "lobby_type"] set game_type [opendota:game_type $game_mode $lobby_type] set kills [dict get $d_result "kills"] set deaths [dict get $d_result "deaths"] set assists [dict get $d_result "assists"] set xp_per_min [dict get $d_result "xp_per_min"] set gold_per_min [dict get $d_result "gold_per_min"] set hero_damage [dict get $d_result "hero_damage"] set tower_damage [dict get $d_result "tower_damage"] set hero_healing [dict get $d_result "hero_healing"] set last_hits [dict get $d_result "last_hits"] set leaver_status [dict get $d_result "leaver_status"] set party_size [dict get $d_result "party_size"] set hero_id [dict get $d_result "hero_id"] set hero_name [opendota:get_hero_name $hero_id] set lastmatchid [dict get $d_result "match_id"] #Output starts here #Finding out if we won if { ($player_slot == "radiant" && $radiant_win == true) || ($player_slot == "dire" && $radiant_win == false) } { puthelp "PRIVMSG $nick :\00303,01GG!\00309,01 Won the last \037$game_type\037 game as \037\00311,01$hero_name\037\00303,01 \00309,01in the \037$lane\037! \00303,01GG!" } else { puthelp "PRIVMSG $nick :\00304,01Unfortunately lost last \037$game_type\037 game as\00311,01 \037\00313,01$hero_name\037\00304,01 in the \037$lane\037.\00307,01.\00308,01." } puthelp "PRIVMSG $nick :\00300,01With a\00304,01 \00303,01K\00304,01D\00307,01A \00309,01$kills\00300,01|\00304,01$deaths\00300,01|\00307,01$assists\00309,01 \00300,01and\00309,01 \037\00313,01$last_hits\037 Last Hits\00300,01," puthelp "PRIVMSG $nick :\037\00311,01$xp_per_min\037 XP/min\00309,01 \00300,01and\00309,01 \037\00308,01$gold_per_min\037 Gold/min\00300,01," puthelp "PRIVMSG $nick :\00300,01they were able to inflict\00309,01 \037\00313,01$hero_damage\037\00305,01 \00313,01damage\00309,01 \00300,01to the enemy heroes and\00309,01 \037\00313,01$tower_damage\037\00306,01 \00313,01damage\00306,01 \00300,01to their towers.\00309,01" if { $hero_healing > 250 } { puthelp "PRIVMSG $nick :\00300,01All the while healing their friends and themselves for\00309,01 \037$hero_healing\037\00300,01." } if { $party_size > 1 && $lobby_type != "9" } { puthelp "PRIVMSG $nick :\00300,01By the by, they had the support of \037\00307,01[expr $party_size - 1] friend/s\037\00300,01 in form of a party." } if { $leaver_status > 0 } { puthelp "PRIVMSG $nick :\00314,01A\00315,01n\00300,01d \00314,01a\00315,01p\00300,01p\00315,01a\00314,01r\00315,01e\00300,01n\00315,01t\00314,01l\00315,01y\00300,01 \00314,01s\00315,01o\00300,01m\00315,01e\00314,01o\00315,01n\00300,01e \00315,01d\00314,01i\00315,01d\00300,01n\00315,01'\00314,01t\00300,01 \00315,01t\00300,01h\00315,01i\00314,01n\00315,01k\00300,01 i\00315,01t\00300,01 \00314,01w\00315,01a\00300,01s \00315,01w\00314,01o\00315,01r\00300,01t\00315,01h\00300,01 \00314,01t\00315,01h\00300,01e\00315,01i\00314,01r\00300,01 \00315,01t\00300,01i\00315,01m\00314,01e\00300,01 \00315,01t\00300,01o \00315,01s\00314,01t\00315,01a\00300,01y \00315,01u\00314,01n\00315,01t\00300,01i\00315,01l\00300,01 \00314,01t\00315,01h\00300,01e\00315,01 e\00314,01n\00315,01d\00300,01 o\00315,01f\00300,01 \00314,01t\00315,01h\00300,01e \00315,01m\00314,01a\00315,01t\00300,01c\00315,01h\00300,01." } puthelp "PRIVMSG $nick :\00304,01O\00307,01p\00308,01e\00303,01n\00309,01d\00311,01o\00312,01t\00306,01a\00304,01 L\00307,01i\00308,01n\00303,01k\00300,01: \00310,01https://www.opendota.com/matches/$lastmatchid" } else { puthelp "PRIVMSG $nick :Please call this function by messaging the bot with '/msg kimbot wl {yourSteamId}'" } } #get last played match from opendota and returns a dict with the stats proc opendota:get_last_match args { set steam_id [lindex $args 0] set url "https://api.opendota.com/api/players/$steam_id/recentMatches" set query "$steam_id" set res [rest::simple $url $query { method get content-type json }] #converting the received json to a dict set d_result [json::json2dict $res] set d_result [lindex $d_result 0] #since the opendota API returns the last 20 matches #we only get the latest match out of the list return $d_result } #periodically posts match data of the most recent match #in the home channel of the bot proc opendota:recent_match args { global dotaapi #check if set steamid is valid if { [steam:checkid $dotaapi(owner_steamid)] && $dotaapi(channel) != ""} { #open/create file and read the last known match (last known to the bot) set fs [open "~/recentmatch" a+] seek $fs 0 gets $fs bots_lastmatchid close $fs putlog "opendota:recent_match - Last Match ID: '$bots_lastmatchid'" set d_result [opendota:get_last_match $dotaapi(owner_steamid)] set lastmatchid [dict get $d_result "match_id"] if { $lastmatchid != $bots_lastmatchid } { putlog "A new game was played by $dotaapi(owner_steamid) with the matchid $lastmatchid" #write the new matchid to the filesystem set fs [open "~/recentmatch" w] puts $fs $lastmatchid close $fs set lane [dict get $d_result "lane_role"] set lane [opendota:get_lane_string $lane] set player_slot [dict get $d_result "player_slot"] set player_slot [opendota:get_radiant_dire $player_slot] set radiant_win [dict get $d_result "radiant_win"] set game_mode [dict get $d_result "game_mode"] set lobby_type [dict get $d_result "lobby_type"] set game_type [opendota:game_type $game_mode $lobby_type] set kills [dict get $d_result "kills"] set deaths [dict get $d_result "deaths"] set assists [dict get $d_result "assists"] set xp_per_min [dict get $d_result "xp_per_min"] set gold_per_min [dict get $d_result "gold_per_min"] set hero_damage [dict get $d_result "hero_damage"] set tower_damage [dict get $d_result "tower_damage"] set hero_healing [dict get $d_result "hero_healing"] set last_hits [dict get $d_result "last_hits"] set leaver_status [dict get $d_result "leaver_status"] set party_size [dict get $d_result "party_size"] set hero_id [dict get $d_result "hero_id"] set hero_name [opendota:get_hero_name $hero_id] #Output starts here #Finding out if we won if { ($player_slot == "radiant" && $radiant_win == true) || ($player_slot == "dire" && $radiant_win == false) } { puthelp "PRIVMSG $dotaapi(channel) :\00303,01GG!\00309,01 Won the last \037$game_type\037 game as \037\00311,01$hero_name\037\00303,01 \00309,01in the \037$lane\037! \00303,01GG!" } else { puthelp "PRIVMSG $dotaapi(channel) :\00304,01Unfortunately lost last \037$game_type\037 game as\00311,01 \037\00313,01$hero_name\037\00304,01 in the \037$lane\037.\00307,01.\00308,01." } puthelp "PRIVMSG $dotaapi(channel) :\00300,01With a\00304,01 \00303,01K\00304,01D\00307,01A \00309,01$kills\00300,01|\00304,01$deaths\00300,01|\00307,01$assists\00309,01 \00300,01and\00309,01 \037\00313,01$last_hits\037 Last Hits\00300,01," puthelp "PRIVMSG $dotaapi(channel) :\037\00311,01$xp_per_min\037 XP/min\00309,01 \00300,01and\00309,01 \037\00308,01$gold_per_min\037 Gold/min\00300,01," puthelp "PRIVMSG $dotaapi(channel) :\00300,01they were able to inflict\00309,01 \037\00313,01$hero_damage\037\00305,01 \00313,01damage\00309,01 \00300,01to the enemy heroes and\00309,01 \037\00313,01$tower_damage\037\00306,01 \00313,01damage\00306,01 \00300,01to their towers.\00309,01" if { $hero_healing > 250 } { puthelp "PRIVMSG $dotaapi(channel) :\00300,01All the while healing their friends and themselves for\00309,01 \037$hero_healing\037\00300,01." } if { $party_size > 1 && $lobby_type != "9" } { puthelp "PRIVMSG $dotaapi(channel) :\00300,01By the by, they had the support of \037\00307,01[expr $party_size - 1] friend/s\037\00300,01 in form of a party." } if { $leaver_status > 0 } { puthelp "PRIVMSG $dotaapi(channel) :\00314,01A\00315,01n\00300,01d \00314,01a\00315,01p\00300,01p\00315,01a\00314,01r\00315,01e\00300,01n\00315,01t\00314,01l\00315,01y\00300,01 \00314,01s\00315,01o\00300,01m\00315,01e\00314,01o\00315,01n\00300,01e \00315,01d\00314,01i\00315,01d\00300,01n\00315,01'\00314,01t\00300,01 \00315,01t\00300,01h\00315,01i\00314,01n\00315,01k\00300,01 i\00315,01t\00300,01 \00314,01w\00315,01a\00300,01s \00315,01w\00314,01o\00315,01r\00300,01t\00315,01h\00300,01 \00314,01t\00315,01h\00300,01e\00315,01i\00314,01r\00300,01 \00315,01t\00300,01i\00315,01m\00314,01e\00300,01 \00315,01t\00300,01o \00315,01s\00314,01t\00315,01a\00300,01y \00315,01u\00314,01n\00315,01t\00300,01i\00315,01l\00300,01 \00314,01t\00315,01h\00300,01e\00315,01 e\00314,01n\00315,01d\00300,01 o\00315,01f\00300,01 \00314,01t\00315,01h\00300,01e \00315,01m\00314,01a\00315,01t\00300,01c\00315,01h\00300,01." } puthelp "PRIVMSG $dotaapi(channel) :\00304,01O\00307,01p\00308,01e\00303,01n\00309,01d\00311,01o\00312,01t\00306,01a\00304,01 L\00307,01i\00308,01n\00303,01k\00300,01: \00310,01https://www.opendota.com/matches/$lastmatchid" } else { putlog "openddota:recentmatch - No new match played since then" } #call the function in 40 minutes again timer 40 opendota:recent_match return 1 } else { putlog "opendota:recent_match - The owner_steamid or channel is not set accordingly" putlog "opendota:recent_match - Please add 'set dotaapi(owner_steamid) {YourSteamId32}' and 'set dotaapi(channel) {#ChannelYourBotIsIn}' before dotaapi.tcl in the bots config" return 0 } } #returns which type of game was played #TODO Add all game_modes proc opendota:game_type args { if { [llength $args] == 2 } { #game_mode: 22=normal, 23=turbo, 2=captains draft set game_mode [lindex $args 0] #lobby_type: 7=ranked, 0=unranked, 9=battlecup set lobby_type [lindex $args 1] switch $lobby_type { 0 { set result "unranked" } 7 { set result "ranked" } 9 { set result "battlecup" } default { set result "unknown lobby" } } switch $game_mode { 2 { set result "$result captains draft" } 22 { set result "$result normal" } 23 { set result "$result turbo" } default { set result "$result unknown game_mode" } } return $result } else { putlog "opendota:game_type - need 2 parameter: 1#: game_mode 2#: lobby_type" return 0 } } #returns if player was radiant or dire proc opendota:get_radiant_dire args { set radiant_dire_id [lindex $args 0] #player_slot: 0-4=radiant, 128-132=dire if { $radiant_dire_id < 5 } { return "radiant" } else { return "dire" } } #returns name of the lane proc opendota:get_lane_string args { set lane [lindex $args 0] #lane: 1=safelane, 2=mid, 3=offlane switch $lane { 1 { set lane "safelane" } 2 { set lane "midlane" } 3 { set lane "offlane" } } return $lane } #get hero name through hero id #this procedure shouldnt actually be called everytime but rather #once per day or so and cached on the filesystem. Will do in a future update #TODO save hero infos locally and crosscheck for updates daily proc opendota:get_hero_name args { set hero_id [lindex $args 0] set url "https://api.opendota.com/api/heroes" set query "" set res [rest::simple $url $query { method get content-type json }] #converting the received json to a dict set d_result [json::json2dict $res] #set heroinfo [lindex $d_result $hero_id-2] foreach hero_info $d_result { if { [dict get $hero_info "id"] == $hero_id } { set hero_name [dict get $hero_info "localized_name"] break } } return $hero_name } #Bot replies with a msg with Wins and Losses of given steamid32 proc msg:restwl { nick host hand args } { set steamid [lindex $args 0] putlog "W/L Call for SteamId $steamid by $nick received" if { [steam:checkid $steamid] } { set url "https://api.opendota.com/api/players/$steamid/wl" set query "$steamid" set res [rest::simple $url $query { method get content-type json }] #converting the received json to a dict set d_result [json::json2dict $res] #Only respond if the received json contained keys and values #and color the response flashy if { [dict size $d_result] > 0 } { puthelp "PRIVMSG $nick :\0033 Wins: \003 [dict get $d_result "win"]" puthelp "PRIVMSG $nick :\0034 Loses: \003 [dict get $d_result "lose"]" } } else { puthelp "PRIVMSG $nick :Please call this function by messaging the bot with '/msg kimbot wl {yourSteamId}'" } } proc msg:httpswl { nick host hand args } { global owner_steamid set token [::http::geturl "https://api.opendota.com/api/players/$steamid/wl" -type application/json] putlog "Token: $token" putlog "Data in Token: [::http::data $token]" puthelp "PRIVMSG $nick :[::http::data $token]" }