|
Listing 1: |
|---|
01 #!/usr/local/bin/perl -w
02 use strict;
03 use local::lib;
04
05 ###########################################
06 package YMBot;
07 ###########################################
08 use base qw( Bot::BasicBot );
09 use FindBin qw($Bin);
10
11 my $ymsend = "$Bin/ymsend";
12 my($home) = glob "~";
13 my $KEYWORD_LIST_FILE =
14 "$home/.irc2ym-keywords";
15 my @KEYWORD_LIST = ();
16
17 keyword_list_read();
18
19 ###########################################
20 sub said {
21 ###########################################
22 my($self, $data) = @_;
23
24 if( keyword_match( $data->{body} ) ) {
25 my $rc = system($ymsend,
26 "$data->{who} said: '$data->{body}'");
27 warn "$ymsend failed: $!" if $rc;
28 }
29
30 return $data;
31 }
32
33 ###########################################
34 sub keyword_list_read {
35 ###########################################
36 if( !open FILE, "<$KEYWORD_LIST_FILE" ) {
37 warn "$KEYWORD_LIST_FILE not found";
38 return;
39 }
40
41 while(<FILE>) {
42 chomp;
43 s/#.*//;
44 next if /^s*$/;
45 push @KEYWORD_LIST, $_;
46 }
47 close FILE;
48 }
49
50 ###########################################
51 sub keyword_match {
52 ###########################################
53 my($said) = @_;
54
55 for my $regex (@KEYWORD_LIST) {
56 return 1 if $said =~ /$regex/i;
57 }
58 return 0;
59 }
60
61 ###########################################
62 package main;
63 ###########################################
64 use Bot::BasicBot;
65
66 my $bot = YMBot->new(
67 server => "irc.freenode.com",
68 channels => ["#ymtest"],
69 nick => "ymbot",
70 name => "Relay to Y!M",
71 charset => "utf-8",
72 );
73
74 $bot->run();
|
In diesem Callback ruft der Bot in Zeile 24 die weiter unten definierte Funktion »keyword_match()« auf, die den Nachrichtentext mit einer Liste vorher eingelesener Schlüsselwörter aus der Datei »~/.irc2ym-keywords« vergleicht. Das Skript liest deren Zeilen zu Beginn in den globalen Array »@KEYWORD_LIST« ein.
Passt einer der im Array »@KEYWORD_LIST« gespeicherten regulären Ausdrücke, triggert Zeile 25 das im selben Verzeichnis liegende Skript »ymsend«. Dieses nimmt den Nachrichtentext auf der Kommandozeile entgegen, loggt sich auf dem Web-API ein, führt einige Autorisierungsschritte nach dem Oauth-Protokoll aus und schickt schließlich den Nachrichtentext an den in der Variablen »$recipient« kodierten User.
Die Interaktion mit dem Yahoo-Web-API erfordert vom Skript einige Bocksprünge mit dem voreingestellten Messenger-User, dessen Passwort sowie einem auf [developer.yahoo.com] einzuholenden API-Key und einem “Shared Secret” für die Applikation.
Oauth-Dschungel
Mit Oauth [4] gibt ein authentifizierter User einen Token an eine Applikation weiter, die dann in seinem Namen für eine bestimmte Zeit Aktionen ausführen kann. Im Fall des Messengers berechtigt der Token die Applikation dazu, eine Stunde lang Nachrichten ins IM-Netzwerk zu senden und Antworten zu empfangen [5]. Da das Skript aber selten anspringt und sich nach dem Abschicken der Nachricht gleich wieder beendet, brächte die Speicherung des Token kaum Vorteile. So authentifiziert es sich auf Yahoos Login-Seite jedes Mal kurzerhand neu mit Username und Passwort (hart kodiert als »$user« und »$password« in Listing 2), holt sich einen neuen Access-Token ab und führt damit den Sendebefehl auf dem Web-API aus.
|
Listing 2: |
|---|
001 #!/usr/local/bin/perl -w
002 use strict;
003 use LWP::UserAgent;
004 use Sysadm::Install qw(qquote);
005 use URI;
006 use JSON;
007
008 my $user = "zangzongzing";
009 my $passwd = "*********";
010 my $recipient = "mikeschi1li";
011
012 my $api_key = "******************";
013 my $secret = "*************";
014
015 my $login_url = "https://login.yahoo.com/WSLogin/V1/get_auth_token";
016 my $auth_token_url = "https://api.login.yahoo.com/oauth/v2/get_token";
017 my $session_url = "http://developer.messenger.yahooapis.com/v1/session";
018 my $message_url = "http://developer.messenger.yahooapis.com/v1/message/yahoo/$recipient";
019
020 my($msg) = join ' ', @ARGV;
021
022 die "usage: $0 message" unless
023 length $msg;
024
025 my $ua = LWP::UserAgent->new();
026
027 my $url = URI->new( $login_url );
028
029 $url->query_form(
030 login => $user,
031 passwd => $passwd,
032 oauth_consumer_key => $api_key );
033
034 my $resp = $ua->get( $url );
035
036 if( $resp->is_error() ) {
037 die "Can't get request token: ",
038 $resp->message(), " ", $resp->content();
039 }
040
041 my($request_token) =
042 ($resp->content() =~ /RequestToken=(.*)/);
043
044 $url = URI->new($auth_token_url);
045
046 $url->query_form(
047 oauth_consumer_key => $api_key,
048 oauth_nonce => int( rand 10000000 ),
049 oauth_signature => "$secret&",
050 oauth_signature_method => "PLAINTEXT",
051 oauth_timestamp => time(),
052 oauth_token => $request_token,
053 oauth_version => "1.0"
054 );
055
056 $resp = $ua->get( $url );
057
058 if( $resp->is_error() ) {
059 die "Can't get access token: ",
060 $resp->message(), " ", $resp->content();
061 }
062
063 my $u = URI->new();
064 $u->query( $resp->content() );
065 my %form = $u->query_form;
066
067 $session_url = URI->new( $session_url );
068
069 $session_url->query_form(
070 oauth_consumer_key => $api_key,
071 oauth_nonce => int( rand 10000000 ),
072 oauth_signature =>
073 "$secret&$form{oauth_token_secret}",
074 oauth_signature_method => "PLAINTEXT",
075 oauth_timestamp => time(),
076 oauth_token => $form{oauth_token},
077 oauth_version => "1.0"
078 );
079
080 $resp = $ua->post( $session_url,
081 Content_Type =>
082 'application/json; charset=utf-8',
083 Content =>
084 q[ {"presenceState" : 0,
085 "presenceMessage" : "I'm alive!" }] );
086
087 if( $resp->is_error() ) {
088 die "Can't get session: ",
089 $resp->message(), " ", $resp->content();
090 }
091
092 my $data = from_json( $resp->content() );
093
094 $message_url = URI->new( $message_url );
095
096 $message_url->query_form(
097 oauth_consumer_key => $api_key,
098 oauth_nonce => int( rand 10000000 ),
099 oauth_signature =>
100 "$secret&$form{oauth_token_secret}",
101 oauth_signature_method => "PLAINTEXT",
102 oauth_timestamp => time(),
103 oauth_token => $form{oauth_token},
104 oauth_version => "1.0",
105 sid => $data->{sessionId},
106 );
107
108 $resp = $ua->post( $message_url,
109 Content_Type =>
110 'application/json; charset=utf-8',
111 Content =>
112 '{"message" : ' . qquote($msg) . ' }'
113 );
114
115 if( $resp->is_error() ) {
116 die "Can't send message: ",
117 $resp->message(), " ", $resp->content();
118 }
|
In Zeile 34 von Listing 2 loggt das Skript den User mit »$user« und »$passwd« auf der in »$login_url« gespeicherten URL ein, von wo Yahoo im Body der Antwort einen Request-Token zurückschickt. Diesen leitet das Skript dann zusammen mit dem API-Key und einem zugehörigen Geheimstring »secret« an die nächste URL, also »$auth_token_url«, weiter, die daraus einen Access-Token »oauth_token« samt einem »oauth_token_secret« fabriziert. Die Antwort des Webservers kommt im Format »field=value&field=value…«, die das Skript in Zeile 64 einfach als »query« in ein URI-Objekt einspeist und die Methode »query_form()« dann parsen lässt – das funktioniert, da die Daten exakt wie in einer URL mit Formparametern vorliegen.
Die Kombination aus Token und Secret identifiziert die Applikation als vom User autorisiert gegenüber dem Webservice. Das Skript »ymsend« leitet sie dann an den Messenger-Webservice unter der »$session_url« weiter, die eine neue Messenger-Session einläutet und den User »$user« im Yahoo-Messenger-Netzwerk anmeldet. Geschieht das, sehen andere IM-Nutzer den User in ihren Buddy-Listen auftauchen und das Skript schickt ab Zeile 108 mit der Post-Methode die vorher per Kommandozeile hereingereichte Nachricht an den in »$recipient« definierten (und hoffentlich eingeloggten) Messenger-Nutzer ab.
Dieser letzte Schritt verlangt die Kodierung des Request im Json-Format nach:
{ message : "Die Nachricht" }
Was, falls der Nachrichtentext ebenfalls Anführungszeichen enthält, eine saubere Kodierung dieser Sonderzeichen erfordert, »qquote()« erledigt das.
Um einen Auth-Token mit Secret für die neu geschaffene Applikation, also das Skript »ymsend«, zu erzeugen, klickt sich der API-Entwickler auf [developer.yahoo.com] durch »My Projects« und »New Project« (Yahoo-Account erforderlich), woraufhin die Pop-up-Box in Abbildung 5 erscheint. Da es sich nicht um eine Webapplikation im Browser handelt, sondern um einen Desktop-Client, ist hier »Or an application using these APIs: BOSS, Contacts, Mail …« zu wählen.
Im daraufhin erscheinenden Formular verpasst der Entwickler der Applikation ein Namenskürzel, etwa »irc2ymessenger«, und fügt als Description ein paar erläuternde Worte ein. Die Auswahlbox »Kind of Application« ist auf »Client/Desktop« (nicht »Web-based«) zu stellen. Unter »Access Scopes« ist dann »This app requires access to private user data.« zu wählen und unter dem Wust der aufklappenden Unterpunkte die Option »Read/Write« unter »Yahoo! Messenger«.





