| 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 | |
|---|
| 27 | if {![info exists bMotion_interbot_enable]} { |
|---|
| 28 | set bMotion_interbot_enable 1 |
|---|
| 29 | } |
|---|
| 30 | |
|---|
| 31 | # Elect a new bot to speak for each channel |
|---|
| 32 | proc 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 | |
|---|
| 56 | proc 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 | |
|---|
| 94 | proc 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 | |
|---|
| 136 | proc 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 | |
|---|
| 183 | proc 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 | |
|---|
| 197 | proc 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 | |
|---|
| 216 | proc 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 |
|---|
| 244 | proc 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 |
|---|
| 292 | proc 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 |
|---|
| 301 | proc 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 |
|---|
| 311 | foreach chan $bMotionChannels { |
|---|
| 312 | set bMotion_interbot_nextbot_score($chan) "-1" |
|---|
| 313 | set bMotion_interbot_nextbot_nick($chan) "" |
|---|
| 314 | } |
|---|
| 315 | bMotion_interbot_next_elect |
|---|
| 316 | |
|---|
| 317 | # bMotion_interbot_link |
|---|
| 318 | # |
|---|
| 319 | # callback for a bot linking to the botnet |
|---|
| 320 | proc 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 |
|---|
| 330 | proc 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) |
|---|
| 354 | proc 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 | |
|---|
| 366 | array set bMotion_interbot_otherbots {} |
|---|
| 367 | |
|---|
| 368 | # bMotion_interbot_resync |
|---|
| 369 | # |
|---|
| 370 | # Broadcasts a HAY to see who's around |
|---|
| 371 | proc 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 |
|---|
| 392 | proc 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 |
|---|
| 409 | proc 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 |
|---|
| 420 | bind bot - "bmotion" bMotion_interbot_catch |
|---|
| 421 | bind link - * bMotion_interbot_link |
|---|
| 422 | |
|---|
| 423 | utimer [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 |
|---|
| 427 | set bMotion_interbot_pending_channels [list] |
|---|
| 428 | |
|---|
| 429 | bMotion_putloglev d * "interbot: interbot module loaded" |
|---|