1use strict; 2use vars qw($VERSION %IRSSI %HELP); 3 4use Irssi 0 qw 5( 6 active_win server_find_tag signal_stop window_find_name parse_special 7); 8 9use Irssi::UI; 10use Irssi::TextUI; 11 12$VERSION = '1.1'; 13%IRSSI = 14( 15 'authors' => 'Marcin Rozycki', 16 'contact' => 'derwan@irssi.pl', 17 'name' => 'paste', 18 'description' => 'Pasting lines to specified targets, type "/paste -help" for help', 19 'license' => 'GNU GPL v2', 20 'modules' => '', 21 'url' => 'http://derwan.irssi.pl', 22 'changed' => '2018-07-14', 23); 24 25$HELP{'paste'} = <<EOF; 26PASTE [-help] [-c] [-q] [-msg | -notice] [-<server tag>] [<target>] [<indexes>] 27 28 -help: print this help 29 -c: enable colors 30 -q: quiet mode (pasted lines are not dispalyed) 31 -msg: sends messages as msg (as default) 32 -notice: sends messages as notice 33 -<server target>: sends messages to specified server 34 <target>: targets (separated with commas) 35 <indexes>: indexes of lines to paste ( separated with spaces) 36 37Examples: 38 39 /PASTE - pasting to active channel or query 40 /PASTE -c - pasting to active channel or query with colors 41 /PASTE -c 1 3-5 - pasting to active channel or query lines 1, 3, 4 and 5 42 /PASTE -notice - pasting to active item - messages sent as notice, not msg 43 /PASTE derwan - sends messages to derwan 44 /PASTE -ircnet -c derwan,#irssi - sends messages (with colors) to derwan and #irssi in IRCNet 45 46Paste window - indexes: 47 48 [0] [<index>] [<index from>-<index to>] 49 50Examples: 51 52 0 - cancel 53 4 - line 4 54 4 8 9 10 - lines 4, 8, 9, 10 55 4 8-10 - lines 4, 8, 9, 10 56 57Themes: 58 59 paste_normal - \$0 line 60 paste_reverse - \$0 line 61 paste_count - \$0 count, \$1 server tag, \$2 target 62 paste_input 63 paste_no_server - \$0 comment 64 paste_argument_missing - \$0 option, \$1 comment 65 paste_argument_unknown - \$0 option, \$1 comment 66 paste_nothing 67 68Your version is $VERSION - for updates visit $IRSSI{url} 69Mail bug reports and suggestions to <$IRSSI{contact}> 70EOF 71 72my ( $p ); 73 74# paste (str data, rec server, rec window) 75sub paste ($$$) 76{ 77 buf_destroy(); 78 79 $p = {}; 80 $p->{color} = 0; 81 $p->{cmd} = 'msg'; 82 $p->{quiet} = 0; 83 84 my $win = active_win(); 85 86 foreach my $arg ( split /\s+/, $_[0] ) 87 { 88 ( $arg eq '-help' ) and Irssi::print($HELP{'paste'}, MSGLEVEL_CLIENTCRAP), return; 89 ( $arg eq '-c' ) and $p->{color} = 1, next; 90 ( $arg eq '-q' ) and $p->{quiet} = 1, next; 91 ( $arg =~ m/^-(msg|notice)$/ ) and $p->{cmd} = $1, next; 92 ( $arg =~ m/^-(.*)$/ and !$p->{tag} ) and $p->{tag} = $1, next; 93 ( $arg =~ m/^([^-\s]*[^-\d]+[^\s]*)$/ and !$p->{target} ) and $p->{target} = $1, next; 94 ( $arg =~ m/^([1-9]\d*)$/ ) and $p->{l}->{$1} = 1, next; 95 if ( $arg =~ m/^([1-9]\d*)-(\d+)$/ and $1 <= $2 ) 96 { 97 map { $p->{l}->{$_} = $p->{buf}->[$_-1] } ( $1 .. $2 ); 98 next; 99 } 100 101 $win->printformat 102 ( 103 MSGLEVEL_CRAP, 'paste_argument_unknown', $arg, 'type /paste -help for help' 104 ); 105 buf_destroy(), return; 106 } 107 108 if ( !exists $p->{tag} or !defined $p->{tag} ) 109 { 110 if ( !ref $_[1] and !ref $win->{server} ) 111 { 112 $win->printformat 113 ( 114 MSGLEVEL_CRAP, 'paste_argument_missing', 'server tag', 'type /paste -help for help' 115 ); 116 buf_destroy(), return; 117 } 118 $p->{tag} = ( ref $_[1] ) ? $_[1]->{tag} : $win->{active}->{server}->{tag}; 119 } 120 elsif ( ! ref server_find_tag($p->{tag}) ) 121 { 122 $win->printformat 123 ( 124 MSGLEVEL_CRAP, 'paste_argument_unknown', $p->{tag}, 'not connected to that server' 125 ); 126 buf_destroy(), return; 127 } 128 129 unless ( exists $p->{target} and defined $p->{target} ) 130 { 131 if ( !ref $win->{active} or !$win->{active}->{name} ) 132 { 133 $win->printformat 134 ( 135 MSGLEVEL_CRAP, 'paste_argument_missing', 'target', 'type /paste -help for help' 136 ); 137 buf_destroy(), return; 138 } 139 $p->{target} = $win->{active}->{name}; 140 } 141 142 if ( buf_create() == 0 ) 143 { 144 $win->printformat 145 ( 146 MSGLEVEL_CRAP, 'paste_nothing' 147 ); 148 buf_destroy(), return; 149 } 150 151 foreach my $idx ( keys %{$p->{l}} ) 152 { 153 $p->{l}->{$idx} = $p->{buf}->[$idx-1]; 154 } 155 156 buf_destroy(), return if ( buf_flush() != 0 ); 157 158 $p->{win} = sprintf('paste.%d', (int(rand(9000))+1000)); 159 my $input = Irssi::Windowitem::window_create($p->{win}, 1); 160 $input->set_name($p->{win}); 161 $input->set_history($p->{win}); 162 $input->change_server(server_find_tag($p->{tag})); 163 164 my $width = $input->{width} - 8; 165 my $theme = 'normal'; 166 167 for ( my $idx = $#{$p->{buf}}; $idx >= 0; $idx-- ) 168 { 169 my $text = $p->{buf}->[$idx]->get_text(0); 170 $text = sprintf 171 ( 172 '%03d %'.( length($text) > $width ? '.'.($width-1).'s$' : '-'.$width.'s' ). 173 ' %03d', $idx+1, $text, $idx+1 174 ); 175 $input->printformat(MSGLEVEL_NOHILIGHT, 'paste_'.$theme, $text); 176 $theme = $theme eq 'normal' ? 'reverse' : 'normal'; 177 } 178 179 $input->printformat(MSGLEVEL_NOHILIGHT, 'paste_input'); 180 $input->set_active(); 181}; 182 183sub buf_create () 184{ 185 return unless ( defined $p and ref $p ); 186 187 my $win = active_win(); 188 return 0 unless ( ref $win ); 189 190 my $curline = $win->view()->{buffer}->{cur_line}; 191 return 0 unless ( ref $curline ); 192 193 for ( my $idx = 0; $idx < 100; $idx++ ) 194 { 195 last unless ( ref $curline ); 196 push @{$p->{buf}}, $curline; 197 $curline = $curline->prev(); 198 } 199 200 return ( $#{$p->{buf}} >= 0 ? 1 : 0 ); 201} 202 203sub buf_flush () 204{ 205 return unless ( defined $p and ref $p ); 206 207 my $serv = server_find_tag($p->{tag}); 208 209 unless ( ref $serv and $serv->{connected} ) 210 { 211 active_win()->printformat 212 ( 213 MSGLEVEL_CRAP, 'paste_no_server', $p->{tag} 214 ); 215 216 return -1; 217 } 218 219 my $count = 0; 220 foreach my $idx ( sort { $b <=> $a } ( keys %{$p->{l}} ) ) 221 { 222 if ( defined $p->{l}->{$idx} and ref $p->{l}->{$idx} and ++$count ) 223 { 224 if ( $p->{quiet} == 0 ) 225 { 226 my $cmd = sprintf 227 ( 228 '%s %s %s', $p->{cmd}, $p->{target}, convertstr($p->{l}->{$idx}->get_text($p->{color})) 229 ); 230 $serv->command($cmd); 231 } 232 else 233 { 234 my $raw = sprintf 235 ( 236 '%s %s :%s', ( $p->{cmd} eq 'msg' ? 'privmsg' : 'notice' ), $p->{target}, 237 convertstr($p->{l}->{$idx}->get_text($p->{color})) 238 ); 239 $serv->send_raw($raw); 240 } 241 242 } 243 } 244 245 if ( $count > 0 ) 246 { 247 active_win()->printformat(MSGLEVEL_CRAP, 'paste_count', $count, $p->{tag}, $p->{target}); 248 } 249 else 250 { 251 active_win()->printformat(MSGLEVEL_CRAP, 'paste_nothing'); 252 } 253 254 return $count; 255} 256 257# sig_send_command (str data, rec server, rec window) 258sub sig_send_command ($$$) 259{ 260 unless ( defined $p and ref $p and defined $p->{win} ) 261 { 262 return; 263 } 264 265 my $win = active_win(); 266 267 if ( $_[0] eq 0 ) 268 { 269 buf_destroy(), return; 270 } 271 272 if ( substr($_[0], 0, 1) eq parse_special('$K') ) 273 { 274 return; 275 } 276 277 unless ( ref $win and $win->{name} eq $p->{win} ) 278 { 279 return; 280 } 281 282 signal_stop (); 283 284 $win->destroy(); 285 delete $p->{win}; 286 287 foreach my $arg ( split /\s+/, $_[0] ) 288 { 289 if ( $arg =~ m/^(\d+)$/ and $1 > 0 ) 290 { 291 $p->{l}->{$1} = $p->{buf}->[$1-1]; 292 } 293 elsif ( $arg =~ m/^([1-9]\d*)-(\d+)$/ and $1 <= $2 ) 294 { 295 map { $p->{l}->{$_} = $p->{buf}->[$_-1] } ( $1 .. $2 ); 296 } 297 else 298 { 299 active_win()->printformat(MSGLEVEL_CRAP, 'paste_argument_unknown', $arg, 'type /paste -help for help'); 300 } 301 } 302 303 buf_flush(); 304 buf_destroy(); 305}; 306 307sub buf_destroy () 308{ 309 if ( defined $p and ref $p ) 310 { 311 @{$p->{buf}} = () if ( defined $p->{buf} and ref $p->{buf} ); 312 %{$p->{l}} = () if ( defined $p->{l} and ref $p->{l} ); 313 if ( defined $p->{win} ) 314 { 315 my $win = window_find_name($p->{win}); 316 $win->destroy if ( ref $win ); 317 } 318 undef ( $p ); 319 } 320} 321 322# convertstr (str text), str text 323# thanks for Stanislaw Halik <weirdo@blindfold.no-ip.com> 324sub convertstr ($) 325{ 326 if ( $_[0] ) 327 { 328 $_[0] =~ s/[\004]g\//\003\002\002/g; 329 $_[0] =~ s/[\004]\?\/+/\0030\002\002/g; 330 $_[0] =~ s/[\004]0\//\0031\002\002/g; 331 $_[0] =~ s/[\004]0/\0031\002\002/g; 332 $_[0] =~ s/[\004]1\//\0032\002\002/g; 333 $_[0] =~ s/[\004]1/\0032\002\002/g; 334 $_[0] =~ s/[\004]2\//\0033\002\002/g; 335 $_[0] =~ s/[\004]2/\0033\002\002/g; 336 $_[0] =~ s/[\004]<\//\0034\002\002/g; 337 $_[0] =~ s/[\004]</\0034\002\002/g; 338 $_[0] =~ s/[\004]4\//\0035\002\002/g; 339 $_[0] =~ s/[\004]4/\0035\002\002/g; 340 $_[0] =~ s/[\004]5\//\0036\002\002/g; 341 $_[0] =~ s/[\004]5/\0036\002\002/g; 342 $_[0] =~ s/[\004]6\//\0037\002\002/g; 343 $_[0] =~ s/[\004]6/\0037\002\002/g; 344 $_[0] =~ s/[\004]>\//\0038\002\002/g; 345 $_[0] =~ s/[\004]>/\0038\002\002/g; 346 $_[0] =~ s/[\004]:\//\0039\002\002/g; 347 $_[0] =~ s/[\004]:/\0039\002\002/g; 348 $_[0] =~ s/[\004]3\//\00310\002\002/g; 349 $_[0] =~ s/[\004]3/\00310\002\002/g; 350 $_[0] =~ s/[\004]\;\//\00311\002\002/g; 351 $_[0] =~ s/[\004]\;/\00311\002\002/g; 352 $_[0] =~ s/[\004]9\//\00312\002\002/g; 353 $_[0] =~ s/[\004]9/\00312\002\002/g; 354 $_[0] =~ s/[\004]=\//\00313\002\002/g; 355 $_[0] =~ s/[\004]=/\00313\002\002/g; 356 $_[0] =~ s/[\004]8\//\00314\002\002/g; 357 $_[0] =~ s/[\004]8/\00314\002\002/g; 358 $_[0] =~ s/[\004]7\//\00315\002\002/g; 359 $_[0] =~ s/[\004]7/\00315\002\002/g; 360 $_[0] =~ s/[\004]g\//\003\002\002/g; 361 $_[0] =~ s/[\004]g/\003\002\002/g; 362 $_[0] =~ s/[\004]8\//\003\002\002/g; 363 $_[0] =~ s/[\004]8/\003\002\002/g; 364 } 365 return $_[0]; 366} 367 368Irssi::theme_register 369([ 370 'paste_normal', '$0-', 371 'paste_reverse', '%c$0-%n', 372 'paste_input', '%7%r type indexes of lines to paste (type "0" for cancel or "/paste -help" for help): %8%n', 373 'paste_count', '%_Irssi%_: {hilight $0} line(s) have been pasted to {nick $2} in $1', 374 'paste_argument_unknown', '%_Irssi%_: Unknown option: {hilight $0} {comment $1}', 375 'paste_argument_missing', '%_Irssi%_: Not enough parameters given: $0 {comment $1}', 376 'paste_no_server', '%_Irssi%_: Not connected to specified server {comment $0}', 377 'paste_nothing', '%_Irssi%_: Nothing to paste', 378]); 379 380Irssi::signal_add_first('send command', 'sig_send_command'); 381Irssi::command_bind('paste', 'paste'); 382