irc.tcl

Go to the documentation of this file.
00001 /*  irc.tcl --*/
00002 /* */
00003 /*  irc implementation for Tcl.*/
00004 /* */
00005 /*  Copyright (c) 2001-2003 by David N. Welton <davidw@dedasys.com>.*/
00006 /*  This code may be distributed under the same terms as Tcl.*/
00007 /* */
00008 /*  $Id: irc.tcl,v 1.26 2006/04/23 22:35:57 patthoyts Exp $*/
00009 
00010 package require Tcl 8.3
00011 
00012 namespace ::irc {
00013     variable version 0.6
00014 
00015     /*  counter used to differentiate connections*/
00016     variable conn 0
00017     variable config
00018     variable irctclfile [info script]
00019     array  config =  {
00020         debug 0
00021         logger 0
00022     }
00023 }
00024 
00025 /*  ::irc::config --*/
00026 /* */
00027 /*  Set global configuration options.*/
00028 /* */
00029 /*  Arguments:*/
00030 /* */
00031 /*  key name of the configuration option to change.*/
00032 /* */
00033 /*  value   value of the configuration option.*/
00034 
00035 ret  ::irc::config ( type args ) {
00036     variable config
00037     if { [llength $args] == 0 } {
00038         return [array get config]
00039     } elseif { [llength $args] == 1 } {
00040         return $config($key)
00041     } elseif { [llength $args] > 2 } {
00042         error "wrong # args: should be \"config key ?val?\""
00043     }
00044     set key [lindex $args 0]
00045     set value [lindex $args 1]
00046     foreach ns [namespace children] {
00047         if { [info exists config($key)] && [info exists ${ns}::config($key)] \
00048                 && [set ${ns}::config($key)] == $config($key)} {
00049             ${ns}::cmd-config $key $value
00050         }
00051     }
00052     set config($key) $value
00053 }
00054 
00055 
00056 /*  ::irc::connections --*/
00057 /* */
00058 /*  Return a list of handles to all existing connections*/
00059 
00060 ret  ::irc::connections ( ) {
00061     set r {}
00062     foreach ns [namespace children] {
00063         lappend r ${ns}::network
00064     }
00065     return $r
00066 }
00067 
00068 /*  ::irc::reload --*/
00069 /* */
00070 /*  Reload this file, and merge the current connections into*/
00071 /*  the new one.*/
00072 
00073 ret  ::irc::reload ( ) {
00074     variable conn
00075     set oldconn $conn
00076     namespace eval :: {
00077     source [set ::irc::irctclfile]
00078     }
00079     foreach ns [namespace children] {
00080         foreach var {sock logger host port} {
00081             set $var [set ${ns}::$var]
00082         }
00083         array set dispatch [array get ${ns}::dispatch]
00084         array set config [array get ${ns}::config]
00085         # make sure our new connection uses the same namespace
00086         set conn [string range $ns 10 end]
00087         ::irc::connection
00088         foreach var {sock logger host port} {
00089             set ${ns}::$var [set $var]
00090         }
00091         array set ${ns}::dispatch [array get dispatch]
00092         array set ${ns}::config [array get config]
00093     }
00094     set conn $oldconn
00095 }
00096 
00097 /*  ::irc::connection --*/
00098 /* */
00099 /*  Create an IRC connection namespace and associated commands.*/
00100 
00101 ret  ::irc::connection ( type args ) {
00102     variable conn
00103     variable config
00104 
00105     # Create a unique namespace of the form irc$conn::$host
00106 
00107     set name [format "%s::irc%s" [namespace current] $conn]
00108 
00109     namespace eval $name {
00110     set sock {}
00111     array set dispatch {}
00112     array set linedata {}
00113     array set config [array get ::irc::config]
00114     if { $config(logger) || $config(debug)} {
00115         package require logger
00116             set logger [logger::init [namespace tail [namespace current]]]
00117             if { !$config(debug) } { ${logger}::disable debug }
00118         }
00119     
00120 
00121     # ircsend --
00122     # send text to the IRC server
00123 
00124     proc ircsend { msg } {
00125         variable sock
00126         variable dispatch
00127         if { $sock == "" } { return }
00128         cmd-log debug "ircsend: '$msg'"
00129         if { [catch {puts $sock $msg} err] } {
00130             catch { close $sock }
00131             set sock {}
00132         if { [info exists dispatch(EOF)] } {
00133             eval $dispatch(EOF)
00134         }
00135         cmd-log error "Error in ircsend: $err"
00136         }
00137     }
00138 
00139 
00140     #########################################################
00141     # Implemented user-side commands, meaning that these commands
00142     # cause the calling user to perform the given action.
00143     #########################################################
00144 
00145 
00146         # cmd-config --
00147         #
00148         # Set or return per-connection configuration options.
00149         #
00150         # Arguments:
00151         #
00152         # key   name of the configuration option to change.
00153         #
00154         # value value (optional) of the configuration option.
00155 
00156         proc cmd-config { args } {
00157             variable config
00158         variable logger
00159         
00160         if { [llength $args] == 0 } {
00161         return [array get config]
00162         } elseif { [llength $args] == 1 } {
00163         return $config($key)
00164         } elseif { [llength $args] > 2 } {
00165         error "wrong # args: should be \"config key ?val?\""
00166         }
00167         set key [lindex $args 0]
00168         set value [lindex $args 1]
00169             if { $key == "debug" } {
00170                 if {$value} {
00171                     if { !$config(logger) } { cmd-config logger 1 }
00172                     ${logger}::enable debug
00173                 } elseif { [info exists logger] } {
00174                     ${logger}::disable debug
00175             }
00176             }
00177             if { $key == "logger" } {
00178                 if { $value && !$config(logger)} {
00179                     package require logger
00180                     set logger [logger::init [namespace tail [namespace current]]]
00181                 } elseif { [info exists logger] } {
00182                     ${logger}::delete
00183                     unset logger
00184             }
00185             }
00186             set config($key) $value
00187         }
00188         
00189         proc cmd-log {level text} {
00190         variable logger
00191             if { ![info exists logger] } return
00192             ${logger}::$level $text
00193         }
00194         
00195         proc cmd-logname { } {
00196             variable logger
00197             if { ![info exists logger] } return
00198             return $logger
00199         }
00200 
00201         # cmd-destroy --
00202         #
00203         # destroys the current connection and its namespace
00204 
00205         proc cmd-destroy { } {
00206             variable logger
00207             variable sock
00208             if { [info exists logger] } { ${logger}::delete }
00209             catch {close $sock}
00210             namespace delete [namespace current]
00211         }
00212 
00213         proc cmd-connected { } {
00214             variable sock
00215             if { $sock == "" } { return 0 }
00216             return 1
00217         }
00218 
00219     proc cmd-user { username hostname servername {userinfo ""} } {
00220         if { $userinfo == "" } {
00221         ircsend "USER $username $hostname server :$servername"
00222         } else {
00223         ircsend "USER $username $hostname $servername :$userinfo"
00224         }
00225     }
00226 
00227     proc cmd-nick { nk } {
00228         ircsend "NICK $nk"
00229     }
00230 
00231     proc cmd-ping { target } {
00232         ircsend "PRIVMSG $target :\001PING [clock seconds]\001"
00233     }
00234 
00235     proc cmd-serverping { } {
00236         ircsend "PING [clock seconds]"
00237     }
00238 
00239     proc cmd-ctcp { target line } {
00240         ircsend "PRIVMSG $target :\001$line\001"
00241     }
00242 
00243     proc cmd-join { chan {key {}} } {
00244         ircsend "JOIN $chan $key"
00245     }
00246 
00247     proc cmd-part { chan {msg ""} } {
00248         if { $msg == "" } {
00249         ircsend "PART $chan"
00250         } else {
00251         ircsend "PART $chan :$msg"
00252         }
00253     }
00254 
00255     proc cmd-quit { {msg {tcllib irc module - http://tcllib.sourceforge.net/}} } {
00256         ircsend "QUIT :$msg"
00257     }
00258 
00259     proc cmd-privmsg { target msg } {
00260         ircsend "PRIVMSG $target :$msg"
00261     }
00262 
00263     proc cmd-notice { target msg } {
00264         ircsend "NOTICE $target :$msg"
00265     }
00266 
00267     proc cmd-kick { chan target {msg {}} } {
00268         ircsend "KICK $chan $target :$msg"
00269     }
00270 
00271     proc cmd-mode { target args } {
00272         ircsend "MODE $target [join $args]"
00273     }
00274 
00275     proc cmd-topic { chan msg } {
00276         ircsend "TOPIC $chan :$msg"
00277     }
00278 
00279     proc cmd-invite { chan target } {
00280         ircsend "INVITE $target $chan"
00281     }
00282 
00283     proc cmd-send { line } {
00284         ircsend $line
00285     }
00286 
00287     proc cmd-peername { } {
00288         variable sock
00289         if { $sock == "" } { return {} }
00290         return [fconfigure $sock -peername]
00291     }
00292 
00293     proc cmd-sockname { } {
00294         variable sock
00295         if { $sock == "" } { return {} }
00296         return [fconfigure $sock -sockname]
00297     }
00298 
00299         proc cmd-socket { } {
00300             variable sock
00301             return $sock
00302         }
00303         
00304     proc cmd-disconnect { } {
00305         variable sock
00306         if { $sock == "" } { return -1 }
00307         catch { close $sock }
00308         set sock {}
00309         return 0
00310     }
00311 
00312     # Connect --
00313     # Create the actual tcp connection.
00314 
00315     proc cmd-connect { h {p 6667} } {
00316         variable sock
00317         variable host
00318         variable port
00319 
00320        set host $h
00321        set port $p
00322 
00323         if { $sock == "" } {
00324         set sock [socket $host $port]
00325         fconfigure $sock -translation crlf -buffering line
00326         fileevent $sock readable [namespace current]::GetEvent
00327         }
00328         return 0
00329     }
00330 
00331     # Callback API:
00332 
00333     # These are all available from within callbacks, so as to
00334     # provide an interface to provide some information on what is
00335     # coming out of the server.
00336 
00337     # action --
00338 
00339     # Action returns the action performed, such as KICK, PRIVMSG,
00340     # MODE etc, including numeric actions such as 001, 252, 353,
00341     # and so forth.
00342 
00343     proc action { } {
00344         variable linedata
00345         return $linedata(action)
00346     }
00347 
00348     # msg --
00349 
00350     # The last argument of the line, after the last ':'.
00351 
00352     proc msg { } {
00353         variable linedata
00354         return $linedata(msg)
00355     }
00356 
00357     # who --
00358 
00359     # Who performed the action.  If the command is called as [who address],
00360     # it returns the information in the form
00361     # nick!ident@host.domain.net
00362 
00363     proc who { {address 0} } {
00364         variable linedata
00365         if { $address == 0 } {
00366         return [lindex [split $linedata(who) !] 0]
00367         } else {
00368         return $linedata(who)
00369         }
00370     }
00371 
00372     # target --
00373 
00374     # To whom was this action done.
00375 
00376     proc target { } {
00377         variable linedata
00378         return $linedata(target)
00379     }
00380 
00381     # additional --
00382 
00383     # Returns any additional header elements beyond the target as a list.
00384 
00385     proc additional { } {
00386         variable linedata
00387         return $linedata(additional)
00388     }
00389 
00390     # header --
00391 
00392     # Returns the entire header in list format.
00393 
00394     proc header { } {
00395         variable linedata
00396         return [concat [list $linedata(who) $linedata(action) \
00397                 $linedata(target)] $linedata(additional)]
00398     }
00399 
00400     # GetEvent --
00401 
00402     # Get a line from the server and dispatch it.
00403 
00404     proc GetEvent { } {
00405         variable linedata
00406         variable sock
00407         variable dispatch
00408         array set linedata {}
00409         set line "eof"
00410         if { [eof $sock] || [catch {gets $sock} line] } {
00411         close $sock
00412         set sock {}
00413         cmd-log error "Error receiving from network: $line"
00414         if { [info exists dispatch(EOF)] } {
00415             eval $dispatch(EOF)
00416         }
00417         return
00418         }
00419         cmd-log debug "Recieved: $line"
00420         if { [set pos [string first " :" $line]] > -1 } {
00421         set header [string range $line 0 [expr {$pos - 1}]]
00422         set linedata(msg) [string range $line [expr {$pos + 2}] end]
00423         } else {
00424         set header [string trim $line]
00425         set linedata(msg) {}
00426         }
00427 
00428         if { [string match :* $header] } {
00429         set header [split [string trimleft $header :]]
00430         } else {
00431         set header [linsert [split $header] 0 {}]
00432         }
00433         set linedata(who) [lindex $header 0]
00434         set linedata(action) [lindex $header 1]
00435         set linedata(target) [lindex $header 2]
00436         set linedata(additional) [lrange $header 3 end]
00437         if { [info exists dispatch($linedata(action))] } {
00438         eval $dispatch($linedata(action))
00439         } elseif { [string match {[0-9]??} $linedata(action)] } {
00440         eval $dispatch(defaultnumeric)
00441         } elseif { $linedata(who) == "" } {
00442         eval $dispatch(defaultcmd)
00443         } else {
00444         eval $dispatch(defaultevent)
00445         }
00446     }
00447 
00448     # registerevent --
00449 
00450     # Register an event in the dispatch table.
00451 
00452     # Arguments:
00453     # evnt: name of event as sent by IRC server.
00454     # cmd: proc to register as the event handler
00455 
00456     proc cmd-registerevent { evnt cmd } {
00457         variable dispatch
00458         set dispatch($evnt) $cmd
00459         if { $cmd == "" } {
00460         unset dispatch($evnt)
00461         }
00462     }
00463 
00464     # getevent --
00465 
00466     # Return the currently registered handler for the event.
00467 
00468     # Arguments:
00469     # evnt: name of event as sent by IRC server.
00470 
00471     proc cmd-getevent { evnt } {
00472         variable dispatch
00473         if { [info exists dispatch($evnt)] } {
00474         return $dispatch($evnt)
00475         }
00476         return {}
00477     }
00478 
00479     # eventexists --
00480 
00481     # Return a boolean value indicating if there is a handler
00482     # registered for the event.
00483 
00484     # Arguments:
00485     # evnt: name of event as sent by IRC server.
00486 
00487     proc cmd-eventexists { evnt } {
00488         variable dispatch
00489         return [info exists dispatch($evnt)]
00490     }
00491 
00492     # network --
00493 
00494     # Accepts user commands and dispatches them.
00495 
00496     # Arguments:
00497     # cmd: command to invoke
00498     # args: arguments to the command
00499 
00500     proc network { cmd args } {
00501         eval [linsert $args 0 [namespace current]::cmd-$cmd]
00502     }
00503 
00504     # Create default handlers.
00505 
00506     set dispatch(PING) {network send "PONG :[msg]"}
00507     set dispatch(defaultevent) #
00508     set dispatch(defaultcmd) #
00509     set dispatch(defaultnumeric) #
00510     }
00511 
00512     set returncommand [format "%s::irc%s::network" [namespace current] $conn]
00513     incr conn
00514     return $returncommand
00515 }
00516 
00517 /*  -------------------------------------------------------------------------*/
00518 
00519 package provide irc $::irc::version
00520 
00521 /*  -------------------------------------------------------------------------*/
00522 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1