# Based on youtube.pl by Christian Garbs <mitch@cgarbs.de>
# which in turn is
# based on trigger.pl by Wouter Coekaerts <wouter@coekaerts.be>
-# This is helena
+
+
+BEGIN {
+ # Get rid of a (possibly old) version of BettIrssi
+ # This is a hack to prevent having to reload irssi just
+ # because BettIrssi.pm changed
+
+ delete($INC{'BettIrssi.pm'});
+}
use strict;
use Irssi 20020324 qw (command_bind command_runsub signal_add_first signal_add_last);
use XML::Simple;
use Data::Dumper;
use File::Spec;
-delete($INC{'BettIrssi.pm'});
-use BettIrssi qw(_bcb _bcs);
+use File::Temp qw(tempfile);
+use BettIrssi 101 qw(_bcb _bcs);
+use LWP::UserAgent;
+use JSON -support_by_pp;
my @grabbers;
my @getters;
my $getter;
my $conf;
-my $conffile = File::Spec->catfile(Irssi::get_irssi_dir(), 'videosite.xml');
+my $xmlconffile = File::Spec->catfile(Irssi::get_irssi_dir(), 'videosite.xml');
+my $conffile = File::Spec->catfile(Irssi::get_irssi_dir(), 'videosite.json');
my $scriptdir = File::Spec->catfile(Irssi::get_irssi_dir(), 'scripts');
my $plugindir = File::Spec->catfile($scriptdir, 'videosite');
my @outputstack = (undef);
my $PARAMS = {
'getter' => '',
- 'mode' => 'download'
+ 'mode' => 'download',
+ 'connectorlist' => ['direct'],
+ 'connectors' => {},
};
signal_add_last(_bcs("message irc own_action" => sub {check_for_link(@_)}));
# For tab completion
+# This does not use BettIrssi (yet)
signal_add_first('complete word', \&sig_complete);
sub push_output {
- unshift(@putputstack, shift);
+ unshift(@outputstack, shift);
}
sub pop_output {
shift(@outputstack);
- unless(@outputstack) @outputstack = (undef);
+ @outputstack = (undef) unless (@outputstack);
}
my $videosite_commands = {
cmd_mode(@_);
},
+ 'connector' => sub {
+ cmd_connector(@_);
+ },
+
'debug' => sub {
$debug = 1;
foreach (@grabbers, @getters) {
}
}
+sub expand_url_shortener {
+ my $s = shift;
+ my $os = '';
+ my @urlshortener = (
+ 'is\.gd/[[:alnum:]]+',
+ 'otf\.me/[[:alnum:]]+',
+ 'hel\.me/[[:alnum:]]+',
+ '7ax\.de/[[:alnum:]]+',
+ 'ow\.ly/[[:alnum:]]+',
+ 'j\.mp/[[:alnum:]]+',
+ 'bit\.ly/[[:alnum:]]+',
+ 'tinyurl\.com/[[:alnum:]]+',
+ 'pop\.is/[[:alnum:]]+',
+ 'post\.ly/[[:alnum:]]+',
+ '1\.ly/[[:alnum:]]+',
+ '2\.ly/[[:alnum:]]+',
+ 't\.co/[[:alnum:]]+',
+ 'shar\.es/[[:alnum:]]+',
+ 'goo\.gl/[[:alnum:]]+',
+ );
+ my $ua = LWP::UserAgent->new(agent => 'Mozilla', max_redirect => 0, timeout => 5);
+ my $i = 10;
+
+ OUTER: while (($os ne $s) and ($i > 0)) {
+ study($s);
+ $os = $s;
+ $i--;
+
+ foreach my $pattern (@urlshortener) {
+ my $p = "https?:\/\/" . $pattern;
+
+ write_debug("Matching %s against %s", $p, $s);
+ if ($s =~ m|($p)|) {
+ my $matched = $1;
+ my $res;
+
+ write_debug("Found %s", $matched);
+ $res = $ua->head($matched);
+ if ($res->is_redirect()) {
+ my $new = $res->headers()->header("Location");
+
+ write_debug("Replacing %s with %s", $matched, $new);
+ $s =~ s/$matched/$new/;
+ next OUTER;
+ } else {
+ write_debug("Error resolving %s", $matched);
+ }
+ }
+ }
+ }
+
+ if ($i == 0) {
+ write_debug("Loop terminated by counter");
+ }
+
+ write_debug("Final string: %s", $s);
+
+ return $s;
+}
+
+sub connectorlist {
+ my @c;
+
+ foreach (@{$conf->{'videosite'}->{'connectorlist'}}) {
+ push(@c, $conf->{'videosite'}->{'connectors'}->{$_});
+ }
+
+ return @c;
+}
+
+
sub check_for_link {
my $event = shift;
my $message = $event->message();
return;
}
+ push_output($event->ewpf);
+ $message = expand_url_shortener($message);
+
study($message);
# Offer the message to all Grabbers in turn
- foreach $g (@grabbers) {
+ GRABBER: foreach $g (@grabbers) {
($m, $p) = $g->get($message);
while (defined($m)) {
write_debug('Metadata: %s', Dumper($m));
# more!)
$message =~ s/$p//;
study($message);
+ last GRABBER if ($message =~ /^\s*$/);
($m, $p) = $g->get($message);
}
}
+
+ pop_output();
}
sub cmd_save {
+ print Dumper($conf);
eval {
- open(CONF, '>'.$conffile) or die 'Could not open config file';
- print CONF XML::Simple::XMLout($conf, KeepRoot => 1, KeyAttr => {'config' => 'module', 'option' => 'key'});
- close(CONF);
+ my ($tempfile, $tempfn) = tempfile("videosite.json.XXXXXX", dir => Irssi::get_irssi_dir());
+ print $tempfile JSON->new->pretty->utf8->encode($conf);
+ close($tempfile);
+ rename($tempfn, $conffile);
};
if ($@) {
write_irssi('Could not save config to %s: %s', ($conffile, $@));
disable [modulename]: disable the usage of this module (grabbers only)
reload: reload all modules (this is somewhat experimental)
mode [modename]: display or set the operation mode (download/display)
+ connector [subcommand]: manage connectors (proxies)
debug: enable debugging messages
nodebug: disable debugging messages
EOT
if ($p->{'NAME'} eq $target) {
$getter = $p;
$conf->{'videosite'}->{'getter'} = $target;
+ write_irssi("Getter changed to %s", $target);
return;
}
}
$mode = lc($mode);
if (('download' eq $mode) or ('display' eq $mode)) {
$conf->{'videosite'}->{'mode'} = $mode;
+ write_irssi('Now using %s mode', $mode);
} else {
write_irssi('Invalid mode: %s', $mode);
}
}
}
+sub cmd_connector {
+ my $subcmd = shift;
+ my $connconf = $conf->{'videosite'}->{'connectors'};
+
+ unless(defined($subcmd)) {
+ $subcmd = "help";
+ }
+
+ $subcmd = lc($subcmd);
+
+ if ($subcmd eq 'list') {
+ write_irssi("Defined connectors");
+ foreach (keys(%{$connconf})) {
+ write_irssi($_);
+ my $schemas = $connconf->{$_}->{'schemas'};
+ if (scalar(keys(%{$schemas})) == 0) {
+ write_irssi(" No schemas defined");
+ } else {
+ foreach (keys(%{$schemas})) {
+ write_irssi(' %s: %s', $_, $schemas->{$_});
+ }
+ }
+ }
+
+ write_irssi();
+ write_irssi("Selected connectors: %s", join(", ", @{$conf->{'videosite'}->{'connectorlist'}}));
+ } elsif ($subcmd eq 'add') {
+ my ($name) = @_;
+
+ unless(defined($name)) {
+ write_irssi("No name given");
+ return;
+ }
+
+ $name = lc($name);
+
+ if (exists($connconf->{$_})) {
+ write_irssi("Connector already exists");
+ return;
+ }
+
+ $connconf->{$name} = {'name' => $name, 'schemas' => {}};
+ } elsif ($subcmd eq 'del') {
+ my ($name) = @_;
+
+ unless(defined($name)) {
+ write_irssi("No name given");
+ return;
+ }
+
+ $name = lc($name);
+
+ unless (exists($connconf->{$name})) {
+ write_irssi("Connector does not exist");
+ return;
+ }
+
+ if (exists($connconf->{$name}->{'_immutable'})) {
+ write_irssi("Connector cannot be removed");
+ return;
+ }
+
+ delete($connconf->{$name});
+
+ # Remove from list of active connectors
+ $conf->{'videosite'}->{'connectorlist'} =
+ [ grep { $_ ne $name } @{$conf->{'videosite'}->{'connectorlist'}} ];
+
+ if (scalar(@{$conf->{'videosite'}->{'connectorlist'}}) == 0) {
+ write_irssi("List of selected connectors is empty, resetting to direct");
+ $conf->{'videosite'}->{'connectorlist'} = [ 'direct' ];
+ }
+ } elsif ($subcmd eq 'addschema') {
+ my ($conn, $schema, $proxy) = @_;
+
+ unless(defined($conn)) {
+ write_irssi("No connector name given");
+ return;
+ }
+
+ $conn = lc($conn);
+
+ if (exists($connconf->{$conn}->{'_immutable'})) {
+ write_irssi("Connector cannot be modified");
+ return;
+ }
+
+ unless(defined($schema)) {
+ write_irssi("No schema given");
+ return;
+ }
+
+ $schema = lc($schema);
+
+ unless(defined($proxy)) {
+ write_irssi("No proxy given");
+ return;
+ }
+
+ unless(exists($connconf->{$conn})) {
+ write_irssi("Connector does not exist");
+ return;
+ }
+
+ $connconf->{$conn}->{'schemas'}->{$schema} = $proxy;
+ } elsif ($subcmd eq 'delschema') {
+ my ($conn, $schema) = @_;
+
+ unless(defined($conn)) {
+ write_irssi("No connector name given");
+ return;
+ }
+
+ $conn = lc($conn);
+
+ if (exists($connconf->{$conn}->{'_immutable'})) {
+ write_irssi("Connector cannot be modified");
+ return;
+ }
+
+ unless(defined($schema)) {
+ write_irssi("No schema given");
+ return;
+ }
+
+ $schema = lc($schema);
+
+ unless(exists($connconf->{$conn})) {
+ write_irssi("Connector does not exist");
+ return;
+ }
+
+ delete($connconf->{$conn}->{'schemas'}->{$schema});
+ } elsif ($subcmd eq 'select') {
+ my @connlist = map { lc } @_;
+
+ if (scalar(@connlist) == 0) {
+ write_irssi("No connectors given");
+ return;
+ }
+
+ foreach (@connlist) {
+ unless(exists($connconf->{$_})) {
+ write_irssi("Connector %s does not exist", $_);
+ return;
+ }
+ }
+
+ $conf->{'videosite'}->{'connectorlist'} = [ @connlist ];
+ } else {
+ write_irssi("connector [list|add|del|addschema|delschema|help] <options>");
+ write_irssi(" help: Show this help");
+ write_irssi(" list: List the defined connectors");
+ write_irssi(" add <name>: Add a connector with name <name>");
+ write_irssi(" del <name>: Delete the connector with name <name>");
+ write_irssi(" addschema <name> <schema> <proxy>: Add proxy to connector for the given schema");
+ write_irssi(" delschema <name> <schema>: Remove the schema from the connector");
+ write_irssi(" select <name> [<name>...]: Select the connectors to use");
+ }
+}
+
+
+
# save on unload
sub sig_command_script_unload {
write_debug("found $g->{'TYPE'} $g->{'NAME'}");
if ($type eq $g->{'TYPE'}) {
push(@g, $g);
- $g->setio(sub {Irssi::print(shift)});
+ $g->setio(\&write_irssi);
+ $g->setconn(\&connectorlist);
} else {
write_irssi('%s has wrong type (got %s, expected %s)', $p, $g->{'TYPE'}, $type);
delete($INC{$p});
my $bindings = shift;
my $p;
- unless(-r $conffile && defined($conf = XML::Simple::XMLin($conffile, ForceArray => ['config', 'option'], KeepRoot => 1, KeyAttr => {'config' => 'module', 'option' => 'key'}))) {
+ if (-r $conffile) {
+ write_debug("Attempting JSON config load from %s", $conffile);
+ eval {
+ local $/;
+ open(CONF, '<', $conffile);
+ $conf = JSON->new->utf8->decode(<CONF>);
+ close(CONF);
+ };
+ } elsif (-r $xmlconffile) {
+ write_debug("Attempting XML config load from %s", $xmlconffile);
+ $conf = XML::Simple::XMLin($xmlconffile, ForceArray => ['config', 'option', 'connectorlist'], KeepRoot => 1, KeyAttr => {'connector' => '+name', 'config' => 'module', 'option' => 'key'});
+ }
+
+ unless(defined($conf)) {
# No config, start with an empty one
write_debug('No config found, using defaults');
$conf = { 'videosite' => { }};
}
+
foreach (keys(%{$PARAMS})) {
unless (exists($conf->{'videosite'}->{$_})) {
$conf->{'videosite'}->{$_} = $PARAMS->{$_};
}
}
+ # Make sure there is a connector called 'direct', which defines no
+ # proxies
+ unless (exists($conf->{'videosite'}->{'connectors'}->{'direct'})) {
+ $conf->{'videosite'}->{'connectors'}->{'direct'} = {
+ 'name' => 'direct',
+ '_immutable' => '1',
+ 'schemas' => {},
+ };
+ }
+
_load_modules($plugindir);
unless (defined(@grabbers) && defined(@getters)) {
Irssi::command_bind(_bcb('videosite' => \&cmdhandler));
}
- write_irssi('videosite initialized');
+ write_irssi('initialized successfully');
}
sub sig_complete {
my $event = shift;
my ($cmd, @params) = split(/\s+/, $event->message());
+ push_output($event->ewpf);
+
if (exists($videosite_commands->{$cmd})) {
$videosite_commands->{$cmd}->(@params);
}
+
+ pop_output();
}
unshift(@INC, $scriptdir);