source: trunk/modules/system.tcl @ 1143

Revision 1136, 35.8 KB checked in by james, 4 months ago (diff)

new triggers for some verbs
stoplist additions for complex_add and spoonerisms
handle being smacked in a specific bodypart better
content additions all round

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1# bMotion - System functions
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
24
25### Set up the binds
26
27#General IRC events
28bind join - *!*@* bMotion_event_onjoin
29bind mode - * bMotion_event_mode
30bind pubm - * bMotion_event_main
31bind sign - * bMotion_event_onquit
32bind nick - * bMotion_event_nick
33bind part - * bMotion_event_onpart
34bind ctcp - ACTION bMotion_event_action
35
36#bMotion IRC events
37bind pub - "!mood" pubm_moodhandler
38bind pub - "!bminfo" bMotionInfo
39bind pub - "!bmstats" bMotionStats
40bind msg - bmotion msg_bmotioncommand
41bind pub - !bmadmin bMotionAdminHandler
42bind pub - !bmotion bMotionAdminHandler2
43bind pub - .bmotion bMotionAdminHandler2
44
45#DCC commands
46bind dcc m mood moodhandler
47bind dcc m bmotion* bMotion_dcc_command
48bind dcc m bmadmin* bMotion_dcc_command
49bind dcc m bmhelp bMotion_dcc_help
50
51#bedtime
52bind time - "* * * * *" bMotion_check_tired2
53
54#
55# rebuilds our channel list based on which channels are +bmotion
56proc bMotion_update_chanlist { } {
57        global bMotionChannels
58        set bMotionChannels [list]
59
60        foreach chan [channels] {
61                if {[channel get $chan bmotion]} {
62                        lappend bMotionChannels $chan
63                }
64        }
65}
66
67#
68# Initalise some variables per channel
69bMotion_update_chanlist
70
71foreach chan $bMotionChannels {
72        set bMotionLastEvent($chan) [clock seconds]
73        set bMotionInfo(adminSilence,$chan) 0
74        #set to 1 when the bot says something, and 0 when someone else says something
75        #used to make the bot a bit more intelligent (perhaps) at conversations
76        set bMotionCache($chan,last) 0
77}
78
79# bMotionStats
80# TODO: retire this
81proc bMotionStats {nick host handle channel text} {
82        global bMotionInfo botnicks bMotionSettings cvsinfo botnick
83        global bMotionVersion
84        if {(![regexp -nocase $botnick $text]) && ($text != "all")} { return 0 }
85        if {!([isvoice $nick] || [isop $nick]) || ($nick != "JamesOff")} { return 0 }
86
87
88        global bMotion_abstract_contents bMotion_abstract_timestamps bMotion_abstract_max_age
89        global bMotion_abstract_ondisk
90
91        set mem [llength [array names bMotion_abstract_contents]]
92        set disk [llength $bMotion_abstract_ondisk]
93        global bMotionFacts
94        set items [lsort [array names bMotionFacts]]
95        set itemcount 0
96        set factcount 0
97        foreach item $items {
98                incr itemcount
99                incr factcount [llength $bMotionFacts($item)]
100        }
101
102        putchan $channel "abstracts: [expr $mem + $disk] total, $mem loaded, $disk on disk"
103        putchan $channel "facts: $factcount facts about $itemcount items"
104}
105
106#
107# get the last event for a channel, or 0 if not available
108proc bMotion_get_last_event { channel } {
109        bMotion_putloglev 4 * "bMotion_get_last_event $channel"
110
111        global bMotionLastEvent
112
113        set last_event 0
114        catch {
115                set last_event $bMotionLastEvent($channel)
116        }
117        if {$last_event == 0} {
118                bMotion_putloglev 1 * "last event for $channel is not available, returning 0"
119        }
120
121        return $last_event
122}
123
124#
125# set the last event time for a channel to now
126proc bMotion_set_last_event { channel } {
127        bMotion_putloglev 4 * "bMotion_set_last_event $channel"
128
129        global bMotionLastEvent
130
131        set bMotionLastEvent($channel) [clock seconds]
132}
133
134#
135# check if a channel is active enough for randomy things
136proc bMotion_is_active_enough { channel { limit 0 } } {
137        global bMotionInfo
138
139        bMotion_putloglev 4 * "bMotion_is_active_enough $channel"
140
141        set last_event [bMotion_get_last_event $channel]
142
143        if {$last_event == 0} {
144                bMotion_putloglev d * "last event info for $channel not available"
145                # force it to be now
146                bMotion_set_last_event $channel
147                # assume we're ok
148                return 1
149        }
150
151        bMotion_putloglev 3 * "last event for $channel was $last_event"
152        if {$limit == 0} {
153                set limit [expr $bMotionInfo(maxIdleGap) * 60]
154        }
155
156        if {([clock seconds] - $last_event) < $limit} {
157                bMotion_putloglev 3 * "it fits!"
158                return 1
159        }
160        return 0
161}
162
163#
164# check if every channel we can see is idle enough for us to go away
165proc bMotion_random_away {} {
166        global bMotionChannels bMotionInfo
167
168        bMotion_putloglev 4 * "bMotion_random_away"
169
170        set timeNow [clock seconds]
171
172        # check if it's worth doing anything
173        if {[bMotion_setting_get "bitlbee"]} {
174                #never go away in bitlbee
175                bMotion_putloglev d * "going away is disabled in bitlbee mode"
176                return 0
177        }
178
179        #override if we should never go away
180        if {[bMotion_setting_get "useAway"] != 1} {
181                bMotion_putloglev d * "going away is disabled"
182                return 0
183        }
184
185        # find the least idle channel
186        set mostRecent 0
187        set line "comparing idle times: "
188        foreach channel $bMotionChannels {
189                catch {
190                        set last_event [bMotion_get_last_event $channel]
191                        append line "$channel=$last_event "
192                        if {$last_event > $mostRecent} {
193                                set mostRecent $last_event
194                        }
195                }
196        }
197
198        set gapTime [expr { int($bMotionInfo(maxIdleGap) * 10) }]
199        bMotion_putloglev 1 * "bMotion: most recent: $mostRecent .. timenow $timeNow .. gap $gapTime"
200
201        if {($timeNow - $mostRecent) < $gapTime} {
202                set chance [rand 100]
203                if {$chance > [bMotion_setting_get "awaychance"]} {
204                        bMotion_putloglev 1 * "most recent is busy enough, not going away"
205                        return 0
206                }
207                bMotion_putloglev 1 * "most recent is busy enough but going away anyway"
208        }
209
210        if {$bMotionInfo(away) == 1} {
211                #away, don't do anything (and don't do randomstuff)
212                bMotion_putloglev d * "I'm already away, not going away again"
213                return 0
214        }
215
216        if {[rand 3] == 0} {
217                putlog "bMotion: All channels are idle, going away"
218                bMotionSetRandomAway
219                return 1
220        } else {
221                bMotion_putloglev d * "All channels are idle, not going away though"
222        }
223
224        return 0
225}
226
227#
228# periodically sprout randomness (or go /away if idle enough)
229proc doRandomStuff {} {
230        global bMotionInfo mood stonedRandomStuff bMotionSettings
231        global bMotionOriginalNick bMotionChannels
232        global BMOTION_SLEEP
233
234        bMotion_putloglev 4 * "doRandomStuff"
235
236        set saidChannels [list]
237        set silentChannels [list]
238        set bMotionOriginalNick ""
239
240        bMotion_update_chanlist
241
242        # choose new time to check
243        set upperLimit [expr $bMotionInfo(maxRandomDelay) - $bMotionInfo(minRandomDelay)]
244        if {$upperLimit < 1} {
245                set upperLimit 1
246        }
247        set temp [expr [rand $upperLimit] + $bMotionInfo(minRandomDelay)]
248        timer $temp doRandomStuff
249        bMotion_putloglev d * "randomStuff next in $temp minutes"
250
251        # don't bother if we're asleep
252        if {$bMotionSettings(asleep) != $BMOTION_SLEEP(AWAKE)} {
253                bMotion_putloglev d * "not doing randomstuff now, i'm asleep"
254                # kill any away status as we don't want to come back from the shops after we've been to bed :P
255                set bMotionInfo(away) 0
256                return
257        }
258
259        if [bMotion_random_away] {
260                # we went away, so stop here
261                bMotion_putloglev d * "we went away, returning from doRandomStuff"
262                return
263        }
264
265        if {$bMotionInfo(away) == 1} {
266                #away and busy again, return
267                bMotion_putloglev d * "was away, setting myself back"
268                bMotionSetRandomBack
269        }
270
271        if {[bMotion_setting_get "bitlbee"] == "1"} {
272                bMotion_putloglev d * "aborting randomstuff, don't do it in bitlbee mode"
273                return 0
274        }
275
276        # if someone spoke in the last 5 mins, use an activeRandomStuff
277        # else use a randomStuff
278
279        set active_idle_sec [bMotion_setting_get "active_idle_sec"]
280
281        foreach channel $bMotionChannels {
282                if [bMotion_is_active_enough $channel] {
283                        if [bMotion_is_active_enough $channel $active_idle_sec] {
284                        #channel is fairly busy
285                                if [bMotionSaySomethingRandom $channel 1] {
286                                        lappend saidChannels "$channel/active"
287                                } else {
288                                        lappend silentChannels $channel
289                                }
290                        } else {
291                        #use a more idle randomstuff
292                                if [bMotionSaySomethingRandom $channel 0] {
293                                        lappend saidChannels $channel
294                                } else {
295                                        lappend silentChannels $channel
296                                }
297                        }
298                } else {
299                        lappend silentChannels $channel
300                }
301        }
302        bMotion_putloglev d * "randomStuff said ($saidChannels) silent ($silentChannels)"
303}
304
305#
306# Output random gibberish
307proc bMotionSaySomethingRandom {channel {busy 0}} {
308        global randomStuff stonedRandomStuff mood bMotionInfo bMotionCache
309
310        bMotion_putloglev 4 * "bMotionSaySomethingRandom $channel $busy"
311
312        if {$busy} {
313                set base_abstract "activeRandomStuff"
314        } else {
315                set base_abstract "randomStuff"
316        }
317
318        bMotion_putloglev d * "base abstract for randomness in $channel is $base_abstract"
319
320        if [rand 2] {
321                set today [clock format [clock seconds] -format "${base_abstract}_%Y_%m_%d"]
322                if [bMotion_abstract_exists $today] {
323                        bMotion_putloglev d * "using abstract $today for randomstuff in $channel"
324                        bMotionDoAction $channel "" "%VAR{$today}"
325                        return 1
326                }
327
328                set today [clock format [clock seconds] -format "${base_abstract}_%m_%d"]
329                if [bMotion_abstract_exists $today] {
330                        bMotion_putloglev d * "using abstract $today for randomstuff in $channel"
331                        bMotionDoAction $channel "" "%VAR{$today}"
332                        return 1
333                }
334
335                bMotion_putloglev 1 * "no special day abstract found for randomStuff in $channel"
336                bMotionDoAction $channel "" "%VAR{${base_abstract}}"
337                return 1
338        }
339
340        return 0
341}
342
343
344#
345#set myself away with a random message
346proc bMotionSetRandomAway {} {
347        global randomAways bMotionInfo bMotionSettings bMotionChannels
348        bMotion_putloglev 4 * "bMotionSetRandomAway"
349
350        set awayReason [bMotion_abstract_get "randomAways"]
351        foreach channel $bMotionChannels {
352                if {[lsearch $bMotionSettings(noAwayFor) $channel] == -1} {
353                        bMotionDoAction $channel $awayReason "/is away: %%"
354                }
355        }
356        putserv "AWAY :$awayReason"
357        set bMotionInfo(away) 1
358        set bMotionInfo(silence) 1
359        bMotion_putloglev d * "bMotion: Set myself away: $awayReason"
360        bMotion_putloglev d * "bMotion: Going silent"
361}
362
363#
364# set myself back
365proc bMotionSetRandomBack {} {
366        #set myself back
367        global bMotionInfo bMotionSettings bMotionChannels
368        bMotion_putloglev 4 * "bMotionSetRandomBack"
369
370        bMotion_update_chanlist
371
372        set bMotionInfo(away) 0
373        set bMotionInfo(silence) 0
374        bMotion_putloglev d * "No longer silent or away"
375        foreach channel $bMotionChannels {
376                if {[lsearch $bMotionSettings(noAwayFor) $channel] == -1} {
377                        bMotionDoAction $channel "" "/is back"
378                }
379        }
380        putserv "AWAY"
381
382        #elect cos we're available now
383        bMotion_interbot_next_elect
384
385        return 0
386}
387
388#
389# check if a line looks like it's addressed to me
390proc bMotionTalkingToMe { text } {
391        global botnicks
392
393        bMotion_putloglev 4 * "bMotionTalkingToMe $text"
394
395        # look for a nick at the start of the line
396        if [regexp -nocase {^([^ :,]+)} $text matches nick] {
397                bMotion_putloglev 2 * "looks like they're talking to $nick"
398                if [regexp -nocase $botnicks $nick] {
399                        bMotion_putloglev 2 * "that's me!"
400                        return 1
401                }
402        }
403
404        if [regexp -nocase "$botnicks\[!?~\]*$" $text] {
405                bMotion_putloglev 2 * "found my name at the end of the line"
406                return 1
407        }
408
409        bMotion_putloglev 2 * "not talking to me"
410        return 0
411}
412
413#
414# We need to shut up
415proc bMotionSilence {nick host channel} {
416        global bMotionInfo silenceAways bMotionSettings
417        if {$bMotionInfo(silence) == 1} {
418                #I already am :P
419                putserv "NOTICE $nick :I already am silent :P"
420                return 0
421        }
422        timer $bMotionSettings(silenceTime) bMotionUnSilence
423        putlog "bMotion: Was told to be silent for $bMotionSettings(silenceTime) minutes by $nick in $channel"
424        bMotion_plugins_settings_set "system" "ruser_skip" $channel "" $nick
425        bMotionDoAction $channel $nick "%VAR{silenceAways}"
426        putserv "AWAY :afk ($nick $channel)"
427        set bMotionInfo(silence) 1
428        set bMotionInfo(away) 1
429}
430
431#
432# Enough shutting up for now
433proc bMotionUnSilence {} {
434        # Timer for silence expires
435        putserv "AWAY"
436        putlog "bMotion: No longer silent."
437        global bMotionInfo
438        set bMotionInfo(silence) 0
439        set bMotionInfo(away) 0
440}
441
442### bMotionLike
443proc bMotionLike {nick { host "" }} {
444        global bMotionInfo mood bMotionSettings
445
446        bMotion_putloglev 4 * "bMotionLike: $nick $host"
447       
448        if {$bMotionSettings(melMode) == 1} {
449                bMotion_putloglev 3 * "like: melmode is on, i'll do anyone"
450                return 1
451        }
452
453        if {$host == ""} {
454                set host [getchanhost $nick]
455        }
456
457        set host "$nick!$host"
458
459        set handle [finduser $host]
460        if {$handle == "*"} {
461                # couldn't find a match
462                #if i'm stoned enough, i'll sleep with anyone
463                if {$mood(stoned) > 20} {
464                        bMotion_putloglev 3 * "like: i'm sufficiently stoned to do anyone"
465                        return 1
466                }
467
468                #if i'm horny enough, i'll sleep with anyone
469                if {$mood(horny) > 10} {
470                        bMotion_putloglev 3 * "like: i'm sufficiently horny to do anyone"
471                        return 1
472                }
473                #else they can get lost
474                bMotion_putloglev 3 * "like: $host doesn't have a matching handle, so they can go away"
475                return 0
476        }
477
478        #don't like people who aren't my friends
479        if {![bMotionIsFriend $nick]} { 
480                bMotion_putloglev 3 * "like: I don't do people I'm not friends with"
481                return 0 
482        }
483
484        # we're friends, now get their gender
485        set gender [getuser $handle XTRA gender]
486        if {$gender == ""} {
487                bMotion_putloglev 3 * "like: $handle is genderless, so I'll do them. it's only 50/50 anyway. i like those odds!"
488                # they don't have a gender. let's assume we'd have sex with them too
489                return 1
490        }
491        if {$gender == $bMotionInfo(gender)} {
492                #they're my gender
493                if {($bMotionInfo(orientation) == "bi") || ($bMotionInfo(orientation) == "gay") || ($bMotionInfo(orientation) == "lesbian")} {
494                        bMotion_putloglev 3 * "like: $handle is my gender and I like that"
495                        return 1
496                }
497                bMotion_putloglev 3 * "like: $handle is my gender and that's not koo"
498                return 0
499        }
500        #they're not my gender. what now?
501        if {($bMotionInfo(orientation) == "bi") || ($bMotionInfo(orientation) == "straight")} {
502                bMotion_putloglev 3 * "like: $handle isn't my gender and that's not koo"
503                return 1
504        }
505        # that only leaves lesbian and gay who won't sleep with the opposite gender
506        bMotion_putloglev 3 * "like: nope, default case"
507        return 0
508}
509
510### bMotionGetGender
511proc bMotionGetGender { nick host } {
512        set host "$nick!$host"
513        set handle [finduser $host]
514        if {$handle == "*"} {
515                return "unknown"
516        }
517        # found a user, now get their gender
518        return [getuser $handle XTRA gender]
519}
520
521### getHour
522proc getHour {} {
523        return [clock format [clock seconds] -format "%H"]
524}
525
526
527### bMotion_dcc_command
528proc bMotion_dcc_command { handle idx arg } {
529        global bMotionInfo
530
531        set cmd $arg
532
533        bMotion_plugins_settings_set "admin" "type" "" "" "dcc"
534        bMotion_plugins_settings_set "admin" "idx" "" "" $idx
535
536        set nfo [bMotion_plugin_find_management $cmd]
537
538        if {$nfo == ""} {
539                bMotion_putadmin "Unknown command (try .bmotion help)"
540                return 1
541        }
542
543        set flags [lindex $nfo 0]
544        set callback [lindex $nfo 1]
545
546        if {![matchattr $handle $flags]} {
547                bMotion_putadmin "What? You need more flags :)"
548                return 1
549        }
550
551        bMotion_putloglev 1 * "bMotion: management callback matched, calling $callback"
552
553        #strip the first command
554        regexp {[^ ]+( .+)?} $cmd {\1} arg
555
556        #run the callback :)
557        set arg [join $arg]
558        set arg [string trim $arg]
559
560        catch {
561                if {$arg == ""} {
562                        $callback $handle
563                } else {
564                        $callback $handle $arg
565                }
566        } err
567        if {($err != "") && ($err != 0)} {
568                putlog "bMotion: ALERT! Callback failed for !bmotion: $callback: $err"
569        } else {
570                return 0
571        }
572
573        # TODO: still in use?
574
575        bMotion_putloglev 2 * "bMotion: admin command $arg from $handle"
576        set info [bMotion_plugin_find_admin $arg $bMotionInfo(language)]
577        if {$info == ""} {
578                putidx $idx "Unknown command (or error). Try .bmotion help"
579                return 1
580        }
581
582        set blah [split $info "Š"]
583        set flags [lindex $blah 0]
584        set callback [lindex $blah 1]
585
586        if {![matchattr $handle $flags]} {
587                putidx $idx "What? You need more flags :)"
588                return 1
589        }
590
591        bMotion_putloglev 1 * "bMotion: admin callback matched, calling $callback"
592
593        #strip the first command
594        regexp {[^ ]+( .+)?} $arg {\1} arg
595
596        #run the callback :)
597        set arg [join $arg]
598        set arg [string trim $arg]
599        catch {
600                if {$arg == ""} {
601                        $callback $handle $idx
602                } else {
603                        $callback $handle $idx $arg
604                }
605        } err
606        if {($err != "") && ($err != 0)} {
607                putlog "bMotion: ALERT! Callback failed for .bmadmin: $callback ($handle $idx $arg)"
608                putidx $idx "Sorry :( Running your callback failed ($err)\r"
609        }
610}
611
612### bMotion_dcc_help
613proc bMotion_dcc_help { handle idx arg } {
614        putidx $idx "Please use .bmotion help"
615        return 0
616}
617
618
619### new admin plugins ("management")
620### bMotionAdminHandler2
621proc bMotionAdminHandler2 {nick host handle channel text} {
622        global botnicks bMotionInfo botnick bMotionSettings
623
624        #first, check botnicks (this is to get round empty-nick-on-startup
625        if {$botnicks == ""} {
626                # need to set this
627                if {[bMotion_setting_get "botnicks"] == ""} {
628                        set botnicks "$botnicks ?"
629                } else {
630                        set botnicks "($botnick|$bMotionSettings(botnicks)) ?"
631                }
632        }
633
634        if {![regexp -nocase "^($botnicks|all) (.+)" $text matches yarr bn cmd]} {
635                #not me
636                return 0
637        }
638
639        #regexp -nocase "^(($botnicks)|all) (.+)" $text matches blah blah2 blah3 cmd
640
641        bMotion_plugins_settings_set "admin" "type" "" "" "irc"
642        bMotion_plugins_settings_set "admin" "target" "" "" $channel
643
644        #putlog "bMotion command from $nick in $channel: $cmd"
645        set nfo [bMotion_plugin_find_management $cmd]
646
647        if {$nfo == ""} {
648                # don't output this on irc
649                #bMotion_putadmin "Unknown command (try .bmotion help)"
650                putlog "bMotion: ignoring admin command $cmd from $nick on $channel (unknown command)"
651                return 1
652        }
653
654        set flags [lindex $nfo 0]
655        set callback [lindex $nfo 1]
656
657        if {![matchattr $handle $flags]} {
658                # don't output this on irc
659                #bMotion_putadmin "What? You need more flags :)"
660                putlog "bMotion: ignoring admin command $cmd from $nick on $channel (insufficient flags)"
661                return 1
662        }
663
664        putlog "bMotion: admin command from $nick on $channel: $cmd"
665        bMotion_putloglev 1 * "bMotion: management callback matched, calling $callback"
666
667        #strip the first command
668        regexp {[^ ]+( .+)?} $cmd {\1} arg
669
670        #run the callback :)
671        set arg [join $arg]
672        set arg [string trim $arg]
673
674        catch {
675                if {$arg == ""} {
676                        $callback $handle
677                } else {
678                        $callback $handle $arg
679                }
680        } err
681        if {($err != "") && ($err != 0)} {
682                putlog "bMotion: admin command $cmd from $nick on $channel failed: $err"
683        }
684}
685
686
687### bMotion_putadmin
688proc bMotion_putadmin { text } {
689
690        # easier than trying to not put tabs in the help stuff ;)
691        set text [string map { "\t" "  " } $text ]
692
693        set output [bMotion_plugins_settings_get "admin" "type" "" ""]
694        if {$output == ""} {
695                return 0
696        }
697
698        if {$output == "dcc"} {
699                set idx [bMotion_plugins_settings_get "admin" "idx" "" ""]
700                putidx $idx $text
701                return 0
702        }
703
704        if {$output == "irc"} {
705                set target [bMotion_plugins_settings_get "admin" "target" "" ""]
706                puthelp "PRIVMSG $target :$text"
707                return 0
708        }
709        return 0
710}
711
712### bMotionAdminHandler
713#TODO: is this ever used now?
714proc bMotionAdminHandler {nick host handle channel text} {
715        global bMotionAdminFlag botnicks bMotionInfo botnick bMotionSettings
716
717        if {![matchattr $handle $bMotionAdminFlag $channel]} {
718                return 0
719        }
720
721        #first, check botnicks (this is to get round empty-nick-on-startup
722        if {$botnicks == ""} {
723                # need to set this
724                set botnicks "($botnick|$bMotionSettings(botnicks)) ?"
725        }
726
727        if [regexp -nocase "$botnicks (shut up|silence|quiet)" $text] {
728                set bMotionInfo(adminSilence,$channel) 1
729                puthelp "NOTICE $nick :OK, silent in $channel until told otherwise"
730                return 1
731        }
732
733        if [regexp -nocase "$botnicks (end|cancel|stop) (shut up|silence|quiet)" $text] {
734                set bMotionInfo(adminSilence,$channel) 0
735                puthelp "NOTICE $nick :No longer silent in $channel"
736                return 1
737        }
738
739        if [regexp -nocase "$botnicks washnick (.+)" $text matches bn nick2] {
740                bMotionDoAction $channel $nick "%%: %2" [bMotionWashNick $nick2]
741                return 1
742        }
743
744        if [regexp -nocase "$botnicks global (shut up|silence|quiet)" $text] {
745                set bMotionInfo(silence) 1
746                set bMotionInfo(away) 1
747                puthelp "NOTICE $nick :Now globally silent"
748                putserv "AWAY :Global silence requested by $nick"
749                return 1
750        }
751
752        if [regexp -nocase "$botnicks (end|cancel|stop) global (shut up|silence|quiet)" $text] {
753                set bMotionInfo(silence) 0
754                set bMotionInfo(away) 0
755                puthelp "NOTICE $nick :No longer globally silent"
756                putserv "AWAY"
757                return 1
758        }
759
760        if [regexp -nocase "$botnicks leet (on|off)" $text blah pop toggle] {
761
762                if {$toggle == "off"} {
763                        putlog "bMotion: Leet mode off by $nick"
764                        set bMotionInfo(leet) 0
765                        bMotionDoAction $channel $nick "/stops talking like a retard."
766                        return 0
767                }
768
769                if {$toggle == "on"} {
770                        putlog "bMotion: Leet mode on by $nick"
771                        set bMotionInfo(leet) 1
772                        bMotionDoAction $channel $nick "Leet mode on ... fear my skills!"
773                }
774                return 1
775        }
776
777        if [regexp -nocase "$botnicks dutch (on|off)" $text blah pop toggle] {
778
779                if {$toggle == "off"} {
780                        putlog "bMotion: Dutch mode off by $nick"
781                        set bMotionInfo(dutch) 0
782                        bMotionDoAction $channel $nick "/stops talking like a European."
783                        return 0
784                }
785
786                if {$toggle == "on"} {
787                        putlog "bMotion: Dutch mode on by $nick"
788                        bMotionDoAction $channel $nick "/snapt wel nederlands"
789                        set bMotionInfo(dutch) 1
790                }
791                return 1
792        }
793
794
795        if [regexp -nocase "$botnicks leetchance (.+)" $text blah pop value] {
796                set bMotionInfo(leetChance) $value
797                puthelp "NOTICE $nick :Ok"
798                return 1
799        }
800
801        if [regexp -nocase "$botnicks reload" $text blah pop value] {
802                puthelp "NOTICE $nick :Reloading random stuff lists"
803                source scripts/bMotionRandoms.tcl
804                putlog "bMotion: Reloaded bMotion randoms ($nick)"
805                return 1
806        }
807
808        if [regexp -nocase "$botnicks parse (.+)" $text matches bot txt] {
809                bMotionDoAction $channel $nick $txt
810                putlog "bMotion: Parsed text for $nick"
811                return 1
812        }
813
814        if [regexp -nocase "$botnicks su (.+?) (.+)" $text matches bot nick2 txt] {
815                bMotion_event_main $nick2 [getchanhost $nick2 $channel] [nick2hand $nick2] $channel $txt
816                putlog "bMotion: su to $nick2 by $nick on $channel: $txt"
817                return 1
818        }
819}
820
821### msg_bmotioncommand
822proc msg_bmotioncommand { nick host handle cmd } {
823        bMotion_plugins_settings_set "admin" "type" "" "" "irc"
824        bMotion_plugins_settings_set "admin" "target" "" "" $nick
825
826        regsub "(bmotion )" $cmd "" cmd
827        set nfo [bMotion_plugin_find_management $cmd]
828
829        if {$nfo == ""} {
830                bMotion_putadmin "Unknown command (try .bmotion help)"
831                return 1
832        }
833
834        set flags [lindex $nfo 0]
835        set callback [lindex $nfo 1]
836
837        if {![matchattr $handle $flags]} {
838                bMotion_putadmin "What? You need more flags :)"
839                return 1
840        }
841
842        bMotion_putloglev 1 * "bMotion: management callback matched, calling $callback"
843        putlog "bMotion: admin command from $nick in query: $cmd"
844
845        #strip the first command
846        regexp {[^ ]+( .+)?} $cmd {\1} arg
847
848        #run the callback :)
849        set arg [join $arg]
850        set arg [string trim $arg]
851
852        catch {
853                if {$arg == ""} {
854                        $callback $handle
855                } else {
856                        $callback $handle $arg
857                }
858        } err
859        if {($err != "") && ($err != 0)} {
860                putlog "bMotion: admin command $cmd from $nick failed: $err"
861        }
862}
863
864### bMotion_get_number
865proc bMotion_get_number { num } {
866        if {$num <= 0} {
867                bMotion_putloglev d * "Warning: bMotion_get_number called with invalid parameter: $num"
868                return 0
869        }
870        return [expr [rand $num] + 1]
871}
872
873### bMotion_rand_nonzero
874proc bMotion_rand_nonzero { limit } {
875        if {$limit <= 0} {
876                return 0
877        }
878
879        set result [rand $limit]
880        incr result
881        return $result
882}
883
884### bMotion_startTimers
885proc bMotion_startTimers { } {
886        global mooddrifttimer
887        if      {![info exists mooddrifttimer]} {
888                timer 10 driftmood
889                #utimer 5 loldec
890                timer [expr [rand 30] + 3] doRandomStuff
891                set mooddrifttimer 1
892                set delay [expr [rand 200] + 1700]
893                utimer $delay bMotion_interbot_next_elect
894        }
895}
896
897### bMotion_cleanNick
898proc bMotion_cleanNick { nick { handle "" } } {
899        #attempt to clean []s etc out of nicks
900
901        if {![regexp {[\\\[\]\{\}]} $nick]} {
902                return $nick
903        }
904
905        if {($handle == "") || ($handle == "*")} {
906                set handle [nick2hand $nick]
907        }
908
909        if {($handle != "") && ($handle != "*")} {
910                set nick $handle
911        }
912
913        #have we STILL got illegal chars?
914        if {[regexp {[\\\[\]\{\}]} $nick]} {
915                #set nick [string map { \[ "_" \] "_" \{ "_" \} "_" } $nick]
916                set nick [string map { \[ "" \] "" \{ "" \} "" } $nick]
917        }
918        return $nick
919}
920
921### bMotion_uncolen
922# clean out $£(($ off the end
923proc bMotion_uncolen { line } {
924        regsub -all {([!\"\£\$\%\^\&\*\(\)\#\@]{3,})} $line "" line
925        return $line
926}
927
928### bMotion_setting_get
929# get a setting out of the two variables that commonly hold them
930proc bMotion_setting_get { setting } {
931        global bMotionSettings
932        global bMotionInfo
933        set val ""
934        catch {
935                set val $bMotionSettings($setting)
936        }
937        if {$val != ""} {
938                return $val
939        }
940
941        bMotion_putloglev 3 * "setting '$setting' doesn't exist in bMotionSettings, trying bMotionInfo..."
942        catch {
943                set val $bMotionInfo($setting)
944        }
945        if {$val != ""} {
946                return $val
947
948        }
949
950        bMotion_putloglev 3 * "nope, not there either, returning nothing"
951        return ""
952}
953#>>>
954proc bMotion_check_botnicks { } {
955        global botnicks bMotionSettings botnick
956
957        set safe_botnick [string map { "|" "\\|" "^" "\\^" "{" "\\{" "}" "\\}" "\[" "\\\[" "\]" "\\\]" } $botnick]
958        if {$botnicks == ""} {
959                if {[bMotion_setting_get "botnicks"] == ""} {
960                        set botnicks "($safe_botnick) ?"
961                } else {
962                        set botnicks "($safe_botnick|$bMotionSettings(botnicks)) ?"
963                }
964                if {[bMotion_setting_get "botnicks_strict"] == 1} {
965                        set botnicks "\\m$botnicks\\M"
966                }
967        }
968}
969
970proc bMotion_check_tired { min hour day month year } {
971        global bMotionSettings BMOTION_SLEEP
972
973        bMotion_putloglev 4 * "bMotion_check_tired $min $hour $day $month $year"
974        if {[bMotion_setting_get "sleepy"] != 1} {
975                return
976        }
977
978        set past_sleepy [bMotion_later_than $bMotionSettings(bedtime_hour) $bMotionSettings(bedtime_minute)]
979        set past_wakey [bMotion_later_than $bMotionSettings(wakeytime_hour) $bMotionSettings(wakeytime_minute)]
980
981        bMotion_putloglev 3 * "past_sleepy: $past_sleepy ... past_wakey: $past_wakey"
982
983        if {$bMotionSettings(asleep) < $BMOTION_SLEEP(ASLEEP)} {
984                # not asleep, so check if we should be
985                set do_sleep 0
986                if {$bMotionSettings(bedtime_hour) > $bMotionSettings(wakeytime_hour)} {
987                        # check that we're past wakeytime and sleepytime
988                        if {$past_wakey && $past_sleepy} {
989                                set do_sleep 1
990                        }
991                } else {
992                        # check we're past sleepytime and NOT past wwakeytime (4am < X < 9am)
993                                if {$past_sleepy && !$past_wakey} {
994                                set do_sleep 1
995                        }
996                }
997
998                if {$do_sleep} {
999                        bMotion_putloglev d * "past my bedtime, going to sleep"
1000                        bMotion_go_to_sleep
1001                        return
1002                }
1003        } else {
1004                set do_wake 0
1005                if {$bMotionSettings(bedtime_hour) > $bMotionSettings(wakeytime_hour)} {
1006                        # check we're not past bedtime but we are past wakeytime
1007                        if {!$past_sleepy && $past_wakey} {
1008                                set do_wake 1
1009                        } else {
1010                                # check we're past
1011                                if {$past_sleepy && !$past_wakey} {
1012                                        set do_wake 1
1013                                }
1014                        }
1015                }
1016
1017                if ($do_wake) {
1018                        bMotion_putloglev d * "ooh, time to wake up"
1019                        bMotion_wake_up
1020                        return
1021                }
1022        }
1023}
1024
1025# go to sleep
1026proc bMotion_go_to_sleep { } {
1027# ok this is the plan
1028# 1. announce we feel tired
1029# 2. ???
1030# 3. sleep
1031        global bMotionSettings BMOTION_SLEEP bMotionChannels
1032        bMotion_update_chanlist
1033
1034        if {$bMotionSettings(asleep) == $BMOTION_SLEEP(AWAKE)} {
1035                bMotion_putloglev 3 * "considering awake -> bedtime"
1036                if {[rand 10] > 3} {
1037                # announce we're tired
1038                        set bMotionSettings(asleep) $BMOTION_SLEEP(BEDTIME)
1039                        putlog "bMotion: preparing to go to bed"
1040                        foreach chan $bMotionChannels {
1041                                if [bMotion_is_active_enough $chan] {
1042                                        bMotion_putloglev 3 * "sending tired output to $chan"
1043                                        bMotionDoAction $chan "" "%VAR{tireds}"
1044                                }
1045                        }
1046                        return
1047                } else {
1048                        bMotion_putloglev d * "tired but not enough to tell anyone yet"
1049                }
1050                return
1051        }
1052
1053        if {$bMotionSettings(asleep) == $BMOTION_SLEEP(BEDTIME)} {
1054                bMotion_putloglev 3 * "considering bedtime -> sleep"
1055                if {[rand 10] > 3} {
1056                # go to sleep
1057                        set hour [bMotion_setting_get "wakeytime_hour"]
1058                        set minute [bMotion_setting_get "wakeytime_minute"]
1059                        set bMotionSettings(sleepy_nextchange) [bMotion_sleep_next_event "$hour:$minute"]
1060                        catch {
1061                                foreach chan $bMotionChannels {
1062                                        if [bMotion_did_i_speak_last $chan] {
1063                                                bMotion_putloglev 3 * "sending sleeping output to $chan"
1064                                                if [rand 2] {
1065                                                        bMotionDoAction $chan "" "%VAR{go_sleeps}"
1066                                                }
1067                                        }
1068                                }
1069                        }
1070                        set bMotionSettings(asleep) $BMOTION_SLEEP(ASLEEP)
1071                        putserv "AWAY :ZzZz"
1072                        putlog "bMotion: gone to sleep"
1073                        return
1074                } else {
1075                        bMotion_putloglev 1 * "not quite tired enough to actually go to sleep yet"
1076                }
1077                return
1078        }
1079        bMotion_putloglev d * "What th... bMotion_go_to_sleep called but I'm already asleep!"
1080}
1081
1082
1083# realise we overslept
1084proc bMotion_overslept { { onlychan "" } } {
1085        global bMotionSettings BMOTION_SLEEP bMotionChannels
1086        if {[bMotion_setting_get "asleep"] != $BMOTION_SLEEP(OVERSLEEPING)} {
1087                bMotion_putloglev d * "overslept timer fired but I wasn't oversleeping"
1088                return
1089        }
1090
1091        set bMotionSettings(asleep) $BMOTION_SLEEP(AWAKE)
1092
1093        if {$onlychan == ""} {
1094                set chanlist $bMotionChannels
1095        } else {
1096                set chanlist [list $onlychan]
1097        }
1098
1099        foreach chan $chanlist {
1100                if {[bMotion_is_active_enough $chan]} {
1101                        bMotionDoAction $chan "" "%VAR{overslept}"
1102                }
1103        }
1104}
1105
1106proc bMotion_wake_up { } {
1107        global bMotionSettings BMOTION_SLEEP bMotionChannels
1108        bMotion_update_chanlist
1109
1110        if {$bMotionSettings(asleep) == $BMOTION_SLEEP(ASLEEP)} {
1111                bMotion_putloglev 3 * "considering asleep -> awake"
1112                if {[rand 100] > 95} {
1113                        putlog "bMotion: really quite tired today... think I'll stay in bed a bit longer"
1114                        set snooze [expr [rand 120] + 30]
1115                        timer $snooze bMotion_overslept
1116                        putlog "bMotion: going to oversleep by $snooze minutes"
1117                        set bMotionSettings(asleep) $BMOTION_SLEEP(OVERSLEEPING)
1118
1119                        set hour [bMotion_setting_get "bedtime_hour"]
1120                        set minute [bMotion_setting_get "bedtime_minute"]
1121                        set bMotionSettings(sleepy_nextchange) [bMotion_sleep_next_event "$hour:$minute"]
1122
1123                        return
1124                }
1125
1126                if {[rand 10] > 7} {
1127                        putlog "bMotion: woke up!"
1128                        set bMotionSettings(asleep) $BMOTION_SLEEP(AWAKE)
1129                        putserv "AWAY"
1130
1131                        set hour [bMotion_setting_get "bedtime_hour"]
1132                        set minute [bMotion_setting_get "bedtime_minute"]
1133                        set bMotionSettings(sleepy_nextchange) [bMotion_sleep_next_event "$hour:$minute"]
1134
1135                        foreach chan $bMotionChannels {
1136                                # don't check for active enough here, as we're waking everyone up
1137                                # but do check we didn't speak last as that just looks dumb
1138                                if {![bMotion_did_i_speak_last $chan]} {
1139                                        bMotion_putloglev 3 * "sending waking output to $chan"
1140                                        if [rand 2] {
1141                                                bMotionDoAction $chan "" "%VAR{wake_ups}"
1142                                        }
1143                                }
1144                        }
1145                       
1146                        return
1147                } else {
1148                        bMotion_putloglev d * "just a few more minutes in bed..."
1149                }
1150                return
1151        }
1152}
1153
1154# check if the time is later than this
1155proc bMotion_later_than { hour minute } {
1156        set now [unixtime]
1157        set target [clock scan "$hour:$minute"]
1158        if {$target == ""} {
1159                return 0
1160        }
1161        if {$now > $target} {
1162                return 1
1163        }
1164        return 0
1165}
1166
1167
1168
1169proc bMotion_check_tired2 { a b c d e } {
1170        global BMOTION_SLEEP bMotionSettings
1171
1172        # check if we're past the time we should be changing
1173        if {[bMotion_setting_get "sleepy"] != 1} {
1174                return 0
1175        }
1176
1177        set limit [bMotion_setting_get "sleepy_nextchange"]
1178        bMotion_putloglev 4 * "current time = [clock seconds], checking if we're past $limit"
1179
1180        if {[clock seconds] >= $limit} {
1181                bMotion_putloglev d * "need to do a sleepy state change"
1182
1183                set state [bMotion_setting_get "asleep"]
1184                if {$state == $BMOTION_SLEEP(AWAKE)} {
1185                        bMotion_putloglev d * "maybe going to sleep"
1186                        bMotion_go_to_sleep
1187                        return
1188                }
1189
1190                if {$state == $BMOTION_SLEEP(BEDTIME)} {
1191                        bMotion_putloglev d * "maybe going to sleep"
1192                        bMotion_go_to_sleep
1193                        return
1194                }
1195
1196                if {$state == $BMOTION_SLEEP(ASLEEP)} {
1197                        bMotion_putloglev d * "maybe going to wake up"
1198                        bMotion_wake_up
1199                        return
1200                }
1201
1202                putlog "Whoops! Tried to do a sleepy state change but I'm not sure if I'm asleep or not :( ($state)"
1203                return
1204        }
1205}
1206
1207proc bMotion_sleep_next_event { when } {
1208        global bMotionSettings
1209
1210        set now [clock seconds]
1211
1212        set ts [clock scan $when]
1213        if {$ts < $now} {
1214                # oh, add 24h
1215                incr ts 86400
1216        }
1217        bMotion_putloglev d * "sleepy: next state change at $ts = [clock format $ts]"
1218        return $ts
1219}
1220
1221proc bMotion_did_i_speak_last { channel } {
1222        global bMotionCache
1223
1224        bMotion_putloglev 1 * "Checking if I spoke last in $channel"
1225       
1226        catch {
1227                bMotion_putloglev 1 * "Cache(last) for $channel is $bMotionCache($channel,last)"
1228                return $bMotionCache($channel,last)
1229        }
1230
1231        #assume no
1232        return 0
1233}
1234
1235# on start up, we should be awake and the next transition will be to sleep
1236if {[bMotion_setting_get "sleepy"] == 1} {
1237        set bMotionSettings(sleepy_nextchange) [bMotion_sleep_next_event "$bMotionSettings(bedtime_hour):$bMotionSettings(bedtime_minute)"]
1238}
1239
1240proc bMotion_get_daytime { } {
1241        set hour [clock format [clock seconds] -format "%k"]
1242
1243        if {$hour < 1} {
1244                return "evening"
1245        }
1246
1247        if {$hour < 12} {
1248                return "morning"
1249        }
1250
1251        if {$hour < 18} {
1252                return "afternoon"
1253        }
1254
1255        return "evening"
1256}
1257
1258# check something aginst our stoplist before we learn it
1259proc bMotion_filter_sillyThings { item } {
1260        if [regexp {[^A-Za-z0-9 '-]} $item] {
1261                bMotion_putloglev 2 * "sillyThing $item rejected for non-alpha chars"
1262                return 0
1263        }
1264
1265        if {[string length $item] == 2} {
1266                bMotion_putloglev 2 * "sillyThing $item rejected for length"
1267                return 0
1268        }
1269
1270        if [regexp -nocase {^(for|i|but)\M} $item] {
1271                bMotion_putloglev 2 * "sillyThing $item rejected for bad start"
1272                return 0
1273        }
1274
1275        # -rty, -ted?
1276
1277        if [regexp -nocase {\m(should|like|better|bigger|clever|other|rather|the|and|for|to|be|cool|dizzy|different|dry|entire|end|expensive|faster|federal|this|illegitimate|illiteracy|implicit|kind|lack|last|late|later|left|less|little|long|maybe|maybeok|meantime|mathematical|mechanical|more|most|much|multi|new|newer|next|particular|past|pure|quick|same|sheer|short|small|sort|specific|ultra|tubal|total|were|what|whole|weird|wrong)$} $item] {
1278                bMotion_putloglev 2 * "sillyThing $item rejected for stoplist"
1279                return 0
1280        }
1281
1282        if [regexp -nocase {(ful|est|ly|ive| he|edible|icable|tty)$} $item] {
1283                bMotion_putloglev 2 * "sillyThing $item rejected for word ending"
1284                return 0
1285        }
1286
1287        if [regexp -nocase {\m(is)$} $item] {
1288                bMotion_putloglev 2 * "sillyThing $item rejected for bad sentence end"
1289                return 0
1290        }
1291
1292        return 1
1293}
1294
1295# generic time-since handler
1296# mingap is in seconds
1297# other params are as for plugin settings
1298# returns 1 if gap since last is > mingap
1299# updates last to be now regardless
1300# nick is optional for this
1301proc bMotion_sufficient_gap { mingap plugin channel {nick ""} } {
1302        bMotion_putloglev 5 * "bMotion_sufficient_gap: $mingap $plugin $channel $nick"
1303                set lasttime [bMotion_plugins_settings_get $plugin "lasttime" $channel $nick]
1304                set diff [expr $mingap + 1]
1305                set gap_ok 0
1306
1307                if {$lasttime != ""} {
1308                        set diff [expr [clock seconds] - $lasttime]
1309                }
1310                bMotion_putloglev 1 * "sufficient_gap: last=$lasttime, now=[clock seconds], gap=$diff"
1311                if {$diff > $mingap} {
1312                        set gap_ok 1
1313                }
1314                bMotion_plugins_settings_set $plugin "lasttime" $channel $nick [clock seconds]
1315                bMotion_putloglev 1 * "sufficient_gap: returning $gap_ok for $plugin $channel ($nick) > $mingap"
1316                return $gap_ok
1317}
1318
1319# automatically pick a type of smiley
1320# store in local/
1321# use version in local if available
1322
1323proc bMotion_auto_smiley { } {
1324        bMotion_putloglev 5 * "bMotion_auto_smiley"
1325
1326        global bMotionLocal
1327        set smileyfile "${bMotionLocal}/smiley"
1328
1329                if {[file exists $smileyfile]} {
1330                        set fileHandle [open $smileyfile]
1331                        set line [gets $fileHandle]
1332                        if {$line == "bMotion smiley configuration (autogenerated)"} {
1333                                set bMotionSettings(smiley_type) [gets $fileHandle]
1334                                set bMotionSettings(smiley_nose) [gets $fileHandle]
1335                                set bMotionSettings(smiley_eyes) [gets $fileHandle]
1336                                close $fileHandle
1337
1338                                bMotion_putloglev d * "Loaded auto-generated smiley info"
1339                        } else {
1340                                putlog "bMotion: attempted to load smiley configuration but file is corrupt"
1341                        }
1342                } else {
1343                        bMotion_putloglev d * "Generating new smiley configuration"
1344                        set smiley_type_list [list "paren" "bracket" "angle"]
1345                        set smiley_nose_list [list "none" "o" "dash"]
1346                        set smiley_eyes_list [list "colon" "equals"]
1347
1348                        global bMotionSettings
1349
1350                        set bMotionSettings(smiley_type) [pickRandom $smiley_type_list]
1351                        set bMotionSettings(smiley_nose) [pickRandom $smiley_nose_list]
1352                        set bMotionSettings(smiley_eyes) [pickRandom $smiley_eyes_list]
1353
1354                        set fileHandle [open $smileyfile "w"]
1355                        puts $fileHandle "bMotion smiley configuration (autogenerated)"
1356                        puts $fileHandle [bMotion_setting_get "smiley_type"]
1357                        puts $fileHandle [bMotion_setting_get "smiley_nose"]
1358                        puts $fileHandle [bMotion_setting_get "smiley_eyes"]
1359
1360                        putlog "bMotion: auto-generated smiley configuration"
1361                        close $fileHandle
1362                }
1363}
1364
1365
1366
1367
1368bMotion_putloglev d * "bMotion: system module loaded"
1369
Note: See TracBrowser for help on using the repository browser.