dimarts, 1 de novembre del 2011

Un "Piulador" amb Twitter4J amb gestió de l'autenticació

En el post anterior vaig fer un "piulador" personal que em permet enviar tweets (o piulades) al compte associat a l'Access Token.

Per obtenir l'Access Token i l'Access Token Secret va caldre demanar-los a la web de Twitter. Els valors que em va proporcionar Twitter  els vaig fer servir posant-los a l'objecte de la classe ConfigurationBuilder que passava com argument al constructor de l'objecte de la classe Twitter que és el que permet l'operació.

Un cop obtinguts l'Access Token i l'Access Token Secretm aquests dos valors els podrà fer servir el Piulador indefinidament. L'usuari pot, però, forçar que se'n generin de nous des de la web de Twitter. 

Per a que el "Piulador" el pugui fer servir tothom que tingui un compte a Twitter cal que sigui el mateix piulador qui el demani i en gestioni l'obtenció. Això és el que faré avui.

El codi del partida és el Piulador del post anterior:

package com.sticipl.proves;

import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.conf.ConfigurationBuilder;

public final class ProvaTwitter {
    /**
     * Usage: java twitter4j.examples.tweets.UpdateStatus [text]
     *
     * @param args message
     */
    public static void main(String[] args) {
        try {
         ConfigurationBuilder cb = new ConfigurationBuilder();
         cb.setDebugEnabled(true)
             .setOAuthConsumerKey("[El consumer Key obtingut]")
             .setOAuthConsumerSecret("[el consumer secret obtingut]")
             .setOAuthAccessToken("[l'access token obtingut]")
             .setOAuthAccessTokenSecret("[l'access token secret obtingut]");
         TwitterFactory tf = new TwitterFactory(cb.build());
         Twitter twitter = tf.getInstance();
            
            Status status = twitter.updateStatus("Aquest és el missatge enviat des de l'aplicació #Java de prova fent us de #Twitter4J");
            System.out.println("Actualitzat status a [" + status.getText() + "].");
            System.exit(0);
        } catch (TwitterException te) {
            te.printStackTrace();
            System.out.println("Excepció de Twitter: " + te.getMessage());
            System.exit(-1);
        }
    }
}

El que cal fer és canviar el bloc de codi del ConfigurationBuilder per un bloc de codi que en gestioni l'obtenció del parell Access Token / Access Token Secret.

Al post anterior vaig dir que el Consumer ha de demanar l'autorització de l'usuari amb un Request Token i que aquest Request Token es bescanvia per l'Access Token.  Com es fa això? A la secció dels exemples de codi de la web Twitter4J expliquen com:

    With OAuth authorization scheme, an application can access the user account without userid/password combination given. You need to register your application athttp://twitter.com/oauth_clients/new to acquire consumer key, and consumer secret in advance. key / secret pair can be set via Twitter#setOAuthConsumer(), or following system properties:

    -Dtwitter4j.oauth.consumerKey=[consumer key]
    -Dtwitter4j.oauth.consumerSecret=[consumer secret]

    Initially, you don't have a permission to access the user's account and need to acquire access token by redirecting the user to an authorization URL as follows:

      public static void main(String args[]) thrwos Exception{
        // The factory instance is re-useable and thread safe.
        Twitter twitter = new TwitterFactory().getInstance();
        twitter.setOAuthConsumer("[consumer key]", "[consumer secret]");
        RequestToken requestToken = twitter.getOAuthRequestToken();
        AccessToken accessToken = null;
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        while (null == accessToken) {
          System.out.println("Open the following URL and grant access to your account:");
          System.out.println(requestToken.getAuthorizationURL());
          System.out.print("Enter the PIN(if aviailable) or just hit enter.[PIN]:");
          String pin = br.readLine();
          try{
             if(pin.length() > 0){
               accessToken = twitter.getOAuthAccessToken(requestToken, pin);
             }else{
               accessToken = twitter.getOAuthAccessToken();
             }
          } catch (TwitterException te) {
            if(401 == te.getStatusCode()){
              System.out.println("Unable to get the access token.");
            }else{
              te.printStackTrace();
            }
          }
        }
        //persist to the accessToken for future reference.
        storeAccessToken(twitter.verifyCredentials().getId() , accessToken);
        Status status = twitter.updateStatus(args[0]);
        System.out.println("Successfully updated the status to [" + status.getText() + "].");
        System.exit(0);
      }
      private static void storeAccessToken(int useId, AccessToken accessToken){
        //store accessToken.getToken()
        //store accessToken.getTokenSecret()
      }

El que faig és adaptar aquest codi al meu piulador:


package com.sticipl.proves;

import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.PrintWriter;

import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.auth.AccessToken;
import twitter4j.auth.RequestToken;

public final class ProvaTwitter {
private Twitter twitter;
private BufferedReader br;
    /**
     * Usage: java twitter4j.examples.tweets.UpdateStatus [text]
     *
     * @param args message
     */
    public static void main(String[] args) {
        new ProvaTwitter();
    }
    
public ProvaTwitter() {
try {
            twitter = new TwitterFactory().getInstance();
            AutenticaConsumer();
            Status status = twitter.updateStatus("Aquest és el missatge enviat des de l'aplicació #Java de prova fent us de #Twitter4J. Versió 2");
            System.out.println("Actualitzat status a [" + status.getText() + "].");
            System.exit(0);
        } catch (TwitterException te) {
            te.printStackTrace();
            System.out.println("Excepció de Twitter: " + te.getMessage());
            System.exit(-1);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Excepció general: " + e.getMessage());
            System.exit(-2);        
        }
}
private void AutenticaConsumer() throws Exception{
twitter.setOAuthConsumer("[consumer key]", "[consumer key secret]");
RequestToken requestToken = twitter.getOAuthRequestToken();
AccessToken accessToken = null;
br = new BufferedReader(new InputStreamReader(System.in));
while (null == accessToken) {
System.out.print("Entra el nom d'aquest perfil d'accés a Twitter:");
String sNomPerfil = br.readLine();
System.out.println("Obre la següent URL a un navegador, identifica't amb el teu compte de twitter i autoritza al piulador:");
System.out.println(requestToken.getAuthorizationURL());
System.out.print("Entra el PIN(si està disponible) i prem enter:");
String pin = br.readLine();
try {
if(pin.length() > 0) {
accessToken = twitter.getOAuthAccessToken(requestToken, pin);
} else {
accessToken = twitter.getOAuthAccessToken();
}
    
//persist to the accessToken for future reference.
storeAccessToken(sNomPerfil, twitter.verifyCredentials().getId() , accessToken);
    
} catch (TwitterException te) {
if (401 == te.getStatusCode()) {
System.out.println("No ha pogut obtenir l'Access Token.");
} else {
te.printStackTrace();
}
}
}
}
    
    private  void storeAccessToken(String sNomPerfil, long l, AccessToken accessToken){
// guarda un fitxer amb:
    // nom del perfil a guardar
    // twitter.verifyCredentials().getId()
// guarda accessToken.getToken()
// guarda accessToken.getTokenSecret()
FileWriter fitxer = null;
PrintWriter pw = null;
try {
fitxer = new FileWriter("./twitter-access-token.txt");
pw = new PrintWriter(fitxer);
pw.println("Nom perfil: " + sNomPerfil );
pw.println("twitter.verifyCredentials().getId(): " + l );
pw.println("Access Token: " + accessToken.getToken() );
pw.println("Access Token Secret: " + accessToken.getTokenSecret() );
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != fitxer)
fitxer.close();
    } catch (Exception e2) {
     e2.printStackTrace();
    }
}      
    }    
}

I l'executo:

Des del main s'invoca al constructor , el qual, primer de tot, va al mètode AutenticaConsumer, que és el que fa la negociació OAuth. 

A AutenticaConsumer,  s'estableixen el Consumer Key i el Cosumer Key Secret i, a continuació s'obté un Request Token. 

Entra el nom d'aquest perfil d'accés a Twitter:sticipl

A continuació demana un nom de perfil d'accés al twitter per a identificar-lo en futures connexions. La idea és que guarda a un fitxer el nom del perfil i l'Access Token i l'Access Token Secret, de forma que per a futures connexions amb aquest perfil,  podria fer servir directament aquests valors emmagatzemats localment, en comptes d'haver de tornar a demanar accés.  En aquesta versió genera el fitxer, però no està implementada la  recuperació per perfil de l'Acces Token. No costaria gaire fer-ho, però potser millor amb una bd HSQLDB que amb un fitxer pla.

Amb el Request Token obté la URL d'autorització. L'usuari ha d'anar a aquesta URL, identificar-se amb el compte de Twitter amb el que vol fer servir el piulador, i autoritzar al Piulador a poder enviar missatges. 

Obre la següent URL a un navegador, identifica't amb el teu compte de twitter i autoritza al piulador:

http://api.twitter.com/oauth/authorize?oauth_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


Dit i fet, copio la URL i l'enganxo a un navegador. Vaig a la web d'Autorització de Twitter. Com que ja estava logat a Twitter amb el compte de sticipl, aquesta és el compte per al que s'autoritzarà el Piulador. Però podria haver sortit i haver-me logat de nou amb un compte diferent :





Faig click a Authorize App i...



La pàgina de Twitter em proporciona un PIN. El piulador estava esperant a que li indiqués aquest PIN:

Entra el PIN(si està disponible) i prem enter:6865240
 
Un cop autoritzat, el mètode AutenticaConsumer invoca al mètode storeAccessToken que genera el fitxer amb l'Access Token i l'Access Token Key per a usos futurs. El fitxer generat és una cosa com aquesta:

Nom perfil: sticipl
twitter.verifyCredentials().getId(): 177894606
Access Token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Access Token Secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

AutenticaConsumer acaba i torna al constructor, i el constructor fa la següent piulada:

Actualitzat status a [Aquest és el missatge enviat des de l'aplicació #Java de prova fent us de #Twitter4J. Versió 2]. 

Vet aquí el resultat:



Vet aquí, doncs, una versió del piulador amb autenticació OAuth gestionada des de la mateixa aplicació.

La web de Twitter4J ens ens proporciona codi d'exemple per a desenvolupar altres funcions del Twitter que aniré explorant en propers posts.

Cap comentari:

Publica un comentari a l'entrada