1#!/usr/local/bin/perl 2 3use strict; 4use LWP::Simple; 5use Irssi; 6use vars qw($VERSION %IRSSI); 7use HTML::Entities; 8 9$VERSION = '0.9'; 10%IRSSI = ( 11 authors => 'Marcus \'darix\' R�ckert, tira, Stefan \'tommie\' Tomanek', 12 contact => 'darix@irssi.de, tira@isx.de, stefan@pico.ruhr.de', 13 name => 'stocks', 14 description => 'prints the stats for german stocks', 15 license => 'Public Domain', 16 url => 'http://irssi.org/scripts/', 17 sbitems => 'stocks_ticker' 18); 19 20my %stocklist = ( 21 DE => 'FSE+DTB+MUN+HAM+HAN+BRE+STU+BER+ETR+DUS+FFM+FFI+FFK+FFC+DFK+FFT', 22 EU => 23'LIF+LCE+MSE+MAT+LME+NLK+ZRH+SFF+EOE+ROT+MEE+MIF+BBA+PSE+ASX+BSE+MSX+HSE+MIX+ISE+WSE+KSX+SSE+OSX+ENM+POP+BAS+BRN+MOP+SQ2+LTO+PAB+ISS+SQ1+ATH+LIS+LUX', 24 OTHERS => 25'FX1+IPE+CSC+FNX+TSE+PAR+TYO+OPR+IMM+FFX+IOM+SMX+HFE+TOR+TIF+OSE+MRF+MRV+TFE+TOC+BIS+TGE+CSH+ICP+NXC+IMC+CBC+HOX+OPA+CBQ+SON+MAS+RDW+BHL+AUS+SSW+ALB+COC+TOE+VSE+CBF+NAT+MBF+TWI+TGT', 26 US => 27'NYS+IND+NAS+CBT+CME+KBT+WPG+NYC+NYM+CMX+NYF+SFE+MIN+ASE+MAC+CEC+FOX+FRE+DOA+BOE+NAP', 28 ASE => 'ASE', 29 ASX => 'ASX', 30 ATH => 'ATH', 31 BER => 'BER', 32 BRE => 'BRE', 33 BSE => 'BSE', 34 C05 => 'C05', 35 CBT => 'CBT', 36 CME => 'CME', 37 DFK => 'DFK', 38 DTB => 'DTB', 39 DUS => 'DUS', 40 ENM => 'ENM', 41 ETR => 'ETR', 42 FFC => 'FFC', 43 FFI => 'FFI', 44 FFM => 'FFM', 45 FFT => 'FFT', 46 FSE => 'FSE', 47 FX1 => 'FX1', 48 HAM => 'HAM', 49 HAN => 'HAN', 50 IND => 'IND', 51 ISE => 'ISE', 52 ISS => 'ISS', 53 MIX => 'MIX', 54 MUN => 'MUN', 55 NAP => 'NAP', 56 NAS => 'NAS', 57 NYS => 'NYS', 58 PAR => 'PAR', 59 PSE => 'PSE', 60 SFF => 'SFF', 61 SON => 'SON', 62 SQ1 => 'SQ1', 63 SQ2 => 'SQ2', 64 SSE => 'SSE', 65 STU => 'STU', 66 TGT => 'TGT', 67 TWI => 'TWI', 68 WSE => 'WSE', 69 ZRH => 'ZRH' 70); 71 72my %stockhelp = ( 73 DE => 74'Deutschland (FSE DTB MUN HAM HAN BRE STU BER ETR DUS FFM FFI FFK FFC DFK FFT)', 75 EU => 76'Europa (LIF LCE MSE MAT LME NLK ZRH SFF EOE ROT MEE MIF BBA PSE ASX BSE MSX HSE MIX ISE WSE KSX SSE OSX ENM POP BAS BRN MOP SQ2 LTO PAB ISS SQ1 ATH LIS LUX)', 77 OTHERS => 78'Andere (FX1 IPE CSC FNX TSE PAR TYO OPR IMM FFX IOM SMX HFE TOR TIF OSE MRF MRV TFE TOC BIS TGE CSH ICP NXC IMC CBC HOX OPA CBQ SON MAS RDW BHL AUS SSW ALB COC TOE VSE CBF NAT MBF TWI TGT)', 79 US => 80'USA (NYS IND NAS CBT CME KBT WPG NYC NYM CMX NYF SFE MIN ASE MAC CEC FOX FRE DOA BOE NAP)', 81 ASE => 'AMEX', 82 ASX => 'Amsterdam', 83 ATH => 'Athen', 84 BER => 'Berlin', 85 BRE => 'Bremen', 86 BSE => 'Br�ssel', 87 C05 => 'LiveTrading', 88 CBT => 'CBoT', 89 CME => 'CME', 90 DFK => 'Fonds DE', 91 DTB => 'EUREX', 92 DUS => 'D�sseldorf', 93 ENM => 'EURO.NM', 94 ETR => 'XETRA', 95 FFC => 'Frankfurt', 96 FFI => 'FFM Indizes 2', 97 FFM => 'FFM Indizes 1', 98 FFT => 'Frankfurt STOXX', 99 FSE => 'Frankfurt', 100 FX1 => 'FOREX', 101 HAM => 'Hamburg', 102 HAN => 'Hannover', 103 IND => 'USA Indizes', 104 ISE => 'London', 105 ISS => 'London Inl.', 106 MIX => 'Mailand', 107 MUN => 'M�nchen', 108 NAP => 'Nasdaq OTC', 109 NAS => 'Nasdaq', 110 NYS => 'NYSE', 111 PAR => 'Int. Indizes', 112 PSE => 'Paris', 113 SFF => 'SOFFEX', 114 SON => 'Sonderwerte', 115 SQ1 => 'London Auslandsak.', 116 SQ2 => 'London Auslandsak.', 117 SSE => 'Stockholm', 118 STU => 'Stuttgart', 119 TGT => 'TD GT', 120 TWI => 'TD Indizes', 121 WSE => 'Wien', 122 ZRH => 'Z�rich' 123); 124 125my %WPArt = ( 126 STK => 'Aktie', 127 BND => 'Anleihe', 128 FND => 'Fonds', 129 FUT => 'Future', 130 IND => 'Index', 131 OPT => 'Option', 132 WNT => 'Optionsschein', 133 OTC => 'Over The Counter', 134 MSC => 'Sonstige', 135 SPC => 'Sonderwert', 136 CUR => 'W�hrung', 137 RTE => 'Zinssatz' 138); 139 140# search 141# -boerse {eu|de|us|others|} 142 143sub cmd_kurs { 144 my $XsearchWPArt = 'UKN'; 145 my $XsearchBoersen = 'UKN'; 146 my $params = shift (); 147 148 if ( $params =~ m/-help/ ) { 149 if ( $params =~ m/stocks/ ) { 150 Irssi::print( "\cBhelp for stocks\cB", MSGLEVEL_CRAP ); 151 foreach my $key ( sort keys %stockhelp ) { 152 Irssi::print( "$key - $stockhelp{$key}", MSGLEVEL_CRAP ); 153 } 154 } 155 else { 156 if ( $params =~ m/bonds/ ) { 157 Irssi::print( "\cBhelp for kind of bonds\cB", MSGLEVEL_CRAP ); 158 foreach my $key ( sort keys %WPArt ) { 159 Irssi::print( "$key - $WPArt{$key}", MSGLEVEL_CRAP ); 160 } 161 } 162 else { 163 Irssi::print( 164"\cBSTOCKS\cB [-stocks <stocklist>] [-bonds <wplist>] <querysting>", 165 MSGLEVEL_CRAP 166 ); 167 Irssi::print( "", MSGLEVEL_CRAP ); 168 Irssi::print( "\cBSee also:\cB", MSGLEVEL_CRAP ); 169 Irssi::print( " STOCKS -help stocks", MSGLEVEL_CRAP ); 170 Irssi::print( " STOCKS -help bonds", MSGLEVEL_CRAP ); 171 } 172 } 173 return; 174 } 175 176 while ( $params =~ m/-(\S+) (\S+)/ ) { 177 178 # Irssi::print($2." ".$3, MSGLEVEL_CRAP); 179 my $vars = $2; 180 my $option = $1; 181 if ( $option eq "stocks" ) { 182 my @stocks = split ( ',', $vars ); 183 for my $stock (@stocks) { 184 if ( exists $stocklist{$stock} ) { 185 $stock = $stocklist{$stock}; 186 } 187 else { 188 Irssi::print( "stock $stock does not exists see /STOCKS -help stocks", 189 MSGLEVEL_CRAP ); 190 return; 191 } 192 } 193 $XsearchBoersen = join ( "+", @stocks ); 194 } 195 else { 196 if ( $option eq "bonds" ) { 197 my @wps = split ( ',', $vars ); 198 for my $wp (@wps) { 199 if ( !exists $WPArt{$wp} ) { 200 Irssi::print( "Kind of bond $wp does not exists see /STOCKS -help bonds", 201 MSGLEVEL_CRAP ); 202 return; 203 } 204 } 205 $XsearchWPArt = join ( "+", @wps ); 206 } 207 else { 208 Irssi::print( "unknown option $option see /STOCKS -help", MSGLEVEL_CRAP ); 209 return; 210 } 211 } 212 $params =~ s/-(\S+) (\S+)//; 213 } 214 $params =~ s/\^s+//; 215 216 # Irssi::print($XsearchBoersen, MSGLEVEL_CRAP); 217 # Irssi::print($XsearchWPArt, MSGLEVEL_CRAP); 218 # Irssi::print($params, MSGLEVEL_CRAP); 219 if ( $params eq "" ) { 220 Irssi::print( "empty query string see /STOCKS -help", MSGLEVEL_CRAP ); 221 return; 222 } 223 my $searchfor = $params; 224 $searchfor =~ s/ /\%20/g; 225 my $host = "http://informer2.comdirect.de"; 226 my $path = '/de/suche/main.html?'; 227 my $searchButton = 'Exakt'; 228 my $querystring = 229"&searchButton=$searchButton&XsearchWPArt=$XsearchWPArt&XsearchBoersen=$XsearchBoersen&searchfor=$searchfor"; 230 231 my $content = get( $host . $path . $querystring ); 232 233 my ( $oldcompany, $comp, $nbr, $boerse ) = ""; 234 235 $searchfor =~ s/\%20/ /g; 236 237 if ( $content =~ m/Suchbegriff/s ) { 238 if ( $content =~ m/Kurszeit/ ) { 239 Irssi::print( "\cB" . $searchfor . " found:\cB", MSGLEVEL_CRAP ); 240 $content =~ s/\ //g; 241 $content =~ m/Kurszeit.*?<\/tr>(.*?)<\/table>/s; 242 $content = $1; 243 while ( $content =~ 244m/<td.*?>(.*?)<\/td>.*?<td.*?>(.*?)<\/td>.*?<td.*?>(.*?)<\/td>.*?<td.*?>(.*?)<\/td>.*?/s 245 ) 246 { 247 $comp = $1; 248 $nbr = $3; 249 $boerse = $2; 250 decode_entities($comp); 251 decode_entities($nbr); 252 decode_entities($boerse); 253 if ($comp) { 254 Irssi::print( " " . $nbr . " " . $boerse . ": " . $comp, 255 MSGLEVEL_CRAP ); 256 $oldcompany = $comp; 257 } 258 else { 259 Irssi::print( 260 " " . $nbr . " " . $boerse . ": " . $oldcompany, 261 MSGLEVEL_CRAP ); 262 } 263 $content =~ m/<tr.*?>.*?<\/tr>(.*)/s; 264 $content = $1; 265 } 266 } 267 else { 268 Irssi::print( "\cBcould not find:\cB $searchfor", MSGLEVEL_CRAP ); 269 } 270 return; 271 } 272 273 if ( $content =~ 274m/<th width="99%" class="news">(.*?)<\/th>.*?<td.*?>WKN.*?class="sym">(\d+)/s 275 ) 276 { 277 Irssi::print( "\c_WKN " . $2 . " - " . $1 . "\c_", MSGLEVEL_CRAP ); 278 } 279 280 if ( $content =~ m/<td.*?>(Aktueller Kurs)<\/td>\s+<td>(.*?)<\/td>/s ) { 281 Irssi::print( " \cB" . $1 . ":\cB " . $2, MSGLEVEL_CRAP ); 282 } 283 284 if ( $content =~ m/<td.*?>Rücknahmepreis<\/td>\s+<td>(.*?)<\/td>/s ) { 285 Irssi::print( " \cBR�cknahmepreis:\cB " . $1, MSGLEVEL_CRAP ); 286 } 287 288 if ( $content =~ m/<td.*?>(Ausgabepreis)<\/td>\s+<td>(.*?)<\/td>/s ) { 289 Irssi::print( " \cB" . $1 . ":\cB " . $2, MSGLEVEL_CRAP ); 290 } 291 292 if ( $content =~ m/<td.*?>(Differenz)<\/td>\s+<td>(.*?)<\/td>/s ) { 293 Irssi::print( " \cB" . $1 . ":\cB " . $2, MSGLEVEL_CRAP ); 294 } 295} 296 297 298# added by Stefan 'tommie' Tomanek 299use vars qw{$ticker_shift $ticker_text $update_tag $refresh_tag}; 300 301sub get_stock { 302 my ($wkn, $exchange) = @_; 303 304 my $XsearchWPArt = 'STK'; 305 my $XsearchBoersen = $exchange; 306 307 my $searchfor = $wkn; 308 #$searchfor =~ s/ /\%20/g; 309 my $host = "http://informer2.comdirect.de"; 310 my $path = '/de/suche/main.html?'; 311 my $searchButton = 'Exakt'; 312 my $querystring = "&searchfor=".$wkn."&searchButton=Exakt&XsearchWPArt=STK&XsearchBoersen=".$exchange; 313 314 my $content = get( $host . $path . $querystring ); 315 316 my ( $oldcompany, $comp, $nbr, $boerse ) = ""; 317 318 my %stock; 319 if ( $content =~ 320m/<th width="99%" class="news">(.*?)<\/th>.*?<td.*?>WKN.*?class="sym">(\d+)/s 321 ) 322 { 323 $stock{'wkn'} = $2; 324 $stock{'company'} = $1; 325 } 326 327 if ( $content =~ m/<td.*?>(Aktueller Kurs)<\/td>\s+<td>(.*?)<\/td>/s ) { 328 $stock{'price'} = $2; 329 $stock{'price'} =~ s/ <small>.*<\/small>//; 330 } 331 if ( $content =~ m/<td.*?>(Differenz)<\/td>\s+<td>(.*?)<\/td>/s ) { 332 $stock{'diff'} = $2; 333 } 334 return %stock; 335} 336 337sub update_ticker { 338 fork_get(); 339} 340 341sub fork_get { 342 my ($rh, $wh); 343 pipe($rh, $wh); 344 my $pid = fork(); 345 if ($pid > 0) { 346 close $wh; 347 Irssi::pidwait_add($pid); 348 my $pipetag; 349 my @args = ($rh, \$pipetag); 350 $pipetag = Irssi::input_add(fileno($rh), INPUT_READ, \&pipe_input, \@args); 351 } else { 352 eval { 353 my $data = get_ticker_data(); 354 print($wh $data); 355 close($wh) 356 }; 357 POSIX::_exit(1); 358 } 359} 360 361sub pipe_input ($) { 362 my ($rh, $pipetag) = @{$_[0]}; 363 my @lines = <$rh>; 364 close($rh); 365 Irssi::input_remove($pipetag); 366 my $text = join("", @lines); 367 $ticker_text = $text; 368} 369 370sub shift_string { 371 my ($string, $pos) = @_; 372 my $first = substr($string, 0, $pos); 373 my $middle = substr($string, $pos); 374 return $middle.$first; 375} 376 377$ticker_shift = 0; 378sub show_ticker { 379 my ($item, $get_size_only) = @_; 380 my $ticker_string = $ticker_text; 381 unless ($get_size_only) { 382 $ticker_shift = 0 if ($ticker_shift >= length($ticker_string)); 383 } 384 my $max_width = Irssi::settings_get_int('stocks_ticker_max_width'); 385 my $ticker_text = shift_string($ticker_string, $ticker_shift); 386 $ticker_text = substr($ticker_text, 0, $max_width-3) if (length($ticker_text)+2 > $max_width); 387 $item->{min_size} = $item->{max_size} = length("$ticker_text")+2; 388 $ticker_text =~ s/\%/\%\%/g; 389 $ticker_text = '>'.$ticker_text.'%n>'; 390 $ticker_text =~ s/\(\-/\%R\(\-/g; 391 $ticker_text =~ s/\(\+/\%G\(\+/g; 392 $ticker_text =~ s/\)/\)\%n/g; 393 my $format = "{sb ".$ticker_text."}"; 394 $item->default_handler($get_size_only, $format, 0, 1); 395} 396 397sub ticker_redraw { 398 $ticker_shift++; 399 Irssi::statusbar_items_redraw('stocks_ticker'); 400} 401 402sub get_ticker_data { 403 my @stocks = split(/,/, Irssi::settings_get_str('stocks_ticker_stocks')); 404 my $tape=''; 405 foreach (@stocks) { 406 my ($wkn, $exchange, $name) = split(/\//, $_); 407 my %stock = get_stock($wkn, $exchange); 408 if ($name eq '') { $name = $stock{'company'}; }; 409 $tape = $tape.'| '.$name.': '.$stock{'price'}.'/'.$stock{'diff'}; 410 } 411 return $tape; 412} 413 414sub load_config { 415 Irssi::timeout_remove($update_tag); 416 Irssi::timeout_remove($refresh_tag); 417 $update_tag = Irssi::timeout_add(Irssi::settings_get_int('stocks_ticker_update_delay'), 'update_ticker', undef); 418 $refresh_tag = Irssi::timeout_add(Irssi::settings_get_int('stocks_ticker_scroll_delay'), 'ticker_redraw', undef); 419 update_ticker(); 420} 421 422Irssi::statusbar_item_register('stocks_ticker', 0, 'show_ticker'); 423Irssi::settings_add_int('misc', 'stocks_ticker_max_width', 20); 424Irssi::settings_add_int('misc', 'stocks_ticker_update_delay', 120000); 425Irssi::settings_add_int('misc', 'stocks_ticker_scroll_delay', 2000); 426Irssi::settings_add_str('misc', 'stocks_ticker_stocks', ''); 427 428Irssi::command_bind( 'stocks', 'cmd_kurs' ); 429Irssi::command_bind( 'stocks_ticker_update', 'load_config' ); 430 431load_config(); 432