1 | # mysaint.tcl |
---|
2 | # |
---|
3 | # I got the inspiration to write this bot from the SmallSaints |
---|
4 | # bot on DalNet and the request of some Malay friends to create a clone. |
---|
5 | # |
---|
6 | # The bot sits on a channel and simply counts words that each user says. |
---|
7 | |
---|
8 | package require irk |
---|
9 | |
---|
10 | namespace eval ::mysaint { |
---|
11 | |
---|
12 | variable state |
---|
13 | |
---|
14 | set state(thisdir) [file dir [info script]] |
---|
15 | set state(words,save) [file join $state(thisdir) savedwords.dat] |
---|
16 | set state(save,interval) 30000 |
---|
17 | |
---|
18 | # For each identified user, there are several entries in the state array: |
---|
19 | # |
---|
20 | # state($user,words) How many words they have total. |
---|
21 | # state($user,session) How many words they said this time. |
---|
22 | # state($user,password) What's their password |
---|
23 | # state($user,ident) What's their ident |
---|
24 | # |
---|
25 | # We also keep a mapping from nicknames to users: |
---|
26 | # |
---|
27 | # state($nick,user) What's the user mask associated with this |
---|
28 | # nick name |
---|
29 | |
---|
30 | proc initialize {} { |
---|
31 | variable state |
---|
32 | variable thisdir |
---|
33 | |
---|
34 | # Set up a timer to save words: |
---|
35 | |
---|
36 | set state(saver) [after $state(save,interval) ::mysaint::save] |
---|
37 | |
---|
38 | # See if we have a saved words file. If so, read it in. |
---|
39 | |
---|
40 | if {[file exists $state(words,save)]} { |
---|
41 | uplevel #0 source $state(words,save) |
---|
42 | } |
---|
43 | } |
---|
44 | |
---|
45 | proc userSays {token nick user comm dest rest} { |
---|
46 | if {[string match "!*" [lindex $rest 0]]} { |
---|
47 | userSaysCommand $token $dest $nick $user \ |
---|
48 | [lindex $rest 0] [lrange $rest 1 end] |
---|
49 | } else { |
---|
50 | userSaysWords $nick $user [llength $rest] |
---|
51 | } |
---|
52 | } |
---|
53 | |
---|
54 | proc userSaysWords {nick user n} { |
---|
55 | variable state |
---|
56 | |
---|
57 | set seconds [clock seconds] |
---|
58 | if {![info exists state($user,session,time)]} { |
---|
59 | set state($user,session,time) [clock seconds] |
---|
60 | } |
---|
61 | set state($user,time) $seconds |
---|
62 | |
---|
63 | if {![info exists state($user,session)]} { |
---|
64 | set state($user,session) $n |
---|
65 | } else { |
---|
66 | incr state($user,session) $n |
---|
67 | } |
---|
68 | if {![info exists state($user,words)]} { |
---|
69 | set state($user,words) $n |
---|
70 | } else { |
---|
71 | incr state($user,words) $n |
---|
72 | } |
---|
73 | |
---|
74 | set state($nick,user) $user |
---|
75 | } |
---|
76 | |
---|
77 | proc userSaysCommand {token dest nick user cmd rest} { |
---|
78 | variable state |
---|
79 | |
---|
80 | # Determine destination of any reply we're going to send: |
---|
81 | |
---|
82 | if {![string compare $dest $state(nick)]} { |
---|
83 | set dest $nick |
---|
84 | } |
---|
85 | |
---|
86 | switch -exact $cmd { |
---|
87 | "!help" {userAsksHelp $token $nick} |
---|
88 | "!time" {userAsksTime $token $nick $user $rest $dest} |
---|
89 | "!heard" {userAsksHeard $token $nick $user $rest $dest} |
---|
90 | "!words" {userAsksWords $token $nick $user $rest $dest} |
---|
91 | "!donate" {userDonates $token $nick $user $rest $dest} |
---|
92 | "!pass" {::irk::pass $token mysaint $nick $user $rest} |
---|
93 | "!ident" {::irk::id $token mysaint $nick $user $rest} |
---|
94 | "!newpass" {::irk::np $token mysaint $nick $user $rest} |
---|
95 | } |
---|
96 | } |
---|
97 | |
---|
98 | proc userAsksHelp {token nick} { |
---|
99 | ::irk::say $token $nick \ |
---|
100 | [list Hello $nick, the following commands are supported: ] |
---|
101 | ::irk::say $token $nick "!help ---- prints this message" |
---|
102 | ::irk::say $token $nick "!words --- how many words you have" |
---|
103 | ::irk::say $token $nick "!donate -- to donate words to someone" |
---|
104 | after 2000 [list ::irk::say $token $nick \ |
---|
105 | "!heard --- when did some speak last time"] |
---|
106 | after 2500 [list ::irk::say $token $nick \ |
---|
107 | "!time ---- when someone connected"] |
---|
108 | after 5000 [list ::irk::say $token $nick \ |
---|
109 | "Note: To donate you have to have a password"] |
---|
110 | after 7500 [list ::irk::say $token $nick \ |
---|
111 | "Note: You can get a password with !pass"] |
---|
112 | } |
---|
113 | |
---|
114 | proc userAsksWords {token nick user rest dest} { |
---|
115 | variable state |
---|
116 | |
---|
117 | set target [lindex $rest 0] |
---|
118 | if {[string compare $target ""]} { |
---|
119 | if {[catch {set user $state($target,user)}]} { |
---|
120 | set who "The unknown user $target" |
---|
121 | set target "The unknown user $target" |
---|
122 | } else { |
---|
123 | set who $user |
---|
124 | } |
---|
125 | } else { |
---|
126 | set who "" |
---|
127 | } |
---|
128 | if {[string compare $who ""]} { |
---|
129 | if {[catch {set words $state($who,words)}]} { |
---|
130 | set words 0 |
---|
131 | } |
---|
132 | if {[catch {set session $state($who,session)}]} { |
---|
133 | set session 0 |
---|
134 | } |
---|
135 | set reply \ |
---|
136 | "$nick, $target has $session words now, total $words words" |
---|
137 | } else { |
---|
138 | if {[catch {set words $state($user,words)}]} { |
---|
139 | set words 0 |
---|
140 | } |
---|
141 | if {[catch {set session $state($user,session)}]} { |
---|
142 | set session 0 |
---|
143 | } |
---|
144 | set reply \ |
---|
145 | "$nick, you have $session words now, $words total words" |
---|
146 | } |
---|
147 | ::irk::say $token $dest $reply |
---|
148 | } |
---|
149 | |
---|
150 | proc userAsksHeard {token nick user rest dest} { |
---|
151 | variable state |
---|
152 | |
---|
153 | set target [lindex $rest 0] |
---|
154 | if {[string compare $target ""]} { |
---|
155 | if {[catch {set user $state($target,user)}]} { |
---|
156 | set answer "I haven't heard $target speak recently." |
---|
157 | } else { |
---|
158 | set time $state($user,time) |
---|
159 | set time [clock format $time] |
---|
160 | set answer "I heard $target speak last at $time" |
---|
161 | } |
---|
162 | } else { |
---|
163 | set answer "I'm talking to you just now, $nick" |
---|
164 | } |
---|
165 | |
---|
166 | ::irk::say $token $dest $answer |
---|
167 | } |
---|
168 | |
---|
169 | proc userAsksTime {token nick user rest dest} { |
---|
170 | variable state |
---|
171 | |
---|
172 | set target [lindex $rest 0] |
---|
173 | if {[string compare $target ""]} { |
---|
174 | if {[catch {set user $state($target,user)}]} { |
---|
175 | set answer "I don't know when $target connected" |
---|
176 | } else { |
---|
177 | if {[catch {set time $state($user,session,time)}]} { |
---|
178 | set answer "I don't know when $target connected" |
---|
179 | } else { |
---|
180 | set time [clock format $time] |
---|
181 | set answer \ |
---|
182 | "I first noticed $target at $time" |
---|
183 | } |
---|
184 | } |
---|
185 | } else { |
---|
186 | if {[catch {set user $state($nick,user)}]} { |
---|
187 | set answer "$nick, I don't know when you connected" |
---|
188 | } else { |
---|
189 | if {[catch {set time $state($user,session,time)}]} { |
---|
190 | set answer "$nick, I don't know when you connected" |
---|
191 | } else { |
---|
192 | set time [clock format $time] |
---|
193 | set answer \ |
---|
194 | "$nick, I first noticed you at $time" |
---|
195 | } |
---|
196 | } |
---|
197 | } |
---|
198 | |
---|
199 | ::irk::say $token $dest $answer |
---|
200 | } |
---|
201 | |
---|
202 | # Start the bot: |
---|
203 | |
---|
204 | proc start {token} { |
---|
205 | variable state |
---|
206 | |
---|
207 | set state($token,token) $token |
---|
208 | |
---|
209 | set symsrv $::irk::state($token,symsrv) |
---|
210 | set state(nick) $::irk::state(-$symsrv,nick) |
---|
211 | set nick $state(nick) |
---|
212 | |
---|
213 | foreach chan [::irk::onchannels $token] { |
---|
214 | ::irk::setaction2 $token PRIVMSG $chan ::mysaint::userSays |
---|
215 | } |
---|
216 | ::irk::setaction2 $token PRIVMSG $nick ::mysaint::userSays |
---|
217 | |
---|
218 | # Set up a timer to save the words info. |
---|
219 | |
---|
220 | if {![info exists state(saver)]} { |
---|
221 | initialize |
---|
222 | } |
---|
223 | |
---|
224 | return "" |
---|
225 | } |
---|
226 | |
---|
227 | # Stop the bot: |
---|
228 | |
---|
229 | proc stop {} { |
---|
230 | variable state |
---|
231 | |
---|
232 | foreach conn [array names state *,token] { |
---|
233 | stopcon $state($conn) |
---|
234 | } |
---|
235 | |
---|
236 | after cancel $state(saver) |
---|
237 | catch {unset state(saver)} |
---|
238 | |
---|
239 | return "" |
---|
240 | } |
---|
241 | proc stopcon {token} { |
---|
242 | variable state |
---|
243 | |
---|
244 | if {[catch {set symsrv $::irk::state($token,symsrv)}]} { |
---|
245 | return |
---|
246 | } |
---|
247 | set nick $state(nick) |
---|
248 | |
---|
249 | foreach chan [::irk::onchannels $token] { |
---|
250 | ::irk::remaction2 $token PRIVMSG $chan ::mysaint::userSays |
---|
251 | } |
---|
252 | ::irk::remaction2 $token PRIVMSG $nick ::mysaint::userSays |
---|
253 | |
---|
254 | return "" |
---|
255 | } |
---|
256 | |
---|
257 | # Save words to a disk file: |
---|
258 | |
---|
259 | proc save {} { |
---|
260 | variable state |
---|
261 | |
---|
262 | puts "Saving words in $state(words,save)" |
---|
263 | |
---|
264 | # First of all reschedule ourselves: |
---|
265 | |
---|
266 | set state(saver) [after $state(save,interval) ::mysaint::save] |
---|
267 | |
---|
268 | # Save the words: |
---|
269 | |
---|
270 | set pt "*,words" |
---|
271 | if {![catch {set fd [open $state(words,save) w]}]} { |
---|
272 | puts $fd "array set ::mysaint::state [list [array get state $pt]]" |
---|
273 | |
---|
274 | catch {close $fd} |
---|
275 | } |
---|
276 | } |
---|
277 | } |
---|