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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
|
use strict;
use Irssi;
use POSIX;
use Socket;
use vars qw($VERSION %IRSSI);
$VERSION = "3.7";
%IRSSI = (
authors => 'Toni Salom�ki',
name => 'autoopper',
contact => 'Toni@IRCNet',
description => 'Auto-op script with dynamic address support and random delay',
license => 'GNU GPLv2 or later',
url => 'http://vinku.dyndns.org/irssi_scripts/'
);
# This is a script to auto-op people on a certain channel (all or, represented with *).
# Users are auto-opped on join with random delay.
# There is a possibility to use dns aliases (for example dyndns.org) for getting the correct address.
# The auto-op list is stored into ~/.irssi/autoop
#
# To get the dynamic addresses to be refreshed automatically, set value to autoop_dynamic_refresh (in hours)
# The value will be used next time the script is loaded (at startup or manual load)
#
# NOTICE: the datafile is in completely different format than in 1.0 and this version cannot read it. Sorry.
#
# COMMANDS:
#
# autoop_show - Displays list of auto-opped hostmasks & channels
# The current address of dynamic host is displayed in parenthesis
#
# autoop_add - Add new auto-op. Parameters hostmask, channel (or *) and dynamic flag
#
# Dynamic flag has 3 different values:
# 0: treat host as a static ip
# 1: treat host as an alias for dynamic ip
# 2: treat host as an alias for dynamic ip, but do not resolve the ip (not normally needed)
#
# autoop_del - Remove auto-op
#
# autoop_save - Save auto-ops to file (done normally automatically)
#
# autoop_load - Load auto-ops from file (use this if you have edited the autoop -file manually)
#
# autoop_check - Check all channels and op people needed
#
# autoop_dynamic - Refresh dynamic addresses (automatically if parameter set)
#
# Data is stored in ~/.irssi/autoop
# format: host channels flag
# channels separated with comma
# one host per line
my (%oplist);
my (@opitems);
srand();
#resolve dynamic host
sub resolve_host {
my ($host, $dyntype) = @_;
if (my $iaddr = inet_aton($host)) {
if ($dyntype ne "2") {
if (my $newhost = gethostbyaddr($iaddr, AF_INET)) {
return $newhost;
} else {
return inet_ntoa($iaddr);
}
} else {
return inet_ntoa($iaddr);
}
}
return "error";
}
# return list of dynamic hosts with real addresses
sub fetch_dynamic_hosts {
my %hostcache;
my $resultext;
foreach my $item (@opitems) {
next if ($item->{dynamic} ne "1" && $item->{dynamic} ne "2");
my (undef, $host) = split(/\@/, $item->{mask}, 2);
# fetch the host's real address (if not cached)
unless ($hostcache{$host}) {
$hostcache{$host} = resolve_host($host, $item->{dynamic});
$resultext .= $host . "\t" . $hostcache{$host} . "\n";
}
}
chomp $resultext;
return $resultext;
}
# fetch real addresses for dynamic hosts
sub cmd_change_dynamic_hosts {
pipe READ, WRITE;
my $pid = fork();
unless (defined($pid)) {
Irssi::print("Can't fork - aborting");
return;
}
if ($pid > 0) {
# the original process, just add a listener for pipe
close (WRITE);
Irssi::pidwait_add($pid);
my $target = {fh => \*READ, tag => undef};
$target->{tag} = Irssi::input_add(fileno(READ), INPUT_READ, \&read_dynamic_hosts, $target);
} else {
# the new process, fetch addresses and write to the pipe
print WRITE fetch_dynamic_hosts;
close (READ);
close (WRITE);
POSIX::_exit(1);
}
}
# get dynamic hosts from pipe and change them to users
sub read_dynamic_hosts {
my $target = shift;
my $rh = $target->{fh};
my %hostcache;
while (<$rh>) {
chomp;
my ($dynhost, $realhost, undef) = split (/\t/, $_, 3);
$hostcache{$dynhost} = $realhost;
}
close($target->{fh});
Irssi::input_remove($target->{tag});
my $mask;
my $count = 0;
undef %oplist if (%oplist);
foreach my $item (@opitems) {
if ($item->{dynamic} eq "1" || $item->{dynamic} eq "2") {
my ($user, $host) = split(/\@/, $item->{mask}, 2);
$count++ if ($item->{dynmask} ne $hostcache{$host});
$item->{dynmask} = $hostcache{$host};
$mask = $user . "\@" . $hostcache{$host};
} else {
$mask = $item->{mask};
}
foreach my $channel (split (/,/,$item->{chan})) {
$oplist{$channel} .= "$mask ";
}
}
chop %oplist;
Irssi::print("$count dynamic hosts changed") if ($count > 0);
}
# Save data to file
sub cmd_save_autoop {
my $file = Irssi::get_irssi_dir."/autoop";
open FILE, ">", "$file" or return;
foreach my $item (@opitems) {
printf FILE ("%s\t%s\t%s\n", $item->{mask}, $item->{chan}, $item->{dynamic});
}
close FILE;
Irssi::print("Auto-op list saved to $file");
}
# Load data from file
sub cmd_load_autoop {
my $file = Irssi::get_irssi_dir."/autoop";
open FILE, "<","$file" or return;
undef @opitems if (@opitems);
while (<FILE>) {
chomp;
my ($mask, $chan, $dynamic, undef) = split (/\t/, $_, 4);
my $item = {mask=>$mask, chan=>$chan, dynamic=>$dynamic, dynmask=>undef};
push (@opitems, $item);
}
close FILE;
Irssi::print("Auto-op list reloaded from $file");
cmd_change_dynamic_hosts;
}
# Show who's being auto-opped
sub cmd_show_autoop {
my %list;
foreach my $item (@opitems) {
foreach my $channel (split (/,/,$item->{chan})) {
$list{$channel} .= "\n" . $item->{mask};
$list{$channel} .= " (" . $item->{dynmask} . ")" if ($item->{dynmask});
}
}
Irssi::print("All channels:" . $list{"*"}) if (exists $list{"*"});
delete $list{"*"}; #this is already printed, so remove it
foreach my $channel (sort (keys %list)) {
Irssi::print("$channel:" . $list{$channel});
}
}
# Add new auto-op
sub cmd_add_autoop {
my ($data) = @_;
my ($mask, $chan, $dynamic, undef) = split(" ", $data, 4);
my $found = 0;
if ($chan eq "" || $mask eq "" || !($mask =~ /.+!.+@.+/)) {
Irssi::print("Invalid hostmask. It must contain both ! and @.") if (!($mask =~ /.+!.+@.+/));
Irssi::print("Usage: /autoop_add <hostmask> <*|#channel> [dynflag]");
Irssi::print("Dynflag: 0 normal, 1 dynamic, 2 dynamic without resolving");
return;
}
foreach my $item (@opitems) {
next unless ($item->{mask} eq $mask);
$found = 1;
$item->{chan} .= ",$chan";
last;
}
if ($found == 0) {
$dynamic = "0" unless ($dynamic eq "1" || $dynamic eq "2");
my $item = {mask=>$mask, chan=>$chan, dynamic=>$dynamic, dynmask=>undef};
push (@opitems, $item);
}
$oplist{$chan} .= " $mask";
Irssi::print("Added auto-op: $chan: $mask");
}
# Remove autoop
sub cmd_del_autoop {
my ($data) = @_;
my ($mask, $channel, undef) = split(" ", $data, 3);
if ($channel eq "" || $mask eq "") {
Irssi::print("Usage: /autoop_del <hostmask> <*|#channel>");
return;
}
my $i=0;
foreach my $item (@opitems) {
if ($item->{mask} eq $mask) {
if ($channel eq "*" || $item->{chan} eq $channel) {
splice @opitems, $i, 1;
Irssi::print("Removed: $mask");
} else {
my $newchan;
foreach my $currchan (split (/,/,$item->{chan})) {
if ($channel eq $currchan) {
Irssi::print("Removed: $channel from $mask");
} else {
$newchan .= $currchan . ",";
}
}
chop $newchan;
Irssi::print("Couldn't remove $channel from $mask") if ($item->{chan} eq $newchan);
$item->{chan} = $newchan;
}
last;
}
$i++;
}
}
# Do the actual opping
sub do_autoop {
my $target = shift;
Irssi::timeout_remove($target->{tag});
# nick has to be fetched again, because $target->{nick}->{op} is not updated
my $nick = $target->{chan}->nick_find($target->{nick}->{nick});
# if nick is changed during delay, it will probably be lost here...
if ($nick->{nick} ne "") {
if ($nick->{host} eq $target->{nick}->{host}) {
$target->{chan}->command("op " . $nick->{nick}) unless ($nick->{op});
} else {
Irssi::print("Host changed for nick during delay: " . $nick->{nick});
}
}
undef $target;
}
# Someone joined, might be multiple person. Check if opping is needed
sub event_massjoin {
my ($channel, $nicklist) = @_;
my @nicks = @{$nicklist};
return if (!$channel->{chanop});
my $masks = $oplist{"*"} . " " . $oplist{$channel->{name}};
foreach my $nick (@nicks) {
my $host = $nick->{host};
$host=~ s/^~//g; # remove this if you don't want to strip ~ from username (no ident)
next unless ($channel->{server}->masks_match($masks, $nick->{nick}, $host));
my $min_delay = Irssi::settings_get_int("autoop_min_delay");
my $max_delay = Irssi::settings_get_int("autoop_max_delay") - $min_delay;
my $delay = int(rand($max_delay)) + $min_delay;
my $target = {nick => $nick, chan => $channel, tag => undef};
$target->{tag} = Irssi::timeout_add($delay, 'do_autoop', $target);
}
}
# Check channel op status
sub do_channel_check {
my $target = shift;
Irssi::timeout_remove($target->{tag});
my $channel = $target->{chan};
my $server = $channel->{server};
my $masks = $oplist{"*"} . " " . $oplist{$channel->{name}};
my $nicks = "";
foreach my $nick ($channel->nicks()) {
next if ($nick->{op});
my $host = $nick->{host};
$host=~ s/^~//g; # remove this if you don't want to strip ~ from username (no ident)
if ($server->masks_match($masks, $nick->{nick}, $host)) {
$nicks = $nicks . " " . $nick->{nick};
}
}
$channel->command("op" . $nicks) unless ($nicks eq "");
undef $target;
}
#check people needing opping after getting ops
sub event_nickmodechange {
my ($channel, $nick, $setby, $mode, $type) = @_;
return unless (($mode eq '@') && ($type eq '+'));
my $server = $channel->{server};
return unless ($server->{nick} eq $nick->{nick});
my $min_delay = Irssi::settings_get_int("autoop_min_delay");
my $max_delay = Irssi::settings_get_int("autoop_max_delay") - $min_delay;
my $delay = int(rand($max_delay)) + $min_delay;
my $target = {chan => $channel, tag => undef};
$target->{tag} = Irssi::timeout_add($delay, 'do_channel_check', $target);
}
#Check all channels / all users if someone needs to be opped
sub cmd_autoop_check {
my ($data, $server, $witem) = @_;
foreach my $channel ($server->channels()) {
Irssi::print("Checking: " . $channel->{name});
next if (!$channel->{chanop});
my $masks = $oplist{"*"} . " " . $oplist{$channel->{name}};
foreach my $nick ($channel->nicks()) {
next if ($nick->{op});
my $host = $nick->{host};
$host=~ s/^~//g; # remove this if you don't want to strip ~ from username (no ident)
if ($server->masks_match($masks, $nick->{nick}, $host)) {
$channel->command("op " . $nick->{nick}) if (!$nick->{op});
}
}
}
}
#Set dynamic refresh period.
sub set_dynamic_refresh {
my $refresh = Irssi::settings_get_int("autoop_dynamic_refresh");
return if ($refresh == 0);
Irssi::print("Dynamic host refresh set for $refresh hours");
Irssi::timeout_add($refresh*3600000, 'cmd_change_dynamic_hosts', undef);
}
Irssi::command_bind('autoop_show', 'cmd_show_autoop');
Irssi::command_bind('autoop_add', 'cmd_add_autoop');
Irssi::command_bind('autoop_del', 'cmd_del_autoop');
Irssi::command_bind('autoop_save', 'cmd_save_autoop');
Irssi::command_bind('autoop_load', 'cmd_load_autoop');
Irssi::command_bind('autoop_check', 'cmd_autoop_check');
Irssi::command_bind('autoop_dynamic', 'cmd_change_dynamic_hosts');
Irssi::signal_add_last('massjoin', 'event_massjoin');
Irssi::signal_add_last('setup saved', 'cmd_save_autoop');
Irssi::signal_add_last('setup reread', 'cmd_load_autoop');
Irssi::signal_add_last("nick mode changed", "event_nickmodechange");
Irssi::settings_add_int('autoop', 'autoop_max_delay', 15000);
Irssi::settings_add_int('autoop', 'autoop_min_delay', 1000);
Irssi::settings_add_int('autoop', 'autoop_dynamic_refresh', 0);
cmd_load_autoop;
set_dynamic_refresh;
|