source: trunk/modules/interbot.tcl @ 1143

Revision 1143, 12.6 KB checked in by james, 45 hours ago (diff)

add a delay to calling an interbot election - should fix the race condition where more than one bot answers an interbot_me_next protected trigger
make the bMotion logging function split long lines into the console

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1# bMotion - interbot stuff
2#
3
4###############################################################################
5# bMotion - an 'AI' TCL script for eggdrops
6# Copyright (C) James Michael Seward 2000-2008
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful, but
14# WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16# General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program; if not, write to the Free Software
20# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21###############################################################################
22
23# allow people to turn off the interbot stuff if they don't want it
24# these values should be set in the settings file, but we'll define
25# some defaults here
26
27if {![info exists bMotion_interbot_enable]} {
28        set bMotion_interbot_enable 1
29}
30
31# Elect a new bot to speak for each channel
32proc bMotion_interbot_next_elect { } {
33        #send a message to all the bots on each of my channels
34        # I pick a number and send it
35        # This makes them all pick a number too and send that as a reply to all the other bots too
36        # Each bot tracks the numbers, highest bot wins and speaks next
37
38        global bMotionInfo bMotion_interbot_timer bMotionChannels
39        global bMotion_interbot_enable
40
41        if {!$bMotion_interbot_enable} {
42                return
43        }
44
45        catch {
46                foreach chan $bMotionChannels {
47                        bMotion_interbot_next_elect_do $chan
48                }
49        }
50        set bMotion_interbot_timer 1
51        set delay [expr [rand 200] + 1700]
52        bMotion_putloglev 2 * "interbot: starting election timer"
53        utimer $delay bMotion_interbot_next_elect
54}
55
56proc bMotion_interbot_next_elect_do { channel } {
57        global bMotion_interbot_nextbot_score bMotion_interbot_nextbot_nick botnick bMotionInfo
58        global bMotion_interbot_pending_channels
59
60        bMotion_putloglev 2 * "interbot: running election on $channel"
61
62        # look to see if this channel is recorded as having a pending election
63        # if it is, remove it. this is slightly a race condition but it's harmless
64        set pos [lsearch $bMotion_interbot_pending_channels [string tolower $channel]]
65        if {$pos > -1 } {
66                set bMotion_interbot_pending_channels [lreplace $bMotion_interbot_pending_channels $pos $pos]
67        }
68
69        set myScore [rand 100]
70        if {$bMotionInfo(away) == 1} {
71                set myScore -2
72        }
73        set bMotion_interbot_nextbot_score($channel) $myScore
74        set bMotion_interbot_nextbot_nick($channel) $botnick
75        bMotion_putloglev 3 * "interbot: assuming I'm the nextbot until I find another"
76        catch {
77                set bots [chanlist $channel]
78                foreach bot $bots {
79                        #not me you idiot
80                        if [isbotnick $bot] { continue }
81                        bMotion_putloglev 4 * "interbot: checking $bot for election in $channel"
82                        set handle [nick2hand $bot $channel]
83                        bMotion_putloglev 4 * "interbot: checking $bot's handle; $handle"
84                        if {[matchattr $handle b $channel] && [islinked $handle]} {
85                                bMotion_putloglev 2 * "interbot: sending elect_initial to $bot for $channel"
86                                putbot $handle "bmotion elect_initial $channel $myScore"
87                        }
88                        bMotion_putloglev 4 * "interbot: checking $handle over" 
89                }
90        }
91        bMotion_putloglev 3 * "interbot: election over for $channel"
92}
93
94proc bMotion_interbot_catch { bot cmd args } {
95        global bMotionInfo
96        bMotion_putloglev 3 * "interbot: incoming !$args!"
97        set args [lindex $args 0]
98        if [regexp {([^ ]+) (.+)} $args matches function params] {
99
100                bMotion_putloglev 2 * "interbot: got command $function ($params) from $bot"
101
102                switch -exact $function {
103                        "say" {
104                                # bmotion say <channel> <text>
105                                bMotionCatchSayChan $bot $params
106                        }
107
108                        "elect_initial" {
109                                bMotion_interbot_next_incoming $bot $params
110                        }
111
112                        "elect_reply" {
113                                bMotion_interbot_next_incoming_reply $bot $params
114                        }
115
116                        "fake_event" {
117                                bMotion_interbot_fake_catch $bot $params
118                        }
119
120                        "HAY" {
121                                bMotion_interbot_hay $bot $params
122                        }
123
124                        "SUP" {
125                                bMotion_interbot_sup $bot $params
126                        }
127                }
128        } else {
129                putlog "bMotion: ERROR: received unparsable interbot command from $bot: $cmd $args"
130        }
131
132        return 0
133}
134
135
136proc bMotion_interbot_next_incoming { bot params } {
137        #another bot is forcing an election
138        global bMotion_interbot_nextbot_score bMotion_interbot_nextbot_nick botnick bMotionInfo
139        global BMOTION_SLEEP bMotionSettings bMotion_interbot_enable
140
141        if {![info exists bMotion_interbot_enable]} {
142                return
143        }
144
145        bMotion_putloglev 1 * "interbot: Incoming election from $bot"
146
147        regexp "(\[#!\].+) (.+)" $params matches channel score
148        catch {
149                if {$score > $bMotion_interbot_nextbot_score($channel)} {
150                        bMotion_putloglev 2 * "interbot: $bot now has highest score on $channel"
151                        set bMotion_interbot_nextbot_score($channel) $score
152                        set bMotion_interbot_nextbot_nick($channel) $bot
153                }
154        }
155       
156        set myScore [rand 100]
157        if {$bMotionInfo(away) == 1} {
158                set myScore -2
159        }
160        if {$bMotionSettings(asleep) == $BMOTION_SLEEP(ASLEEP)} {
161                set myScore -2
162        }
163
164        bMotion_putloglev 2 * "interbot: My score is $myScore"
165
166        if {(![info exists bMotion_interbot_nextbot_score($channel)]) || ($myScore > $bMotion_interbot_nextbot_score($channel))} {
167                bMotion_putloglev 2 * "interbot: Actually, I have highest score on $channel, sending out reply"
168                set bMotion_interbot_nextbot_score($channel) $myScore
169                set bMotion_interbot_nextbot_nick($channel) $botnick
170
171                set bots [chanlist $channel]
172                foreach bot $bots {
173                        #not me you idiot
174                        if [isbotnick $bot] { continue }
175                        set handle [nick2hand $bot $channel]
176                        if [matchattr $handle b $channel] {
177                                putbot $handle "bmotion elect_reply $channel $myScore"
178                        }
179                }
180        }
181}
182
183proc bMotion_interbot_next_incoming_reply { bot params } {
184        #another bot is forcing an election
185        global bMotion_interbot_nextbot_score bMotion_interbot_nextbot_nick
186
187        bMotion_putloglev 1 * "interbot: Incoming election reply from $bot"
188
189        regexp "(\[#!\].+) (.+)" $params matches channel score
190        if {$score > $bMotion_interbot_nextbot_score($channel)} {
191                bMotion_putloglev 2 * "interbot: $bot now has highest score on $channel"
192                set bMotion_interbot_nextbot_score($channel) $score
193                set bMotion_interbot_nextbot_nick($channel) $bot
194        }
195}
196
197proc bMotionSendSayChan { channel text thisBot} {
198        #replace all ¬ with %
199        global bMotion_interbot_enable
200
201        if {!$bMotion_interbot_enable} {
202                return ""
203        }
204
205        set text [bMotionInsertString $text "¬" "%"]
206        bMotion_putloglev 1 * "interbot: pushing command say ($channel $text) to $thisBot"
207        if [islinked $thisBot] {
208                putbot $thisBot "bmotion say $channel :$text"
209                return $thisBot
210        } else {
211                putlog "interbot: ALERT! Trying to talk to bot $thisBot, but it isn't linked"
212                return ""
213        }
214}
215
216proc bMotionCatchSayChan { bot params } {
217        global bMotionInfo
218        global bMotionQueueTimer
219
220        bMotion_putloglev 4 * "interbot: bMotionCatchSayChan $bot $params"
221
222        if [regexp {([#!][^ ]+) :(.+)} $params matches channel txt] {
223       
224                if {$bMotionInfo(silence) == 1} {
225                        set bMotionInfo(silence) 2
226                }
227               
228                #check we haven't been sent broken text to output by %bot
229                regsub "^\[0-9\]+,(.+)" $txt {\1} txt
230
231                bMotionDoAction $channel $bot $txt "" 0 1
232                bMotion_putloglev 1 * "interbot: done say command from $bot"
233                if {$bMotionInfo(silence) == 2} {
234                        set bMotionInfo(silence) 1
235                }
236        } else {
237                putlog "bMotion ALERT! Error unwrapping command !say $params! from $bot"
238        }
239        return 0
240}
241
242# Check if we're due to talk next on the channel
243# if yes, then force an election for that channel immediately afterwards
244proc bMotion_interbot_me_next { channel } {
245        global bMotion_interbot_nextbot_nick bMotion_interbot_nextbot_score botnick
246        global bMotion_interbot_enable bMotion_interbot_pending_channels
247
248        set channel [string tolower $channel]
249
250        if {!$bMotion_interbot_enable} {
251                # if interbot stuff is turned off, we'll have to assume we should respond
252                # else we'd never say anything...
253                return 1
254        }
255
256        bMotion_putloglev 2 * "checking interbot_me_next for $channel"
257
258        if {[bMotion_setting_get "bitlbee"] == "1"} {
259                return 1
260        }
261
262        #let's look to see if we know any other bots on the botnet
263        if {[llength [bMotion_interbot_otherbots $channel]] == 0} {
264                bMotion_putloglev 4 * "interbot: no other bots, returning 1"
265                return 1
266        }
267
268        set me 0 
269        catch {
270                if {$bMotion_interbot_nextbot_score($channel) < 0} {
271                        bMotion_putloglev 4 * "interbot: nextbot_score is <0, I'm not answering"
272                }
273
274                if {$bMotion_interbot_nextbot_nick($channel) == $botnick} {
275                        bMotion_putloglev 4 * "interbot: nextbot_nick is me; calling election and returning 1"
276                        if {[lsearch $bMotion_interbot_pending_channels [string tolower $channel]] == -1} {
277                                lappend bMotion_interbot_pending_channels [string tolower $channel]
278                                utimer 3 " bMotion_interbot_next_elect_do $channel "
279                        } else {
280                                bMotion_putloglev 4 * "interbot: not calling another election as one is pending for this channel"
281                        }
282                        set me 1 
283                }
284        }
285        #
286        #if it's noone, the winning bot will force an election anyway
287        bMotion_putloglev 2 * "interbot: returning $me for $channel"
288        return $me 
289}
290
291# send a fake event
292proc bMotion_interbot_fake_event { botnick channel fromnick line } {
293        if {[matchattr $botnick b $channel] && [islinked $botnick]} {
294                putbot $botnick "bmotion fake_event $channel $fromnick $line"
295
296                return 1
297        }
298}
299
300# catch the fake event
301proc bMotion_interbot_fake_catch { bot params } {
302        bMotion_putloglev 1 * "Incoming fake event from $bot: $params"
303        regexp {([^ ]+) ([^ ]+) (.+)} $params matches channel fromnick line
304        #proc bMotion_event_main {nick host handle channel text}
305        #putlog $line
306        bMotion_event_main $fromnick "fake@fake.com" $fromnick $channel $line
307        return 1
308}
309
310#call an election when we start/rehash
311foreach chan $bMotionChannels {
312        set bMotion_interbot_nextbot_score($chan) "-1"
313        set bMotion_interbot_nextbot_nick($chan) ""
314}
315bMotion_interbot_next_elect
316
317# bMotion_interbot_link
318#
319# callback for a bot linking to the botnet
320proc bMotion_interbot_link { botname via } {
321        global bMotionChannels
322        bMotion_update_chanlist
323        #let's announce we're a bmotion bot
324        putbot $botname "bmotion SUP $bMotionChannels"
325}
326
327# bMotion_interbot_hay
328#
329# Catches a HAY from another bot, replies with a SUP
330proc bMotion_interbot_hay { bot channels } {
331        #we've met another bmotion bot, we need to tell it what channels we're on
332        global bMotion_interbot_otherbots network bMotionChannels bMotion_interbot_enable
333
334        if {!$bMotion_interbot_enable} {
335                return
336        }
337
338        bMotion_update_chanlist
339        if [regexp -nocase {(.+) network:(.+)} $channels matches chans nw] {
340                if {[string tolower $nw] != [string tolower $network]} {
341                        bMotion_putloglev 2 * "Ignoring HAY from bot on wrong network (me: $network; $bot: $nw)"
342                        return
343                }
344                set channels $chans
345        }
346        set bMotion_interbot_otherbots($bot) $channels
347        bMotion_putloglev 1 * "interbot: Met bMotion bot $bot on channels $channels"
348        putbot $bot "bmotion SUP $bMotionChannels"
349}
350
351# bMotion_interbot_sup
352#
353# Catches a SUP (reply to my HAY)
354proc bMotion_interbot_sup { bot channels } {
355        #we've met another bmotion bot
356        global bMotion_interbot_otherbots bMotion_interbot_enable
357
358        if {!$bMotion_interbot_enable} {
359                return
360        }
361
362        set bMotion_interbot_otherbots($bot) $channels
363        bMotion_putloglev 1 * "interbot: bMotion bot $bot on channels $channels"
364}
365
366array set bMotion_interbot_otherbots {}
367
368# bMotion_interbot_resync
369#
370# Broadcasts a HAY to see who's around
371proc bMotion_interbot_resync { } {
372        #let's find out who's on the botnet
373        global bMotion_interbot_otherbots network bMotionChannels bMotion_interbot_enable
374
375        utimer [expr [rand 900] + 300] bMotion_interbot_resync
376
377        if {$bMotion_interbot_enable != 1} {
378                return
379        }
380
381        bMotion_update_chanlist
382        unset bMotion_interbot_otherbots
383        array set bMotion_interbot_otherbots {}
384
385        putloglev d * "interbot: Resyncing with botnet for bMotion bots"
386        putallbots "bmotion HAY $bMotionChannels ($network)"
387}
388
389# bMotion_interbot_otherbots
390#
391# Returns other bots we know on this channel
392proc bMotion_interbot_otherbots { channel } {
393        global bMotion_interbot_otherbots
394
395        set otherbots [list]
396
397        foreach bot [array names bMotion_interbot_otherbots] {
398                if {[lsearch $bMotion_interbot_otherbots($bot) $channel] > -1} {
399                        lappend otherbots $bot
400                }
401        }
402        return $otherbots
403}
404
405
406# bMotion_interbot_is_bmotion
407#
408# checks if another bot is a bmotion bot
409proc bMotion_interbot_is_bmotion { handle } {
410        global bMotion_interbot_otherbots
411
412        if [info exists bMotion_interbot_otherbots($handle)] {
413                return 1
414        }
415        return 0
416}
417
418
419# set up our binds
420bind bot - "bmotion" bMotion_interbot_catch
421bind link - * bMotion_interbot_link
422
423utimer [expr [rand 900] + 300] bMotion_interbot_resync
424
425# this list holds names of channels we've got timers to elect on
426# stops multiple timers being set for each channel
427set bMotion_interbot_pending_channels [list]
428
429bMotion_putloglev d * "interbot: interbot module loaded"
Note: See TracBrowser for help on using the repository browser.