Skip to content

Instantly share code, notes, and snippets.

@mniip
Created July 4, 2014 19:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mniip/6d64525e1ae9a26ee39b to your computer and use it in GitHub Desktop.
Save mniip/6d64525e1ae9a26ee39b to your computer and use it in GitHub Desktop.
use strict;
use Coro;
use Xchat;
Xchat::register("op helper", "0.0");
{
package Promise;
sub new($)
{
my $class = shift;
my $self = {};
$self->{coro} = $Coro::current;
bless $self, $class;
return $self;
}
sub wait($)
{
my $self = shift;
while(!exists $self->{value})
{
Coro::cede;
}
return $self->{value};
}
sub fulfill($;$)
{
my $self = shift;
$self->{value} = shift;
$self->{coro}->cede_to;
}
}
{
package WhoisPromise;
our @ISA = qw(Promise);
my @promises;
my %lastwhois;
sub new($;$)
{
my $class = shift;
my $self = {};
$self->{coro} = $Coro::current;
$self->{nick} = shift;
bless $self, $class;
push \@promises, $self;
Xchat::command("quote WHOIS :$self->{nick}");
return $self;
}
sub provide($;$)
{
my $whois = shift;
for my $i (0 .. $#promises)
{
if(!Xchat::nickcmp($promises[$i]->{nick}, $whois->{nick}))
{
$promises[$i]->fulfill($whois);
undef $promises[$i];
}
}
@promises = grep { defined } @promises;
}
Xchat::hook_server("311", sub {
my $w = shift;
$lastwhois{nick} = $w->[3];
$lastwhois{ident} = $w->[4];
$lastwhois{host} = $w->[5];
return Xchat::EAT_NONE;
});
Xchat::hook_server("330", sub {
my $w = shift;
$lastwhois{account} = $w->[4];
return Xchat::EAT_NONE;
});
Xchat::hook_server("318", sub {
WhoisPromise::provide(\%lastwhois);
%lastwhois = ();
return Xchat::EAT_NONE;
});
}
{
package ChanservPromise;
our @ISA = qw(Promise);
my @promises;
sub new($;$;$)
{
my $class = shift;
my $self = {};
$self->{coro} = $Coro::current;
$self->{channel} = shift;
$self->{nick} = shift;
bless $self, $class;
push \@promises, $self;
Xchat::command("quote CS OP $self->{channel} $self->{nick}");
return $self;
}
Xchat::hook_server("MODE", sub {
my $w = shift;
if($w->[0] =~ /^:ChanServ!/ && $w->[3] eq "+o")
{
my $channel = $w->[2];
my $nickname = $w->[4];
for my $i (0 .. $#promises)
{
if(!Xchat::nickcmp($promises[$i]->{nick}, $nickname) && !Xchat::nickcmp($promises[$i]->{channel}, $channel))
{
$promises[$i]->fulfill(1);
undef $promises[$i];
}
}
@promises = grep { defined } @promises;
}
return Xchat::EAT_NONE;
});
Xchat::hook_server("NOTICE", sub {
my $w = shift;
my $we = shift;
if($w->[0] =~ /^:ChanServ!/ && $we->[3] eq ":You are not authorized to perform this operation.")
{
my $p = pop @promises;
if(defined $p)
{
$p->fulfill(0);
}
}
});
}
Xchat::hook_command("czap", sub {
Coro::killall;
@WhoisPromise::promises = ();
@ChanservPromise::promises = ();
return Xchat::EAT_ALL;
});
{
package Action;
sub new($)
{
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}
sub execute($)
{
}
}
{
package CommandAction;
sub new($;$)
{
my $class = shift;
my $self = {};
$self->{command} = shift;
bless $self, $class;
return $self;
}
sub execute($)
{
my $self = shift;
Xchat::print($self->{command});
Xchat::command($self->{command});
}
}
{
package ModeAction;
sub new($;$)
{
my $class = shift;
my $self = {};
$self->{channel} = shift;
$self->{letters} = [];
$self->{arguments} = [];
$self->{num} = 0;
my @l = Xchat::get_list("channels");
$self->{max} = $l[0]->{maxmodes};
bless $self, $class;
return $self;
}
sub execute($)
{
my $self = shift;
my $letters = "";
my $arguments = "";
for my $i (0 .. $#{$self->{arguments}})
{
if(ref $self->{arguments}->[$i] eq "CODE")
{
my $arg = &{$self->{arguments}->[$i]}();
if(defined $arg)
{
$arguments .= " " . $arg;
$letters .= $self->{letters}->[$i];
}
}
else
{
$arguments .= " " . $self->{arguments}->[$i];
$letters .= $self->{letters}->[$i];
}
}
Xchat::print("quote MODE $self->{channel} $letters $arguments");
Xchat::command("quote MODE $self->{channel} $letters $arguments");
}
sub can_add($)
{
my $self = shift;
return $self->{num} < $self->{max};
}
sub add($;$;$)
{
my $self = shift;
push $self->{letters}, shift;
push $self->{arguments}, shift;
$self->{num}++;
}
}
sub execute_actions
{
for my $action (@_)
{
$action->execute;
}
}
sub add_mode
{
my ($actions, $channel, $letter, $arguments) = @_;
my $last = $actions->[-1];
unless(defined $last && $last->isa("ModeAction") && $last->can_add)
{
$last = ModeAction->new($channel);
push $actions, $last;
}
$last->add($letter, $arguments);
}
sub parse_actions
{
my $channel = shift;
my $string = shift;
my @actions = ();
for my $oaction (split /,/, $string)
{
my $action = $oaction;
$action =~ s/^\s*//;
$action =~ s/\s*$//;
if(length($action))
{
my $c = substr $action, 0, 1;
$action = substr $action, 1;
if($c eq '+' || $c eq '-' || $c eq '=')
{
$action =~ s/^\s*//;
if(length($action))
{
my $mode = substr $action, 0, 1;
$action = substr $action, 1;
if(length($action))
{
$action =~ /^(\s*:|(?:\s*[=!@])+)?\s*(.*)$/;
my $whoiser = $1;
$action = $2;
$whoiser =~ s/\s//g;
if($action =~ /^[^:=!@]/)
{
if(length($whoiser))
{
$action =~ /^([^\s\$]+)(.*)$/;
my $nick = $1;
my $forward = $2;
$forward =~ s/\s//g;
if(defined $nick)
{
my $p = WhoisPromise->new($nick);
add_mode(\@actions, $channel, "$c$mode", sub {
my %whois = %{$p->wait};
if($whoiser eq ':')
{
if(defined $whois{account})
{
return "\$a:$whois{account}";
}
Xchat::print("Warning: whois for $p->{nick} contains no account name");
}
elsif(defined $whois{nick})
{
my $nickmask = ($whoiser =~ /=/) ? $whois{nick} : "*";
my $identmask = ($whoiser =~ /\!/) ? $whois{ident} : "*";
my $hostmask = ($whoiser =~ /@/) ? $whois{host} : "*";
return "$nickmask!$identmask\@$hostmask$forward";
}
Xchat::print("Warning: whois for $p->{nick} failed");
});
next;
}
}
else
{
$action =~ s/\s//g;
add_mode(\@actions, $channel, "$c$mode", $action);
next;
}
}
}
else
{
add_mode(\@actions, $channel, "$c$mode", "");
next;
}
}
}
elsif($c eq 'r' || $c eq 'k')
{
$action =~ /^\s*(\S+)\s*(?:\s+(.*\S)\s*)?$/;
my $nick = $1;
if(defined $nick)
{
my $reason = $2;
my $cmd = $c eq 'r' ? "REMOVE" : "KICK";
if(defined $reason)
{
push @actions, CommandAction->new("quote $cmd $channel $nick :$reason");
next;
}
else
{
push @actions, CommandAction->new("quote $cmd $channel $nick");
next;
}
}
}
}
Xchat::print("Warning: Ignoring unknown action '$oaction'");
}
return @actions;
}
sub command
{
my $w = shift;
my $cmd = $w->[0];
my $channel = Xchat::get_info("channel");
my $me = Xchat::get_info("nick");
my $we = shift;
async
{
if($cmd eq "cd" || $cmd eq "co")
{
my $c = ChanservPromise->new($channel, $me);
unless($c->wait())
{
Xchat::print("Warning: Could not perform CS OP, actions aborted");
return;
}
}
my @actions = parse_actions($channel, $we->[1]);
if($cmd eq "d" || $cmd eq "cd")
{
add_mode(\@actions, $channel, "-o", $me);
}
execute_actions(@actions);
};
Coro::cede;
return Xchat::EAT_ALL;
}
Xchat::hook_command("d", \&command);
Xchat::hook_command("o", \&command);
Xchat::hook_command("cd", \&command);
Xchat::hook_command("co", \&command);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment