html/stocks.pl
1 #!/usr/bin/env perl
2
3 use strict;
4 use LWP::Simple;
5 use Irssi;
6 use vars qw($VERSION %IRSSI);
7 use 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
20 my %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
72 my %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
125 my %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
143 sub 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 =~
244 m/<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 =~
274 m/<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
299 use vars qw{$ticker_shift $ticker_text $update_tag $refresh_tag};
300
301 sub 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 =~
320 m/<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
337 sub update_ticker {
338 fork_get();
339 }
340
341 sub 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
361 sub 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
370 sub 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;
378 sub 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
397 sub ticker_redraw {
398 $ticker_shift++;
399 Irssi::statusbar_items_redraw('stocks_ticker');
400 }
401
402 sub 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
414 sub 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
422 Irssi::statusbar_item_register('stocks_ticker', 0, 'show_ticker');
423 Irssi::settings_add_int('misc', 'stocks_ticker_max_width', 20);
424 Irssi::settings_add_int('misc', 'stocks_ticker_update_delay', 120000);
425 Irssi::settings_add_int('misc', 'stocks_ticker_scroll_delay', 2000);
426 Irssi::settings_add_str('misc', 'stocks_ticker_stocks', '');
427
428 Irssi::command_bind( 'stocks', 'cmd_kurs' );
429 Irssi::command_bind( 'stocks_ticker_update', 'load_config' );
430
431 load_config();