Script: kickban.pl

Customizable kickban command with unban timer and mask resolver.
Author: ArZa — Version: 0.3 — License: GPL-3.0-or-later
For WeeChat ≥ 0.3.0.
Tags: irc, kick, ban
Added: 2011-07-08 — Updated: 2014-03-24

Download GitHub Repository

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# kickban.pl by arza <arza@arza.us>: A new, customizable kickban command

# This program is free software: you can modify/redistribute it under the terms of
# GNU General Public License by Free Software Foundation, either version 3 or later
# which you can get from <http://www.gnu.org/licenses/>.
# This program is distributed in the hope that it will be useful, but without any warranty.

# This script provides command /kickban2. You probably want to alias it to /kb: /alias kb kickban2

# Changelog:
# 24.03.2014 0.3 fix bug with uninitialized variable
# 15.10.2011 0.2 fix bug with ban when host isn't found in memory
# 08.07.2011 0.1 initial release

weechat::register("kickban", "arza <arza\@arza.us>", "0.3", "GPL3", "A new, customizable kickban command", "", "");
weechat::hook_command(
  "kickban2",
  "A new, customizable kickban command", "[-nuhsd#] nick[,nick2...] [reason]",
"The ban mask can be specified by setting plugins.var.perl.kickban.banmask or by a switch (default: u,h):
  n: nick
  u: username
  h: full host
  s: subdomain
  d: domain

Timeout for unban in minutes can be set by setting plugins.var.perl.kickban.time or by a number in a switch. (default: 0, don't unban)

Multiple nicks is supported. If nick isn't found in the channel, the mask is looked up automatically. (v>=0.3.4)

The default kick reason can be set by plugins.var.perl.kickban.kick_reason.

Whether kicking will be done before banning can be speficied by plugins.var.perl.kickban.kick_first.
There may be a delay of irc.server_default.anti_flood_prio_high between commands.

Examples:

Kick nick lamer and ban its nick!*\@*.domain:
  /kickban2 -nd lamer
Kick nicks badone and badtwo with reason \"Bye bye\", and ban them by *!user\@*.domain for ten minutes:
  /kickban2 -10ud badone,badtwo Bye bye",
  "%(nick)", "kickban", "");

my $version=weechat::info_get("version_number", "") || 0;
weechat::hook_hsignal("irc_redirection_sigwhois_whois", "get_whois", "") if $version>=0x00030400;

my ($buffer, %banmask, $time, $reason);

init_config();

sub init_config {
  
  if($version>=0x00030500){ # descriptions for settings
    weechat::config_set_desc_plugin("banmask", "mask used for banning, default: u,h (*!user\@host)");
    weechat::config_set_desc_plugin("time", "time in minutes to unban after banning, 0=never (default)");
    weechat::config_set_desc_plugin("kick_first", "kick before ban (default: on)");
    weechat::config_set_desc_plugin("kick_reason", "default kick reason");
  }
  
  my %options = ( # default options
                 "banmask" => "u,h",
                 "time" => "0",
                 "kick_first" => "on",
                 "kick_reason" => "",
                );
  
  foreach my $option (keys %options){ # sync the defaults
    weechat::config_set_plugin($option, $options{$option}) unless weechat::config_is_set_plugin($option);
  }
  
}

sub kickban {
  
  if(!$_[2]){ weechat::command("", "/help kickban2"); return weechat::WEECHAT_RC_OK; }
  
  $buffer=$_[1];
  my @args=split(/ /, $_[2]);
  $time=weechat::config_get_plugin("time");
  %banmask=();
  my @nicks;
  
  for(my $i=0; $i<=$#args+1; $i++){ my $arg=$args[$i] || last; # go through arguments
    if($arg=~/^\-/){ # begins '-': banmask/time switches
      foreach("n","u","h","s","d"){ $banmask{$_}=1 if $arg=~/$_/; } # set banmask type
      if($arg=~/(\d+)/){ $time=$1; } # any number = unban time
    }else{ # the rest is nicks (and reason)
      @nicks=split(/,/, $arg);
      $reason=join(' ', @args[$i+1..$#args]); # the reason
      last;
    }
  }
  
  return weechat::WEECHAT_RC_ERROR unless @nicks; # return if didn't get nicks
  
  if(!%banmask){ $banmask{$_}=1 foreach (split(/,/, weechat::config_get_plugin("banmask"))); } # get the banmask from the setting if it's not given as an argument
  
  if($banmask{"h"}){ $banmask{"s"}=$banmask{"d"}=1; } # host -> subdomain and domain
  
  $reason=weechat::config_get_plugin("kick_reason") unless $reason; # get the reason from the setting if it's not given as an argument
  
  foreach my $nick (@nicks) { # go through nicks to be kicked
    
    my $infolist = weechat::infolist_get( # get the irc_nick infolist
                                         "irc_nick",
                                         "",
                                         weechat::buffer_get_string($buffer, "localvar_server").
                                          ",".
                                          weechat::buffer_get_string($buffer, "localvar_channel").
                                          ",".
                                          $nick
                                        );
    next unless $infolist;
    
    if(weechat::infolist_next($infolist) && lc(weechat::infolist_string($infolist, "name")) eq lc($nick)){
      my $host=weechat::infolist_string($infolist, "host") || next;
      my $ban=gen_mask($nick, split(/@/, $host)); # split variable host from infolist to user and host, get banmask
      weechat::command($buffer, "/kick $nick $reason") if weechat::config_get_plugin("kick_first") ne "off"; # kick before ban
      weechat::command($buffer, "/ban $ban"); # ban
      weechat::command($buffer, "/kick $nick $reason") if weechat::config_get_plugin("kick_first") eq "off"; # kick after ban
      weechat::command($buffer, "/wait ".60*$time." /unban $ban") if $time;
      $nick='';
    }
    weechat::infolist_free($infolist);
  }
  
  if($version>=0x00030400){ # hook_hsignal reguires v>=0.3.4
    my $server=weechat::buffer_get_string($buffer, "localvar_server"); # current server
    foreach my $nick (@nicks) { # the nicks that weren't found in the channel
      if($nick){
        weechat::hook_hsignal_send("irc_redirect_command", { "server" => "$server", "pattern" => "whois", "signal" => "sigwhois" }); # redirection
        weechat::hook_signal_send("irc_input_send", weechat::WEECHAT_HOOK_SIGNAL_STRING, "$server;;1;;/whois $nick"); # send whois command
      }
    }
  }
  
}

sub get_whois { my %hashtable=%{$_[2]}; # get the answer for whois
  if($hashtable{"output"}=~/^:\S+ 311 \S+ (\S+) (\S+) (\S+)/){
    my $ban=gen_mask($1,$2,$3);
    weechat::command($buffer, "/ban $ban"); # ban
    weechat::command($buffer, "/wait ".60*$time." /unban $ban") if $time; # unban
    return weechat::WEECHAT_RC_OK;
  }elsif($hashtable{"output"}=~/^:\S+ 40[12] \S+ (\S+)/){
    weechat::print("", weechat::prefix("error")."Kickban: Didn't find nick $1");
    return weechat::WEECHAT_RC_OK;
  }
}

sub gen_mask { my ($nick, $user, $fullhost) = @_; # generate banmask
  
  my ($ban, $sub, $domain);
  
  if($fullhost=~/\w/){ # if there are letters in the host (it's not an ip)
    my @hostparts=split(/\./, $fullhost); # split host to subdomain and domain
    if(@hostparts>2){ # if there are at least three parts
      $sub=join(".", @hostparts[0..$#hostparts-2]); # subdomain is the beginning
      $domain=$hostparts[$#hostparts-1].".".$hostparts[$#hostparts]; # domain is the last two parts
    }
  }
  
  if($banmask{"n"}){ # if nick is in banmask
    $ban=$nick."!";
  }else{
    $ban="*!";
  }
  
  if($banmask{"u"}){ # if user is in banmask
    $ban.=$user."\@";
  }else{
    $ban.="*\@";
  }
  
  if($banmask{"s"}){ # if subdomain is in banmask
    if($banmask{"d"} || !$sub){ # use the full host if also domain is in banmask or anyway if subdomain isn't specified
      $ban.=$fullhost;
    }else{
      $ban.=$sub.".*";
    } 
  }elsif($banmask{"d"}){ # if domain is in banmask but subdomain isn't
    if($domain){ $ban.="*.$domain"; }
    else{ $ban.=$fullhost; } # use the full host anyway if domain isn't separated from subdomain
  }else{
    $ban.="*";
  }
  
  return $ban;
  
}