chiark / gitweb /
Do not have this checked in, duh.
[ircbot] / irccore.tcl
CommitLineData
574abac6 1proc defset {varname val} {
281f2c0e 2 upvar 1 $varname var
574abac6
IJ
3 if {![info exists var]} { set var $val }
4}
5
6# must set host
7defset port 6667
8
9defset nick testbot
10defset ownfullname "testing bot"
11defset ownmailaddr test-irc-bot@example.com
12
13defset musthaveping_ms 10000
14defset out_maxburst 6
15defset out_interval 2100
16defset out_lag_lag 5000
17defset out_lag_very 25000
18
19defset marktime_min 300
20defset marktime_join_startdelay 5000
21
22proc manyset {list args} {
23 foreach val $list var $args {
24 upvar 1 $var my
25 set my $val
26 }
27}
28
29proc try_except_finally {try except finally} {
30 global errorInfo errorCode
31 set er [catch { uplevel 1 $try } emsg]
32 if {$er} {
33 set ei $errorInfo
34 set ec $errorCode
35 if {[catch { uplevel 1 $except } emsg3]} {
36 append ei "\nALSO ERROR HANDLING ERROR:\n$emsg3"
37 }
38 }
39 set er2 [catch { uplevel 1 $finally } emsg2]
40 if {$er} {
41 if {$er2} {
42 append ei "\nALSO ERROR CLEANING UP:\n$emsg2"
43 }
44 return -code $er -errorinfo $ei -errorcode $ec $emsg
45 } elseif {$er2} {
46 return -code $er2 -errorinfo $errorInfo -errorcode $errorCode $emsg2
47 } else {
48 return $emsg
49 }
50}
51
52proc out__vars {} {
53 uplevel 1 {
54 global out_queue out_creditms out_creditat out_interval out_maxburst
55 global out_lag_lag out_lag_very
56#set pr [lindex [info level 0] 0]
57#puts $pr>[clock seconds]|$out_creditat|$out_creditms|[llength $out_queue]<
58 }
59}
60
61proc out_lagged {} {
62 out__vars
63 if {[llength $out_queue]*$out_interval > $out_lag_very} {
64 return 2
65 } elseif {[llength $out_queue]*$out_interval > $out_lag_lag} {
66 return 1
67 } else {
68 return 0
69 }
70}
71
72proc out_restart {} {
73 out__vars
74
75 set now [clock seconds]
76 incr out_creditms [expr {($now - $out_creditat) * 1000}]
77 set out_creditat $now
78 if {$out_creditms > $out_maxburst*$out_interval} {
79 set out_creditms [expr {$out_maxburst*$out_interval}]
80 }
81 out_runqueue $now
82}
83
84proc out_runqueue {now} {
85 global sock
86 out__vars
87
88 while {[llength $out_queue] && $out_creditms >= $out_interval} {
89#puts rq>$now|$out_creditat|$out_creditms|[llength $out_queue]<
90 manyset [lindex $out_queue 0] orgwhen msg
91 set out_queue [lrange $out_queue 1 end]
92 if {[llength $out_queue]} {
93 append orgwhen "+[expr {$now - $orgwhen}]"
94 append orgwhen "([llength $out_queue])"
95 }
96 puts "$orgwhen -> $msg"
97 puts $sock $msg
98 incr out_creditms -$out_interval
99 }
100 if {[llength $out_queue]} {
101 after $out_interval out_nextmessage
102 }
103}
104
105proc out_nextmessage {} {
106 out__vars
107 set now [clock seconds]
108 incr out_creditms $out_interval
109 set out_creditat $now
110 out_runqueue $now
111}
112
113proc sendout_priority {priority command args} {
114 global sock out_queue
115 if {[llength $args]} {
116 set la [lindex $args end]
117 set args [lreplace $args end end]
118 foreach i $args {
119 if {[regexp {[: ]} $i]} {
120 error "bad argument in output $i ($command $args)"
121 }
122 }
123 lappend args :$la
124 }
125 set args [lreplace $args 0 -1 $command]
126 set string [join $args { }]
127 set now [clock seconds]
128 set newe [list $now $string]
129 if {$priority} {
130 set out_queue [concat [list $newe] $out_queue]
131 } else {
132 lappend out_queue $newe
133 }
134 if {[llength $out_queue] == 1} {
135 out_restart
136 }
137}
138
139proc sendout {command args} { eval sendout_priority [list 0 $command] $args }
140
141proc log {data} {
142 puts $data
143}
144
145proc logerror {data} {
146 log $data
147}
148
149proc saveeic {} {
150 global saveei saveec errorInfo errorCode
151
152 set saveei $errorInfo
153 set saveec $errorCode
154
155 puts ">$saveec|$saveei<"
156}
157
158proc bgerror {msg} {
159 global save
160 logerror $msg
161 saveeic
162}
163
164proc onread {args} {
281f2c0e
IJ
165 global sock nick calling_nick errorInfo errorCode line_org_1char
166
574abac6 167 if {[gets $sock line] == -1} { fail "EOF/error on input" }
281f2c0e 168 set line_org_1char [string range $line 0 0]
574abac6
IJ
169 regsub -all "\[^ -\176\240-\376\]" $line ? line
170 set org $line
171
281f2c0e 172 new_event
574abac6
IJ
173
174 if {[regexp -nocase {^:([^ ]+) (.*)} $line dummy prefix remain]} {
175 set line $remain
176 if {[regexp {^([^!]+)!} $prefix dummy maybenick]} {
177 set calling_nick $maybenick
178 if {"[irctolower $maybenick]" == "[irctolower $nick]"} return
179 }
180 } else {
181 set prefix {}
182 }
183 if {![string length $line]} { return }
184 if {![regexp -nocase {^([0-9a-z]+) *(.*)} $line dummy command line]} {
185 log "bad command: $org"
186 return
187 }
188 set command [string toupper $command]
189 set params {}
190 while {[regexp {^([^ :]+) *(.*)} $line dummy thisword line]} {
191 lappend params $thisword
192 }
193 if {[regexp {^:(.*)} $line dummy thisword]} {
194 lappend params $thisword
195 } elseif {[string length $line]} {
196 log "junk at end: $org"
197 return
198 }
281f2c0e
IJ
199 if {"$command" == "PRIVMSG" && \
200 [privmsg_unlogged $prefix [ischan [lindex $params 0]] $params]} {
574abac6
IJ
201 return
202 }
203 log "[clock seconds] <- $org"
204 set procname msg_$command
205 if {[catch { info body $procname }]} { return }
206 if {[catch {
207 eval [list $procname $prefix $command] $params
208 } emsg]} {
209 logerror "error: $emsg ($prefix $command $params)"
210 saveeic
211 }
212}
213
214proc sendprivmsg {dest l} {
215 foreach v [split $l "\n"] {
216 sendout [expr {[ischan $dest] ? "PRIVMSG" : "NOTICE"}] $dest $v
217 }
218}
219proc sendaction_priority {priority dest what} {
220 sendout_priority $priority PRIVMSG $dest "\001ACTION $what\001"
221}
222proc msendprivmsg {dest ll} { foreach l $ll { sendprivmsg $dest $l } }
223proc msendprivmsg_delayed {delay dest ll} { after $delay [list msendprivmsg $dest $ll] }
224
225proc check_nick {n} {
226 if {[regexp -nocase {[^][\\`_^{|}a-z0-9-]} $n]} { error "bad char in nick" }
227 if {[regexp {^[-0-9]} $n]} { error "bad nick start" }
228}
229
230proc ischan {dest} {
231 return [regexp {^[&#+!]} $dest]
232}
233
234proc irctolower {v} {
235 foreach {from to} [list "\\\[" "{" \
236 "\\\]" "}" \
237 "\\\\" "|" \
238 "~" "^"] {
239 regsub -all $from $v $to v
240 }
241 return [string tolower $v]
242}
243
281f2c0e
IJ
244proc prefix_none {} {
245 upvar 1 p p
246 if {[string length $p]} { error "prefix specified" }
247}
248
249proc prefix_nick {} {
250 global nick
251 upvar 1 p p
252 upvar 1 n n
253 if {![regexp {^([^!]+)!} $p dummy n]} { error "not from nick" }
254 check_nick $n
255 if {"[irctolower $n]" == "[irctolower $nick]"} {
256 error "from myself" {} {}
257 }
258}
259
574abac6
IJ
260proc msg_PING {p c s1} {
261 global musthaveping_after
262 prefix_none
263 sendout PONG $s1
281f2c0e
IJ
264 if {[info exists musthaveping_after]} {
265 after cancel $musthaveping_after
266 unset musthaveping_after
267 connected
268 }
269}
270
271proc ensure_outqueue {} {
272 out__vars
273 if {[info exists out_queue]} return
274 set out_creditms 0
275 set out_creditat [clock seconds]
276 set out_queue {}
277 set out_lag_reported 0
278 set out_lag_reportwhen $out_creditat
279}
280
281proc fail {msg} {
282 logerror "failing: $msg"
283 exit 1
284}
285
286proc ensure_connecting {} {
287 global sock ownfullname host port nick socketargs
288 global musthaveping_ms musthaveping_after
289
290 ensure_outqueue
291
292 if {[info exists sock]} return
293 set sock [eval socket $socketargs [list $host $port]]
294 fconfigure $sock -buffering line
295 fconfigure $sock -translation crlf
296
297 sendout USER blight 0 * $ownfullname
298 sendout NICK $nick
299 fileevent $sock readable onread
300
301 set musthaveping_after [after $musthaveping_ms \
302 {fail "no ping within timeout"}]
574abac6 303}