summaryrefslogtreecommitdiffstats
path: root/scripts/autorealname.pl
blob: 33103e6438394d0587bd3fb086a7b24523df0293 (plain)
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
# Print realname of everyone who join to channels
# for irssi 0.7.99 by Timo Sirainen

use Irssi 20011207;
use strict;
use vars qw($VERSION %IRSSI); 
$VERSION = "0.8.7";
%IRSSI = (
    authors	=> "Timo \'cras\' Sirainen, Bastian Blank",
    contact	=> "tss\@iki.fi, waldi\@debian.org",
    name	=> "auto realname",
    description	=> "Print realname of everyone who join to channels",
    license	=> "GPLv2 or later",
    url		=> "http://irssi.org/",
    changed	=> "2021-01-16"
);

# v0.8.7 changes - bw1
#  - fix Can't call method "nick_find" ... line 282.
# v0.8.6 changes - Juhamatti Niemelä
#  - fix join msg printing when there are multiple common channels
# v0.8.5 changes - Bastian Blank
#  - really use the introduced state variable
# v0.8.4 changes - Bastian Blank
#  - fix queue abort
# v0.8.3 changes - Bastian Blank
#  - on queue abort print any join messages
# v0.8.2 changes - Bastian Blank
#  - use channelname instead of Irssi::Irc::Channel for channelname within message on join
# v0.8.1 changes - Bastian Blank
#  - print any join messages on abort
#  - also remove any timeouts
# v0.8 changes - Bastian Blank
#  - join message includes realname
#  - add timeout
#  - don't print realname for our self
#  - change license because german law doesn't allow to give away the copyright
# v0.71 changes
#  - a bit safer now with using "redirect last"
# v0.7 changes
#  - pretty much a rewrite - shouldn't break anymore
# v0.62 changes
#  - upgraded redirection to work with latest CVS irssi - lot easier
#    to handle it this time :)
# v0.61 changes
#  - forgot to reset %chan_list..
# v0.6 changes
#  - works now properly when a nick joins to multiple channels
# v0.5 changes
#  - Use "message join" so we won't ask realname for ignored people

Irssi::theme_register([
  'join', '{channick_hilight $0} {chanhost_hilight $1} has joined {channel $2}',
  'join_realname', '{channick_hilight $0} ({hilight $1}) {chanhost_hilight $2} has joined {channel $3}',
  'join_realname_only', '{channick_hilight $0} is {hilight $1}',
]);

my $whois_queue_length_before_abort = 10; # max. whois queue length before we should abort the whois queries for next few seconds (people are probably joining behind a netsplit)
my $whois_abort_seconds = 10; # wait for 10 secs when there's been too many joins
my $debug = 0;

my %servers;

my %whois_waiting;
my %whois_queue;
my %aborted;
my %chan_list;

sub sig_connected {
  my $server = shift;
  $servers{$server->{tag}} = {
    abort_time => 0,  # if join event is received before this, abort
    waiting => 0,     # waiting reply for WHOIS request
    queue => [],      # whois queue
    nicks => {}       # nick => [ #chan1, #chan2, .. ]
  };
}

sub sig_disconnected {
  my $server = shift;
  delete $servers{$server->{tag}};
}

sub msg_join {
  my ($server, $channame, $nick, $host) = @_;
  $channame =~ s/^://;
  my $rec = $servers{$server->{tag}};

  # don't display realname for our self
  return if ($nick eq $server->{nick});

  # don't whois people who netjoin back
  return if ($server->netsplit_find($nick, $host));

  return if (time < $rec->{abort_time});
  $rec->{abort_time} = 0;

  Irssi::signal_stop();

  # check if the nick is already found from another channel
  {
    my $ret = 0;
    foreach my $channel ($server->channels()) {
      my $nickrec = $channel->nick_find($nick);
      if ($nickrec && $nickrec->{realname}) {
        # this user already has a known realname - use it.
        $channel = $server->channel_find($channame);
        $channel->printformat(MSGLEVEL_JOINS, 'join_realname', $nick, $nickrec->{realname}, $nickrec->{host}, $channel->{name});
        $channel->print("autorealname: already found: $nick", MSGLEVEL_CLIENTCRAP) if $debug;
        $ret = 1;
        last;
      }
    }

    return if ($ret);
  }

  # save channel to nick specific hash so we can later check which channels
  # it needs to print the realname

  if ($rec->{nicks}->{$nick}) {
    # don't send the WHOIS again if nick is already in queue
    push @{$rec->{nicks}->{$nick}->{chans_join}}, $channame;
    push @{$rec->{nicks}->{$nick}->{chans_realname}}, $channame;
    $rec->{nicks}->{$nick}->{state} = 0;
    my $channel = $server->channel_find($channame);
    $channel->print("autorealname: already in queue: $nick", MSGLEVEL_CLIENTCRAP) if $debug;
  }
  else {
    $rec->{nicks}->{$nick} = {};
    $rec->{nicks}->{$nick}->{chans_join} = [$channame];
    $rec->{nicks}->{$nick}->{chans_realname} = [$channame];
    $rec->{nicks}->{$nick}->{state} = 0;

    # add the nick to queue
    push @{$rec->{queue}}, $nick;

    # timeout
    $rec->{nicks}->{$nick}->{timeout} = Irssi::timeout_add(1000, \&timeout_whois, [$server, $nick]);
    my $channel = $server->channel_find($channame);
    $channel->print("autorealname: add to queue: $nick", MSGLEVEL_CLIENTCRAP) if $debug;
  }

  if (scalar @{$rec->{queue}} >= $whois_queue_length_before_abort) {
    # too many whois requests in queue, abort
    foreach $nick (@{$rec->{queue}}) {
      foreach my $channel (@{$rec->{nicks}->{$nick}->{chans_join}}) {
        my $chanrec = $server->channel_find($channel);
        my $nickrec = $chanrec->nick_find($nick);
        if ($chanrec && $nickrec) {
          $chanrec->printformat(MSGLEVEL_JOINS, 'join', $nick, $nickrec->{host}, $channel);
          $chanrec->print("autorealname: queue abort: $nick", MSGLEVEL_CLIENTCRAP) if $debug;
        }
      }
      Irssi::timeout_remove($rec->{nicks}->{$nick}->{timeout}) if (!($rec->{nicks}->{$nick}->{state} & 2));
      delete $rec->{nicks}->{$nick};
    }
    $rec->{queue} = [];
    $rec->{abort_time} = time+$whois_abort_seconds;
    return;
  }

  # waiting for WHOIS reply..
  return if $rec->{waiting};

  request_whois($server, $rec);
}

sub request_whois {
  my ($server, $rec) = @_;
  return if (scalar @{$rec->{queue}} == 0);

  my @whois_nicks = splice(@{$rec->{queue}}, 0, $server->{max_whois_in_cmd});
  my $whois_query = join(',', @whois_nicks);

  # ignore all whois replies except the first line of the WHOIS reply
  my $redir_arg = $whois_query.' '.join(' ', @whois_nicks);
  $server->redirect_event("whois", 1, $redir_arg, 0, 
			  "redir autorealname_whois_last", {
			    "event 311" => "redir autorealname_whois",
			    "event 401" => "redir autorealname_whois_unknown",
			    "redirect last" => "redir autorealname_whois_last",
			    "" => "event empty" });

  $server->send_raw("WHOIS :$whois_query");
  $rec->{waiting} = 1;
}

sub event_whois {
  my ($server, $data) = @_;
  my ($num, $nick, $user, $host, $empty, $realname) = split(/ +/, $data, 6);
  $realname =~ s/^://;
  my $rec = $servers{$server->{tag}};

  return if not $rec->{nicks}->{$nick};

  $rec->{nicks}->{$nick}->{state} |= 1;

  if (!($rec->{nicks}->{$nick}->{state} & 2)) {
    Irssi::timeout_remove($rec->{nicks}->{$nick}->{timeout});
    foreach my $channel (@{$rec->{nicks}->{$nick}->{chans_join}}) {
      my $chanrec = $server->channel_find($channel);
      my $nickrec = $chanrec->nick_find($nick);
      if ($chanrec && $nickrec) {
        $chanrec->printformat(MSGLEVEL_JOINS, 'join_realname', $nick, $realname, $nickrec->{host}, $channel);
        $chanrec->print("autorealname: got whois: $nick, state: ".$rec->{nicks}->{$nick}->{state}, MSGLEVEL_CLIENTCRAP) if $debug;
      }
    }
    $rec->{nicks}->{$nick}->{chans_join} = [];
    $rec->{nicks}->{$nick}->{chans_realname} = [];
    $rec->{nicks}->{$nick}->{state} |= 2;
  }
  else {
    foreach my $channel (@{$rec->{nicks}->{$nick}->{chans_realname}}) {
      my $chanrec = $server->channel_find($channel);
      next unless (defined $chanrec);
      my $nickrec = $chanrec->nick_find($nick);
      if ($chanrec && $nickrec) {
        $chanrec->printformat(MSGLEVEL_JOINS, 'join_realname_only', $nick, $realname);
        $chanrec->print("autorealname: got whois: $nick, state: ".$rec->{nicks}->{$nick}->{state}, MSGLEVEL_CLIENTCRAP) if $debug;
      }
    }
    $rec->{nicks}->{$nick}->{chans_realname} = [];
  }

  delete $rec->{nicks}->{$nick} if ($rec->{nicks}->{$nick}->{state} == 3);
}

sub event_whois_unknown {
  my ($server, $data) = @_;
  my ($temp, $nick) = split(" ", $data);
  my $rec = $servers{$server->{tag}};

  return if not $rec->{nicks}->{$nick};

  $rec->{nicks}->{$nick}->{state} |= 1;

  if (!($rec->{nicks}->{$nick}->{state} & 2)) {
    Irssi::timeout_remove($rec->{nicks}->{$nick}->{timeout});
    foreach my $channel (@{$rec->{nicks}->{$nick}->{chans_join}}) {
      my $chanrec = $server->channel_find($channel);
      my $nickrec = $chanrec->nick_find($nick);
      if ($chanrec && $nickrec) {
        $chanrec->printformat(MSGLEVEL_JOINS, 'join', $nick, $nickrec->{host}, $channel);
        $chanrec->print("autorealname: got unknown whois: $nick", MSGLEVEL_CLIENTCRAP) if $debug;
      }
    }
    $rec->{nicks}->{$nick}->{chans_join} = [];
  } else {
    foreach my $channel (@{$rec->{nicks}->{$nick}->{chans_join}}) {
      my $chanrec = $server->channel_find($channel);
      $chanrec->print("autorealname: got unknown whois (already considered): $nick", MSGLEVEL_CLIENTCRAP) if $debug;
    }
  }

  delete $rec->{nicks}->{$nick} if ($rec->{nicks}->{$nick}->{state} == 3);
}

sub event_whois_last {
  my $server = shift;
  my $rec = $servers{$server->{tag}};

  $rec->{waiting} = 0;
  request_whois($server, $rec);
}

foreach my $server (Irssi::servers()) {
  sig_connected($server);
}

sub timeout_whois {
  my $server = shift @{$_[0]};
  my $nick = shift @{$_[0]};

  my $rec = $servers{$server->{tag}};

  return if not $rec->{nicks}->{$nick};

  Irssi::timeout_remove($rec->{nicks}->{$nick}->{timeout});
  $rec->{nicks}->{$nick}->{state} |= 2;

  my @channels = @{$rec->{nicks}->{$nick}->{chans_join}};
  foreach my $channel (@channels) {
    my $chanrec = $server->channel_find($channel);
    next unless (defined $chanrec);
    my $nickrec = $chanrec->nick_find($nick);
    if ($nickrec && $chanrec) {
      $chanrec->printformat(MSGLEVEL_JOINS, 'join', $nick, $nickrec->{host}, $channel);
      $chanrec->print("autorealname: timeout: $nick, state: ".$rec->{nicks}->{$nick}->{state}, MSGLEVEL_CLIENTCRAP) if $debug;
    }
  }

  $rec->{nicks}->{$nick}->{chans_join} = [];
}

Irssi::signal_add( {
        'server connected' => \&sig_connected,
	'server disconnected' => \&sig_disconnected,
	'message join' => \&msg_join,
	'redir autorealname_whois' => \&event_whois,
	'redir autorealname_whois_unknown' => \&event_whois_unknown,
	'redir autorealname_whois_last' => \&event_whois_last });

# vim:set sw=2 ts=8 et: