html/bitlbee_typing_notice.pl


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