1use Irssi 20011210.0000 (); 2$VERSION = "1.12"; 3%IRSSI = ( 4 authors => 'David Leadbeater', 5 contact => 'dgl@dgl.cx', 6 name => 'on.pl', 7 description => '/on command - this is very simple and not really designed to be the same as ircII - it tries to fit into Irssi\'s usage style more than emulating ircII.', 8 license => 'GNU GPLv2 or later', 9 url => 'http://irssi.dgl.cx/', 10); 11 12use strict; 13my %on; 14 15=head1 on.pl 16 17/on command - this is very simple and not really designed to 18be the same as ircII - it tries to fit into Irssi's usage style 19more than emulating ircII. 20 21=head1 Features 22 23This script allow you to bind Irssi commands or a piece of perl 24code to s particular signal with some forms of filtering. 25 26A command can be set to run in a particular channel (nearly) 27and on a particular chatnet. The commands that you add are 28automatically saved into a file (usually ~/.irssi/on.save). 29 30 31=head1 Usage 32 33 /on list 34 /on add [-perl] [-server] [-channel #channel] [-stop] 'signal name' command 35 /on remove signal name 36 /on reload 37 38=head2 ON ADD 39 40 -perl: Interpret command as perl instead of the default Irssi 41 -server: Only trigger for events from this chat network 42 -channel #channel: only trigger for events in #channel 43 (only works where $channel->{name} is present (message signals mostly) 44 -stop: Call Irssi::signal_stop() (probably not a good idea to use this) 45 46If you supply a signal name then it must be quoted so it is 47interpeted as one, if you wish to bind to a numeric then just 48entering it will work. 49 50Currently if you specify a Irssi command $0 and $$0 are escaped, 51$0 $1 and so on are the parameters sent to the signal (except the first 52REC), $$0 and so on are the results of spliting $0 on a space so if 53the signal is an event then $$0 will usually be your nickname, $$1 54will be the channel or nickname the numeric is targeting and so on.. 55 56=head2 ON REMOVE 57 58This removes *all* events from the signal specified (if you 59want to remove a numeric you must add event eg: 60 /on remove event 401 61 62=head2 ON RELOAD 63 64Reloads the saved list from ~/.irssi/on.save into memory, 65useful if you have to edit it manually (and very useful during debugging :) 66 67=head1 Examples 68 69These are pretty generic examples, there are many more 70specific uses for the commands. 71 72To automatically run a /whowas when the no such nick/channel 73event is recieved: 74 /on add 401 /whowas $$0 75 76To automatically run a command when you become an irc operator 77on this chatnet: 78 /on add -server 381 /whatever 79 80To automatically move to a window with activtiy in it on a hilight: 81 /on add 'window hilight' /window goto active 82 83Obviously perl commands could be used here or many different 84signals (see docs/signals.text in the irssi sources for a list 85of all the signals) 86 87=cut 88 89Irssi::command_bind('on','cmd_on'); 90# This makes tab completion work :) 91Irssi::command_set_options('on','stop server perl +channel'); 92load(); 93add_signals(); 94 95# Loads the saved on settings from the saved file 96sub load { 97 my $file = Irssi::get_irssi_dir . '/on.save'; 98 return 0 unless -f $file; 99 open(ON, $file) or return 0; 100 while(<ON>) { 101 chomp; 102 my($event,$chatnet,$settings,$channel,$cmd) = split /\0/; 103 push(@{$on{$event}}, 104 { 105 'chatnet' => $chatnet || 'all', 106 'settings' => $settings, 107 'channel' => $channel, 108 'cmd' => $cmd, 109 } 110 ); 111 } 112 close(ON); 113 return 1; 114} 115 116# Saves the settings currently in the %on hash into the save file 117sub save { 118 my $file = Irssi::get_irssi_dir . '/on.save'; 119 open(ON, ">$file") or return 0; 120 for my $event(keys %on) { 121 for(@{$on{$event}}) { 122 print ON join("\0", $event, @$_{'chatnet','settings','channel','cmd'}) . "\n"; 123 } 124 } 125 close(ON) or return 0; 126 return 1; 127} 128 129# Adds signals from the hash to irssi (only needs to be called once) 130sub add_signals { 131 for(keys %on) { 132 Irssi::signal_add($_, 'signal_handler'); 133 } 134} 135 136# Irssi calls this and it figures out what to do with the event 137sub signal_handler { 138 my($item, @stuff) = @_; 139 my $signal = Irssi::signal_get_emitted(); 140 141 if(exists $on{$signal}) { 142 for(@{$on{$signal}}) { 143 next if $_->{chatnet} ne 'all' and $_->{chatnet} ne $item->{chatnet}; 144 next if $_->{channel} and $item->{name} ne $_->{channel}; 145 event_handle(@$_{'settings','cmd'},$item,@stuff); 146 } 147 }else{ 148 Irssi::signal_remove($signal,'signal_handler'); 149 } 150} 151 152# Called with the params needed to handle an event from signal_handler 153sub event_handle { 154 my($settings,$cmd,$item,@stuff) = @_; 155 my %settings = settings_to_hash($settings); 156 157 if($settings{type} == 1) { 158 local @_; 159 @_ = ($item,@stuff); 160 eval('no strict;' . $cmd); 161 }else{ 162 $cmd =~ s!\$\$(\d)!(split / /,$stuff[0])[$1]!ge; 163 $cmd =~ s/\$(\d)/$stuff[$1]/g; 164 $item->command($cmd); 165 } 166 167 Irssi::signal_stop() if $settings{stop}; 168} 169 170# Converts the settings string to a nice hash 171sub settings_to_hash { 172 my $settings = shift; 173 my %settings; 174 @settings{'type','stop'} = split //, $settings; 175 return %settings; 176} 177 178# Converts a hash to the settings string 179sub hash_to_settings { 180 my %settings = @_; 181 return join '', @settings{'type','stop'}; 182} 183 184# Called by the /on command 185sub cmd_on { 186 my $text = shift; 187 188 if($text =~ s/^add //) { 189 my($cmd,%options) = option_parse($text); 190 if(!$options{event} || !$cmd) { 191 Irssi::print('No '.($cmd ? 'command' : 'event'). ' supplied'); 192 }else{ 193 my($chatnet,%settings,$channel,$event); 194 $chatnet = ($options{server} ? Irssi::active_server()->{chatnet} : 'all'); 195 $event = $options{event}; 196 $channel = $options{channel}; 197 $settings{type} = $options{perl}; 198 $settings{stop} = $options{stop}; 199 add_on($event,$cmd,$chatnet,$channel,%settings); 200 save(); 201 } 202 }elsif($text =~ s/^remove //) { 203 if(del_on($text)) { 204 Irssi::print("Event $text deleted",MSGLEVEL_CLIENTCRAP); 205 }else{ 206 Irssi::print("Event not found",MSGLEVEL_CLIENTCRAP); 207 } 208 save(); 209 }elsif($text =~ /^reload/) { 210 %on = (); 211 load(); 212 }elsif($text eq "help") { 213 Irssi::print( <<EOF 214Usage: 215/on list 216/on add [-perl] [-server] [-channel #channel] [-stop] 'signal name' command 217/on remove signal name 218/on reload 219EOF 220 ); 221 }else{ 222 Irssi::print("/on help for usage information"); 223 on_list(); 224 } 225} 226 227# Output a list of the current contents of %on 228sub on_list { 229 if(!keys %on) { 230 Irssi::print("On list is empty", MSGLEVEL_CLIENTCRAP); 231 return; 232 } 233 for my $event(keys %on) { 234 for(@{$on{$event}}) { 235 Irssi::print("$event: " . 236 ($_->{chatnet} ne 'all' ? $_->{chatnet} : '') . 237 ' ' . $_->{cmd}, 238 MSGLEVEL_CLIENTCRAP 239 ); 240 } 241 } 242} 243 244# Adds into %on and adds a signal if needed. 245sub add_on { 246 my($event,$cmd,$chatnet,$channel,%settings) = @_; 247 248 Irssi::signal_add($event, 'signal_handler') unless $on{$event}; 249 250 push(@{$on{$event}}, 251 { 252 'chatnet' => $chatnet || 'all', 253 'settings' => hash_to_settings(%settings), 254 'channel' => $channel, 255 'cmd' => $cmd, 256 } 257 ); 258} 259 260# Deletes all ons under the event 261sub del_on { 262 my $on = shift; 263 Irssi::signal_remove($on, 'signal_handler'); 264 return delete($on{$on}); 265} 266 267# This is nasty. 268# It would be nice if perl scripts could use Irssi's internal stuff for option 269# parsing 270sub option_parse { 271 my $text = shift; 272 my($last,%options,$cmd); 273 for(split(' ',$text)) { 274 if($cmd) { 275 $cmd .= " $_"; 276 }elsif(/^-(.+)$/) { 277 $last = 'channel' if $1 eq 'channel'; 278 $options{$1}++; 279 }elsif(/^["'0-9]/) { 280 if(/^\d+$/) { 281 $options{event} = "event $_" if /^\d+$/; 282 }else{ 283 $last = 'event'; 284 s/^['"]//; 285 $options{event} = $_; 286 } 287 }elsif($last eq 'event'){ 288 $last = "" if s/['"]$//; 289 $options{event} .= " $_"; 290 }elsif($last) { 291 $options{$last} = $_; 292 $last = ""; 293 }else{ 294 $cmd = $_; 295 } 296 } 297 return ($cmd,%options); 298} 299 300