Aus Linux-Magazin 09/2010

Börsenkurs-Alerting im Eigenbau

© akai, Photocase.com / Maksim Tselishchev, 123RF.com

Ein in Perl geschriebenes Pidgin-Plugin ignoriert dröge Börsentage, an denen die Kurse stillstehen, aber schlägt per Instant Message Alarm, falls die Aktien anfangen Achterbahn zu fahren .

Der Instant-Messenger-Client Pidgin [2] läuft nicht nur auf mehreren Betriebssystemen, sondern unterstützt auch eine Vielzahl IM-Protokolle: Ob Yahoo oder MSN, ob Google Talk oder IRC – das freie Pidgin bringt sie alle unter einen Hut und vereinfacht die Kommunikation mit Online-Freunden, die unterschiedliche Angebote nutzen. Pidgins Plugin-Architektur trägt maßgeblich dazu bei, dass der Tausendsassa mit neu auftauchenden oder sich ändernden Protokollen mithält und seinen funktionellen Abdeckungsradius stetig erweitert.

Dabei ist es nicht einmal notwendig, Plugins in Pidgins Muttersprache C zu schreiben und als Shared Libs bereitzustellen. Auch in Hochsprachen wie Perl geschriebene Skripte bindet er klaglos ein. Falls sie mit Pidgins Glib-basierter Eventloop zusammenarbeiten, dürfen sie sogar langwierige Operationen wie das Einholen von Webseiten ausführen, ohne dass Pidgins grafische Oberfläche zu ruckeln anfängt.

Es erscheint zunächst verrückt, dass ein C-Programm zusammen mit einem Perl-Skript im Springseiltakt in derselben Eventschleife hüpft. Wer sich aber erinnert, dass auch Perl letztlich nur ein Abstraktionslayer über einer C-Schicht ist und deshalb genauso gut Events einhängen und empfangen kann, dem erschließt sich die Logik.

Alarm bei Achterbahnfahrt

Das hier vorgestellte Pidgin-Plugin zeigt sich zunächst nicht, nimmt aber in regelmäßigen Abständen Kontakt mit einem Aktienticker-Service auf. Mit dessen Hilfe schaut es, ob eines der in einer Konfigurationsdatei abgelegten Papiere anfängt wild zu steigen oder kräftig an Wert zu verlieren. Tritt dies ein, öffnet das Plugin ein Kommunikationsfenster zum eingeloggten User nach Abbildung 1 und zeigt die Gesamtliste aller überwachten Aktien an, samt den einzelnen prozentualen Tagesänderungen.

Abbildung 1: Der Aktienmarkt-Wecker alarmiert den Anleger, weil die Google-Aktie um mehr als 2 Prozent gestiegen ist.

Abbildung 1: Der Aktienmarkt-Wecker alarmiert den Anleger, weil die Google-Aktie um mehr als 2 Prozent gestiegen ist.

Der User gibt in der Konfigurationsdatei nach Abbildung 2 vor, welche Tickersymbole ihn interessieren und ab welcher prozentualen Änderung er einen Weckruf wünscht. Bei Zeilen, die lediglich das Aktienkürzel und keine Prozentzahl vorgeben, nimmt das Plugin automatisch 2 Prozent Toleranz an.

Abbildung 2: In der Yaml-Datei setzt der User bestimmte Aktien auf eine Beobachtungsliste und gibt bei Bedarf Prozentpunkte vor.

Abbildung 2: In der Yaml-Datei setzt der User bestimmte Aktien auf eine Beobachtungsliste und gibt bei Bedarf Prozentpunkte vor.

Simple Erweiterung

Das Plugin in Listing 1 gestaltet sich mit 74 Zeilen recht simpel. Pidgin lädt es, falls das entsprechende Perl-Skript mit einer »pl«-Endung ausführbar im richtigen Verzeichnis liegt und der User es im Plugin-Menü konfiguriert hat (siehe Abschnitt “Installation”).

Listing 1:
»pidgin-stockwatch.pl«

01 #!/usr/local/bin/perl -w
02 use strict;
03
04 use Pidgin;
05 use lib local::lib;
06 use Glib;
07 use WatchQuotes;
08
09 our %PLUGIN_INFO = (
10   perl_api_version => 2,
11   name        => "Pidgin Stockwatch",
12   summary     => "Stock Alert via IM",
13   version     => "1.0",
14   author      => "Mike Schilli " .
15                  "<m@perlmeister.com>",
16   load        => "plugin_load",
17   unload      => "plugin_unload",
18 );
19
20 our $USER     = "mikeschilli";
21 our $PROTOCOL = "prpl-yahoo";
22
23 our $WATCH_QUOTES = WatchQuotes->new();
24
25 ###########################################
26 sub plugin_init {
27 ###########################################
28   return %PLUGIN_INFO;
29 }
30
31 ###########################################
32 sub plugin_unload {
33 ###########################################
34   my($plugin) = @_;
35
36   Purple::Debug::info("stockwatch",
37       "Plugin unloaded.n");
38
39   1;
40 }
41
42 ###########################################
43 sub plugin_load {
44 ###########################################
45   my($plugin) = @_;
46
47   Purple::Debug::info("stockwatch",
48       "Plugin loaded.n");
49
50   $WATCH_QUOTES->init();
51   $WATCH_QUOTES->watch( &quotes_update );
52 }
53
54 ###########################################
55 sub quotes_update {
56 ###########################################
57     my($msg) = @_;
58
59     Purple::Debug::info("stockwatch",
60         "Updating Quotes.n");
61
62     my $account = Purple::Accounts::find(
63         $USER, $PROTOCOL);
64
65     my $conv = Purple::Conversation->new(
66         1, $account, $USER);
67
68       # user not online?
69     return unless defined $conv;
70
71     $conv->get_im_data->write(
72         $PLUGIN_INFO{name}, $msg,
73         0, time );
74 }

Das Plugin muss die vom Pidgin-API vorgeschriebenen Callbacks »plugin_init()« und »plugin_load()« definieren. Ersteres springt Pidgin an, um Informationen über das Plugin einzuholen und es später im Menü »Tools | Plugins« anzuzeigen.

Der Zocker entscheidet per Mausklick, ob er das Plugin aktivieren möchte. Tut er dies, kommt der Callback »plugin_load« ab Zeile 43 zum Zug, der die Aufgaben des Plugins definiert. Deaktiviert ein entmutigter Anleger das Plugin wieder, erledigt es in »plugin_unload()« eventuell noch letzte Aufräumarbeiten, bevor Pidgin es aus dem Speicher wirft.

Das weiter unten beschriebene Modul »WatchQuotes« liest die Konfigurationsdatei, sobald Zeile 50 im Plugin dessen »init()«-Methode aufruft. Zeile 51 startet anschließend den kontinuierlich arbeitenden Überwachungsprozess mit dem Aufruf der Methode »watch()«. Ihr überreicht das Plugin-Skript eine Referenz auf den ab Zeile 55 definierten Callback »quotes_update«, der eine ihm übergebene Nachricht in eine IM-Konversation verpackt und dem angenehm oder unangenehm überraschten User schickt.

Dazu findet die Methode »find()« eines »Purple::Account«-Objekts in den Zeilen 62 und 63 den hoffentlich eingeloggten User, die Zeilen 65 und 66 bauen zu diesem eine Konversation auf, die sich in einem Objekt der Klasse »Purple::Conversation« manifestiert. Geht dabei etwas schief, zum Beispiel weil der festgelegte User gar nicht auf dem angegebenen Service eingeloggt ist, bricht Zeile 69 ab und die Nachricht bekommt nie das Licht eines turbulenten Börsentages zu sehen.

Geht aber alles gut, schickt die in Zeile 71 aufgerufene »write()«-Methode die Nachricht los und legt als Absender willkürlich den Namen des Plugins fest. Der letzte Parameter setzt mit der Perl-Funktion »time()« noch die aktuelle Uhrzeit, die Pidgin neben jeder Nachricht anzeigt.

Schnatter-Framework

Die Variablen »$USER« und »$PROTOCOL« in den Zeilen 20 und 21 definieren den im Alarmfall zu benachrichtigenden Usernamen und den dabei verwendeten IM-Service, im vorliegenden Fall »prpl-yahoo«, das ist Yahoos Messenger-Protokoll. »pidgin-stockwatch.pl« bindet in Zeile 7 »WatchQuotes« ein. Beim Implementieren dieses Moduls war zu beachten, dass es mit Pidgins Eventschleife zusammenarbeiten muss, damit auch während lange laufender Operationen, etwa dem Einholen einer Webseite, der Glib-Kern Mauseingaben des Users verarbeitet und Pidgin nicht etwa zur Salzsäule erstarrt.

Hierfür bieten sich mehrere Frameworks an. POE [3] haben schon mehrere Snapshots verwendet, weshalb diesmal eine neuere Entwicklung zu Ehren kommt, die die coolen Kids der Perl-Szene ganz aufgeregt schnattern lässt: das Framework Any Event [4]. Es bindet sich nicht direkt an eine bestimmte Eventschleife, sondern arbeitet mit einem halben Dutzend Implementierungen zusammen.

Universell verwendbar

Von Vorteil ist, dass sich so ein Modul wie »WatchQuotes.pm« ganz generisch implementieren lässt und später ohne Änderung auf allen denkbaren Eventschleifen und Plattformen läuft. Einzige Voraussetzung ist, dass das Hauptprogramm eine Referenz auf die verwendeten Any-Event-Objekte behält, was durch das Speichern der entsprechenden Objektreferenz in der globalen Variablen »$WATCH_QUOTES« geschieht.

Dieses Verfahren ist notwendig, da Pidgin die definierten Plugin-Callbacks nur kurz aufruft und deren lokale Variablen darum wieder verschwinden, sobald der Programmfluss wieder in das reguläre Biotop von Pidgin zurückkehrt.

Der Aktienwecker in Aktion

Noch ein weiterer Vorteil der lockeren Bindung durch Any Event: Das Modul »WatchQuotes.pm« lässt sich unabhängig von Pidgin testen, etwa mit dem Skript in Listing 2. Es erzeugt eine Instanz der Klasse »WatchQuotes« und ruft die Methode »init()« auf, mit der »WatchQuotes« die vom User definierte Konfigurationsdatei »~/.pidgin-stockwatch.yml« einliest und intern in eine Perl-Datenstruktur umformt (Abbildung 3).

Abbildung 3: Perl formt aus der Yaml-Datei in Abbildung 2 eine für sich geeignete Datenstruktur.

Abbildung 3: Perl formt aus der Yaml-Datei in Abbildung 2 eine für sich geeignete Datenstruktur.

Listing 2:
»watch-quotes«

01 #!/usr/bin/perl -w
02 use strict;
03 use lib local::lib;
04 use AnyEvent;
05 use WatchQuotes;
06
07 my $watcher = WatchQuotes->new();
08 $watcher->init();
09 $watcher->watch( &callback );
10
11 my $quit_program = AnyEvent->condvar;
12 $quit_program->recv;
13
14 ###########################################
15 sub callback {
16 ###########################################
17     print "$_[0]n";
18 }

Als Beispiel einer Eventloop bringt Any Event eine in Perl implementierte Schleife mit, die das Testskript benutzt. Listing 2 definiert mit »condvar()« eine Bedingungsvariable, welcher der Any-Event-Kernel Nachrichten schicken kann. Zeile 12 wartet mit »recv()« auf eine solche Nachricht – die aber nie kommt – und lässt derweil den Any-Event-Kernel Events von Modulen wie »WatchQuotes« bearbeiten.

Das aufgerufene Testskript holt also in regelmäßigen Zeitabständen Börsenkurse ein und ruft den ab Zeile 15 definierten Callback mit einer Tabelle formatierter Stockquotes auf, falls der Kurs einer der überwachten Aktien die in der Konfigurationsdatei festgelegten Grenzwerte überschreitet. Die Funktion »print()« in Zeile 17 gibt die Nachricht jeweils auf Stdout aus. Das Skript läuft weiter, bis der User es mit [Ctrl]+[C] abbricht.

Das Testskript hilft etwaige Fehler in Ruhe zu lokalisieren, bevor das Skript in die feindliche Pidgin-Umgebung gerät, wo das Debuggen schwer ist, ganz besonders wenn Pidgin das Plugin aus irgendwelchen Gründen nicht korrekt lädt (siehe Kasten “RTFM, falls existent”). Das Modul »WatchQuotes.pm« selbst definiert in Listing 3 zunächst Perl-typisch einen Konstruktor »new()«, der lediglich das Homedirectory des Aktienspezies herausfindet und im Objekthash einige Standardwerte wie den Namen der Konfigurationsdatei festlegt.

RTFM, falls existent

Dokumentation über das Schreiben von Pidgin-Plugins ist rar. Zwar hat Pidgin-Chefentwicker Sean Egan ein Buch zum Thema “Building and Extending Gaim” [5] geschrieben, doch nicht nur der Name des Projekts hat sich seither geändert (Pidgin statt Gaim), sondern auch so ziemlich jeder Funktionsaufruf und jede Datenstruktur. Das an sich sehr gut geschriebene Werk taugt daher allenfalls zum Studium der Pidgin-Architektur.

Auch die Aktualität der Online-Dokumentation ([6], [7]) lässt schwer zu wünschen übrig. Die automatisch generierte Doxygen-Dokumentation ist, wie so oft, lieblos zusammengeschustert, unvollständig und damit für die Katz. Die effektivste Methode, um in der aktuellen Pidgin-Version einen Parameter zu einem Funktionsaufruf zu finden, ist das Studium real existierenden Plugins neuerer Bauart, zum Beispiel [8].

Listing 3:
»WatchQuotes.pm«

001 ###########################################
002 package WatchQuotes;
003 # Mike Schilli, 2010 (m@perlmeister.com)
004 ###########################################
005 use strict;
006 use warnings;
007 use AnyEvent;
008 use AnyEvent::HTTP;
009 use YAML qw(LoadFile);
010
011 ###########################################
012 sub new {
013 ###########################################
014   my($class, %options) = @_;
015
016   my ($home) = glob "~";
017
018   my $self = {
019       watcher => undef,
020       data    => {},
021       refdata => {},
022       conf_file =>
023         "$home/.pidgin-stockwatch.yml",
024       conf    => {},
025       %options,
026   };
027
028   bless $self, $class;
029 }
030
031 ###########################################
032 sub init {
033 ###########################################
034   my($self) = @_;
035
036   my $yml = LoadFile( $self->{conf_file} );
037
038   for my $e ( @$yml ) {
039     if( ref $e eq "HASH") {
040       my($key, $val) = %$e;
041       $val =~ s/%//g;
042       $self->{conf}->{ $key } = $val;
043     } else {
044         # 2% by default
045       $self->{conf}->{ $e } = 2;
046     }
047   }
048 }
049
050 ###########################################
051 sub watch {
052 ###########################################
053   my($self, $cb) = @_;
054
055   $self->{watcher} = AnyEvent->timer (
056     after    => 10,
057     interval => 300,
058     cb    => sub {
059       $self->fetch( $cb );
060     },
061   );
062 }
063
064 ###########################################
065 sub fetch {
066 ###########################################
067   my($self, $cb) = @_;
068
069   my $url = "http://" .
070    "download.finance.yahoo.com/d/" .
071    "quotes.csvr?e=.csv" .
072    "&f=spl1p2&s=" .
073    join('+', sort keys %{ $self->{conf} });
074
075   http_get($url, sub {
076     $self->parse_csv( $_[0] );
077     $self->check( $cb );
078   });
079 }
080
081 ###########################################
082 sub parse_csv {
083 ###########################################
084   my($self, $csv) = @_;
085
086   for my $line ( split /n/, $csv ) {
087
088     my($symbol, $prev, $last, $change) =
089       map { s/[^w.-]//g; $_ }
090       split /,/, $line;
091
092     next unless defined $symbol;
093
094     $symbol = lc $symbol;
095
096     $self->{data}->{$symbol} =
097       [ $prev, $last, $change ];
098   }
099 }
100
101 ###########################################
102 sub check {
103 ###########################################
104   my($self, $cb) = @_;
105
106   if(!scalar keys %{ $self->{refdata} }) {
107     $self->{refdata} = {%{$self->{data}}};
108   }
109
110   for my $stock (keys %{ $self->{data}} ) {
111     if( $self->noteworthy( $stock ) ) {
112       $self->{refdata} = {
113           %{$self->{data}} };
114
115         # reset 'prev'
116       for my $s (
117           keys %{$self->{refdata}} ) {
118         $self->{refdata}->{$s}->[0] =
119             $self->{data}->{$s}->[1];
120       }
121
122       $cb->( $self->message );
123       last;
124     }
125   }
126 }
127
128 ###########################################
129 sub message {
130 ###########################################
131   my($self) = @_;
132
133   my $msg = "n";
134
135   for my $stock (keys %{ $self->{data} }) {
136       my($prev, $last, $change) =
137          @{ $self->{data}->{$stock} };
138       $msg .= "$stock: $last $change%n";
139   }
140
141   return $msg;
142 }
143
144 ###########################################
145 sub noteworthy {
146 ###########################################
147   my($self, $stock) = @_;
148
149   my $price_ref =
150     $self->{refdata}->{$stock}->[0];
151
152   my $price_now =
153     $self->{data}->{$stock}->[1];
154
155   my $change_percent = abs(
156       ($price_now - $price_ref))/
157       $price_ref*100;
158
159   return($change_percent >
160          $self->{conf}->{$stock});
161 }
162
163 1;

Yaml für Mensch und Maschine

Die ab Zeile 32 definierte Methode »init()« liest die Konfiguration mit der Funktion »LoadFile()« ein, die aus dem Yaml-Modul vom CPAN stammt. Das Yaml-Format hat den Vorteil, dass es sowohl für Menschen wie Maschinen gleichermaßen leicht lesbar ist (Abbildung 2).

Die Applikation erlaubt sowohl einfache Array-Einträge der Art »- amzn« genauso wie kleine Hashes nach dem Muster »- goog: 2%«, die Yaml als Referenz auf einen Hash unter dem Array-Eintrag speichert (»”goog” => “2%”«). Zeile 39 prüft, ob Perls »ref«-Funktion das Wort »HASH« zurückliefert, was auf eine Hashreferenz hindeutet.

Kommt aber der Leerstring zurück, handelt es sich um einen einfachen Skalar und Zeile 45 setzt einen Standard-Schwellenwert von 2 Prozent an. Die überwachten Aktienkürzel speichert das Modul unter dem Eintrag »conf« im Objekthash und ordnet ihnen die konfigurierten prozentualen Triggerwerte zu.

Die Methode »watch« ab Zeile 51 erzeugt einen periodischen Timer. Der Parameter »after => 10« bestimmt, dass der Timer den unter »cb« definierten Callback genau 10 Sekunden nach dem Start aufruft. Die Verzögerung ist gewollt und nötig, falls das Plugin schon startet, bevor der User im Instant-Messaging-Netzwerk ansprechbar ist. Ohne diese Denkpause würden frühe Nachrichten in die ewigen Jagdgründe gehen statt zum Stockholder. Der Parameter »interval« gibt mit dem Wert 300 an, dass der Timer den Callback nach dem ersten Aufruf alle 5 Minuten erneut anspringt, um die neuesten Börsenkurse vom Yahoo-Server einzuholen und die Werte auf Ausreißer hin zu untersuchen.

Einklinken ins Event-Framework

Die der Methode »watch()« übergebene Referenz auf eine Callbackfunktion zeigt auf die Funktion »quotes_update()« des Hauptskripts. Die Methode »fetch()« bekommt die Referenz durchgereicht und holt sich die Webseite mit den Kursen. Das kann bei stürmischem Internetwetter ein paar Sekündchen dauern; doch die in Zeile 75 aufgerufene Funktion »http_get()« stammt aus dem Fundus des Moduls »AnyEvent::HTTP» vom CPAN und arbeitet den Request asynchron ab. Sie nimmt die URL für den Kursservice und einen Callback entgegen, den sie anspringt, sobald die Internetdaten vollständig eingetrudelt sind.

Zu beachten ist dabei, dass Perl den Programmfluss sofort nach dem Aufruf von »http_get()« fortsetzt, ohne dass zu diesem Zeitpunkt die angeforderten HTTP-Daten vorliegen.

Differenziert melken

Wie die Dokumentation des CPAN-Moduls »Finance::YahooQuote« [9] verrät, versteht der Kurslieferant eine Fülle von Parametern, von denen das Modul »WatchQuotes.pm« die in Tabelle 1 genannten auswählt: das Tickersymbol, den Vortageskurs, den aktuellen Wert sowie die prozentuale Änderung. Zu einem String kombiniert, konfrontiert »WatchQuotes« den Yahoo-Dienst also mit »spl1p2« plus »s=goog+yahoo«, falls der risikofreudige Beispieluser Google- und Yahoo-Aktien auf seinem Radar hat.

Zurück kommen die Daten im CSV-Format, also zwei Zeilen Text wie:

"GOOG",467.49,475.83,"+1.78%"
"YHOO",14.89,14.94,"+0.34%"

Aus ihnen formt der aus einfachen regulären Ausdrücken aufgebaute Parser in der Methode »parse_csv()« eine Datenstruktur. Im Hasheintrag »data« des »WatchQuotes«-Objekts steht dann eine Referenz auf ein Array, das jeweils den Vortageskurs, den letzten Kurs (üblicherweise um 20 Minuten verzögert) und die Änderung in Prozent enthält.

Tabelle 1:
Ticker-Parameter

 

Parameter

Bedeutung

s

Symbol

p

Letzter Schlusskurs

l1

Preis des letzten Trade

p2

Änderung in Prozent

Crash-Alarm schlagen oder nicht

Ob die bis dato erfolgten Kursschwankungen an der Börse das Alarmieren des Vorstadt-Kostolany rechtfertigen, prüft die Methode »check« ab Zeile 102. Auch sie bekommt den Callback weitergereicht, der den User per IM-Message zu kontaktieren vermag. Zeile 107 kopiert die aktuell eingelesenen Daten im Objekteintrag »{data}« in das Archiv unter »{refdata}«, damit der Code später eine Referenz zum Vergleich parat hat. Dabei reicht es nicht, die Referenz zu retten. Stattdessen kopiert »{ %{ $self->{data} } };« die hinter der Referenz »$self->{data}« stehenden Daten, formt daraus einen neuen Hash und gibt eine Referenz darauf zurück.

Lagen schon vor dem Aufruf Archivdaten vor, passt die For-Schleife ab Zeile 116 die Einträge für den Vortages-Kurs auf den aktuellen an. Schließlich soll der Code bei einer gestiegenen Aktie nicht alle 5 Minuten eine neue Nachricht schicken, sondern nur, falls sich der Kurs weiter verschiebt und erneut den eingestellten Schwellenwert überschreitet.

Die Methode »noteworthy()« prüft, ob ein Aktienkurs das eingestellte Limit gegenüber dem Vortageskurs (oder dem als Vortageskurs gesetzten Tageskurs, falls vorher schon ein Alarm ausgelöst wurde) gerissen hat. Die Methode »message()« ab Zeile 129 formatiert in diesem Fall die Daten aller überwachten Aktien zu einem Textstring und Zeile 122 ruft den Callback auf, der im Hauptprogramm den User per IM alarmiert.

Installation

Gängige Linux-Distributionen bieten Pidgin als Paket an, das auch die Perl-Schnittstelle schon enthält (Ubuntu: »pidgin« und »libpurple0«, beide in Version 2.7.0). Das Plugin-Skript »pidgin-stockwatch.pl« (Listing 1) wandert dann ausführbar nach »~/.purple/plugins« ins Homeverzeichnis des Börsenhais. Das Modul »WatchQuotes.pm« (Listing 3) gehört unter einen Pfad, den die lokale Perl-Installation findet. Notfalls weist der Skriptcode, wie in Zeile 7 von Listing 1 zu sehen, mit der Anweisung »use local::lib« auf die entsprechende Stelle im Dateisystem hin.

Die Module »AnyEvent« und »AnyEvent::HTTP« haben sich noch nicht bei allen bekannten Distributionen durchgesetzt – hier muss deshalb CPAN ran. Damit zusätzliche CPAN-Module nicht die sauberen Perl-Pakete des Linux-Package-Managers vollkleistern, installiert der ordnungsliebende User sie mittels des CPAN-Moduls »local::lib« unter dem Pfad »~/perl5« in seinem Home. Nach

perl Makefile.PL --bootstrap
make test && make install

in der »local::lib«-Distribution hängt der ordentliche Admin die Ausgabe von

perl -I$HOME/perl5/lib/perl5 -Mlocal::lib

an seine lokale ».bashrc«-Datei an. Nach einem Neustart der Shell (oder dem Sourcen der ».bashrc«-Datei) setzt »local::lib« eine Reihe von Environment-Variablen, die die CPAN-Shell anweisen zusätzliche Module unter »~/perl5« zu installieren. Die Variablen weisen auch aufgerufene Perl-Skripte auf zusätzliche Suchpfade hin. Nur Pidgin kriegt davon nichts mit und benötigt eine explizite »use local::lib«-Anweisung als Nachhilfe.

Ätzend für Entwickler

Das Plugin-Skript in Listing 1 lässt sich übrigens nicht ohne Pidgin starten und bewirft jeden, der es doch versucht, mit wirren Fehlermeldungen. Sie deuten an, dass Pidgin bestimmte Glib-Funktionen nicht in den entsprechenden Shared Libraries findet. Laut Pidgin-Programmierern ist das normal, aus Sicht anderer Entwickler freilich ätzend. Abhilfe schafft sich der findige Hacker, indem er die Zeilen »use Pidgin« und »use Glib« auskommentiert und wenigstens mit

perl -c pidgin-stockwatch.pl

prüft, ob die Syntax stimmt und das Skript das Modul »WatchQuotes.pm« sowie die darin aufgerufenen CPAN-Module findet. Wer Pidgin per »pidgin -d« im Debugmodus aufruft, kriegt Schnipsel wie

Purple::Debug::info("stockwatch",
  "Plugin loaded.n");

auf dem Bildschirm geworfen und sieht zumindest, was das Plugin treibt und ob Pidgin es überhaupt findet. Klappt dies und führt Pidgin zumindest die »plugin_init()«-Routine von Listing 1 ab Zeile 26 aus, erscheint das Plugin im Menü »Tools-Plugins« unter dem in Zeile 11 angegebenen Namen »Pidgin Stockwatch« (Abbildung 4). Ein Mausklick auf die linksseitige Checkbox aktiviert das Plugin und führt dessen »plugin_load()«-Routine aus. Testweise kann man das Plugin auch deaktivieren, worauf Pidgin »plugin_unload()« (Zeile 32) ausführt.

Abbildung 4: Ein Mausklick aktiviert das neu installierte Plugin, das Pidgin im »Plugins«-Menü anzeigt.

Abbildung 4: Ein Mausklick aktiviert das neu installierte Plugin, das Pidgin im »Plugins«-Menü anzeigt.

Anschließend füllt der Wertpapier-Heuschreck die Konfigurationsdatei »~/.pidgin-stockwatch.yml« mit den Tickersymbolen der interessierenden Aktien und weist ihnen Prozentwerte zu oder akzeptiert den voreingestellten Trigger bei 2 Prozent. Nach einem Neustart arbeitet Pidgin mit den aktualisierten Werten.

Euro- statt Dollar-Papiere

Statt amerikanischer Aktien wie in Abbildung 1 darf der Bausparer natürlich auch an deutschen Märkten gehandelte Werte angeben, zum Beispiel »SIE1.de«, für die Siemens-Aktie, deren Haupthandelsplatz die Xetra-Börse in Frankfurt ist. Die Chance, dass Siemens an einem Tag tierische Kurssprünge vollführt, fällt geringer aus als bei den Papieren von Yahoo oder Google. Doch falls es tatsächlich passiert, erfährt es der Perl-kundige Investor sofort und kann in Ruhe seine Panikkäufe und -verkäufe tätigen. (jk)

Infos

[1] Listings zu diesem Artikel: [ftp://www.linux-magazin.de/pub/listings/magazin/2010/09/Perl]

[2] Pidgin, der vielseitige Instant-Messenger-Client: [http://pidgin.im]

[3] Perl Object Environment: [http://poe.perl.org]

[4] Any Event: [http://software.schmorp.de/pkg/AnyEvent.html]

[5] Sean Egan, “Open source messaging application development: building and extending Gaim”, Berkeley: Apress; New York: Distributed by Springer-Verlag, 2005, ISBN 1-59059-467-3.

[6] Kurze Anleitung zum Schreiben von Pidgin-Plugins in Perl: [http://developer.pidgin.im/doxygen/dev/html/perl-howto.html]

[7] Beispiel-Plugin in Perl: [http://code.google.com/p/pidgin-knotifications/downloads/detail?name=knotifications.pl&can=2&q=]

[8] Weitere 3rd-Party-Plugins: [http://developer.pidgin.im/wiki/ThirdPartyPlugins#DevelopmentofThird-PartyPlugins]

[9] Finance::YahooQuote: [http://search.cpan.org/~edd/Finance-YahooQuote/]

Der Autor

Michael Schilli arbeitet als Software-Engineer bei Yahoo in Sunnyvale, Kalifornien. Er hat die Bücher “Goto Perl 5” (deutsch) und “Perl Power” (englisch) für Addison-Wesley geschrieben und ist unter [mschilli@perlmeister.com] zu erreichen.

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben