1#!/usr/local/bin/perl -w 2 3## Bugreports and Licence disclaimer. 4# 5# For bugreports and other improvements contact Geert Hauwaerts <geert@irssi.org> 6# 7# This program is free software; you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation; either version 2 of the License, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this script; if not, write to the Free Software 19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20# 21## 22 23use strict; 24use Irssi; 25use vars qw($VERSION %IRSSI); 26 27$VERSION = '0.02'; 28 29%IRSSI = ( 30 authors => 'Geert Hauwaerts', 31 contact => 'geert@irssi.org', 32 name => 'invitejoin.pl', 33 description => 'This script will join a channel if somebody invites you to it.', 34 license => 'Public Domain', 35 url => 'https://github.com/irssi/scripts.irssi.org/blob/master/scripts/invitejoin.pl', 36 changed => 'Di 17. Jan 19:32:45 CET 2017', 37); 38 39my $help = <<EOF; 40 41/SET invitejoin 0|1 42/TOGGLE invitejoin 43 Description: If this setting is turned on, you will join the channel 44 when invited to. 45 46Default is to follow every invite, you can specify a list of allowed nicks. 47 48/INVITEJOIN [addnick <ircnet> <nick>] 49 [delnick <ircnet> <nick>] 50 [listnick] 51 [help] 52 53addnick: Add a new nickname on the given net as allowed autoinvite source. 54delnick: Delete a nickname from the allowed list. 55listnick: Display the contents of the allowed nickname list. 56help: Display this useful little helptext. 57 58Examples: (all on one line) 59/INVITEJOIN addnick Freenode ChanServ 60 61Note: This script doesn't allow wildcards 62EOF 63 64my @allowed_nicks = (); 65my $allowed_nicks_file = "invitejoin.nicks"; 66 67my $irssidir = Irssi::get_irssi_dir(); 68 69Irssi::theme_register([ 70 'invitejoin_usage', '%R>>%n %_Invitejoin:%_ Insufficient parameters: Use "%_/INVITEJOIN help%_" for further instructions.', 71 'invitejoin_help', '$0', 72 'invitejoin_loaded', '%R>>%n %_Scriptinfo:%_ Loaded $0 version $1 by $2.', 73 'invitejoin_invited', '%R>>%n %_Invitejoin:%_ Joined $1 (Invited by $0).', 74 'invitejoin_usage_add_nick', '%R>>%n %_Invitejoin:%_ Insufficient parameters: Usage "%_/INVITEJOIN addnick ircnet ChanServ%_".', 75 'invitejoin_no_net', '%R>>%n %_Invitejoin:%_ Unknown Irssi ircnet %_$0%_.', 76 'saved_nick', '%R>>%n %_Invitejoin:%_ Added allowed nick "%_$1%_" on %_$0%_.', 77 'nick_already_present', '%R>>%n %_Invitejoin:%_ Nick already present.', 78 'invitejoin_delusage', '%R>>%n %_Invitejoin:%_ Insufficient parameters: Usage "%_/INVITEJOIN delnick ircnet nick%_".', 79 'invitejoin_delled', '%R>>%n %_Invitejoin:%_ Deleted %_$1%_ on %_$0%_ from allowed list.', 80 'invitejoin_nfound', '%R>>%n %_Invitejoin:%_ The nick %_$1%_ on %_$0%_ could not be found.', 81 'allowed_nicks_info', '%_Ircnet Nick%_', 82 'allowed_nicks_empty', '%R>>%n %_Invitejoin:%_ Your allowed nick list is empty. All invites will be followed.', 83 'allowed_nicks_print', '$[18]0 $1', 84 'invite_denied', '%R>>%n %_Invitejoin:%_ Invite from nick %_$1%_ on %_$0%_ to %_$2%_ not followed because it is not in the allowed list.', 85]); 86 87sub load_allowed_nicks { 88 my ($file) = @_; 89 90 @allowed_nicks = load_file($file, sub { 91 my $new_allowed = new_allowed_nick(@_); 92 93 return undef if ($new_allowed->{net} eq '' || $new_allowed->{nick} eq ''); 94 return $new_allowed; 95 }); 96} 97 98sub save_allowed_nicks { 99 my ($file) = @_; 100 save_file($file, \@allowed_nicks, \&allowed_nick_to_list); 101} 102 103sub allowed_nick_to_list { 104 my $allowed_nick = shift; 105 106 return ( 107 $allowed_nick->{net}, 108 $allowed_nick->{nick} 109 ); 110} 111 112sub new_allowed_nick { 113 return { 114 net => shift, 115 nick => shift 116 }; 117} 118 119# file: filename to be read 120# parse_line_fn: receives array of entries of a single line as input, should 121# return parsed data object or undef in the data is incomplete 122# returns: parsed data array 123sub load_file { 124 my ($file, $parse_line_fn) = @_; 125 my @parsed_data = (); 126 127 if (-e $file) { 128 open(my $fh, "<", $file); 129 local $/ = "\n"; 130 131 while (<$fh>) { 132 chomp; 133 my $data = $parse_line_fn->(split("\t")); 134 push(@parsed_data, $data) if $data; 135 } 136 137 close($fh); 138 } 139 140 return @parsed_data; 141} 142 143# file: filename to be written, is created accessable only by the user 144# data_ref: array ref of data entries 145# serialize_fn: receives a data reference and should return an array or tuples 146# for that data that will be serialized into one line 147sub save_file { 148 my ($file, $data_ref, $serialize_fn) = @_; 149 150 create_private_file($file) unless -e $file; 151 152 open(my $fh, ">", $file) or die "Can't create $file. Reason: $!"; 153 154 for my $data (@$data_ref) { 155 print($fh join("\t", $serialize_fn->($data)), "\n"); 156 } 157 158 close($fh); 159} 160 161sub create_private_file { 162 my ($file) = @_; 163 my $umask = umask 0077; # save old umask 164 open(my $fh, ">", $file) or die "Can't create $file. Reason: $!"; 165 close($fh); 166 umask $umask; 167} 168 169sub add_allowed_nick { 170 my ($network, $nick) = split(" ", $_[0], 2); 171 my ($correct_net); 172 173 if ($network eq '' || $nick eq '') { 174 Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'invitejoin_usage_add_nick'); 175 return; 176 } 177 178 if ($network) { 179 my ($ircnet) = Irssi::chatnet_find($network); 180 if (!$ircnet) { 181 Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'invitejoin_no_net', $network); 182 return; 183 } else { 184 $correct_net = 1; 185 } 186 } 187 188 if ($correct_net && $nick) { 189 if (is_nick_in_list($network, $nick)) { 190 Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nick_already_present'); 191 return; 192 } 193 194 push(@allowed_nicks, new_allowed_nick($network, $nick)); 195 save_allowed_nicks("$irssidir/$allowed_nicks_file"); 196 197 Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'saved_nick', $network, $nick); 198 } 199} 200 201sub del_allowed_nick { 202 my ($ircnet, $nick) = split(" ", $_[0], 2); 203 204 if ($ircnet eq '' || $nick eq '') { 205 Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'invitejoin_delusage'); 206 return; 207 } 208 209 my $size_before = scalar(@allowed_nicks); 210 @allowed_nicks = grep { ! ($_->{net} eq $ircnet && $_->{nick} eq $nick) } @allowed_nicks; 211 my $size_after = scalar(@allowed_nicks); 212 213 if ($size_after != $size_before) { 214 Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'invitejoin_delled', $ircnet, $nick); 215 save_allowed_nicks("$irssidir/$allowed_nicks_file"); 216 } else { 217 Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'invitejoin_nfound', $ircnet, $nick); 218 } 219 220 if ($size_after == 0) { 221 Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'allowed_nicks_empty'); 222 } 223} 224 225sub list_allowed_nicks { 226 if (@allowed_nicks == 0) { 227 Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'allowed_nicks_empty'); 228 } else { 229 Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'allowed_nicks_info'); 230 231 for my $allowed (@allowed_nicks) { 232 Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'allowed_nicks_print', $allowed->{net}, $allowed->{nick}); 233 } 234 } 235} 236 237sub invitejoin_runsub { 238 my ($data, $server, $item) = @_; 239 $data =~ s/\s+$//g; 240 241 if ($data) { 242 Irssi::command_runsub('invitejoin', $data, $server, $item); 243 } else { 244 Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'invitejoin_usage'); 245 } 246} 247 248sub is_nick_in_list { 249 my ($net, $nick) = @_; 250 251 return (grep { 252 $_->{net} eq $net && 253 $_->{nick} eq $nick 254 } @allowed_nicks) > 0; 255} 256 257sub is_allowed_nick { 258 my ($net, $nick) = @_; 259 260 # If no allowed nicks are specified (initial configuration) accept 261 # all invite requests. 262 # # (This mimics previous behavior of this script 263 # before there was an allowed list) 264 return 1 if @allowed_nicks == 0; 265 266 return is_nick_in_list($net, $nick); 267} 268 269sub invitejoin { 270 my ($server, $channel, $nick, $address) = @_; 271 my $invitejoin = Irssi::settings_get_bool('invitejoin'); 272 273 if ($invitejoin) { 274 if (is_allowed_nick($server->{tag}, $nick)) { 275 $server->command("join $channel"); 276 277 Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'invitejoin_invited', $nick, $channel); 278 Irssi::signal_stop(); 279 } 280 else { 281 Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'invite_denied', $server->{tag}, $nick, $channel); 282 } 283 } 284} 285 286Irssi::signal_add('message invite', 'invitejoin'); 287 288Irssi::settings_add_bool('invitejoin', 'invitejoin' => 1); 289 290load_allowed_nicks("$irssidir/$allowed_nicks_file"); 291 292Irssi::command_bind('invitejoin', 'invitejoin_runsub'); 293Irssi::command_bind('invitejoin addnick', 'add_allowed_nick'); 294Irssi::command_bind('invitejoin delnick', 'del_allowed_nick'); 295Irssi::command_bind('invitejoin listnick', 'list_allowed_nicks'); 296Irssi::command_bind('invitejoin help' => sub { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'invitejoin_help', $help) }); 297 298Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'invitejoin_loaded', $IRSSI{name}, $VERSION, $IRSSI{authors}); 299