- Add qdb.us support
[quotesite.git] / quotesite.pl
1 # autodisplay quotes from quotesites
2 #
3 # (c) 2007-2008 by Ralf Ertzinger <ralf@camperquake.de>
4 # licensed under GNU GPL v2
5
6 use strict;
7 use Irssi 20020324 qw (command_bind command_runsub signal_add_first signal_add_last);
8 use vars qw($VERSION %IRSSI);
9 use XML::Simple;
10 use Data::Dumper;
11 use File::Spec;
12
13 my @grabbers;
14 my $conf;
15 my $conffile = File::Spec->catfile(Irssi::get_irssi_dir(), 'quotesite.xml');
16 my $plugindir = File::Spec->catfile(Irssi::get_irssi_dir(), 'scripts', 'quotesite');
17
18 my $PARAMS = {
19 };
20
21
22 # activate debug here
23 my $debug = 0;
24
25 # "message public", SERVER_REC, char *msg, char *nick, char *address, char *target
26 signal_add_last("message public" => sub {check_for_link(\@_,1,4,2,0);});
27 # "message own_public", SERVER_REC, char *msg, char *target
28 signal_add_last("message own_public" => sub {check_for_link(\@_,1,2,-1,0);});
29
30 # "message private", SERVER_REC, char *msg, char *nick, char *address
31 signal_add_last("message private" => sub {check_for_link(\@_,1,-1,2,0);});
32 # "message own_private", SERVER_REC, char *msg, char *target, char *orig_target
33 signal_add_last("message own_private" => sub {check_for_link(\@_,1,2,-1,0);});
34
35 # "message irc action", SERVER_REC, char *msg, char *nick, char *address, char *target
36 signal_add_last("message irc action" => sub {check_for_link(\@_,1,4,2,0);});
37 # "message irc own_action", SERVER_REC, char *msg, char *target
38 signal_add_last("message irc own_action" => sub {check_for_link(\@_,1,2,-1,0);});
39
40 sub write_irssi {
41     my $witem = shift;
42     my @text = @_;
43
44     $text[0] = 'quotesite: ' . $text[0];
45
46     if (defined $witem) {
47         $witem->print(sprintf(shift(@text), @text), MSGLEVEL_CLIENTCRAP);
48     } else {
49         Irssi::print(sprintf(shift(@text), @text));
50     }
51
52 }
53
54 sub write_debug {
55     if ($debug) {
56         write_irssi(shift, @_);
57     }
58 }
59
60 sub check_for_link {
61     my ($signal,$parammessage,$paramchannel,$paramnick,$paramserver) = @_;
62     my $server = $signal->[$paramserver];
63     my $target = $signal->[$paramchannel];
64     my $message = ($parammessage == -1) ? '' : $signal->[$parammessage];
65     my $g;
66     my $m;
67     my $p;
68
69
70     my $witem;
71     if (defined $server) {
72         $witem = $server->window_item_find($target);
73     } else {
74         $witem = Irssi::window_item_find($target);
75     }
76
77     # Look if we should ignore this line
78     if ($message =~ m,(?:\s|^)/nosave(?:\s|$),) {
79         return;
80     }
81
82     study($message);
83
84     # Offer the message to all Grabbers in turn
85     foreach $g (@grabbers) {
86         ($m, $p) = $g->get($message);
87         while (defined($m)) {
88             write_irssi($witem, '%%R>>> %%Y%s%%N %%G%s', $m->{'SOURCE'}, $m->{'ID'});
89
90             foreach (split(/[\n\r]+/, $m->{'CONTENT'})) {
91                 write_irssi($witem, '    %%g%s', $_);
92             }
93
94             # Remove the matched part from the message and try again (there may be
95             # more!)
96             $message =~ s/$p//;
97             study($message);
98
99             ($m, $p) = $g->get($message);
100         }
101     }
102 }
103
104 sub cmd_save {
105
106     eval {
107         open(CONF, '>'.$conffile) or die 'Could not open config file';
108         print CONF XML::Simple::XMLout($conf, KeepRoot => 1, KeyAttr => {'config' => 'module', 'option' => 'key'});
109         close(CONF);
110     };
111     if ($@) {
112         write_irssi(undef, 'Could not save config to %s: %s', ($conffile, $@));
113     } else {
114         write_irssi(undef, 'configuration saved to %s', $conffile);
115     }
116 }
117
118 sub cmd_set {
119     my $target = shift;
120     my $key = shift;
121     my $val = shift;
122     my $p;
123
124     foreach $p (@grabbers) {
125         if ($p->{'NAME'} eq $target) {
126             $p->setval($key, $val);
127             return;
128         }
129     }
130     write_irssi(undef, 'No such module');
131 }
132
133
134 sub cmd_enable {
135     my $target = shift;
136     my $p;
137
138     foreach $p (@grabbers) {
139         if ($p->{'NAME'} eq $target) {
140             $p->enable();
141             return;
142         }
143     }
144     write_irssi(undef, 'No such module');
145 }
146
147
148 sub cmd_disable {
149     my $target = shift;
150     my $p;
151
152     foreach $p (@grabbers) {
153         if ($p->{'NAME'} eq $target) {
154             $p->disable();
155             return;
156         }
157     }
158     write_irssi(undef, 'No such module');
159 }
160
161
162 sub cmd_show {
163     my $target = shift;
164     my $p;
165     my $e;
166
167     if (defined($target)) {
168         foreach $p (@grabbers) {
169             if ($p->{'NAME'} eq $target) {
170                 write_irssi(undef, $p->getconfstr());
171                 return;
172             }
173         }
174         write_irssi(undef, 'No such module');
175     } else {
176         write_irssi(undef, 'Loaded grabbers (* denotes enabled modules):');
177         foreach $p (@grabbers) {
178             $e = $p->_getval('enabled');
179             write_irssi(undef, ' %s%s', $p->{'NAME'}, $e?'*':'');
180         };
181     }
182 }
183
184 sub cmd_help {
185     my $target = shift;
186     my $p;
187
188     if (defined($target)) {
189         foreach $p (@grabbers) {
190             if ($p->{'NAME'} eq $target) {
191                 write_irssi(undef, $p->gethelpstr());
192                 return;
193             }
194         }
195         write_irssi(undef, 'No such module');
196     } else {
197         write_irssi(undef, <<'EOT');
198 Supported commands:
199  save: Save the current configuration
200  help [modulename]: Display this help, or module specific help
201  show [modulename]: Show loaded modules, or the current parameters of a module
202  set modulename parameter value: set a module parameter to a new value
203  enable [modulename]: enable the usage of this module (grabbers only)
204  disable [modulename]: disable the usage of this module (grabbers only)
205  reload: reload all modules (this is somewhat experimental)
206  debug: enable debugging messages
207  nodebug: disable debugging messages
208 EOT
209     }
210 }
211
212 # save on unload
213 sub sig_command_script_unload {
214     my $script = shift;
215     if ($script =~ /(.*\/)?quotesite(\.pl)?$/) {
216         cmd_save();
217     }
218 }
219
220 sub ploader {
221
222     my $dir = shift;
223     my $pattern = shift;
224     my $type = shift;
225     my @list;
226     my $p;
227     my $g;
228     my @g = ();
229
230     opendir(D, $dir) || return ();
231     @list = grep {/$pattern/ && -f File::Spec->catfile($dir, $_) } readdir(D);
232     closedir(D);
233
234     foreach $p (@list) {
235         write_debug(undef, "Trying to load $p:");
236         $p =~ s/\.pm$//;
237         eval qq{ require $p; };
238         if ($@) {
239             write_debug(undef, "Failed to load plugin: $@");
240             next;
241         }
242
243         $g = eval $p.q{->new();};
244         if ($@) {
245             write_debug(undef, "Failed to instanciate: $@");
246             delete($INC{$p});
247             next;
248         }
249
250         write_debug(undef, "found $g->{'TYPE'} $g->{'NAME'}");
251         if ($type eq $g->{'TYPE'}) {
252             push(@g, $g);
253             $g->setio(sub {Irssi::print(shift)});
254         } else {
255             write_irssi(undef, '%s has wrong type (got %s, expected %s)', $p, $g->{'TYPE'}, $type);
256             delete($INC{$p});
257         }
258     }
259
260     write_debug(undef, "Loaded %d plugins", $#g+1);
261     
262     return @g;
263 }
264
265 sub _load_modules($) {
266
267     my $path = shift;
268
269     foreach (keys(%INC)) {
270         if ($INC{$_} =~ m|^$path|) {
271             write_debug(undef, "Removing %s from \$INC", $_);
272             delete($INC{$_});
273         }
274     }
275     @grabbers = ploader($path, '.*Grabber\.pm$', 'grabber');
276 }
277
278
279 sub init_quotesite {
280
281     my $bindings = shift;
282     my $p;
283
284     unless(-r $conffile && defined($conf = XML::Simple::XMLin($conffile, ForceArray => ['config', 'option'], KeepRoot => 1, KeyAttr => {'config' => 'module', 'option' => 'key'}))) {
285         # No config, start with an empty one
286         write_debug(undef, 'No config found, using defaults');
287         $conf = { 'quotesite' => { }};
288     }
289     foreach (keys(%{$PARAMS})) {
290         unless (exists($conf->{'quotesite'}->{$_})) {
291             $conf->{'quotesite'}->{$_} = $PARAMS->{$_};
292         }
293     }
294
295     _load_modules($plugindir);
296
297     unless (defined(@grabbers)) {
298         write_irssi(undef, 'No grabbers found, can not proceed.');
299         return;
300     }
301
302
303     # Loop through all plugins and load the config
304     foreach $p (@grabbers) {
305         $conf->{'quotesite'}->{'config'}->{$p->{'NAME'}} = $p->mergeconfig($conf->{'quotesite'}->{'config'}->{$p->{'NAME'}});
306     }
307
308     if ($bindings) {
309
310         Irssi::signal_add_first('command script load', 'sig_command_script_unload');
311         Irssi::signal_add_first('command script unload', 'sig_command_script_unload');
312         Irssi::signal_add('setup saved', 'cmd_save');
313
314
315         Irssi::command_bind('quotesite' => \&cmdhandler);
316     }
317
318     write_irssi(undef, 'quotesite initialized');
319 }
320
321 sub cmdhandler {
322     my ($data, $server, $item) = @_;
323     my @params = split(/\s+/, $data);
324
325     if ($params[0] eq 'save') {
326         cmd_save();
327     } elsif ($params[0] eq 'set') {
328         shift(@params);
329         cmd_set(@params);
330     } elsif ($params[0] eq 'show') {
331         shift(@params);
332         cmd_show(@params);
333     } elsif ($params[0] eq 'help') {
334         shift(@params);
335         cmd_help(@params);
336     } elsif ($params[0] eq 'enable') {
337         shift(@params);
338         cmd_enable(@params);
339     } elsif ($params[0] eq 'disable') {
340         shift(@params);
341         cmd_disable(@params);
342     } elsif ($params[0] eq 'reload') {
343         init_quotesite(0);
344     } elsif ($params[0] eq 'debug') {
345         $debug = 1;
346         foreach (@grabbers) {
347             $_->setdebug(1);
348         }
349         write_irssi(undef, 'Enabled debugging');
350     } elsif ($params[0] eq 'nodebug') {
351         $debug = 0;
352         foreach (@grabbers) {
353             $_->setdebug(0);
354         }
355         write_irssi(undef, 'Disabled debugging');
356     }
357 }
358
359 unshift(@INC, $plugindir);
360 init_quotesite(1);