-
Notifications
You must be signed in to change notification settings - Fork 235
/
nm.pl
693 lines (580 loc) · 18.5 KB
/
nm.pl
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
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
use Irssi;
use strict;
use vars qw($VERSION %IRSSI);
$VERSION="0.3.10";
%IRSSI = (
authors=> 'BC-bd',
contact=> 'bd@bc-bd.org',
name=> 'nm',
description=> 'right aligned nicks depending on longest nick',
license=> 'GPL v2',
url=> 'http://bc-bd.org/blog/irssi/',
);
# $Id: 9cb009e8b7e6f5ce60294334faf88715ef01413e $
# nm.pl
# for irssi 0.8.4 by bd@bc-bd.org
#
# right aligned nicks depending on longest nick
#
# inspired by neatmsg.pl from kodgehopper <kodgehopper@netscape.net
# formats taken from www.irssi.de
# thanks to adrianel <adrinael@nuclearzone.org> for some hints
# thanks to Eric Wald <eswald@gmail.com> for the left alignment patch
# inspired by nickcolor.pl by Timo Sirainen and Ian Peters
# thanks to And1 <and1@meinungsverstaerker.de> for a small patch
# thanks to berber@tzi.de for the save/load patch
# thanks to Dennis Heimbert <dennis.heimbert@gmail.com> for a bug report/patch
# thanks to Roy Sigurd Karlsbakk <roy@karlsbakk.net> for an autosave patch
#
#########
# USAGE
###
#
# use
#
# /neatcolor help
#
# for help on available commands
#
#########
# OPTIONS
#########
my $help = "
/set neat_colorize <ON|OFF>
* ON : colorize nicks
* OFF : do not colorize nicks
/set neat_colors <string>
Use these colors when colorizing nicks, eg:
/set neat_colors yYrR
See the file formats.txt on an explanation of what colors are
available.
/set neat_left_actions <ON|OFF>
* ON : print nicks left-aligned on actions
* OFF : print nicks right-aligned on actions
/set neat_left_messages <ON|OFF>
* ON : print nicks left-aligned on messages
* OFF : print nicks right-aligned on messages
/set neat_right_mode <ON|OFF>
* ON : print the mode of the nick e.g @%+ after the nick
* OFF : print it left of the nick
/set neat_maxlength <number>
* number : Maximum length of Nicks to display. Longer nicks are truncated.
* 0 : Do not truncate nicks.
/set neat_melength <number>
* number : number of spaces to substract from /me padding
/set neat_ignorechars <str>
* str : regular expression used to filter out unwanted characters in
nicks. this can be used to assign the same color for similar
nicks, e.g. foo and foo_:
/set neat_ignorechars [_]
/set neat_allow_shrinking <ON|OFF>
* ON : shrink padding when longest nick disappears
* OFF : do not shrink, only allow growing
/set neat_autosave <number>
* number : autosave after <number> seconds, defaults to 60. Set to 0 to
disable.
";
#
###
################
###
#
# Changelog
#
# Version 0.3.11
# - added autosave, idea from Roy Sigurd Karlsbakk
#
# Version 0.3.10
# - fix losing of saved color when changing nick shares more than one channel
# with you
#
# Version 0.3.9
# - fix longest nick calculation for nicks shorter than the current longest
# nick
# - updated url
#
# Version 0.3.8
# - fixed error in the nickchange tracking code, reported by Kevin Ballard
# - added --all switch to reset command
# - skip broken lines in saved_colors
#
# Version 0.3.7
# - fixed crash when calling /neatcolor without parameters
# - fixed url
#
# Version 0.3.6
# - added option to ignore certain characters from color hash building, see
# https://bc-bd.org/trac/irssi/ticket/22
# - added option to save and specify colors for nicks, see
# https://bc-bd.org/trac/irssi/ticket/23
# - added option to disallow shrinking, see
# https://bc-bd.org/trac/irssi/ticket/12
#
# Version 0.3.5
# - now also aligning own messages in queries
#
# Version 0.3.4
# - fxed off by one error in nick_to_color, patch by jrib, see
# https://bc-bd.org/trac/irssi/ticket/24
#
# Version 0.3.3
# - added support for alignment in queries, see
# https://bc-bd.org/trac/irssi/ticket/21
#
# Version 0.3.2
# - integrated left alignment patch from Eric Wald <eswald@gmail.com>, see
# https://bc-bd.org/trac/irssi/ticket/18
#
# Version 0.3.1
# - /me padding, see https://bc-bd.org/trac/irssi/ticket/17
#
# Version 0.3.0
# - integrate nick coloring support
#
# Version 0.2.1
# - moved neat_maxlength check to reformat() (thx to Jerome De Greef <jdegreef@brutele.be>)
#
# Version 0.2.0
# - by adrianel <adrinael@nuclearzone.org>
# * reformat after setup reload
# * maximum length of nicks
#
# Version 0.1.0
# - got lost somewhere
#
# Version 0.0.2
# - ugly typo fixed
#
# Version 0.0.1
# - initial release
#
###
################
###
#
# BUGS
#
# Empty nicks, eg "<> message"
# This seems to be triggered by some themes. As of now there is no known
# fix other than changing themes, see
# https://bc-bd.org/trac/irssi/ticket/19
#
# Well, it's a feature: due to the lacking support of extendable themes
# from irssi it is not possible to just change some formats per window.
# This means that right now all windows are aligned with the same nick
# length, which can be somewhat annoying.
# If irssi supports extendable themes, I will include per-server indenting
# and a setting where you can specify servers you don't want to be indented
#
###
################
my ($longestNick, %saved_colors, @colors, $alignment, $sign, %commands,);
my ($pending_save);
my $colorize = -1;
sub reformat() {
my $max = Irssi::settings_get_int('neat_maxlength');
my $actsign = Irssi::settings_get_bool('neat_left_actions')? '': '-';
$sign = Irssi::settings_get_bool('neat_left_messages')? '': '-';
if ($max && $max < $longestNick) {
$longestNick = $max;
}
my $me = $longestNick - Irssi::settings_get_int('neat_melength');
$me = 0 if ($me < 0);
Irssi::command('^format own_action {ownaction $['.$actsign.$me.']0} $1');
Irssi::command('^format action_public {pubaction $['.$actsign.$me.']0}$1');
Irssi::command('^format action_private {pvtaction $['.$actsign.$me.']0}$1');
Irssi::command('^format action_private_query {pvtaction_query $['.$actsign.$me.']0} $2');
my $length = $sign . $longestNick;
if (Irssi::settings_get_bool('neat_right_mode') == 0) {
Irssi::command('^format own_msg {ownmsgnick $2 {ownnick $['.$length.']0}}$1');
Irssi::command('^format own_msg_channel {ownmsgnick $3 {ownnick $['.$length.']0}{msgchannel $1}}$2');
Irssi::command('^format pubmsg_me {pubmsgmenick $2 {menick $['.$length.']0}}$1');
Irssi::command('^format pubmsg_me_channel {pubmsgmenick $3 {menick $['.$length.']0}{msgchannel $1}}$2');
Irssi::command('^format pubmsg_hilight {pubmsghinick $0 $3 $['.$length.']1%n}$2');
Irssi::command('^format pubmsg_hilight_channel {pubmsghinick $0 $4 $['.$length.']1{msgchannel $2}}$3');
Irssi::command('^format pubmsg {pubmsgnick $2 {pubnick $['.$length.']0}}$1');
Irssi::command('^format pubmsg_channel {pubmsgnick $2 {pubnick $['.$length.']0}}$1');
} else {
Irssi::command('^format own_msg {ownmsgnick {ownnick $['.$length.']0$2}}$1');
Irssi::command('^format own_msg_channel {ownmsgnick {ownnick $['.$length.']0$3}{msgchannel $1}}$2');
Irssi::command('^format pubmsg_me {pubmsgmenick {menick $['.$length.']0}$2}$1');
Irssi::command('^format pubmsg_me_channel {pubmsgmenick {menick $['.$length.']0$3}{msgchannel $1}}$2');
Irssi::command('^format pubmsg_hilight {pubmsghinick $0 $0 $['.$length.']1$3%n}$2');
Irssi::command('^format pubmsg_hilight_channel {pubmsghinick $0 $['.$length.']1$4{msgchannel $2}}$3');
Irssi::command('^format pubmsg {pubmsgnick {pubnick $['.$length.']0$2}}$1');
Irssi::command('^format pubmsg_channel {pubmsgnick {pubnick $['.$length.']0$2}}$1');
}
# format queries
Irssi::command('^format own_msg_private_query {ownprivmsgnick {ownprivnick $['.$length.']2}}$1');
Irssi::command('^format msg_private_query {privmsgnick $['.$length.']0}$2');
};
sub findLongestNick {
$longestNick = 0;
# get own nick length
map {
my $len = length($_->{nick});
$longestNick = $len if ($len > $longestNick);
} Irssi::servers();
# find longest other nick
foreach (Irssi::channels()) {
foreach ($_->nicks()) {
my $len = length($_->{nick});
$longestNick = $len if ($len > $longestNick);
}
}
reformat();
}
sub delayed_save
{
# skip if we have already a save pending. we don't reset the timeout
# here, else you could end up with changes never being automatically
# saved if they happen more often than <neat_autosave> seconds
return if $pending_save;
return unless Irssi::settings_get_int('neat_autosave');
Irssi::timeout_add_once(Irssi::settings_get_int('neat_autosave') * 1000,
\&save_colors, undef);
}
# a new nick was created
sub sig_newNick
{
my ($channel, $nick) = @_;
my $len = length($nick->{nick});
if ($len > $longestNick) {
$longestNick = $len;
reformat();
}
return if (exists($saved_colors{$nick->{nick}}));
$saved_colors{$nick->{nick}} = "%".nick_to_color($nick->{nick});
delayed_save();
}
# something changed
sub sig_changeNick
{
my ($channel, $nick, $old_nick) = @_;
# if no saved color exists, we already handled this nickchange. irssi
# generates one signal per channel the nick is in, so if you share more
# than one channel with this nick, you'd lose the coloring.
return unless exists($saved_colors{$old_nick});
# we need to update the saved colorors hash independent of nick lenght
$saved_colors{$nick->{nick}} = $saved_colors{$old_nick};
delete $saved_colors{$old_nick};
delayed_save();
my $new = length($nick->{nick});
# in case the new nick is longer than the old one, simply remember this
# as the new longest nick and reformat.
#
# if the new nick is as long as the known longest nick nothing has to be
# done
#
# if the new nick is shorter than the current longest one and if the
# user allows us to shrink, find new longest nick and reformat.
if ($new > $longestNick) {
$longestNick = $new;
} elsif ($new == $longestNick) {
return;
} else {
return unless Irssi::settings_get_bool('neat_allow_shrinking');
findLongestNick();
}
reformat();
}
sub sig_removeNick
{
my ($channel, $nick) = @_;
my $thisLen = length($nick->{nick});
# we only need to recalculate if this was the longest nick and we are
# allowed to shrink
if ($thisLen == $longestNick && Irssi::settings_get_bool('neat_allow_shrinking')) {
findLongestNick();
reformat();
}
# we do not remove a known color for a gone nick, as they may return
}
# based on simple_hash from nickcolor.pl
sub nick_to_color($) {
my ($string) = @_;
chomp $string;
my $ignore = Irssi::settings_get_str("neat_ignorechars");
$string =~ s/$ignore//g;
my $counter;
foreach my $char (split(//, $string)) {
$counter += ord $char;
}
return $colors[$counter % ($#colors + 1)];
}
sub color_left($) {
Irssi::command('^format pubmsg {pubmsgnick $2 {pubnick '.$_[0].'$['.$sign.$longestNick.']0}}$1');
Irssi::command('^format pubmsg_channel {pubmsgnick $2 {pubnick '.$_[0].'$['.$sign.$longestNick.']0}}$1');
}
sub color_right($) {
Irssi::command('^format pubmsg {pubmsgnick {pubnick '.$_[0].'$['.$sign.$longestNick.']0}$2}$1');
Irssi::command('^format pubmsg_channel {pubmsgnick {pubnick '.$_[0].'$['.$sign.$longestNick.']0}$2}$1');
}
sub sig_public {
my ($server, $msg, $nick, $address, $target) = @_;
&$alignment($saved_colors{$nick});
}
sub sig_setup {
@colors = split(//, Irssi::settings_get_str('neat_colors'));
# check left or right alignment
if (Irssi::settings_get_bool('neat_right_mode') == 0) {
$alignment = \&color_left;
} else {
$alignment = \&color_right;
}
# check if we switched coloring on or off
my $new = Irssi::settings_get_bool('neat_colorize');
if ($new != $colorize) {
if ($new) {
Irssi::signal_add('message public', 'sig_public');
} else {
if ($colorize >= 0) {
Irssi::signal_remove('message public', 'sig_public');
}
}
}
$colorize = $new;
reformat();
&$alignment('%w');
}
# make sure that every nick has an assigned color
sub assert_colors() {
foreach (Irssi::channels()) {
foreach ($_->nicks()) {
next if (exists($saved_colors{$_->{nick}}));
$saved_colors{$_->{nick}} = "%".nick_to_color($_->{nick});
delayed_save();
}
}
}
# load colors from file
sub load_colors() {
open(FID, "<", $ENV{HOME}."/.irssi/saved_colors") || return;
while (<FID>) {
chomp;
my ($k, $v) = split(/:/);
# skip broken lines, those may have been introduced by nm.pl
# version 0.3.7 and earlier
if ($k eq '' || $v eq '') {
neat_log(Irssi::active_win(), "Warning, broken line in saved_colors file, skipping '$k:$v'");
next;
}
$saved_colors{$k} = $v;
}
close(FID);
}
# save colors to file
sub save_colors() {
open(FID, ">", $ENV{HOME}."/.irssi/saved_colors");
print FID $_.":".$saved_colors{$_}."\n" foreach (keys(%saved_colors));
close(FID);
# clear possible pending save.
Irssi::timeout_remove($pending_save) if $pending_save;
$pending_save = undef;
}
# log a line to a window item
sub neat_log($@) {
my ($witem, @text) = @_;
$witem->print("nm.pl: ".$_) foreach(@text);
}
# show available colors
sub cmd_neatcolor_colors($) {
my ($witem, undef, undef) = @_;
neat_log($witem, "Available colors: ".join("", map { "%".$_.$_ } @colors));
}
# display the configured color for a nick
sub cmd_neatcolor_get() {
my ($witem, $nick, undef) = @_;
if (!exists($saved_colors{$nick})) {
neat_log($witem, "Error: no such nick '$nick'");
return;
}
neat_log($witem, "Color for ".$saved_colors{$nick}.$nick);
}
# display help
sub cmd_neatcolor_help() {
my ($witem, $cmd, undef) = @_;
if ($cmd) {
if (!exists($commands{$cmd})) {
neat_log($witem, "Error: no such command '$cmd'");
return;
}
if (!exists($commands{$cmd}{verbose})) {
neat_log($witem, "No additional help for '$cmd' available");
return;
}
neat_log($witem, ( "", "Help for ".uc($cmd), "" ) );
neat_log($witem, @{$commands{$cmd}{verbose}});
return;
}
neat_log($witem, split(/\n/, $help));
neat_log($witem, "Available options for /neatcolor");
neat_log($witem, " ".$_.": ".$commands{$_}{text}) foreach(sort(keys(%commands)));
my @verbose;
foreach (sort(keys(%commands))) {
push(@verbose, $_) if exists($commands{$_}{verbose});
}
neat_log($witem, "Verbose help available for: '".join(", ", @verbose)."'");
}
# list configured nicks
sub cmd_neatcolor_list() {
my ($witem, undef, undef) = @_;
neat_log($witem, "Configured nicks: ".join(", ", map { $saved_colors{$_}.$_ } sort(keys(%saved_colors))));
}
# reset a nick to its default color
sub cmd_neatcolor_reset() {
my ($witem, $nick, undef) = @_;
if ($nick eq '--all') {
%saved_colors = ();
assert_colors();
neat_log($witem, "Reset all colors");
return;
}
if (!exists($saved_colors{$nick})) {
neat_log($witem, "Error: no such nick '$nick'");
return;
}
$saved_colors{$nick} = "%".nick_to_color($nick);
delayed_save();
neat_log($witem, "Reset color for ".$saved_colors{$nick}.$nick);
}
# save configured colors to disk
sub cmd_neatcolor_save() {
my ($witem, undef, undef) = @_;
save_colors();
neat_log($witem, "color information saved");
}
# set a color for a nick
sub cmd_neatcolor_set() {
my ($witem, $nick, $color) = @_;
my @found = grep(/$color/, @colors);
if ($#found) {
neat_log($witem, "Error: trying to set unknown color '%$color$color%n'");
cmd_neatcolor_colors($witem);
return;
}
if ($witem->{type} ne "CHANNEL" && $witem->{type} ne "QUERY") {
neat_log($witem, "Warning: not a Channel/Query, can not check nick!");
neat_log($witem, "Remember, nicks are case sensitive to nm.pl");
} else {
my @nicks = grep(/^$nick$/i, map { $_->{nick} } ($witem->nicks()));
if ($#nicks < 0) {
neat_log($witem, "Warning: could not find nick '$nick' here");
} else {
if ($nicks[0] ne $nick) {
neat_log($witem, "Warning: using '$nicks[0]' instead of '$nick'");
$nick = $nicks[0];
}
}
}
$saved_colors{$nick} = "%".$color;
delayed_save();
neat_log($witem, "Set color for $saved_colors{$nick}$nick");
}
%commands = (
colors => {
text => "show available colors",
verbose => [
"COLORS",
"",
"displays all available colors",
"",
"You can restrict/define the list of available colors ".
"with the help of the neat_colors setting"
],
func => \&cmd_neatcolor_colors,
},
get => {
text => "retrieve color for a nick",
verbose => [
"GET <nick>",
"",
"displays color used for <nick>"
],
func => \&cmd_neatcolor_get,
},
help => {
text => "print this help message",
func => \&cmd_neatcolor_help,
},
list => {
text => "list configured nick/color pairs",
func => \&cmd_neatcolor_list,
},
reset => {
text => "reset color to default",
verbose => [
"RESET --all|<nick>",
"",
"resets the color used for all nicks or for <nick> to ",
"its internal default",
],
func => \&cmd_neatcolor_reset,
},
save => {
text => "save color information to disk",
verbose => [
"SAVE",
"",
"saves color information to disk, so that it survives ".
"an irssi restart.",
"",
"Color information will be automatically saved on /quit",
],
func => \&cmd_neatcolor_save,
},
set => {
text => "set a specific color for a nick",
verbose => [
"SET <nick> <color>",
"",
"use <color> for <nick>",
"",
"This command will perform a couple of sanity checks, ".
"when called from a CHANNEL/QUERY window",
"",
"EXAMPLE:",
" /neatcolor set bc-bd r",
"",
"use /neatcolor COLORS to see available colors"
],
func => \&cmd_neatcolor_set,
},
);
# the main command callback that gets called for all neatcolor commands
sub cmd_neatcolor() {
my ($data, $server, $witem) = @_;
my ($cmd, $nick, $color) = split (/ /, $data);
$cmd = lc($cmd);
# make sure we have a valid witem to print text to
$witem = Irssi::active_win() unless ($witem);
if (!exists($commands{$cmd})) {
neat_log($witem, "Error: unknown command '$cmd'");
&{$commands{"help"}{"func"}}($witem) if (exists($commands{"help"}));
return;
}
&{$commands{$cmd}{"func"}}($witem, $nick, $color);
}
Irssi::settings_add_bool('misc', 'neat_left_messages', 0);
Irssi::settings_add_bool('misc', 'neat_left_actions', 0);
Irssi::settings_add_bool('misc', 'neat_right_mode', 1);
Irssi::settings_add_int('misc', 'neat_maxlength', 0);
Irssi::settings_add_int('misc', 'neat_melength', 2);
Irssi::settings_add_bool('misc', 'neat_colorize', 1);
Irssi::settings_add_str('misc', 'neat_colors', 'rRgGyYbBmMcC');
Irssi::settings_add_str('misc', 'neat_ignorechars', '');
Irssi::settings_add_bool('misc', 'neat_allow_shrinking', 1);
Irssi::settings_add_int('misc', 'neat_autosave', 60);
Irssi::command_bind('neatcolor', 'cmd_neatcolor');
Irssi::signal_add('nicklist new', 'sig_newNick');
Irssi::signal_add('nicklist changed', 'sig_changeNick');
Irssi::signal_add('nicklist remove', 'sig_removeNick');
Irssi::signal_add('setup changed', 'sig_setup');
Irssi::signal_add_last('setup reread', 'sig_setup');
findLongestNick();
sig_setup;
load_colors();
assert_colors();
# we need to add this signal _after_ the colors have been loaded, to make sure
# no race condition exists wrt color saving
Irssi::signal_add('gui exit', 'save_colors');