Richard Suchenwirth 2007-10-21 - In the Tcl chatroom we discussed IRC chatting, and one user was not happy with picoIRC 0.2 - he didn't want the GUI, only the bot behavior... which I modelled as a first shot to

  • connect to channel #tcl on IRC
  • listen to what is said
  • if someone mentions its name (minibot), tries to parse the message and answer

So, in several iterations, this script minibot.tcl came out, which has some rudimentary knowledge of Chinese and Russian, can do math, and string toupper:

 #!/usr/bin/env tclsh
 set ::server
 set ::chan   #tcl
 set ::me     minibot 
 proc recv {} {
     gets $::fd line
     puts $line
     # handle PING messages from server
     if {[lindex [split $line] 0] eq "PING"} {
        send "PONG [info hostname] [lindex [split $line] 1]"; return
     if {[regexp {:([^!]*)![^ ].* +PRIVMSG ([^ :]+) +(.*[Mm]inibot)(.+)} $line -> \
         nick target msg cmd]} {
            if {$nick eq "ijchain"} {regexp {<([^>]+)>(.+)} $msg -> nick msg}
            set hit 0
            foreach pattern [array names ::patterns] {
                if [string match "*$pattern*" $cmd] {
                    set cmd [string trim $cmd {.,:? }]
                    if [catch {mini eval $::patterns($pattern) $cmd} res] {
                        set res $::errorInfo
                    foreach line [split $res \n] {
                        send "PRIVMSG $::chan :$line"
                    incr hit
            if !$hit {send "PRIVMSG $::chan :Sorry, no idea."}

#----------- Patterns for response:

 set patterns(time) {clock format [clock sec] ;#}
 set patterns(expr) safeexpr
 proc safeexpr args {expr [string map {\[ ( \] ) expr ""} $args]}
 set patterns(eggdrop) {set _ "Please check" ;#}
 set patterns(toupper) string
 set patterns(Windows) {set _ "I'd prefer not to discuss Windows..." ;#}
 set {patterns(translate "good" to Russian)} {set _ \u0425\u043E\u0440\u043E\u0448\u043E ;#}
 set patterns(Beijing) {set _ \u5317\u4EAC ;#}
 set patterns(Tokyo) {set _ \u4E1C\u4EAC ;#}
 set {patterns(your Wiki page)} {set _ ;#}
 set patterns(zzz) {set _ "zzz well!" ;#}
 set patterns(man) safeman
 proc safeman args {return[lindex $args 1].htm}
 set {patterns(where can I read about)} gotowiki
 proc gotowiki args {return "Try[lindex $args end]"}
 set patterns(thank) {set _ "You're welcome." ;#}
 set patterns(worry) worry
 proc worry args {
    return "Why do [string map {I you my your your my you me} $args]?"

#-- let the show begin... :^)

 interp create -safe mini
 foreach i {safeexpr safeman gotowiki worry} {
     interp alias mini $i {} $i
 proc in {list element} {expr {[lsearch -exact $list $element]>=0}}
 proc send str {puts $::fd $str;flush $::fd}

 set ::fd [socket $::server 6667]
 fconfigure $fd  -encoding utf-8
 send "NICK minibot"
 send "USER $::me 0 * :PicoIRC user"
 send "JOIN $::chan"
 fileevent $::fd readable recv

 vwait forever

Examples from the chat:

 suchenwi  minibot, which is your Wiki page?
 suchenwi  ah, thanks 
 suchenwi  minibot expr 6*7
 <minibot> 42
 suchenwi  minibot, what's your local time?
 <minibot> Sun Oct 21 01:26:59 (MEZ) - Mitteleurop. Sommerzeit 2007

With sufficient tweaking, this could be used to replace Eggdrop some day :^)

Lectus: I was searching for some example code of an IRC bot, but instead using irc library from tcllib which hides the IRC protocol details. Found the code below on the internet:

#!/usr/bin/env tclsh8.5
# (If that doesn't work, try /usr/bin/tclsh8.5 or /usr/local/bin/tclsh8.5)

# First off, get the irc package out of tcllib.
package require irc

# This creates a variable called name which contains the name - you'll probably want to change this.
# Note that two : in front of a variable name just means that it's in the global namespace.
set name tclbot

# More variables, with the channel and server names.
set server
set channel #shellium-bots

# Now, create the connection object. This just creates a handle to use for managing the connection, and saves it to a variable
set conn [irc::connection]

# Next, register an "event handler". These are called when the relevant IRC command is received
$conn registerevent PRIVMSG {
    # The "msg" command can be used inside an event handler to get the IRC message. "split" is used to split the message into a list at blanks.
    set msg [split [msg]]
    # If the bot received the message directly, then send it back the person directly
    # Otherwise, send it to the channel
    # The "who" command (event handler only) says who sent the message
    # The "target" command (event handler only) says who the message was sent to
    if {[target] eq $::name} {
        set who [who]
    } else {
        set who [target]
    # Now, only do further processing if the first word is the bot's name. "string tolower" makes a string lowercase. "lindex" returns a list item at a certain index.
    if {[string tolower [lindex $msg 0]] eq $::name} {
        # Switch, based on the second word of the message.
        switch [string tolower [lindex $msg 1]] {
            stardate {
                # Send a message with the current stardate.
                # the "privmsg" subcommand sends a message to a user or channel
                # "clock seconds" returns the number of seconds since the Unix Epoch
                # "clock format" formats a Unix date into a human-readable format. We'll be taking advantage of an easter egg that returns the Stardate.
                $::conn privmsg $who [clock format [clock seconds] -format "%Q"]
            default {
                # This is called if the second word of the message doesn't match anything else in the "switch".
                # We'll just send an ACTION (/me) saying that this bot shrugs. \001 means ASCII char 1 (SOH).
                $::conn privmsg $who "\001ACTION shrugs\001"

# Now that all the event handlers we need are registered, connect. These commands should be pretty self-explanatory.
$conn connect $server
$conn nick $name
$conn user $name $name $name "A little pure-Tcl bot"
$conn join $channel

# Now just sit and wait for messages - the irc package handles everything else.
vwait forever

Credit goes to: