1# INSTALLATION 2# [&bitlbee] set typing_notice true 3# <@root> typing_notice = `true' 4# AND 5# /statusbar window add typing_notice 6# 7# SETTINGS 8# [bitlbee] 9# bitlbee_send_typing = ON 10# -> send typing messages to buddies 11# bitlbee_typing_allwin = OFF 12# -> show typing notifications in all windows 13# 14# 15# Changelog: 16# 17# 2016-01-01 (version 1.7.2) 18# * Fix crash in Irssi during disconnect 19# 20# 2010-08-09 (version 1.7.1) 21# * Multiple control channels supported by checking chanmodes 22# 23# 2010-07-27 (version 1.7) 24# * Using new server detection for latest BitlBee support 25# 26# 2010-07-26 (version 1.6.3) 27# * Removed checking if nicks exists in &bitlbee channel, this because BitlBee 28# can be used without control channel from this date 29# 30# 2007-03-03 (version 1.6.2) 31# * Fix: timers weren't deleted correctly. This resulted in huge mem usage. 32# 33# 2006-11-02 (version 1.6.1) 34# * Sending typing works again. 35# 36# 2006-10-27 (version 1.6) 37# * 'channel sync' re-implemented. 38# * bitlbee_send_typing was a string setting, It's a boolean now, like it should. 39# 40# 2006-10-24 (version 1.5) 41# * Sending notices to online users only. ( removed this again at 2010-07-26, see above ) 42# * Using the new get_channel function; 43# 44# 2005-12-15 (version 1.42): 45# * Fixed small bug with typing notices disappearing under certain circumstances 46# in channels 47# * Fixed bug that caused outgoing notifications not to work 48# * root cares not about our typing status. 49# 50# 2005-12-04 (version 1.41): 51# * Implemented stale states in statusbar (shows "(stale)" for OSCAR connections) 52# * Introduced bitlbee_typing_allwin (default OFF). Set this to ON to make 53# typing notifications visible in all windows. 54# 55# 2005-12-03 (version 1.4): 56# * Major code cleanups and rewrites for bitlbee 1.0 with the updated typing 57# scheme. TYPING 0, TYPING 1, and TYPING 2 are now supported from the server. 58# * Stale states (where user has typed in text but has stopped typing) are now 59# recognized. 60# * Bug where user thinks you are still typing if you close the window after 61# typing something and then erasing it quickly.. fixed. 62# * If a user signs off while they are still typing, the notification is removed 63# This update by Matt "f0rked" Sparks 64# 65# 2005-08-26: 66# Some fixes for AIM, Thanks to Dracula. 67# 68# 2005-08-16: 69# AIM supported, for sending notices, using CTCP TYPING 0. (Use the AIM patch from Hanji http://get.bitlbee.org/patches/) 70# 71# 2004-10-31: 72# Sends typing notice to the bitlbee server when typing a message in irssi. bitlbee > 0.92 73# 74# 2004-06-11: 75# shows [typing: ] in &bitlbee with multiple users. 76# 77use strict; 78use Irssi::TextUI; 79use Data::Dumper; 80 81use vars qw($VERSION %IRSSI); 82 83$VERSION = '1.7.3'; 84%IRSSI = ( 85 authors => 'Tijmen "timing" Ruizendaal, Matt "f0rked" Sparks', 86 contact => 'tijmen.ruizendaal@gmail.com, root@f0rked.com', 87 name => 'BitlBee_typing_notice', 88 description => '1. Adds an item to the status bar wich shows [typing] when someone is typing a message on the supported IM-networks 2. Sends typing notices to the supported IM networks (the other way arround). (For bitlbee 3.0+)', 89 sbitems => 'typing_notice', 90 license => 'GPLv2', 91 url => 'http://the-timing.nl/stuff/irssi-bitlbee, http://f0rked.com', 92); 93 94my %bitlbee_tag; # server object 95my %control_channels; # mostly: &bitlbee, &facebook etc. 96init(); 97 98sub init { # if script is loaded after connect 99 my @servers = Irssi::servers(); 100 foreach my $server(@servers) { 101 if( $server->isupport('NETWORK') eq 'BitlBee' ){ 102 my $T = $server->{tag}; 103 $bitlbee_tag{$T} = 1; 104 my @channels = $server->channels(); 105 foreach my $channel(@channels) { 106 if( $channel->{mode} =~ /C/ ){ 107 push @{ $control_channels{$T} }, $channel->{name} unless (grep $_ eq $channel->{name}, @{ $control_channels{$T} // [] }); 108 } 109 } 110 } 111 } 112} 113# if connect after script is loaded 114Irssi::signal_add_last('event 005' => sub { 115 my( $server ) = @_; 116 if( $server->isupport('NETWORK') eq 'BitlBee' ){ 117 $bitlbee_tag{ $server->{tag} } = 1; 118 } 119}); 120# if new control channel is synced after script is loaded 121Irssi::signal_add_last('channel sync' => sub { 122 my( $channel ) = @_; 123 my $T = $channel->{server}->{tag}; 124 if( $channel->{mode} =~ /C/ && $bitlbee_tag{$T} ){ 125 push @{ $control_channels{$T} }, $channel->{name} unless (grep $_ eq $channel->{name}, @{ $control_channels{$T} // [] }); 126 } 127}); 128 129# How often to check if we are typing, or on msn, 130# how long to keep the typing notice up, or check 131# if the other user is still typing... 132my $KEEP_TYPING_TIMEOUT = 1; 133my $STOP_TYPING_TIMEOUT = 7; 134 135my %timer_tag; 136 137my %typing; 138my %tag; 139my $line; 140my %out_typing; 141my $lastkey; 142my $keylog_active = 1; 143my $command_char = Irssi::settings_get_str('cmdchars'); # mostly: / 144my $to_char = Irssi::settings_get_str("completion_char"); # mostly: : 145 146sub event_ctcp_msg { 147 my ($server, $msg, $from, $address) = @_; 148 my $tag = $server->{tag}; 149 return unless $bitlbee_tag{ $tag }; 150 if ( my($type) = $msg =~ "TYPING ([0-9])" ){ 151 Irssi::signal_stop(); 152 if( $type == 0 ){ 153 unset_typing($tag, $from); 154 } elsif( $type == 1 ){ 155 my $k = "$tag/$from"; 156 $typing{$k}=1; 157 if( $address !~ /\@login\.oscar\.aol\.com/ and $address !~ /\@YAHOO/ and $address !~ /\@login\.icq\.com/ ){ 158 Irssi::timeout_remove($tag{$k}); 159 delete($tag{$k}); 160 $tag{$k}=Irssi::timeout_add_once($STOP_TYPING_TIMEOUT*1000,"unset_typing",[$tag,$from]); 161 } 162 redraw($tag, $from); 163 } elsif( $type == 2 ){ 164 stale_typing($tag, $from); 165 } 166 } 167} 168 169sub unset_typing { 170 my($tag,$from,$no_redraw)=@_; 171 my $k = "$tag/$from"; 172 delete $typing{$k} if $typing{$k}; 173 Irssi::timeout_remove($tag{$k}); 174 delete($tag{$k}); 175 redraw($tag, $from) if !$no_redraw; 176} 177 178sub stale_typing { 179 my($tag,$from)=@_; 180 my $k = "$tag/$from"; 181 $typing{$k}=2; 182 redraw($tag, $from); 183} 184 185sub redraw { 186 my($tag,$from)=@_; 187 my $window = Irssi::active_win(); 188 my $name = $window->get_active_name(); 189 190 # only redraw if current window equals to the typing person, is a control channel or if allwin is set 191 if( $from eq $name || (grep $_ eq $name, @{ $control_channels{$tag} // [] }) || Irssi::settings_get_bool("bitlbee_typing_allwin") ){ 192 Irssi::statusbar_items_redraw('typing_notice'); 193 } 194} 195 196sub event_msg { 197 my ($server,$data,$from,$address,$target) = @_; 198 my $tag = $server->{tag}; 199 return unless $bitlbee_tag{ $tag }; 200 my $channel=Irssi::active_win()->get_active_name(); 201 unset_typing $tag, $from, "no redraw"; 202 unset_typing $tag, $channel; 203} 204 205sub event_quit { 206 my $server = shift; 207 my $tag = $server->{tag}; 208 return unless $bitlbee_tag{ $tag }; 209 my $nick = shift; 210 unset_typing $tag, $nick; 211} 212 213sub typing_notice { 214 my ($item, $get_size_only) = @_; 215 my $window = Irssi::active_win(); 216 my $tag = $window->{active}{server}{tag} // ""; 217 my $channel = $window->get_active_name(); 218 my $k = "$tag/$channel"; 219 220 if (exists($typing{$k})) { 221 my $append=$typing{$k}==2 ? " (stale)" : ""; 222 $item->default_handler($get_size_only, "{sb typing$append}", 0, 1); 223 } else { 224 $item->default_handler($get_size_only, "", 0, 1); 225 Irssi::timeout_remove($tag{$k}); 226 delete($tag{$k}); 227 } 228 # we check for correct windows again, because the statusbar item is redrawn after window change too. 229 if( (grep $_ eq $channel, @{ $control_channels{$tag} // [] }) || Irssi::settings_get_bool("bitlbee_typing_allwin")) { 230 foreach my $key (sort keys(%typing)) { 231 $line .= " ".$key; 232 if ($typing{$key}==2) { $line .= " (stale)"; } 233 } 234 if ($line ne "") { 235 $item->default_handler($get_size_only, "{sb typing:$line}", 0, 1); 236 $line = ""; 237 } 238 } 239} 240 241sub window_change { 242 Irssi::statusbar_items_redraw('typing_notice'); 243 my $win = !Irssi::active_win() ? undef : Irssi::active_win()->{active}; 244 if (ref $win && defined $win->{server}->{tag} && $bitlbee_tag{ $win->{server}->{tag} }) { 245 if (!$keylog_active) { 246 $keylog_active = 1; 247 Irssi::signal_add_last('gui key pressed', 'key_pressed'); 248 } 249 } else { 250 if ($keylog_active) { 251 $keylog_active = 0; 252 Irssi::signal_remove('gui key pressed', 'key_pressed'); 253 } 254 } 255} 256 257sub key_pressed { 258 return if !Irssi::settings_get_bool("bitlbee_send_typing"); 259 my $key = shift; 260 if ($key != 9 && $key != 10 && $key != 13 && $lastkey != 27 && $key != 27 261 && $lastkey != 91 && $key != 126 && $key != 127) 262 { 263 my $window = Irssi::active_win(); 264 my $nick = $window->get_active_name(); 265 my $tag = $window->{active}{server}{tag}; 266 if (defined $tag && $bitlbee_tag{ $tag } && $nick ne "(status)" && $nick ne "root") { 267 if( grep $_ eq $nick, @{ $control_channels{$tag} // [] } ){ # send typing if in control channel 268 my $input = Irssi::parse_special("\$L"); 269 my ($first_word) = split(/ /,$input); 270 if ($input !~ /^$command_char.*/ && $first_word =~ s/$to_char$//){ 271 send_typing($tag, $first_word); 272 } 273 } else { # or any other channels / query 274 my $input = Irssi::parse_special("\$L"); 275 if ($input !~ /^$command_char.*/ && length($input) > 0){ 276 send_typing($tag, $nick); 277 } 278 } 279 } 280 } 281 $lastkey = $key; 282} 283 284sub delete_server { 285 my $tag = shift; 286 delete $bitlbee_tag{$tag}; 287 delete $control_channels{$tag}; 288 undef; 289} 290 291sub out_empty { 292 my ($a) = @_; 293 my($nick,$tag)=@{$a}; 294 my $k = "$tag/$nick"; 295 delete($out_typing{$k}); 296 Irssi::timeout_remove($timer_tag{$k}); 297 delete($timer_tag{$k}); 298 if (my $bitlbee_server = Irssi::server_find_tag($tag)) { 299 $bitlbee_server->command("^CTCP $nick TYPING 0"); 300 } else { 301 delete_server($tag); 302 } 303} 304 305sub send_typing { 306 my ($tag, $nick) = @_; 307 my $k = "$tag/$nick"; 308 if (!exists($out_typing{$k}) || time - $out_typing{$k} > $KEEP_TYPING_TIMEOUT) { 309 if (my $bitlbee_server = Irssi::server_find_tag($tag)) { 310 $bitlbee_server->command("^CTCP $nick TYPING 1"); 311 } else { 312 delete_server($tag); 313 } 314 $out_typing{$k} = time; 315 ### Reset 'stop-typing' timer 316 Irssi::timeout_remove($timer_tag{$k}); 317 delete($timer_tag{$k}); 318 319 ### create new timer 320 $timer_tag{$k} = Irssi::timeout_add_once($STOP_TYPING_TIMEOUT*1000, 'out_empty', ["$nick", $tag]); 321 } 322} 323 324#README: Delete the old bitlbee_send_typing string from ~/.irssi/config. A boolean is better. 325 326sub db_typing { 327 local $Data::Dumper::Sortkeys = 1; 328 print "Detected channels: "; 329 print Dumper(%control_channels); 330 print "Detected server tag: ".join ", ", sort keys %bitlbee_tag; 331 print "Tag: ".Dumper(%tag); 332 print "Timer Tag: ".Dumper(%timer_tag); 333 print "Typing: ".Dumper(%typing); 334 print "Out Typing: ".Dumper(%out_typing); 335} 336 337Irssi::command_bind('db_typing','db_typing'); 338 339Irssi::settings_add_bool("bitlbee","bitlbee_send_typing",1); 340Irssi::settings_add_bool("bitlbee","bitlbee_typing_allwin",0); 341 342Irssi::signal_add("ctcp msg", "event_ctcp_msg"); 343Irssi::signal_add("message private", "event_msg"); 344Irssi::signal_add("message public", "event_msg"); 345Irssi::signal_add("message quit", "event_quit"); 346Irssi::signal_add_last('window changed', 'window_change'); 347Irssi::signal_add_last('gui key pressed', 'key_pressed'); 348Irssi::statusbar_item_register('typing_notice', undef, 'typing_notice'); 349Irssi::statusbars_recreate_items(); 350