]> ruin.nu Git - NDIRC.git/blob - Bot.pm
Initial Discord support
[NDIRC.git] / Bot.pm
1 #**************************************************************************
2 #   Copyright (C) 2009 by Michael Andreen <harvATruinDOTnu>               *
3 #                                                                         *
4 #   This program is free software; you can redistribute it and/or modify  *
5 #   it under the terms of the GNU General Public License as published by  *
6 #   the Free Software Foundation; either version 2 of the License, or     *
7 #   (at your option) any later version.                                   *
8 #                                                                         *
9 #   This program is distributed in the hope that it will be useful,       *
10 #   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12 #   GNU General Public License for more details.                          *
13 #                                                                         *
14 #   You should have received a copy of the GNU General Public License     *
15 #   along with this program; if not, write to the                         *
16 #   Free Software Foundation, Inc.,                                       *
17 #   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
18 #**************************************************************************/
19 package NDIRC::Bot;
20
21 use strict;
22 use warnings;
23 use feature ':5.10';
24
25 use Moose;
26
27 use POE::Component::IRC::Common qw/irc_to_utf8/;
28 use POE::Session;
29 use POE::Component::IRC::Plugin::Logger;
30 use POE::Component::IRC::Plugin::BotTraffic;
31 use POE::Component::IRC::Plugin::Connector;
32 use POE::Component::IRC::Plugin::AutoJoin;
33 use POE::Component::IRC::Plugin::NickReclaim;
34
35 use Mojo::Discord;
36 use Mojo::IOLoop;
37
38 use NDIRC::Dispatcher;
39 use NDIRC::Context;
40
41 use IO::File;
42
43 has disp => (
44         is => 'rw',
45         isa => 'Object',
46         lazy_build => 1
47 );
48
49 has discord => (
50         is => 'rw',
51         isa => 'Object'
52 );
53
54 has discord_name => (
55         is => 'rw',
56         isa => 'Str'
57 );
58
59 has discord_id => (
60         is => 'rw',
61         isa => 'Str'
62 );
63
64 # We registered for all events, this will produce some debug info.
65 sub _default {
66         my ($event, $args) = @_[ARG0 .. $#_];
67         my @output = ( "$event: " );
68
69         for my $arg (@$args) {
70                 if ( ref $arg eq 'ARRAY' ) {
71                         push( @output, '[' . join(', ', @$arg ) . ']' );
72                 }
73                 else {
74                         push ( @output, "'$arg'" );
75                 }
76         }
77         print join ' ', @output, "\n";
78         return 0;
79 }
80
81 sub _start {
82         my ($self,$kernel,$heap,$session) = @_[OBJECT,KERNEL,HEAP,SESSION];
83
84         # retrieve our component's object from the heap where we stashed it
85         my $irc = $heap->{irc};
86         $kernel->sig( DIE => 'sig_DIE' );
87         $kernel->sig( USR1 => 'sig_usr1' );
88         $kernel->sig( USR2 => 'sig_usr2' );
89         $kernel->sig( INT => 'signal_handler' );
90
91         $irc->plugin_add( 'NickReclaim', POE::Component::IRC::Plugin::NickReclaim->new() );
92         $irc->plugin_add( 'AutoJoin', POE::Component::IRC::Plugin::NickReclaim->new() );
93         $irc->plugin_add( 'BotTraffic', POE::Component::IRC::Plugin::BotTraffic->new() );
94         $irc->plugin_add('Logger', POE::Component::IRC::Plugin::Logger->new(
95                 Path    => 'irclogs',
96                 DCC     => 0,
97                 Private => 1,
98                 Public  => 1,
99                 Sort_by_date => 1,
100                 Strip_color => 1,
101                 Strip_formatting => 1,
102                 Notices => 1,
103         ));
104
105         $heap->{connector} = POE::Component::IRC::Plugin::Connector->new(
106                 servers => [['irc.netgamers.org'], ['underworld.no.eu.netgamers.org']
107                         ,['firefly.no.eu.netgamers.org'], ['underworld.ca.us.netgamers.org'] ]
108         );
109         $irc->plugin_add( 'Connector' => $heap->{connector} );
110
111         $irc->yield( register => 'all' );
112         $irc->yield( connect => { server => 'irc.netgamers.org' } );
113
114         $kernel->delay( refresh => 60 );
115
116         if (my $f =  new IO::File 'discord'){
117                 my $user = <$f>;
118                 chomp $user;
119                 my $token = <$f>;
120                 chomp $token;
121
122                 $self->discord(Mojo::Discord->new(
123                                 'token'     => $token,
124                                 'name'      => $user,
125                                 'url'       => 'https://nd.ruin.nu',
126                                 'version'   => '1.0',
127                                 'callbacks' => {
128                                         'READY'          => sub { $self->discord_ready(@_) },
129                                         'MESSAGE_CREATE' => sub { $self->discord_message_create(@_) },
130                                         'GUILD_CREATE' => sub { $self->discord_guild_create(@_) },
131                                 },
132                                 'reconnect' => 1,
133                                 'verbose'   => 1,
134                         ));
135                 $self->discord->init();
136         }
137         return;
138 }
139
140 sub auth {
141         my $heap = $_[HEAP];
142
143         if (my $f =  new IO::File 'auth'){
144                 my $user = <$f>;
145                 chomp $user;
146                 my $pass = <$f>;
147                 chomp $pass;
148                 $heap->{irc}->yield(qbot_auth => $user => $pass);
149         }
150 }
151
152 sub sig_usr1 {
153         my ($kernel,$heap) = @_[KERNEL,HEAP];
154
155         $kernel->yield( 'refresh' );
156 }
157
158 sub clear_constraint {
159         my $tc = shift;
160
161         while (1) {
162                 if (ref $tc eq 'MooseX::Meta::TypeConstraint::Structured'){
163                         for my $t (@{$tc->{type_constraints}}){
164                                 clear_constraint($t);
165                         }
166
167                 }
168                 if (ref $tc eq 'Moose::Meta::TypeConstraint::Parameterized'){
169                         clear_constraint($tc->{type_parameter});
170                 }
171                 last if ref $tc eq 'HASH';
172                 last if ref $tc eq '';
173                 if (defined $tc->{_type_constraint}){
174                         $tc = $tc->{_type_constraint};
175                 }elsif(defined $tc->{__type_constraint}){
176                         $tc = $tc->{__type_constraint};
177                 }else{
178                         last;
179                 }
180         }
181 }
182
183 sub clear_metains {
184         my $ins = shift;
185
186         for my $a (@{$ins->{attributes}}){
187                 for my $m (@{$a->{associated_methods}}){
188                         $m->{body} = undef;
189                 }
190                 clear_constraint($a->{isa});
191         }
192 }
193
194 sub clear_cycles {
195         my $c = shift;
196
197         for my $m (values %{$c->meta->{methods}}){
198                 clear_constraint($m->{type_constraint});
199
200                 my $ps = $m->{parsed_signature};
201                 for my $p (@{$ps->{_positional_params}->{params}}){
202                         clear_metains($p->{__MOP__}->{_meta_instance});
203                 }
204
205                 $m->{body} = undef;
206         }
207         clear_metains($c->meta->{_meta_instance});
208 }
209
210
211 sub sig_usr2 {
212         my $self = shift @_;
213
214         for my $c (values %{$self->disp->commands}){
215                 clear_cycles($c);
216         }
217
218         $self->disp($self->_build_disp);
219 }
220
221 sub _build_disp {
222         my ($self) = @_;
223         my $disp = new NDIRC::Dispatcher;
224
225         if (my $commands = new IO::File 'commands'){
226                 my @commands = split /\W+/, do{local $/; <$commands>};
227                 say "Loading commands from: @commands";
228                 $disp->load(@commands);
229         }
230
231         my $channels = new IO::File 'channels' or die $!;;
232         while (<$channels>){
233                 my ($chan, @types) = split /\s+/;
234                 say "$chan - @types";
235                 if ($chan =~ /^(.*):(.*)$/){
236                         $chan = $1;
237                         $disp->set_target($2,$chan);
238                 }
239                 $disp->add_channel($chan,\@types);
240         }
241
242         return $disp;
243 }
244
245 sub sig_DIE {
246         my( $kernel,$sig, $ex ) = @_[ KERNEL,ARG0, ARG1 ];
247         say "DIED!!!!!!!!!!!!!!";
248         # $sig is 'DIE'
249         # $ex is the exception hash
250         warn "$$: error in event: $ex->{error_str}";
251         $kernel->sig_handled();
252
253         # Send the signal to session that sent the original event.
254         #if( $ex->{source_session} ne $_[SESSION] ) {
255         #$kernel->signal( $ex->{source_session}, 'DIE', $sig, $ex );
256         #}
257 }
258
259 sub signal_handler {
260         my ($kernel, $signal_name, $heap) = @_[KERNEL, ARG0, HEAP];
261         print "First session caught SIG$signal_name\n";
262
263         given($signal_name){
264                 when ('INT') {
265                         exit unless $heap->{irc}->connected;
266                         $heap->{INT} = 1;
267                         $heap->{irc}->yield(quit => 'Bye!');
268                         $kernel->sig_handled();
269                 }
270         }
271         #$kernel->sig_handled();
272 }
273
274 sub irc_disconnected {
275         my ($sender,$heap) = @_[SENDER,HEAP];
276
277         exit if $heap->{INT};
278 }
279
280 sub irc_001 {
281         my ($self,$sender,$kernel) = @_[OBJECT,SENDER,KERNEL];
282
283         # Since this is an irc_* event, we can get the component's object by
284         # accessing the heap of the sender. Then we register and connect to the
285         # specified server.
286         my $irc = $sender->get_heap();
287
288         print "Connected to ", $irc->server_name(), "\n";
289
290         $kernel->yield( 'auth' );
291         $irc->yield( mode => $irc->nick_name, '+ix');
292
293         # we join our channels
294         $irc->yield( join => $_ ) for grep /^#/, keys %{$self->disp->channels};
295         return;
296 }
297
298 sub irc_invite {
299         my ($self,$sender, $who, $channel) = @_[OBJECT,SENDER, ARG0 .. ARG1];
300         my $irc = $sender->get_heap();
301
302         $irc->yield( join => $_ ) for grep /^$channel$/i, keys %{$self->disp->channels}
303 }
304
305 sub irc_public {
306         $_[ARG2] = irc_to_utf8 $_[ARG2];
307 }
308
309 sub irc_msg {
310         $_[ARG2] = irc_to_utf8 $_[ARG2];
311 }
312
313 sub  refresh {
314 }
315
316 sub irc_join {
317 }
318
319 sub discord_ready {
320         my $self = shift;
321         my $hash = shift;
322         $self->discord_id($hash->{user}{id});
323         $self->discord_name($hash->{user}{username});
324
325         say localtime(time) . " - Connected to Discord. $self->{discord_id}";
326 }
327
328 sub discord_message_create {
329 }
330
331 sub discord_guild_create {
332 }
333
334 sub parseCommand {
335         my ($self, $msg, $server, $nick, $address, $channel, $model) = @_;
336
337         return if $channel !~ /^#/ && $msg =~ /^~/;
338         $msg = ".$msg"  if $channel !~ /^#/ && $msg =~ /^[^.!]/;
339
340         my ($p,$command,$args) = ($msg =~ /^([.!~])(\S+)(?: (.+))?/);
341
342         if ($msg =~ m{https?://[\w.]+/.+?scan(_id|_grp)?=(\w+)}){
343                 if (!$command || $command =~ m{^https?://}){
344                         ($p,$command,$args) = ('.','addscan',$msg);
345                 }elsif($command ne 'addscan'){
346                         $self->parseCommand (".addscan $msg", $server, $nick, $address, $channel, $model)
347                 }
348         }
349
350         return 0 unless $self->disp->has_command($command,$channel);
351
352         my $reply_string;
353         given ($p){
354                 when ('!'){
355                         $reply_string = "privmsg $nick";
356                 }
357                 when ('~'){
358                         $reply_string = "privmsg $channel";
359                 }
360                 default {
361                         $reply_string = "notice $nick";
362                 }
363         }
364
365         $address =~ s/.*@(.*)/$1/;
366         my $c = NDIRC::Context->new({
367                         host => $address,
368                         nick => $nick,
369                         channel => $channel,
370                         disp => $self->disp,
371                         model => $model,
372                         server => $server,
373                         reply_string => $reply_string,
374                 });
375
376         return $self->disp->run_command($c,$command,$args);
377 }
378
379 1;