#!/usr/bin/tclsh set ov(server.host) "world.openverse.com" set ov(server.port) 7000 set ov(server.socket) 0 set ov(client.ip) "0.0.0.0" set ov(client.nick) "Unununium" set ov(client.x) 30 set ov(client.y) 120 set ov(wait_forever) 0 set ov(nick_change) "true" # Send $msg to OV server proc send_to_server {msg} { global ov # Check if connected if {[string compare $ov(server.socket) 0] != 0} { puts $ov(server.socket) $msg flush $ov(server.socket) } else { ansi_color "blue" "true" puts -nonewline "* " ansi_color "white" "false" puts -nonewline "Error: " ansi_color "white" "true" puts -nonewline "Connect first" line_break } } # Emit an ANSI color code proc ansi_color {color bold} { set code "\033\[0\;" if {[string compare $bold "true"] == 0} { append code "1\;" } switch -- $color { "black" {append code 30} "red" {append code 31} "green" {append code 32} "yellow" {append code 33} "blue" {append code 34} "magenta" {append code 35} "cyan" {append code 36} "white" {append code 37} } append code "m" puts -nonewline $code } # Resets the color and goes to the next line proc line_break {} { puts "\033\[0m" } # Connect to OV server proc connect_to_server {} { global ov # Log the connection ansi_color "blue" "true" puts -nonewline "* " ansi_color "white" "false" puts -nonewline "Trying to connect to " ansi_color "white" "true" puts -nonewline "$ov(server.host):$ov(server.port)" line_break # Connect and remember the socket if {[catch {set ov(server.socket) [socket $ov(server.host) $ov(server.port)]}]} { # Log the failure ansi_color "blue" "true" puts -nonewline "* " ansi_color "white" "false" puts -nonewline "Error: " ansi_color "white" "true" puts -nonewline "Unable to connect" line_break } else { # Find the client's IP address fconfigure $ov(server.socket) -blocking 0 set ov(client.ip) [lindex [fconfigure $ov(server.socket) -sockname] 0] # Call read_from_server when server sends data fileevent $ov(server.socket) readable read_from_server # Log into server send_to_server "AUTH $ov(client.nick) $ov(client.x) $ov(client.y) default.gif 0 0 0 0 0" # Log the success ansi_color "blue" "true" puts -nonewline "* " ansi_color "white" "false" puts -nonewline "Connected! Nick: " ansi_color "white" "true" puts -nonewline $ov(client.nick) ansi_color "white" "false" puts -nonewline " IP: " ansi_color "white" "true" puts -nonewline $ov(client.ip) line_break } } # Disconnect from the server proc disconnect_from_server {} { global ov # Log the disconnect ansi_color "blue" "true" puts -nonewline "* " ansi_color "white" "false" puts -nonewline "Disconnected from " ansi_color "white" "true" puts -nonewline "$ov(server.host):$ov(server.port)" line_break # Close the socket close $ov(server.socket) # Clean up variables set ov(server.socket) 0 set ov(client.ip) "0.0.0.0" } # Converts a ^E color code to a ^C code proc ctrl_e_to_ctrl_c {e} { switch -- $e { "0" {set c "1"} "1" {set c "7"} "2" {set c "3"} "3" {set c "5"} "4" {set c "2"} "5" {set c "6"} "6" {set c "10"} "7" {set c "15"} ")" {set c "14"} "!" {set c "4"} "@" {set c "9"} "#" {set c "8"} "$" {set c "12"} "%" {set c "13"} "^" {set c "11"} "&" {set c "16"} default {set c "99"} } return $c } # Process user input proc process_stdin {} { global ov set msg [gets stdin] # Replace ^K (\x0b) with ^C (\x03) # Replace ^E (\x05) with equivalent ^C code for {set index 0} {$index < [string length $msg]} {incr index} { if {[string compare [string index $msg $index] "\x0b"] == 0} { set msg [string replace $msg $index $index "\x03"] } if {[string compare [string index $msg $index] "\x05"] == 0} { set msg [string replace $msg $index [expr $index + 2] "\x03[ctrl_e_to_ctrl_c [string index $msg [expr $index + 1]]],[ctrl_e_to_ctrl_c [string index $msg [expr $index + 2]]]"] } } set parms [split $msg " "] switch -- [string tolower [lindex $parms 0]] { "/connect" { # Check if already connected if {[string compare $ov(server.socket) 0] == 0} { # Check if host and port are provided on commandline if {[llength $parms] > 2} { # Yes; use 'em set ov(server.host) [lindex $parms 1] set ov(server.port) [lindex $parms 2] } # Actually connect to the server connect_to_server } else { # Log the error ansi_color "blue" "true" puts -nonewline "* " ansi_color "white" "false" puts -nonewline "Error: " ansi_color "white" "true" puts -nonewline "Disconnect first" line_break } } "/disconnect" { # Check if connected if {[string compare $ov(server.socket) 0] == 0} { # Not connected; log the error ansi_color "blue" "true" puts -nonewline "* " ansi_color "white" "false" puts -nonewline "Error: " ansi_color "white" "true" puts -nonewline "Connect first" line_break } else { disconnect_from_server } } "/sing" { send_to_server "SCHAT SING [strip $msg 1]" } "/steam" { send_to_server "SCHAT STEAM [strip $msg 1]" } "/love" { send_to_server "SCHAT LOVE [strip $msg 1]" } "/idea" { send_to_server "SCHAT IDEA [strip $msg 1]" } "/smile" { send_to_server "SCHAT SMILE [strip $msg 1]" } "/frown" { send_to_server "SCHAT FROWN [strip $msg 1]" } "/wink" { send_to_server "SCHAT WINK [strip $msg 1]" } "/url" { send_to_server "URL * [strip $msg 1]" } "/msg" { send_to_server "PRIVMSG [lindex $parms 1] [strip $msg 2]" } "/effect" { send_to_server "EFFECT [string toupper [strip $msg 1]]" } "/nick" { # Change the user's nick # Check if connected if {[string compare $ov(server.socket) 0] == 0} { # Not connected; change nick locally # Log the change ansi_color "blue" "true" puts -nonewline "* " ansi_color "white" "false" puts -nonewline "Nick changed from " ansi_color "white" "true" puts -nonewline "$ov(client.nick) " ansi_color "white" "false" puts -nonewline "to " # Change it! set ov(client.nick) [lindex $parms 1] # Log the change, part two ansi_color "white" "true" puts -nonewline "$ov(client.nick) " ansi_color "white" "false" puts -nonewline "while offline" line_break } else { # Connected; request a nick change from the server send_to_server "NICK [lindex $parms 1]" } } "/direct" { # Send a message directly to the server send_to_server [strip $msg 1] } "/whois" { send_to_server "WHOIS [strip $msg 1]" } default { send_to_server "CHAT $msg" } } } # Accept data from the OV server proc read_from_server {} { global ov # Test for EOF if {[eof $ov(server.socket)] == 1} { disconnect_from_server return } # The entire message from the server set msg [gets $ov(server.socket)] # The message split into a list set parms [split $msg " "] # Do something based on the first word of $msg switch -- [lindex $parms 0] { "PING" { # Respond to the PING send_to_server "PONG" } "NAMEINUSE" { # The user's nick is already in use # Log the problem ansi_color "blue" "true" puts -nonewline "* " ansi_color "white" "false" puts -nonewline "Error: " ansi_color "white" "true" puts -nonewline "Nick \"$ov(client.nick)\" in use" line_break # Break the connection disconnect_from_server } "ROOMFULL" { # The room's full # Log the problem ansi_color "blue" "true" puts -nonewline "* " ansi_color "white" "false" puts -nonewline "Error: " ansi_color "white" "true" puts -nonewline "Room \"$ov(server.host):$ov(server.port)\" is full" # Break the connection disconnect_from_server } "CHAT" { # Show the CHAT message set who [lindex $parms 1] ansi_color "white" "true" puts -nonewline "> " if {[string compare $who $ov(client.nick)] == 0} { ansi_color "cyan" "true" } else { ansi_color "blue" "true" } puts -nonewline "$who" ansi_color "white" "false" puts -nonewline ": " show_chat $msg 2 line_break } "SCHAT" { # Show the SCHAT message set type [lindex $parms 1] set who [lindex $parms 2] ansi_color "yellow" "true" puts -nonewline "> " if {[string compare $who $ov(client.nick)] == 0} { ansi_color "cyan" "true" } else { ansi_color "blue" "true" } puts -nonewline "$who " ansi_color "white" "false" puts -nonewline "\[" switch -- $type { "SING" { ansi_color "cyan" "false" puts -nonewline "singing" } "LOVE" { ansi_color "red" "true" puts -nonewline "adoring" } "STEAM" { ansi_color "black" "true" puts -nonewline "fuming" } "IDEA" { ansi_color "yellow" "false" puts -nonewline "thinking" } "SMILE" { ansi_color "green" "true" puts -nonewline "smiling" } "FROWN" { ansi_color "red" "false" puts -nonewline "frowning" } "WINK" { ansi_color "yellow" "true" puts -nonewline "winking" } default { ansi_color "white" "true" puts -nonewline "$type" } } ansi_color "white" "false" puts -nonewline "\]: " show_chat $msg 3 line_break } "NEW" { # Announce the new guy set who [lindex $parms 1] if {[string compare $ov(nick_change) "true"] == 0} { set ov(nick_change) "false" set ov(client.nick) $who } ansi_color "green" "true" puts -nonewline "+ " if {[string compare $who $ov(client.nick)] == 0} { ansi_color "cyan" "true" } else { ansi_color "blue" "true" } puts -nonewline "$who" ansi_color "green" "true" puts -nonewline " arrives" line_break } "NOMORE" { # Announce the exit set who [lindex $parms 1] ansi_color "red" "true" puts -nonewline "+ " if {[string compare $who $ov(client.nick)] == 0} { ansi_color "cyan" "true" set ov(nick_change) "true" } else { ansi_color "blue" "true" } puts -nonewline "$who " ansi_color "red" "true" puts -nonewline "departs" line_break } "PRIVMSG" { # Show the whispered private message set who [lindex $parms 1] ansi_color "blue" "false" puts -nonewline "> " if {[string compare $who $ov(client.nick)] == 0} { ansi_color "cyan" "true" } else { ansi_color "blue" "true" } puts -nonewline "$who " ansi_color "white" "false" puts -nonewline "\[" ansi_color "blue" "false" puts -nonewline "private" ansi_color "white" "false" puts -nonewline "\]: " show_chat $msg 2 line_break } "ROOMNAME" { # Show the room name ansi_color "blue" "true" puts -nonewline "* " ansi_color "white" "false" puts -nonewline "Room name: " show_chat $msg 1 line_break } "URL" { # Show the URL set who [lindex $parms 1] ansi_color "magenta" "false" puts -nonewline "@ " if {[string compare $who $ov(client.nick)] == 0} { ansi_color "cyan" "true" } else { ansi_color "blue" "true" } puts -nonewline "$who" ansi_color "white" "false" puts -nonewline ": " show_chat $msg 2 line_break } "EFFECT" { # Show the effect set who [lindex $parms 1] ansi_color "magenta" "true" puts -nonewline "# " if {[string compare $who $ov(client.nick)] == 0} { ansi_color "cyan" "true" } else { ansi_color "blue" "true" } puts -nonewline "$who " ansi_color "white" "false" switch -- [string tolower [lindex $parms 2]] { "jump" {puts -nonewline "jumps"} "shiver" {puts -nonewline "shivers"} default {puts -nonewline "[lindex $parms 2]"} } line_break } "WHOIS" { # Show the user's IP set who [lindex $parms 1] set ip [lindex $parms 2] set ip [string range $ip [expr [string last "@" $ip] + 1] end] ansi_color "cyan" "false" puts -nonewline "? " if {[string compare $who $ov(client.nick)] == 0} { ansi_color "cyan" "true" } else { ansi_color "blue" "true" } puts -nonewline "$who" ansi_color "white" "false" puts -nonewline ": " ansi_color "cyan" "true" puts -nonewline $ip line_break } } } # Strips the first $count words off $msg and returns the result proc strip {msg count} { for {set i 0} {$i < $count} {incr i} { set msg [string range $msg [expr [string first " " $msg] + 1] end] } return $msg } # Strips the first $count words off $msg, and then displays it in color proc show_chat {msg count} { # Get rid of the commands at the begining set msg [strip $msg $count] # Replace the ^K-style control codes for ANSI control codes (colorize!) # Find the first control code set index [string first "\x03" $msg] set index_2 "" set code "" set fg "" set bg "" set ansi "" set len "" # Loop until there are no more codes while {$index != "-1"} { # Find the length of the code set len 0 for {set index_2 [expr $index + 1]} {$len < 3} {incr index_2; incr len} { switch -- [string index $msg $index_2] { 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 {} "," {set len 0} default {break} } } # Extract the fg,bg part of the code set code [split [string range $msg [expr $index + 1] [expr $index_2 - 1]] ","] set fg [lindex $code 0] set bg [lindex $code 1] # Trim leading zeroes from fg and bg if {[string compare [string trimleft $fg "0"] ""] != 0} { set fg [string trimleft $fg "0"] } if {[string compare [string trimleft $bg "0"] ""] != 0} { set bg [string trimleft $bg "0"] } # Calculate the ANSI equivalent set ansi "\033\[0\;" switch -- $fg { "1" {append ansi "30"} "2" {append ansi "34"} "3" {append ansi "32"} "4" {append ansi "1\;31"} "5" {append ansi "33"} "6" {append ansi "35"} "7" {append ansi "31"} "8" {append ansi "1\;33"} "9" {append ansi "1\;32"} "10" {append ansi "36"} "11" {append ansi "1\;36"} "12" {append ansi "1\;34"} "13" {append ansi "1\;35"} "14" {append ansi "1\;30"} "15" {append ansi "37"} default {append ansi "1\;37"} } switch -- $bg { "0" {append ansi "\;5\;47"} "2" {append ansi "\;44"} "3" {append ansi "\;42"} "4" {append ansi "\;5\;41"} "5" {append ansi "\;43"} "6" {append ansi "\;45"} "7" {append ansi "\;41"} "8" {append ansi "\;5\;43"} "9" {append ansi "\;5\;42"} "10" {append ansi "\;46"} "11" {append ansi "\;5\;46"} "12" {append ansi "\;5\;44"} "13" {append ansi "\;5\;45"} "14" {append ansi "\;5\;40"} "15" {append ansi "\;47"} "16" {append ansi "\;5\;47"} default {append ansi "\;40"} } append ansi "m" # Replace the ^K code with the ANSI code set msg [string replace $msg $index [expr $index_2 - 1] $ansi] # Find the next control code set index [string first "\x03" $msg $index_2] } # Start with white ansi_color "white" "true" # Print the message puts -nonewline $msg } # Call send_stdin_to_server when user types something fileevent stdin readable process_stdin # Wait forever vwait ov(wait_forever)