[Richard Suchenwirth] 2007-01-31: The code of [picoIRC 0.2] is sweet and short, but a user asked for explanations on how and why things are done. So, here goes: [WikiDbImage irc.gif] === proc , args {#avoid clash with comment syntax highlighting on a wiki page} , We will use the [Tk] GUI toolkit, so best tell it early on} package require Tk , Set some parameters (user may want to modify these) set ::server irc.freenode.org set ::chan #tcl set ::me $tcl_platform(user) , The GUI consists mostly of a [text] widget: text .t -height 30 -wrap word -font {Arial 9} , Some tags to make emphasized strings look nicer: .t tag config bold -font [linsert [.t cget -font] end bold] .t tag config italic -font [linsert [.t cget -font] end italic] .t tag config blue -foreground blue , Second UI element is an [entry] widget for typing messages entry .cmd , Brought to screen by [pack] (entry first, so it doesn't disappear on resizing) pack .cmd -side bottom -fill x pack .t -fill both -expand 1 , When is hit in the entry, the command ''post'' is executed bind .cmd post , But first for the receiving proc, which is called when the socket has new , content: proc recv {} { gets $::fd line ;#-- read a line from socket # handle PING messages from server if {[lindex [split $line] 0] eq "PING"} { send "PONG [info hostname] [lindex [split $line] 1]" return } #-- Add some markup for messages and actions if {[regexp {:([^!]*)![^ ].* +PRIVMSG ([^ :]+) +:(.*)} $line -> \ nick target msg]} { set tag {} if [regexp {\001ACTION(.+)\001} $msg -> msg] {set tag italic} #-- suppress the names of two well-known bridge bots if [in {azbridge ijchain} $nick] {regexp {<([^>]+)>(.+)} $msg -> nick msg} #-- Display nick in bold, message as tagged above .t insert end $nick\t bold $msg\n $tag } else {.t insert end $line\n italic} #-- make sure the last part is visible .t yview end } , Just a little helper to check list membership proc in {list element} {expr {[lsearch -exact $list $element]>=0}} , This command is called on proc post {} { set msg [.cmd get] ;#-- read what was typed in the entry #-- Lines beginning with /me are marked as actions: if [regexp {^/me (.+)} $msg -> action] {set msg "\001ACTION $action\001"} #-- Multiple lines (as e.g. from pasting) are sent one by one: foreach line [split $msg \n] {send "PRIVMSG $::chan :$line"} .cmd delete 0 end ;#-- clear the entry #-- the sent message is locally reported in the text, with possible markup set tag "" if [regexp {\001ACTION(.+)\001} $msg -> msg] {set tag italic} .t insert end $::me\t {bold blue} $msg\n [list blue $tag] #-- make sure the end of the text widget is visible .t yview end } , A very simple wrapper for sending a string, not forgetting to [flush] proc send str {puts $::fd $str; flush $::fd} , And now, the action begins with "logging in" to the IRC server set ::fd [socket $::server 6667] send "NICK $::me" send "USER $::me 0 * :PicoIRC user" send "JOIN $::chan" , If the [socket] gets readable, ''recv'' will be called fileevent $::fd readable recv , This is just a convenient debugging helper for rapid restart after changes bind . {exec wish $argv0 &; exit} === ---- How does this script deal with the ping-pong requirements of an IRC server? Is that handled by the socket itself? [RS]: The script as above worked nicely from home (at work, IRC is blocked). I think I remember timeouts if I wasn't chatting for a while, but I'm not an IRC specialist anyway... Who knows better, please let us know! [MJ]: The handling of the PING request has been added to the script. Regarding that particular segment of code; ====== send "PONG [info hostname]" ====== can someone explain the [info hostname] command to me? [LV]: Did you read the wiki page to which you referred? Basically, every computer has some sort of name - info hostname returns that name. No I didn't read that page... posted the question without even looking at it afterwards. Many thanks to you all though! You've been very helpful! ---- Alternatively, some servers send ping requests with a unique key that must be returned. a rawlog excerpt from xchat on irc.sorcery.net: ======none << PING LAG2509966063 >> :nebula.sorcery.net PONG nebula.sorcery.net :LAG2509966063 ====== In that case, the following replacement of the server code can suffice (if not, please edit): ====== if {[lindex [split $line] 0] eq {PING}} { send "PONG [lindex [split $line] 1]" return } ====== [MJ]: The return from xchat seems to violate RFC1459, to quote: ======none Command: PONG Parameters: [] PONG message is a reply to ping message. If parameter is given this message must be forwarded to given daemon. The parameter is the name of the daemon who has responded to PING message and generated this message. ====== So the first parameter of pong should be the name of the daemon who has responded (e.g. the name of the client) Therefore the way I read it, the correct reply should be: ====== if {[lindex [split $line] 0] eq {PING}} { send "PONG [info hostname] [lindex [split $line] 1]" ====== <> Example | Networking | Channel | Tutorial