1# keepnick - irssi 0.7.98.CVS
2#
3#    $Id: keepnick.pl,v 1.17 2003/01/04 10:18:42 peder Exp $
4#
5# Copyright (C) 2001, 2002 by Peder Stray <peder@ninja.no>
6#
7
8use strict;
9use Irssi 20011118.1727;
10use Irssi::Irc;
11
12# ======[ Script Header ]===============================================
13
14use vars qw{$VERSION %IRSSI};
15($VERSION) = '$Revision: 1.17 $' =~ / (\d+\.\d+) /;
16%IRSSI = (
17          name        => 'keepnick',
18          authors     => 'Peder Stray',
19          contact     => 'peder@ninja.no',
20          url         => 'http://ninja.no/irssi/keepnick.pl',
21          license     => 'GPL',
22          description => 'Try to get your nick back when it becomes available.',
23         );
24
25# ======[ Variables ]===================================================
26
27my(%keepnick);		# nicks we want to keep
28my(%getnick);		# nicks we are currently waiting for
29my(%inactive);		# inactive chatnets
30my(%manual);		# manual nickchanges
31
32# ======[ Helper functions ]============================================
33
34# --------[ change_nick ]-----------------------------------------------
35
36sub change_nick {
37    my($server,$nick) = @_;
38    $server->redirect_event('keepnick nick', 1, ":$nick", -1, undef,
39			    {
40			     "event nick" => "redir keepnick nick",
41			     "" => "event empty",
42			    });
43    $server->send_raw("NICK :$nick");
44}
45
46# --------[ check_nick ]------------------------------------------------
47
48sub check_nick {
49    my($server,$net,$nick);
50
51    %getnick = ();	# clear out any old entries
52
53    for $net (keys %keepnick) {
54	next if $inactive{$net};
55	$server = Irssi::server_find_chatnet($net);
56	next unless $server;
57	next if lc $server->{nick} eq lc $keepnick{$net};
58
59	$getnick{$net} = $keepnick{$net};
60    }
61
62    for $net (keys %getnick) {
63	$server = Irssi::server_find_chatnet($net);
64	next unless $server;
65	$nick = $getnick{$net};
66	if (lc $server->{nick} eq lc $nick) {
67	    delete $getnick{$net};
68	    next;
69	}
70	$server->redirect_event('keepnick ison', 1, '', -1, undef,
71				{ "event 303" => "redir keepnick ison" });
72	$server->send_raw("ISON :$nick");
73    }
74}
75
76# --------[ load_nicks ]------------------------------------------------
77
78sub load_nicks {
79    my($file) = Irssi::get_irssi_dir."/keepnick";
80    my($count) = 0;
81    local(*CONF);
82
83    %keepnick = ();
84    open CONF, "<", $file;
85    while (<CONF>) {
86	my($net,$nick) = split;
87	if ($net && $nick) {
88	    $keepnick{lc $net} = $nick;
89	    $count++;
90	}
91    }
92    close CONF;
93
94    Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'keepnick_crap',
95		       "Loaded $count nicks from $file");
96}
97
98# --------[ save_nicks ]------------------------------------------------
99
100sub save_nicks {
101    my($auto) = @_;
102    my($file) = Irssi::get_irssi_dir."/keepnick";
103    my($count) = 0;
104    local(*CONF);
105
106    return if $auto && !Irssi::settings_get_bool('keepnick_autosave');
107
108    open CONF, ">", $file;
109    for my $net (sort keys %keepnick) {
110	print CONF "$net\t$keepnick{$net}\n";
111	$count++;
112    }
113    close CONF;
114
115    Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'keepnick_crap',
116		       "Saved $count nicks to $file")
117	unless $auto;
118}
119
120# --------[ server_printformat ]----------------------------------------
121
122sub server_printformat {
123    my($server,$level,$format,@params) = @_;
124    my($emitted) = 0;
125    for my $win (Irssi::windows) {
126	for my $item ($win->items) {
127	    next unless ref $item;
128	    if ($item->{server}{chatnet} eq $server->{chatnet}) {
129		$item->printformat($level,$format,@params);
130		$emitted++;
131		last;
132	    }
133	}
134    }
135    $server->printformat(undef,$level,$format,@params)
136	unless $emitted;
137}
138
139# ======[ Signal Hooks ]================================================
140
141# --------[ sig_message_nick ]------------------------------------------
142
143# if anyone changes their nick, check if we want their old one.
144sub sig_message_nick {
145    my($server,$newnick,$oldnick) = @_;
146    my($chatnet) = lc $server->{chatnet};
147    if (lc $oldnick eq lc $getnick{$chatnet}) {
148	change_nick($server, $getnick{$chatnet});
149    }
150}
151
152# --------[ sig_message_own_nick ]--------------------------------------
153
154# if we change our nick, check it to see if we wanted it and if so
155# remove it from the list.
156sub sig_message_own_nick {
157    my($server,$newnick,$oldnick) = @_;
158    my($chatnet) = lc $server->{chatnet};
159    if (lc $newnick eq lc $keepnick{$chatnet}) {
160	delete $getnick{$chatnet};
161	if ($inactive{$chatnet}) {
162	    delete $inactive{$chatnet};
163	    Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'keepnick_unhold',
164			       $newnick, $chatnet);
165	}
166    } elsif (lc $oldnick eq lc $keepnick{$chatnet} &&
167	     lc $newnick eq lc $manual{$chatnet}) {
168	$inactive{$chatnet} = 1;
169	delete $getnick{$chatnet};
170	Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'keepnick_hold',
171			   $oldnick, $chatnet);
172    }
173}
174
175# --------[ sig_message_own_nick_block ]--------------------------------
176
177sub sig_message_own_nick_block {
178    my($server,$new,$old,$addr) = @_;
179    Irssi::signal_stop();
180    if (Irssi::settings_get_bool('keepnick_quiet')) {
181	Irssi::printformat(MSGLEVEL_NICKS | MSGLEVEL_NO_ACT,
182			   'keepnick_got_nick', $new, $server->{chatnet});
183    } else {
184	server_printformat($server, MSGLEVEL_NICKS | MSGLEVEL_NO_ACT,
185			   'keepnick_got_nick', $new, $server->{chatnet});
186    }
187}
188
189# --------[ sig_message_quit ]------------------------------------------
190
191# if anyone quits, check if we want their nick.
192sub sig_message_quit {
193    my($server,$nick) = @_;
194    my($chatnet) = lc $server->{chatnet};
195    if (lc $nick eq lc $getnick{$chatnet}) {
196	change_nick($server, $getnick{$chatnet});
197    }
198}
199
200# --------[ sig_redir_keepnick_ison ]-----------------------------------
201
202sub sig_redir_keepnick_ison {
203    my($server,$text) = @_;
204    my $nick = $getnick{lc $server->{chatnet}};
205    change_nick($server, $nick)
206      unless $text =~ /:\Q$nick\E\s?$/i;
207}
208
209# --------[ sig_redir_keepnick_nick ]-----------------------------------
210
211sub sig_redir_keepnick_nick {
212    my($server,$args,$nick,$addr) = @_;
213    Irssi::signal_add_first('message own_nick', 'sig_message_own_nick_block');
214    Irssi::signal_emit('event nick', @_);
215    Irssi::signal_remove('message own_nick', 'sig_message_own_nick_block');
216}
217
218# --------[ sig_setup_reread ]------------------------------------------
219
220# main setup is reread, so let us do it too
221sub sig_setup_reread {
222    load_nicks;
223}
224
225# --------[ sig_setup_save ]--------------------------------------------
226
227# main config is saved, and so we should save too
228sub sig_setup_save {
229    my($mainconf,$auto) = @_;
230    save_nicks($auto);
231}
232
233# ======[ Commands ]====================================================
234
235# --------[ KEEPNICK ]--------------------------------------------------
236
237# Usage: /KEEPNICK [-net <chatnet>] [<nick>]
238sub cmd_keepnick {
239    my(@params) = split " ", shift;
240    my($server) = @_;
241    my($chatnet,$nick,@opts);
242
243    # parse named parameters from the parameterlist
244    while (@params) {
245	my($param) = shift @params;
246	if ($param =~ /^-(chat|irc)?net$/i) {
247	    $chatnet = shift @params;
248	} elsif ($param =~ /^-/) {
249	    Irssi::print("Unknown parameter $param");
250	} else {
251	    push @opts, $param;
252	}
253    }
254    $nick = shift @opts;
255
256    # check if the ircnet specified (if any) is valid, and if so get the
257    # server for it
258    if ($chatnet) {
259	my($cn) = Irssi::chatnet_find($chatnet);
260	unless ($cn) {
261	    Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'keepnick_crap',
262			       "Unknown chat network: $chatnet");
263	    return;
264	}
265	$chatnet = $cn->{name};
266	$server = Irssi::server_find_chatnet($chatnet);
267    }
268
269    # if we need a server, check if the one we got is connected.
270    unless ($server || ($nick && $chatnet)) {
271	Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'keepnick_crap',
272			   "Not connected to server");
273	return;
274    }
275
276    # lets get the chatnet, and the nick we want
277    $chatnet ||= $server->{chatnet};
278    $nick    ||= $server->{nick};
279
280    # check that we really have a chatnet
281    unless ($chatnet) {
282	Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'keepnick_crap',
283			   "Unable to find a chatnet");
284	return;
285    }
286
287    if ($inactive{lc $chatnet}) {
288	delete $inactive{lc $chatnet};
289	Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'keepnick_unhold',
290			   $nick, $chatnet);
291    }
292
293    Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'keepnick_add', $nick,
294		       $chatnet);
295
296    $keepnick{lc $chatnet} = $nick;
297
298    save_nicks(1);
299    check_nick();
300}
301
302# --------[ UNKEEPNICK ]------------------------------------------------
303
304# Usage: /UNKEEPNICK [<chatnet>]
305sub cmd_unkeepnick {
306    my($chatnet,$server) = @_;
307
308    # check if the ircnet specified (if any) is valid, and if so get the
309    # server for it
310    if ($chatnet) {
311	my($cn) = Irssi::chatnet_find($chatnet);
312	unless ($cn) {
313	    Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'keepnick_crap',
314			       "Unknown chat network: $chatnet");
315	    return;
316	}
317	$chatnet = $cn->{name};
318    } else {
319	$chatnet = $server->{chatnet};
320    }
321
322    Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'keepnick_remove',
323		       $keepnick{lc $chatnet}, $chatnet);
324
325    delete $keepnick{lc $chatnet};
326    delete $getnick{lc $chatnet};
327
328    save_nicks(1);
329}
330
331# --------[ LISTNICK ]--------------------------------------------------
332
333# Usage: /LISTNICK
334sub cmd_listnick {
335    my(@nets) = sort keys %keepnick;
336    my $net;
337    if (@nets) {
338	Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'keepnick_list_header');
339	for (@nets) {
340	    $net = Irssi::chatnet_find($_);
341	    Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'keepnick_list_line',
342			       $keepnick{$_},
343			       $net ? $net->{name} : ">$_<",
344			       $inactive{$_}?'inactive':'active');
345	}
346	Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'keepnick_list_footer');
347    } else {
348	Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'keepnick_list_empty');
349    }
350}
351
352# --------[ NICK ]------------------------------------------------------
353
354sub cmd_nick {
355    my($data,$server) = @_;
356    my($nick) = split " ", $data;
357    return unless $server;
358    $manual{lc $server->{chatnet}} = $nick;
359}
360
361# ======[ Setup ]=======================================================
362
363# --------[ Register settings ]-----------------------------------------
364
365Irssi::settings_add_bool('keepnick', 'keepnick_autosave', 1);
366Irssi::settings_add_bool('keepnick', 'keepnick_quiet', 0);
367
368# --------[ Register formats ]------------------------------------------
369
370Irssi::theme_register(
371[
372 'keepnick_crap',
373 '{line_start}{hilight Keepnick:} $0',
374
375 'keepnick_add',
376 '{line_start}{hilight Keepnick:} Now keeping {nick $0} on [$1]',
377
378 'keepnick_remove',
379 '{line_start}{hilight Keepnick:} Stopped trying to keep {nick $0} on [$1]',
380
381 'keepnick_hold',
382 '{line_start}{hilight Keepnick:} Nickkeeping deactivated on [$1]',
383
384 'keepnick_unhold',
385 '{line_start}{hilight Keepnick:} Nickkeeping reactivated on [$1]',
386
387 'keepnick_list_empty',
388 '{line_start}{hilight Keepnick:} No nicks in keep list',
389
390 'keepnick_list_header',
391 '',
392
393 'keepnick_list_line',
394 '{line_start}{hilight Keepnick:} Keeping {nick $0} in [$1] ($2)',
395
396 'keepnick_list_footer',
397 '',
398
399 'keepnick_got_nick',
400 '{hilight Keepnick:} Nickstealer left [$1], got {nick $0} back',
401
402]);
403
404# --------[ Register signals ]------------------------------------------
405
406Irssi::signal_add('message quit', 'sig_message_quit');
407Irssi::signal_add('message nick', 'sig_message_nick');
408Irssi::signal_add('message own_nick', 'sig_message_own_nick');
409
410Irssi::signal_add('redir keepnick ison', 'sig_redir_keepnick_ison');
411Irssi::signal_add('redir keepnick nick', 'sig_redir_keepnick_nick');
412
413Irssi::signal_add('setup saved', 'sig_setup_save');
414Irssi::signal_add('setup reread', 'sig_setup_reread');
415
416# --------[ Register commands ]-----------------------------------------
417
418Irssi::command_bind("keepnick", "cmd_keepnick");
419Irssi::command_bind("unkeepnick", "cmd_unkeepnick");
420Irssi::command_bind("listnick", "cmd_listnick");
421Irssi::command_bind("nick", "cmd_nick");
422
423# --------[ Register timers ]-------------------------------------------
424
425Irssi::timeout_add(12000, 'check_nick', '');
426
427# --------[ Register redirects ]----------------------------------------
428
429Irssi::Irc::Server::redirect_register('keepnick ison', 0, 0,
430			 undef,
431			 {
432			  "event 303" => -1,
433			 },
434			 undef );
435
436Irssi::Irc::Server::redirect_register('keepnick nick', 0, 0,
437			 undef,
438			 {
439			  "event nick" => 0,
440			  "event 432" => -1,	# ERR_ERRONEUSNICKNAME
441			  "event 433" => -1,	# ERR_NICKNAMEINUSE
442			  "event 437" => -1,	# ERR_UNAVAILRESOURCE
443			  "event 484" => -1,	# ERR_RESTRICTED
444			 },
445			 undef );
446
447# --------[ Load config ]-----------------------------------------------
448
449load_nicks;
450
451# ======[ END ]=========================================================
452
453# Local Variables:
454# header-initial-hide: t
455# mode: header-minor
456# end:
457