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/\&nbsp//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&uuml;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/&nbsp;<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