1# by Stefan 'tommie' Tomanek <stefan@pico.ruhr.de>
2use strict;
3
4use vars qw($VERSION %IRSSI);
5$VERSION = "20030208";
6%IRSSI = (
7    authors     => "Stefan 'tommie' Tomanek",
8    contact     => "stefan\@pico.ruhr.de",
9    name        => "Xetra",
10    description => "brings the stock exchanges of the world to your irssi",
11    license     => "GPLv2",
12    changed     => "$VERSION",
13    sbitems     => "xetra",
14    commands	=> "xetra"
15);
16
17
18use Irssi 20020324;
19use Irssi::TextUI;
20use LWP::Simple;
21use vars qw($forked @ticker $shift $timer);
22
23sub draw_box ($$$$) {
24    my ($title, $text, $footer, $colour) = @_;
25    my $box = '';
26    $box .= '%R,--[%n%9%U'.$title.'%U%9%R]%n'."\n";
27    foreach (split(/\n/, $text)) {
28        $box .= '%R|%n '.$_."\n";
29    }
30    $box .= '%R`--<%n'.$footer.'%R>->%n';
31    $box =~ s/%.//g unless $colour;
32    return $box;
33}
34
35sub show_help() {
36    my $help=$IRSSI{name}." ".$VERSION."
37/xetra update
38    Retrieve new stock information for ticker
39/xetra get WKN/EXC
40    Retrieve data for stock <WKN> at stock exchange <EXC>
41";
42    my $text = '';
43    foreach (split(/\n/, $help)) {
44        $_ =~ s/^\/(.*)$/%9\/$1%9/;
45        $text .= $_."\n";
46    }
47    print CLIENTCRAP draw_box($IRSSI{name}." help", $text, "help", 1) ;
48}
49
50sub stock ($) {
51    if ($_[0] =~ /^(.*?)\/(.*?)$/) {
52	return $_[0];
53    } elsif ($_[0] =~ /^(.*?)\.(.*?)$/) {
54	return $_[0].'/'.$2;
55    } else {
56	my $exchange = Irssi::settings_get_str('xetra_default_stock_exchange');
57	return $_[0].'/'.$exchange if ($_[0] =~ /^\d+$/);
58	return $_[0].'.'.$exchange.'/'.$exchange;
59    }
60}
61
62sub get_stock ($$) {
63    my ($wkn, $exchange) = @_;
64    #my $data = get('http://informer2.comdirect.de/de/default/_pages/fokus/main.html?sSymbol='.$symbol);
65    my $data = get('http://informer2.comdirect.de/de/suche/main.html?nop=0&searchButton=Exakt&searchfor='.$wkn.'&XsearchBoersen='.$exchange);
66    my $stock;
67    if ($data =~ /&nbsp;WKN:&nbsp;(.*?)&nbsp;/) {
68	$stock->{wkn} = $1;
69    }
70    if ($data =~ /<th class="right">(&nbsp;|<img src="\/_common\/images\/pfeil_.*?\.gif" width=11 height=10 alt="">)&nbsp;(.*?)<\/th>/) {
71	$stock->{current} = $2;
72    }
73    if ($data =~ /<td align="right"><b><div class="color.*?"><nobr>&nbsp;(.*?)&nbsp;<\/nobr><\/div><\/b><\/td>/) {
74	$stock->{change} = $1;
75    }
76    if ($data =~ /Symbol:&nbsp;(.*?)</) {
77	$stock->{symbol} = $1;
78    }
79    if ($data =~ /B&ouml;rse:&nbsp;(.*?)&nbsp;/) {
80	$stock->{exchange} = $1;
81    }
82    if ($data =~ /<nobr>&nbsp;([0-9,+-]+\%)&nbsp;<\/nobr>/) {
83	$stock->{percent} = $1;
84    }
85    if ($data =~ /<td align="right">(\d+)\.(\d+)\.&nbsp;<\/td><td align="right">(\d+):(\d+)&nbsp;<\/td>/) {
86	$stock->{date} = $1.'.'.$2.'.';
87	$stock->{time} = $3.':'.$4;
88    }
89    return $stock;
90}
91
92sub bg_fetch ($$) {
93    my ($symbols, $job) = @_;
94    my ($rh, $wh);
95    pipe($rh, $wh);
96    return if $forked > 3;
97    $forked++;
98    my $pid = fork();
99    if ($pid > 0) {
100        close $wh;
101        Irssi::pidwait_add($pid);
102        my ($pipetag);
103        my @args = ($rh, \$pipetag, $job);
104        $pipetag = Irssi::input_add(fileno($rh), INPUT_READ, \&pipe_input, \@args);
105    } else {
106	my $result;
107	foreach (@$symbols) {
108	    $_ = stock($_);
109	    my ($wkn, $exchange) = split('/', $_);
110	    my $item = get_stock($wkn, $exchange);
111	    push @$result, $item;
112	}
113	my $dumper = Data::Dumper->new([$result]);
114	$dumper->Purity(1)->Deepcopy(1);
115	my $data = $dumper->Dump;
116	print($wh $data);
117	close($wh);
118	POSIX::_exit(1);
119    }
120}
121
122sub pipe_input ($$$) {
123    my ($rh, $pipetag, $job) = @{$_[0]};
124    $forked--;
125    my $text;
126    $text .= $_ foreach (<$rh>);
127    close($rh);
128    Irssi::input_remove($$pipetag);
129    return unless($text);
130    no strict;
131    my $result = eval "$text";
132    return unless ref $result;
133    if ($job eq 'ticker') {
134	@ticker = @$result;
135	Irssi::statusbar_items_redraw('xetra');
136    } elsif ($job eq 'get') {
137	foreach (@$result) {
138	    show_stock($_);
139	}
140    }
141}
142
143sub show_stock ($) {
144    my ($stock) = @_;
145    my $text;
146    $text .= '%9WKN:%9     '.$stock->{wkn}." (".$stock->{exchange}.")\n";
147    $text .= '%9Current:%9 '.$stock->{current}."\n";
148    $text .= '%9Change:%9  '.$stock->{change}.' ('.$stock->{percent}.")\n\n";
149    $text .= $stock->{date}.", ".$stock->{time}."\n";
150    print CLIENTCRAP &draw_box('Xetra stockinfo', $text, $stock->{symbol}, 1);
151}
152
153sub update_ticker {
154    my @stocks = split(/ /, Irssi::settings_get_str('xetra_ticker_stocks'));
155    bg_fetch(\@stocks, 'ticker');
156}
157
158sub show_ticker ($$) {
159    my ($item, $get_size_only) = @_;
160    $shift = 0 if $shift+1 > scalar(@ticker);
161    return unless defined $ticker[$shift];
162    my $tape;
163    $_ = $ticker[$shift];
164    $tape .= $_->{symbol}.': ';
165    $tape .= $_->{current}.' ';
166    if ($_->{change} =~ /^\+/) {
167	$tape .= '%g';
168    } elsif ($_->{change} =~ /^\-/) {
169	$tape .= '%r';
170    }
171    $tape .= $_->{change}.'%n';
172
173    $shift++ unless $get_size_only;
174    my $format = "{sb ".$tape."}";
175    $item->{min_size} = $item->{max_size} = length($tape);
176    $item->default_handler($get_size_only, $format, 0, 1);
177    Irssi::timeout_remove($timer);
178    $timer = Irssi::timeout_add(Irssi::settings_get_int('xetra_ticker_interval')*1000, 'update_ticker', undef);
179}
180
181sub cmd_xetra ($$$) {
182    my ($args, $server, $witem) = @_;
183    my @arg = split(/ /, $args);
184    return unless defined $arg[0];
185    if ($arg[0] eq 'get' && defined $arg[1]) {
186	shift(@arg);
187	bg_fetch(\@arg, 'get');
188    } elsif ($arg[0] eq 'update') {
189	update_ticker();
190    } elsif ($arg[0] eq 'help') {
191	show_help();
192    }
193}
194
195Irssi::statusbar_item_register('xetra', 0, 'show_ticker');
196
197Irssi::settings_add_str($IRSSI{name}, 'xetra_default_stock_exchange', 'ETR');
198Irssi::settings_add_str($IRSSI{name}, 'xetra_ticker_stocks', '');
199Irssi::settings_add_int($IRSSI{name}, 'xetra_ticker_interval', 3);
200
201Irssi::command_bind('xetra' => \&cmd_xetra);
202foreach my $cmd ('get', 'update', 'help') {
203    Irssi::command_bind('xetra '.$cmd => sub { cmd_xetra("$cmd ".$_[0], $_[1], $_[2]);});
204}
205
206$timer = Irssi::timeout_add(Irssi::settings_get_int('xetra_ticker_interval')*1000, 'update_ticker', undef);
207update_ticker();
208
209print CLIENTCRAP '%B>>%n '.$IRSSI{name}.' '.$VERSION.' loaded: /xetra help for help';
210