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
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 irc.freenode.org
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
break
}
}
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 https://wiki.tcl-lang.org/6601" ;#}
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 _ https://wiki.tcl-lang.org/20205 ;#}
set patterns(zzz) {set _ "zzz well!" ;#}
set patterns(man) safeman
proc safeman args {return https://www.tcl-lang.org/man/tcl8.4/TclCmd/[lindex $args 1].htm}
set {patterns(where can I read about)} gotowiki
proc gotowiki args {return "Try https://wiki.tcl-lang.org/[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 foreverExamples from the chat:
suchenwi minibot, which is your Wiki page? <minibot> https://wiki.tcl-lang.org/20205 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 chat.freenode.net
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 foreverCredit goes to: http://wiki.shellium.org/w/Writing_an_IRC_bot_in_Tcl