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