]> git.somenet.org - irc/bugbot.git/blob - BotModules/Greeting.bm
GITOLITE.txt
[irc/bugbot.git] / BotModules / Greeting.bm
1 # -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
2 ################################
3 # Greeting Module              #
4 ################################
5
6 package BotModules::Greeting;
7 use vars qw(@ISA);
8 @ISA = qw(BotModules);
9 1;
10
11 sub Help {
12     my $self = shift;
13     my ($event) = @_;
14     return {
15         '' => 'A polite module for saying hello and goodbye and so on.',
16         'hi' => 'To greet the bot.',
17         'bye' => 'To say goodbye to the bot.',
18         'ping' => 'To check the bot is alive.',
19         'status' => 'Gives the amount of time that the bot has been active.',
20     };
21 }
22
23 # RegisterConfig - Called when initialised, should call registerVariables
24 sub RegisterConfig {
25     my $self = shift;
26     $self->SUPER::RegisterConfig(@_);
27     $self->registerVariables(
28       # [ name, save?, settable? ]
29         ['greetings', 1, 1, ['hi %', 'yo %', 'salut %', '%! dude!', '%: hello', '%', 'bonjour %', 'g\'day mate']],
30         ['greetingsIndex', 1, 1, 0],
31         ['byes', 1, 1, ['seeya %', 'bye %', 'night %', '/me waves goodbye to %']],
32         ['byesIndex', 1, 1, 0],
33         ['ow', 1, 1, ['%!! stop it!!', '%? You want something?', 'I\'m working! Leave me alone!', 'ow!', 'Leave me out of it!', '%: mean!']],
34         ['owIndex', 1, 1, 0],
35         ['veryow', 1, 1, ['OOOOWWWW!!!', 'GETOFF!!!', '/me fights back', 'Yikes! I\'m being attacked!!', '/me hits % over the head with a 2-by-4']],
36         ['veryowIndex', 1, 1, 0],
37         ['hit', 1, 1, ['/me smacks %target', '/me hits %target over the head with a hammer', '/me trips %target up and laughs', '%target! look over there! *smack*', '/me pokes %target in the ribs']],
38         ['hitIndex', 1, 1, 0],
39         ['hitProtected', 1, 1, {'hixie' => '%target: %source wanted me to hurt you but don\'t worry, i wuv you, i\'d never hurt you...', 'me' => '/me wacks %source in the legs with a crowbar', '' => '%source: Oh you\'d like that, wouldn\'t you, you sadist pervert.', 'yourself' => 'hey look everyone! %source likes to see others hurt themselves!', 'urself' => 'oh my! %source can\'t even spell! It\'s written "yourself", moron!'}],
40         ['hitEnabled', 1, 1, 1], # set to 0 to disable hitting
41         ['pat', 1, 1, ['/me patpats %target', '%target: yes dear, *pat* *pat*', '/me pats %target condescendingly', '%target: *pat* *pat*']],
42         ['patIndex', 1, 1, 0],
43         ['patProtected', 1, 1, {'' => '%source: what did I do now?', 'yourself' => '%source: why? what did i do wrong?'}],
44         ['hug', 1, 1, ['/me hugs %target', '%target: *hug*', '/me hugs %target lovingly', '%target: come \'ere! *hugs and kisses*']],
45         ['hugIndex', 1, 1, 0],
46         ['yousuck', 1, 1, ['%: no, *you* suck!', '/me pouts', '/me cries', '/me . o O ( now what have i done... )']],
47         ['yousuckIndex', 1, 1, 0],
48         ['thanks', 1, 1, ['sure thing %', 'np', '%: np', '%: just doing my job!']],
49         ['thanksIndex', 1, 1, 0],
50         ['listen', 1, 1, ['(*', '%: I\'m listening.', '%?']],
51         ['listenIndex', 1, 1, 0],
52         ['happy', 1, 1, [':)', '/me smiles', 'yay', '/me beams']],
53         ['happyIndex', 1, 1, 0],
54         ['unhappy', 1, 1, [':(', '/me sobs', '/me cries', '*sniff*', 'but... but...', '/me is all sad']],
55         ['unhappyIndex', 1, 1, 0],
56         ['vhappy', 1, 1, ['OOoh! %!', 'I love you too, %.']],
57         ['vhappyIndex', 1, 1, 0],
58         ['kinky', 1, 1, ['eep!', 'me-ow!', 'oh yeah! spank me baby!', '/me tickles %', 'he-llo, baby!']],
59         ['kinkyIndex', 1, 1, 0],
60         ['tickle', 1, 1, ['eep!', 'iiiih!', 'meep!', '/me tickles % back', 'yelp!']],
61         ['tickleIndex', 1, 1, 0],
62         ['apology', 1, 1, ['Apology accepted.', 'thanks', 's\'ok', 'heh', 'that\'s ok']],
63         ['apologyIndex', 1, 1, 0],
64         ['whoami', 1, 1, 'I am a bot. /msg me the word \'help\' for a list of commands.'],
65         ['lastrheet', 0, 0, 0], # time of last rheet
66         ['rheetbuffer', 1, 1, 10], # max of 1 rheet per this many seconds 
67         ['rheetMaxEs', 1, 1, 100], # number of es at which to stop responding.
68         ['autoGreetMute', 1, 1, []], # channels to mute in
69         ['autoGreetings', 1, 1, {}], # people to greet and their greeting
70         ['autoGreeted', 0, 0, {}], # people to NOT greet, and the last time
71         ['autoGreetedBackoffTime', 1, 1, 20], # how long to not greet people (seconds)
72         ['evil', 1, 1, ['c++ is evil', '/me mumbles something about c++ being evil', 'c++ is e-- ah, nevermind.', 'c++ sucks', '/me frowns at %']],
73         ['evilIndex', 1, 1, 0],
74         ['evilBackoffTime', 1, 1, 36000], # how long to not insult c++ (10 hours by default)
75         ['evilMute', 1, 1, []], # channels to disable evil in, * for all channels
76         ['lastEvil', 1, 0, 0], # when the last c++ insult took place
77         ['assumeThanksTime', 1, 1, 10], # how long to assume that thanks are directed to us after hearing from them (seconds)
78         ['_lastSpoken', 0, 0, {}], # who has spoken to us
79         ['source', 1, 1, 'http://lxr.mozilla.org/mozilla/source/webtools/mozbot/'], # reply to give for CTCP SOURCE
80     );
81 }
82
83 sub Told {
84     my $self = shift;
85     my ($event, $message) = @_;
86     my $now = $event->{'time'};
87     $self->{'_lastSpoken'}->{$event->{'user'}} = $now;
88     my $me = quotemeta($event->{'bot'}->nick);
89     my $expandedme = join('+', split(//gos, $me)).'+';
90     if ($message =~ /^\s*(?:(?:g[ood\']*\s*)?(?:mornin[g\']?|evenin[g\']?|afternoon|day)|hi|heya?|bonjour|hoi|w+a+[sz]+u+p+\?*|hello|lo|wb|welcome\s+back|greetings|yo(?:\s+yo)*(?:\s+du+de)?|m+[ayh]+(?:\s+m+a+i+n+)?\s+m+a+n+|d+u+d+e+)[?!1.\s]*(?::-?[\)Pp]\s*)*$/osi) {
91         if ($self->canGreet($event)) {
92             $self->Perform($event, 'greetings');
93         }
94     } elsif ($message =~ /^\s*(?:bye|(?:g?'?|good\s+)night|seeya|ciao)[?!1.\s]*$/osi) {
95         $self->Perform($event, 'byes');
96     } elsif ($message =~ /^\s*say[\s:,\"\']+(hi|hello|lo|good\s*bye|seeya)(?:\s+to\s+(\S+))(?:[,\s]*please)?[?!1.\s]*$/osi) {
97         if ($2) {
98             $self->say($event, "$2: $1");
99         } else {
100             $self->say($event, "$1");
101         }
102     } elsif ($message =~ /^\s*
103                               (?:  (?:you|u)  \s+
104                                    (?:really\s+)?
105                                    suck
106                                    (?:  \s+hard
107                                      |  (?:\s+big)? \s+ rocks)?
108                                 |  (?:you|u) \s+
109                                    (?:smell|stick)
110                                 |  (?:you|u)
111                                    (?:\s+are|\s+r|'re|r) \s+
112                                    (?:an?\s+)?
113                                    (?:really\s+)*
114                                    (?:idiot|stupid|dumb|moron|moronic|useless)
115                                    (?:\s+bot)?
116                                 |  i \s+ hate \s+ (?:you|u)
117                                 |  bi+tch)
118                             [?!1.\s]*$/osix) {
119         $self->Perform($event, 'yousuck');
120     } elsif ($message =~ /^\s*(?:oh[!1?.,\s]*)?(?:thanks|thank\s+you|cheers)[\s!1.]*(?:[;:8][-o]?[]()\|O0<>[]\s*)?$/osi) {
121         $self->Perform($event, 'thanks');
122     } elsif ($message =~ /^\s*(?:good\s+bot[.!1\s]*|(?:you|u)\s+rock(?:\s+bot)?|:-?\)|(?:have\s+a\s+)?bot\s*snack[.!1\s]*)\s*(?:[;:8][-o]?[]()\|O0<>[]\s*)?$/osi) {
123         $self->Perform($event, 'happy');
124     } elsif ($message =~ /^\s*(?:i|we)\s+love\s+(?:you|u)[.!1\s]*(?:[;:8][-o]?[]()\|O0<>[]\s*)?$/osi) {
125         $self->Perform($event, 'happy');
126     } elsif ($message =~ /^\s*(?:please[\s,.]+)?(?:(?:would|will)\s+you\s+)?(?:hit|kick|slap|smack)\s+(\S+?)(?:[\s,.]+please)?[.!?\s]*\s*$/osi) {
127         if ($self->{'hitEnabled'}) {
128             $self->PerformOnOther($event, 'hit', $1);
129         }
130     } elsif ($message =~ /^\s*(?:please[\s,.]+)?(?:(?:would|will)\s+you\s+)?(?:pat|pat\s*pat)\s+(\S+?)(?:[\s,.]+please)?[.!?\s]*\s*$/osi) {
131         $self->PerformOnOther($event, 'pat', $1);
132     } elsif ($message =~ /^\s*(?:please[\s,.]+)?(?:(?:would|will)\s+you\s+)?(?:hug)\s+(\S+?)(?:[\s,.]+please)?[.!?\s]*\s*$/osi) {
133         $self->PerformOnOther($event, 'hug', $1);
134     } elsif ($message =~ /^\s*(?:useless|die|get\s+a\s+life|kiss\s+my\s+ass|you\s+stupid\s+piece\s+o[f']?\s+code)[!1.\s]*$/osi) {
135         $self->Perform($event, 'unhappy');
136     } elsif ($message =~ /^\s*sorry\b/osi) { # note that any trailing text is ignored
137         $self->Perform($event, 'apology');
138     } elsif ($message =~ /^\s*(?:how\s+are\s+you|how\s+do\s+you\s+do|how\'?s\s+things|are\s+you\s+ok)(?:[?!1.,\s]+$expandedme)?\s*[?!1.\s]*$/osi) {
139         $uptime = $self->days($^T);
140         $self->say($event, "$event->{'from'}: fine thanks! I've been up $uptime so far!");
141     } elsif ($message =~ /^\s*(?:who\s+are\s+you)\s*[?!1.\s]*$/osi) {
142         $self->say($event, "$event->{'from'}: $self->{'whoami'}");
143     } elsif ($message =~ /^\s*(?:up\s*(?:time)|status)[?!1.\s]*$/osi) {
144         $uptime = $self->days($^T);
145         $self->say($event, "$event->{'from'}: I've been up $uptime.");
146     } elsif ($message =~ /^\s*r+h+e(e+)t+[!1.\s]*$/osi) {
147         if (length($1) < $self->{'rheetMaxEs'}) {
148             $self->say($event, "$event->{'from'}: rhe$1$1t!");
149         } else {
150             $self->say($event, "$event->{'from'}: uh, whatever.");
151         }
152     } elsif ($message =~ /^\s*ping\s*$/osi) {
153         $self->say($event, "$event->{'from'}: pong");
154     } else {
155         return $self->SUPER::Told(@_);
156     }
157     return 0; # we've dealt with it, no need to do anything else.
158 }
159
160 sub Heard {
161     my $self = shift;
162     my ($event, $message) = @_;
163     my $me = quotemeta($event->{'bot'}->nick);
164     my $expandedme = join('+', split(//gos, $me)).'+';
165     if ($message =~ /^\s*(?:(?:(?:(?:g[ood\']*\s*)?(?:mornin[g\']?|evenin[g\']?|afternoon|day)|hi|heya?|bonjour|hoi|w+a+[sz]+u+p+|hello|lo|wb|welcome\s+back|greetings|yo(?:\s+yo)*)\s+)?$expandedme[!1\s]*|o+h[\s,.!?]+look[\s,.!?]+a\s+$me[\s.!1]*)(?::-?[\)Pp]\s*)*$/si) {
166         if ($self->canGreet($event)) {
167             $self->Perform($event, 'greetings');
168         }
169     } elsif ($message =~ /^\s*(?:bye|(?:g?\'?|good\s+)night|seeya|ciao)\s+$me[!1.\s]*$/si) {
170         $self->Perform($event, 'byes');
171     } elsif ($message =~ /^\s*(?:oh[!1?,.\s]*)?(?:thanks|thank\s*you|cheers)\s+$me[\s!1.]*(?:[;:8][-o]?[]()\|O0<>[]\s*)?$/si) {
172         $self->Perform($event, 'thanks');
173     } elsif (($message =~ /^\s*(?:oh[!1?,.\s]*)?(?:thanks|thank\s*you|cheers)[\s!1.]*(?:[;:8][-o]?[]()\|O0<>[]\s*)?$/osi) and ($self->canAssumeThanks($event))) {
174         $self->Perform($event, 'thanks');
175     } elsif (($message =~ /^\s*(?:good\s+bot)[!1.\s]*(?:[;:8][-o]?[]()\|O0<>[]\s*)?$/osi) and ($self->canAssumeThanks($event))) {
176         $self->Perform($event, 'happy');
177     } elsif (($message =~ /^\s*(?:bad|foo[l\']?|idiot|dumb|useless|moron|moronic)(?:\s+bot)?[!.\s]*?$/osi) and ($self->canAssumeThanks($event))) {
178         $self->Perform($event, 'unhappy');
179     } elsif (($message =~ /^\s*bad\s*$me[!.\s]*$/si) and ($self->canAssumeThanks($event))) {
180         $self->Perform($event, 'unhappy');
181     } elsif (($message =~ /^\s*
182                               (?:  (?:you|u)  \s+
183                                    (?:really\s+)?
184                                    suck
185                                    (?:  \s+hard
186                                      |  (?:\s+big)? \s+ rocks)?
187                                 |  (?:you|u) \s+
188                                    (?:smell|stick)
189                                 |  (?:you|u)
190                                    (?:\s+are|\s+r|'re|r) \s+
191                                    (?:an?\s+)?
192                                    (?:really\s+)?
193                                    (?:idiot|stupid|dumb|moron|moronic)
194                                    (?:\s+bot)?
195                                 |  i \s+ hate \s+ (?:you|u)
196                                 |  bi+tch)
197                             [?!1.\s]*$/osix) and
198              ($self->canAssumeThanks($event))) {
199         $self->Perform($event, 'yousuck');
200     } elsif ($message =~ /^\s*(?:good(?:\s$me)?|yay[\s!1.]*|i\s+love\s+(?:you|u))\s+$me[\s!1.]*(?:[;:8][-o]?[]()\|O0<>[]\s*)?$/si) {
201         $self->Perform($event, 'happy');
202     } elsif ($message =~ /^\s*(?:$me\s*[.?\/]+)\s*$/si) {
203         $self->Perform($event, 'listen');
204     } elsif ($message =~ /^\s*r+h(e+)t+[!1.\s]*$/osi) {
205         if (($event->{'time'}-$self->{'lastrheet'}) > $self->{'rheetbuffer'}) {
206             if (length($1) < $self->{'rheetMaxEs'}) {
207                 $self->say($event, "rhe$1$1t!");
208             }
209             $self->{'lastrheet'} = $event->{'time'};
210         }
211     } elsif ($message =~ /^.+\s+c\+\+\s+.+$/osi) {
212         if (!(grep {$_ eq '*' or lc($_) eq $event->{'channel'}} @{$self->{'evilMute'}}) &&
213             ($event->{'time'} - $self->{'lastEvil'}) > $self->{'evilBackoffTime'}) {
214             $self->{'lastEvil'} = $event->{'time'};
215             $self->Perform($event, 'evil'); # calls GetNext which calls saveConfig
216         }
217     } else {
218         return $self->SUPER::Heard(@_);
219     }
220     return 0; # we've dealt with it, no need to do anything else.
221 }
222
223 sub Felt {
224     my $self = shift;
225     my ($event, $message) = @_;
226     my $me = quotemeta($event->{'bot'}->nick);
227     if ($message =~ /^\s*(?:greets\s+$me|shakes\s+$me'?s\s+hand)[\s!1.]*$/si) {
228         $self->Perform($event, 'greetings');
229     } elsif ($message =~ /^\s*(?:pokes|prods)\s+$me(?:[,\s]+too|\s+as\s+well)?[\s!1.]*$/si) {
230         $self->Perform($event, 'ow');
231     } elsif ($message =~ /^\s*(?:stabs|slaps|kicks|kills|hits|punches)\s+$me[\s!1.]*$/si) {
232         $self->Perform($event, 'veryow');
233     } elsif ($message =~ /^\s*lights\s+$me\s+on\s+fire[!1.\s]*$/si) {
234         $self->Perform($event, 'veryow');
235     } elsif ($message =~ /^\s*(?:pats|strokes|pets)\s+$me(:?\s+affectionately|\s+lovingly)?[!1.\s]*$/si) {
236         $self->Perform($event, 'happy');
237     } elsif ($message =~ /^\s*slaps\s+$me\s+(?:around\s+)?(?:a\s+(?:bit|lot|little|while)\s+)?with\s+a\s+(?:(?:big|fat|large|wet|and)[\s,]+)*trout[\s!1.]*$/si) {
238         $self->Perform($event, 'ow');
239     } elsif ($message =~ /^\s*(?:hits|kicks|slaps|smacks)\s+$me[\s!1.]*$/si) {
240         $self->Perform($event, 'yousuck');
241     } elsif ($message =~ /^\s*(?:glares|stares)\s+at\s+$me[\s!1.]*$/si) {
242         $self->Perform($event, 'yousuck');
243     } elsif ($message =~ /^\s*(?:hugs|cuddles|snuggles(?:\s+up\s*to|\s+with)?|kisses|loves)\s+$me[\s!1.]*$/si) {
244         $self->Perform($event, 'vhappy');
245     } elsif ($message =~ /^\s*(?:bites|spanks)\s+$me[\s.]*$/si) {
246         $self->Perform($event, 'kinky');
247     } elsif ($message =~ /^\s*(?:tickles)\s+$me[\s.]*$/si) {
248         $self->Perform($event, 'tickle');
249     } elsif ($message =~ /^\s*(?:gives|hands|passes|offers)\s+$me\s+(?:a\s+(?:bot\s*)?(?:snack|cookie)|a\s+present|cash|congratulations|applause|praise)[\s!1.]*$/si) {
250         $self->Perform($event, 'happy');
251     } elsif ($message =~ /^\s*(?:gives|hands|passes|offers)\s+$me\s+(?:a\s+hot\s+date)[\s!1.]*$/si) {
252         $self->Perform($event, 'vhappy');
253     } else {
254         return $self->SUPER::Felt(@_);
255     }
256     return 0; # we've dealt with it, no need to do anything else.
257 }
258
259 sub Saw {
260     my $self = shift;
261     my ($event, $message) = @_;
262     if ($message =~ /^\s*r+h+e(e+)t+s?[!1.\s]*$/osi) {
263         if (($event->{'time'}-$self->{'lastrheet'}) > $self->{'rheetbuffer'}) {
264            $self->say($event, "rhe$1$1t!");
265            $self->{'lastrheet'} = $event->{'time'};
266         }
267     } elsif (($message =~ /^\s*(?:smiles)\s*[!1.\s]*$/si) and ($self->canAssumeThanks($event))) {
268         $self->Perform($event, 'happy');
269     } else {
270         return $self->SUPER::Felt(@_);
271     }
272     return 0; # we've dealt with it, no need to do anything else.
273 }
274
275 # SpottedJoin - Called when someone joins a channel
276 sub SpottedJoin {
277     my $self = shift;
278     my ($event, $channel, $who) = @_;
279     return if grep lc($_) eq $channel, @{$self->{'autoGreetMute'}};
280     my $user = $event->{'user'};
281     if ($self->canGreet($event) and $self->{'autoGreetings'}->{$who}) {
282         $self->sayOrEmote($event, $self->Expand($event, $self->{'autoGreetings'}->{$who}));
283         $self->{'autoGreeted'}->{$user} = $event->{'time'};
284     }
285     return 1; # don't block other modules...
286 }
287
288 sub CTCPPing {
289     my $self = shift;
290     my ($event, $who, $what) = @_;
291     $self->ctcpReply($event, 'PING', $what);
292 }
293
294 sub CTCPSource {
295     my $self = shift;
296     my ($event, $who, $what) = @_;
297     $self->ctcpReply($event, 'SOURCE', $self->{'source'});
298 }
299
300 sub GetNext {
301     my $self = shift;
302     my ($list) = @_;
303     $self->{"${list}Index"} = 0 if $self->{"${list}Index"} > $#{$self->{$list}};
304     my $reply = $self->{$list}->[$self->{"${list}Index"}++];
305     $self->saveConfig();
306     return $reply;
307 }
308
309 sub canGreet {
310     my $self = shift;
311     my ($event) = @_;
312     my $user = $event->{'user'};
313     my $reply = 1;
314     if (defined($self->{'autoGreeted'}->{$user})) {
315        $reply = (($event->{'time'} - $self->{'autoGreeted'}->{$user}) > $self->{'autoGreetedBackoffTime'});
316        delete($self->{'autoGreeted'}->{$user});
317     }
318     return $reply;
319 }
320
321 sub canAssumeThanks {
322     my $self = shift;
323     my ($event) = @_;
324     my $who = $event->{'user'};
325     return ((defined($self->{'_lastSpoken'}->{$who})) and (($event->{'time'} - $self->{'_lastSpoken'}->{$who}) <= $self->{'assumeThanksTime'}));
326 }
327
328 sub Perform {
329     my $self = shift;
330     my ($event, $list) = @_;
331     $self->sayOrEmote($event, $self->Expand($event, $self->GetNext($list))); 
332 }
333
334 # replaces '%' with the target nick (XXX cannot escape a "%"!!!)
335 sub Expand {
336     my $self = shift;
337     my ($event, $data) = @_;
338     $data =~ s/%/$event->{'from'}/gos;
339     return $data;
340 }
341
342 sub PerformOnOther {
343     my $self = shift;
344     my ($event, $list, $other) = @_;
345     my $data;
346     my $me = quotemeta($event->{'nick'});
347     if ($other =~ m/^$me$/si and
348         defined $self->{"${list}Protected"}->{''}) {
349         $data = $self->{"${list}Protected"}->{''};
350     } elsif (defined $self->{"${list}Protected"}->{lc $other}) {
351         $data = $self->{"${list}Protected"}->{lc $other};
352     } else {
353         $data = $self->GetNext($list);
354     }
355     if ($other eq 'me') {
356         $other = $event->{'from'};
357     }
358     $data =~ s/%source/$event->{'from'}/gos;
359     $data =~ s/%target/$other/gos;
360     $self->sayOrEmote($event, $data); 
361 }