mirror of
git://git.psyced.org/git/psyced
synced 2024-08-15 03:25:10 +00:00
362 lines
10 KiB
Perl
362 lines
10 KiB
Perl
|
# INSTALLATION
|
||
|
#
|
||
|
# - Add the statusbar item:
|
||
|
# /statusbar window add typing_notice
|
||
|
# You won't see anything until someone is typing.
|
||
|
#
|
||
|
# SETTINGS
|
||
|
#
|
||
|
# [typing_notice]
|
||
|
# send_typing = ON
|
||
|
# -> send typing notifications to supported users
|
||
|
#
|
||
|
# CHANGES
|
||
|
# 2008-01-11
|
||
|
# * threw out most bitlbee stuff
|
||
|
# * all XEP 0085 typing levels
|
||
|
# * working interop with 0085
|
||
|
# 2008-01-05
|
||
|
# * ctcp capab protocol - irssis clientinfo is buggy
|
||
|
# * renamed typing levels - inspired by XEP 0085
|
||
|
# * started to remove bitlbee stuff
|
||
|
# -----------------------------------------------
|
||
|
# fippoism starts
|
||
|
# this script is based on version 1.9.1 of
|
||
|
# http://the-timing.nl/Projects/Irssi-BitlBee/typing_notice.pl
|
||
|
# the plan is to get compat to xmpp xep 0085 at least
|
||
|
# and of course psyc legacy ctcp support :-)
|
||
|
# -----------------------------------------------
|
||
|
#
|
||
|
use strict;
|
||
|
use Irssi::TextUI;
|
||
|
use Data::Dumper;
|
||
|
|
||
|
use vars qw($VERSION %IRSSI);
|
||
|
|
||
|
$VERSION = '0.2';
|
||
|
%IRSSI = (
|
||
|
authors => 'Philipp "fippo" Hancke',
|
||
|
contact => 'fippo@goodadvice.pages.de',
|
||
|
name => 'typing_notice_psyc',
|
||
|
description => 'based on Tijmen\' typing notification script for bitlbee
|
||
|
1. Receiving typing notices: Adds an item to the status bar which says [typing] when someone is typing a message.
|
||
|
2. Sending typing notices: Sends CTCP TYPING messages to PSYC, XMPP and IRC users (If they support it)',
|
||
|
license => 'GPLv2',
|
||
|
url => 'http://www.psyced.org/',
|
||
|
changed => '2008-01-11',
|
||
|
);
|
||
|
|
||
|
my $debug = 0;
|
||
|
|
||
|
my %TIMEOUTS = (
|
||
|
"PAUSED" => 5 * 1000,
|
||
|
"INACTIVE" => 30 * 1000,
|
||
|
"GONE" => 180 * 1000,
|
||
|
);
|
||
|
|
||
|
|
||
|
my %typers; # for storage
|
||
|
|
||
|
my $line;
|
||
|
my $lastkey;
|
||
|
my $keylog_active = 1;
|
||
|
my $command_char = Irssi::settings_get_str('cmdchars');
|
||
|
my $to_char = Irssi::settings_get_str("completion_char");
|
||
|
|
||
|
## IRC only ##############
|
||
|
# this is used to append a non-printable control sequence to all messages
|
||
|
# quite a smart hack indeed - but i would prefer ctcp for it
|
||
|
my $o = "\cO";
|
||
|
my $oo = $o.$o;
|
||
|
##########################
|
||
|
|
||
|
sub get_current {
|
||
|
my $server = Irssi::active_server();
|
||
|
my $window = Irssi::active_win();
|
||
|
if ($server && $window) {
|
||
|
return ($server->{tag}, $window->get_active_name());
|
||
|
}
|
||
|
return undef;
|
||
|
}
|
||
|
|
||
|
sub event_ctcp_msg { # called for ctcp msg, not ctcp replies
|
||
|
my ($server, $msg, $from, $address) = @_;
|
||
|
$server = $server->{tag};
|
||
|
|
||
|
if ($msg =~ /TYPING (INACTIVE|PAUSED|COMPOSING|ACTIVE|GONE)/ ) {
|
||
|
if ( not $debug ) {
|
||
|
Irssi::signal_stop();
|
||
|
}
|
||
|
# if someone sends this, they usually support that stuff
|
||
|
$typers{$server}{$from}{capability} = 1;
|
||
|
|
||
|
$typers{$server}{$from}{typing_in} = $1;
|
||
|
Irssi::statusbar_items_redraw('typing_notice');
|
||
|
Irssi::signal_stop();
|
||
|
} elsif ( my($type) = $msg =~ /CAPAB TYPING/ ) {
|
||
|
if ($debug) {
|
||
|
print "capab typing query from $from.";
|
||
|
}
|
||
|
my $serverobj = Irssi::server_find_tag($server);
|
||
|
$serverobj->ctcp_send_reply("NOTICE $from :\001CAPAB TYPING\001");
|
||
|
Irssi::signal_stop();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub event_ctcp_reply { # called for ctcp replies
|
||
|
my ($server, $msg, $from, $address) = @_;
|
||
|
if ( $msg =~ /CAPAB TYPING/ && exists( $typers{$server->{tag}}{$from} )) {
|
||
|
if ($debug) {
|
||
|
print "capab typing reply from $from.";
|
||
|
}
|
||
|
$typers{$server->{tag}}{$from}{capability} = 1;
|
||
|
Irssi::signal_stop();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub unset_typing_in {
|
||
|
my ($a) = @_;
|
||
|
my ($server, $nick) = @{$a};
|
||
|
if ($debug) {
|
||
|
print "unset: $server, $nick";
|
||
|
}
|
||
|
$typers{$server}{$nick}{typing_in} = undef;
|
||
|
Irssi::timeout_remove($typers{$server}{$nick}{timer_tag_in});
|
||
|
Irssi::statusbar_items_redraw('typing_notice');
|
||
|
}
|
||
|
|
||
|
sub event_msg {
|
||
|
my ($server, $data, $nick, $address, $target) = @_;
|
||
|
$server = $server->{tag};
|
||
|
|
||
|
# haeh??? ist das eine art <active/> angehaengt als leeres ctcp
|
||
|
if ( $data =~ /$oo\z/ ) {
|
||
|
if ( not exists( $typers{$server}{$nick} ) ) {
|
||
|
$typers{$server}{$nick}{capability} = 1;
|
||
|
if ($debug) {
|
||
|
print "This user supports typing! $server, $nick";
|
||
|
}
|
||
|
}
|
||
|
} elsif (0) { # ah... this ensures that it stays valid
|
||
|
if ( exists( $typers{$server}{$nick} ) ) {
|
||
|
if ($debug) {
|
||
|
print "This user does not support typing anymore! $nick. splice: ";
|
||
|
}
|
||
|
delete $typers{$server}{$nick};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( 0 and exists( $typers{$server}{$nick} ) ) {
|
||
|
unset_typing_in( [$server, $nick] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub typing_notice { ## redraw statusbar item
|
||
|
my ($item, $get_size_only) = @_;
|
||
|
my ($server, $channel) = get_current();
|
||
|
|
||
|
return unless exists $typers{$server}{$channel};
|
||
|
|
||
|
if ( $typers{$server}{$channel}{typing_in} ne undef ) {
|
||
|
my $append = $typers{$server}{$channel}{typing_in};
|
||
|
$item->default_handler($get_size_only, "{sb typing $append}", 0, 1);
|
||
|
if ($debug >= 2) {
|
||
|
print "typing: $server, $channel.";
|
||
|
}
|
||
|
} else {
|
||
|
if ($debug) {
|
||
|
print "clear: $server, $channel ";
|
||
|
}
|
||
|
$item->default_handler($get_size_only, "", 0, 1);
|
||
|
if ($typers{$server}{$channel}{timer_tag_in} ne undef) {
|
||
|
Irssi::timeout_remove($typers{$server}{$channel}{timer_tag_in});
|
||
|
$typers{$server}{$channel}{timer_tag_in} = undef;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub window_change {
|
||
|
Irssi::statusbar_items_redraw('typing_notice');
|
||
|
my ($server, $channel) = get_current();
|
||
|
|
||
|
if ( exists( $typers{$server}{$channel} ) ) {
|
||
|
if ( not $keylog_active ) {
|
||
|
$keylog_active = 1;
|
||
|
Irssi::signal_add_last('gui key pressed', 'key_pressed');
|
||
|
}
|
||
|
} else {
|
||
|
if ($keylog_active) {
|
||
|
$keylog_active = 0;
|
||
|
Irssi::signal_remove('gui key pressed', 'key_pressed');
|
||
|
}
|
||
|
}
|
||
|
return if not Irssi::settings_get_bool("send_typing");
|
||
|
# send INACTIVE?
|
||
|
}
|
||
|
|
||
|
sub window_close {
|
||
|
return if not Irssi::settings_get_bool("send_typing");
|
||
|
my ($server, $channel) = get_current();
|
||
|
if ( exists( $typers{$server}{$channel} ) and $typers{$server}{$channel}{state_out} ne "GONE" ) {
|
||
|
my $serverobj = Irssi::server_find_tag($server);
|
||
|
$serverobj->command("^CTCP $channel TYPING GONE");
|
||
|
$typers{$server}{$channel}{state_out} = "GONE";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub window_open {
|
||
|
return if not Irssi::settings_get_bool("send_typing");
|
||
|
my ($item) = @_;
|
||
|
# look if we should disco
|
||
|
}
|
||
|
|
||
|
sub key_pressed {
|
||
|
return if not Irssi::settings_get_bool("send_typing");
|
||
|
my $key = shift;
|
||
|
if ($key == 9 && $key == 10 && $lastkey == 27 && $key == 27 && $lastkey == 91 && $key == 126 && $key == 127) { # ignore these keys
|
||
|
$lastkey = $key;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
my ($server, $channel) = get_current();
|
||
|
|
||
|
if ( exists( $typers{$server}{$channel} ) ) {
|
||
|
|
||
|
my $input = Irssi::parse_special("\$L");
|
||
|
if ($input !~ /^$command_char.*/ && length($input) > 0){
|
||
|
send_typing( $server, $channel );
|
||
|
}
|
||
|
}
|
||
|
$lastkey = $key; # some keys, like arrow-up, contain two events.
|
||
|
}
|
||
|
|
||
|
|
||
|
sub send_typing_pause {
|
||
|
return if not Irssi::settings_get_bool("send_typing");
|
||
|
my ($a) = @_;
|
||
|
my( $server, $nick ) = @{$a};
|
||
|
send_typing_update_state($server, $nick, "PAUSED");
|
||
|
|
||
|
Irssi::timeout_remove($typers{$server}{$nick}{timer_tag_out});
|
||
|
$typers{$server}{$nick}{timer_tag_out} = Irssi::timeout_add_once($TIMEOUTS{INACTIVE}, 'send_typing_inactive', [$server, $nick]);
|
||
|
|
||
|
}
|
||
|
|
||
|
sub send_typing_inactive {
|
||
|
return if not Irssi::settings_get_bool("send_typing");
|
||
|
my ($a) = @_;
|
||
|
my( $server, $nick ) = @{$a};
|
||
|
send_typing_update_state($server, $nick, "INACTIVE");
|
||
|
|
||
|
Irssi::timeout_remove($typers{$server}{$nick}{timer_tag_out});
|
||
|
$typers{$server}{$nick}{timer_tag_out} = Irssi::timeout_add_once($TIMEOUTS{GONE}, 'send_typing_gone', [$server, $nick]);
|
||
|
|
||
|
}
|
||
|
|
||
|
sub send_typing_gone {
|
||
|
return if not Irssi::settings_get_bool("send_typing");
|
||
|
my ($a) = @_;
|
||
|
my( $server, $nick ) = @{$a};
|
||
|
send_typing_update_state($server, $nick, "GONE");
|
||
|
Irssi::timeout_remove($typers{$server}{$nick}{timer_tag_out});
|
||
|
}
|
||
|
|
||
|
sub send_typing {
|
||
|
my ( $server, $nick ) = @_;
|
||
|
|
||
|
send_typing_update_state($server, $nick, "COMPOSING");
|
||
|
|
||
|
Irssi::timeout_remove($typers{$server}{$nick}{timer_tag_out});
|
||
|
$typers{$server}{$nick}{timer_tag_out} = Irssi::timeout_add_once($TIMEOUTS{PAUSED}, 'send_typing_pause', [$server, $nick]);
|
||
|
}
|
||
|
|
||
|
sub send_typing_update_state {
|
||
|
return if not Irssi::settings_get_bool("send_typing");
|
||
|
my ( $server, $nick, $state ) = @_;
|
||
|
if (not exists($typers{$server}{$nick}) or $typers{$server}{$nick}{capability} != 1) {
|
||
|
print Dumper(%typers);
|
||
|
print "(dont) send_typing_update_state to non-typer $server->$nick"; #if ($debug);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if ($state eq $typers{$server}{$nick}{state_out}) {
|
||
|
print "(dont) send_typing_update_state: $state already known" if $debug;
|
||
|
return;
|
||
|
}
|
||
|
# FIXME: allowed state transitions could be checked here
|
||
|
if ($debug) {
|
||
|
print "$server: ctcp $nick typing $state";
|
||
|
}
|
||
|
|
||
|
my $serverobj = Irssi::server_find_tag($server);
|
||
|
if (!$serverobj) {
|
||
|
print "send typing update state: server not found";
|
||
|
return;
|
||
|
}
|
||
|
$serverobj->command("^CTCP $nick TYPING $state");
|
||
|
|
||
|
$typers{$server}{$nick}{state_out} = $state;
|
||
|
}
|
||
|
|
||
|
sub db_typing {
|
||
|
print "------ Typers -----\n".Dumper(%typers);
|
||
|
}
|
||
|
|
||
|
sub event_send_msg { # outgoing messages
|
||
|
my ($msg, $server, $window) = @_;
|
||
|
return unless $window and $window->{type} eq "QUERY";
|
||
|
my $nick = $window->{name};
|
||
|
|
||
|
if ($debug) {
|
||
|
print "send msg: $server->{tag}, $nick";
|
||
|
}
|
||
|
if ( exists($typers{$server->{tag}}{$nick}) and
|
||
|
$typers{$server->{tag}}{$nick}{capability} == 1) {
|
||
|
$typers{$server->{tag}}{$nick}{state_out} = "ACTIVE";
|
||
|
Irssi::timeout_remove($typers{$server->{tag}}{$nick}{timer_tag_out});
|
||
|
}
|
||
|
|
||
|
if (!exists( $typers{$server->{tag}}{$nick} ) ) {
|
||
|
if ($debug) {
|
||
|
print "send capa query to $nick.";
|
||
|
}
|
||
|
$typers{$server->{tag}}{$nick}{capability} = -1;
|
||
|
if (my $serverobj = Irssi::server_find_tag($server->{tag})) {
|
||
|
$serverobj->command("^CTCP $nick CAPAB TYPING");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( 0 and length($msg) > 0) {
|
||
|
# ist das eine art <active/>
|
||
|
$msg .= $oo;
|
||
|
}
|
||
|
|
||
|
Irssi::signal_stop();
|
||
|
Irssi::signal_remove('send text', 'event_send_msg');
|
||
|
Irssi::signal_emit('send text', $msg, $server, $window);
|
||
|
Irssi::signal_add_first('send text', 'event_send_msg');
|
||
|
}
|
||
|
|
||
|
# Command
|
||
|
Irssi::command_bind('db_typing','db_typing');
|
||
|
|
||
|
# Settings
|
||
|
Irssi::settings_add_bool("typing_notice","send_typing",1);
|
||
|
|
||
|
# IRC events
|
||
|
Irssi::signal_add_first("send text", "event_send_msg"); # Outgoing messages
|
||
|
Irssi::signal_add("ctcp msg", "event_ctcp_msg");
|
||
|
Irssi::signal_add("ctcp reply", "event_ctcp_reply");
|
||
|
Irssi::signal_add("message private", "event_msg");
|
||
|
Irssi::signal_add("message public", "event_msg");
|
||
|
|
||
|
# GUI events
|
||
|
Irssi::signal_add_last('window changed', 'window_change');
|
||
|
Irssi::signal_add_last('window destroyed', 'window_close');
|
||
|
Irssi::signal_add_last('window created', 'window_open');
|
||
|
Irssi::signal_add_last('gui key pressed', 'key_pressed');
|
||
|
|
||
|
# Statusbar
|
||
|
Irssi::statusbar_item_register('typing_notice', undef, 'typing_notice');
|
||
|
Irssi::statusbars_recreate_items();
|