Archive

Posts Tagged ‘twitter’

HTTP request through proxy in Erlang

July 5, 2010 Leave a comment

Last week I was testing at university  the code I used in the post XML and Erlang. Oddly the first few lines regarding the http request to the direct messages fell into an error. This seemed very strange, because testing the code at home everything was working fine.

The code was more or less the following:

receive_msg(User, Pwd) ->
    application:start(inets),
    Aut = make_auth(User, Pwd),
    http:request(get, {"http://twitter.com/direct_messages.xml",
		       [{"Authorization", Aut}]}, [], []).

After a while I came to the solution: at the university there is a proxy server!

In Erlang, if you are behind a proxy server, all of your requests should specify the proxy address and port, here is the previous function modified to support connection through proxy server:

receive_msg(User, Pwd, Proxy, Port) ->
    application:start(inets),
    http:set_options([{proxy, {{Proxy, Port}, ["localhost"]}}]),
    Aut = make_auth(User, Pwd),
    http:request(get, {"http://twitter.com/direct_messages.xml",
		       [{"Authorization", Aut}]}, [], []).

In the previous lines Proxy is the proxy addres, in my case “proxy.university.it” and Port is simply the port number.

After this correction the code was working fine and I could go further with the testing!

Categories: English, Erlang Tags: , ,

XML and Erlang

June 28, 2010 5 comments

As I told you many times, I’m working on this side project about XMPP and Twitter.

The main goal of the project is to provide a sort of interface between XMPP users and their Twitter accounts.

The first feature I would like to provide to users is the recovery of their private message: basically users registered on an XMPP server will have the opportunity to receive direct Twitter messages as normal message stanzas.

I showed you in one of my first posts (here you can find it in Italian) how to recover one user’s timeline, to retrieve the direct messages you just need to slightly adapt that code as follows:

1> application:start(inets).
2> Aut = lists:flatten(["Basic ", base64:encode("user:password")]).
[66,97,115,105,99,32,<<"dXNlcjpwYXNzd29yZA==">>]
3> {ok, {Status, Head, Body}} = http:request(get,
                {"http://api.twitter.com/1/direct_messages.rss",
                    [{"Authorization", Aut}]
                }, [], []).

where the variable Body will be bound to the actual HTML representing the content of the page.

Body will contain in this case a bunch of XML with all our direct messages, now what we need is a parser to extract the useful information: the sender of the message and the message itself.

Erlang provides, among the others, two useful modules to parse XML: xmerl and xmerl_scan; these modules can be very useful if you want to parse, validate or export to other formats your XML. By parsing an XML document you will get a record, displaying the structure of the document, as return value. The record also holds the data of the document and is defined in xmerl.hrl.

Ok, now let’s use play with them a little bit  by using the command xmerl_scan:string(Variable): its result (if the parsing was successful) is a tuple {XML, Misc}. Misc is the XML production which is the mark-up that comes after the element of the document.

4> {Xml, Misc} = xmerl_scan:string(Body).

Note that if you stored your XHTML/RSS somewhere as a file you can also use:

5> {Xml, Misc} = xmerl_scan:file(FileName).

Xml is an xmlElement record, with different fields (e.g. name, parents, attributes and content) those fields can be accessed as you usually do with record’s fields. Seems trivial that name of the element is in the name field, parents field is the names of the parent elements saved. Parents is just a list of tuples where the first element in each tuple is the name of the parent element. The list is in reverse order.

The record xmlAttribute contains the name and value of an attribute in the fields name and value. All attributes of an element is a list of xmlAttribute in the field attributes of the xmlElement record.

Taking a look to the actual XHTML page you will see that all of the messages are incapsulated as XML in the following form:

<item>
<title>Message from Sender to recipient</title>
<description>Text</description>
<pubDate>Publishing date</pubDate>
<guid>a link</guid>
<link>another link</link>
</item>

It seems quite obvious that the information we need from this XML are <description> and <title> elements , we can extract them in  this way:

6> Descriptions = xmerl_xpath:string("//item/description/text()", Xml).
7> Titles = xmerl_xpath:string("//item/title/text()", Xml).

Now we have two lists, one with the actual messages and one with the Senders.

If you are working on the shell as I am, you should import the record xmerl_scan:

8> rr(xmerl_scan).

while if you are writing you own module you should use:

-include_lib("xmerl/include/xmerl.hrl").

How can we access value insides the XML element now? It is quite triavial again! You can just do something like this:

9> [H|T] = Descriptions.
10> H#xmlText.value.
"Text of the message"

Ok, from now I leave you alone! Just try and retry!

Categories: English, Erlang Tags: , , ,

How to provide In-Band Registration to users

June 15, 2010 Leave a comment

As you should already know, I am currently working (when I have some free time) to an External Component for Jabber servers which is supposed to be a sort of interface to some Twitter functionalities that I’m not going to explain within this post.

We saw a couple of posts ago, that some information are mandatory in order to exploits Twitter APIs, so we implemented by using Mnesia and Erlang two functions for storing Twitter username and password of our customers; those data should be provided to our component by the user using In-Band Registration, the XEP-0077 of XMPP Protocol).

In few lines we can say that XEP-0077 is a method for in-band registration with instant messaging servers and associated services. In-band registration allows an entity to register with a host, cancel an existing registration with a host, or change a password with a host where the host can be either a server or service.

I strongly suggest you to read the extension’s specs on the web page; I’m not going to write them here since brevity is the soul of wit, and tediousness the limbs and outward flourishes”.

Even though I’m not writing the specs, I will post here the code I wrote for my robot (or at least a snippet of it) 😀

First of all when we receive an IQ with the namespace jabber:iq:register we reply one of the following ways: a form with the parameters to be filled in with the user’s data or the IQ containing the element <registered/>, followed by the data already provided in the previous registration.

Here is a code example:

handle_form_request(Session, IQ, NS) ->
    From = exmpp_jid:parse(exmpp_stanza:get_sender(IQ)),
    BareFrom = binary_to_list(exmpp_jid:prep_bare_to_binary(From)),

    case get_subscribtion(BareFrom) of
	notsubscribed ->
	    send_registration_fields(Session, IQ, NS);
	{subscribed, UId, _Pwd} ->
	    send_registration_form(UId, Session, IQ)
    end.

Where:

send_registration_form(UId, Session, IQ) ->
    Registered = exmpp_xml:element(?NS_INBAND_REGISTER, 'registered', [], []),
    Username = exmpp_xml:element(?NS_INBAND_REGISTER, 'username', [],
				 [{xmlcdata, list_to_binary(UId)}]),
    Password = exmpp_xml:element(?NS_INBAND_REGISTER, 'password', [],
				 [{xmlcdata, "empty due to security"}]),
    Result = exmpp_iq:result(IQ, exmpp_xml:element(?NS_INBAND_REGISTER,
                                         'query', [],
                                         [Registered, Username, Password])),
    exmpp_component:send_packet(Session, Result).

And:

send_registration_fields(Session, IQ, NS) ->
    Instructions = exmpp_xml:element(NS, 'instructions', [], [{xmlcdata,
                <<"Choose a username and password for use with this service.">>}]),
    Pwd = exmpp_xml:element(NS, 'password', [], []),
    User = exmpp_xml:element(NS, 'username', [], []),
    Result = exmpp_iq:result(IQ, exmpp_xml:element(NS, 'query', [],
                                        [Instructions, User, Pwd])),
    exmpp_component:send_packet(Session, Result).

Here is a screenshot of the service working:

In-Band registration process

In-Band registration process


All the other functionalities provided by In-Band Registration can be implemented with really little effort.
If you have any kind of problem or if you want to point out something feel free to contact me!

something more about HTTP requests and erlang

September 7, 2009 1 comment

Un paio di posts fa vi ho introdotto le API di Twitter e vi ho mostrato come ottenere la timeline di un utente. In tale post ho  utilizzato il modulo gen_tcp fornito da erlang, e utilizzato tale modulo per spedire le mie requests al web server di twitter.

In questo post cercherò di utilizzare il modulo http, che mette a disposizione il metodo request, questo modulo ci dovrebbe risparmiare un sacco di codice!

La prima cosa da fare quando si lavora con il modulo http  consiste nell’avviare l’applicazione inets, tale applicazione mette a disposizione tra le varie cose delle funzioni utili nella scrittura di client e server web.

Iniziamo quindi richiamando la nostra applicazione inets ed eseguiamo una richiesta tramite la funzione request/1 del modulo http. Tale richiesta ci permetterà ti recuperare gli ultimi posts provenienti dalla timeline pubblica. Essendo appunto tale timeline pubblica, non abbiamo bisogno di alcuna autenticazione.

Erlang (BEAM) emulator version 5.6.5  [async-threads:0]
[kernel-poll:false]

Eshell V5.6.5  (abort with ^G)
1> application:start(inets).
ok
2> http:request("http://twitter.com/statuses/public_timeline.xml").

Come specificato nel post precedente, nell’indirizzo utilizzato per la richiesta, possiamo specificare una diversa estensione tra quelle consentite, a seconda dei nostri gusti o bisogni.

Quale funzione dobbiamo utilizzare invece se vogliamo richiedere una timeline associata ad uno specifico utente? Beh in questo caso la procedura da seguire è la seguente:

Erlang (BEAM) emulator version 5.6.5  [async-threads:0]
[kernel-poll:false]

Eshell V5.6.5  (abort with ^G)
1> application:start(inets).
ok
2> Aut = lists:flatten(["Basic ", base64:encode("user:password")]).
[66,97,115,105,99,32,<<"dXNlcjpwYXNzd29yZA==">>]
3> http:request(get,
                {"http://twitter.com/statuses/user_timeline.xml",
                    [{"Authorization", Aut}]
                }, [], []).

Come vedete la funzione request/4 all’interno del modulo http ci permette di risparmiare un sacco di codice!

Tale funzione prende 4 parametri: il primo indica metodo che si vuole effettuare (nel nostro caso get, ma avrebbe potuto essere anche uno qualsiasi tra head, get, put, post, trace, options, delete come vedremo tra poco). il secondo parametro è una tupla rappresentante la richiesta vera e propria al web server. Tale richiesta è composta da una stringa che identifica la risorsa che vogliamo ottenere/modificare e da una lista di headers. Vi consiglio di andare alla pagina http://erlang.org/doc/man/http.html per avere maggiori informazioni.  🙂

Per autenticarci al server inseriamo un nuovo header di tipo Authentication e gli assegniamo il valore ottenuto dall’operazione di unione tra la stringa “Basic ” (ricordatevi di lasciare una spazio prima delle virgolette finali) e il risultato ottenuto dall’encoding delle nostre credentials.

Se avete fatto tutto come si deve, dovreste ottenere la timeline del utente che avete utilizzato per l’autenticazione, altrimenti riceverete un messaggio di errore dal server (come è capitato a me che avevo lasciato user:password 🙂 )

Gli ultimi due valori dati in ingresso alla funzione request/4 sono due liste vuote; la prima identifica le HTT Options (ad esempio un timeout per la richiesta o la versione dell’HTTP) mentre la seconda lista identifica altre options che vi invito a controllare nel link sopra riportato.

Ora vediamo qualcosa di diverso: proviamo ad aggiornare lo status di un utente!

La documentazione sulle API di Twitter dice che per aggiornare lo status di un utente, bisogna utilizzare nella richista il metodo POST e la URL http://twitter.com/statuses/update.format dove format può essere scelto fra xml e json.  A tale URL possono essere aggiunti altri parametri, il cui unico obbligatorio è status, che rappresenta l’effettivo status che vogliamo postare.

Se ad esempio volessimo modificare il nostro status in “erlang rules” dovremmo utilizzare il seguente codice:

Erlang (BEAM) emulator version 5.6.5  [async-threads:0]
[kernel-poll:false]

Eshell V5.6.5  (abort with ^G)
1> application:start(inets).
ok
2> Aut = lists:flatten(["Basic ", base64:encode("user:password")]).
[66,97,115,105,99,32,<<"dXNlcjpwYXNzd29yZA==">>]
3> http:request(post,{"http://twitter.com/statuses/update.xml",
       [{"Authorization", Aut}],
        "application/x-www-form-urlencoded",
         "status=erlang rules"}, [], []).

Come vedete alcune cose sono rimaste uguali e altre sono cambiate: rimane l’autorizzazione, e le liste vuote alla fine. Cambia il tipo di richiesta (si passa ad un post). E’ stata aggiunta una nuova stringa che specifica che si tratta di codice che va codificato come URL. Cosa più importante è stato aggiunto un body: in questo caso il parametro status che viene fatto coincidere con il la stringa erlang rules.

Molto semplice direi…la cosa che mi piace di questo modulo è che la nostra stringa viene codificata in URL senza grandi sforzi da parte nostra…infatti se volessimo fare lo stesso tipo di operazione tramite un gen_tcp send, dovremmo codificare da soli la nostra stringa in URL, ad esempio sostituendo ogni spazio con un %20.

Ultima cosa importante per chi vuole sperimentare: ricordatevi che la lunghezza del messaggio può essere al massimo 140 caratteri!

Categories: Erlang Tags: , ,

HTTP requests, Erlang and Twitter

September 1, 2009 Leave a comment

Una delle cosette a cui mi sto interessando ultimamente, sono le API di Twitter di cui potete trovare una completa descrizione a questo indirizzo: http://apiwiki.twitter.com/

Un’operazione che uno sviluppatore potrebbe voler eseguire, consiste nell’accedere alla timeline di un preciso utente, per fare questo dobbiamo in qualche modo avere un metodo di autenticazione verso Twitter.

Twitter.com

Originally from: Twitter.com

I due metodi messi a disposizione sono OAuth e Basic Auth: lasciamo stare OAuth al momento e vediamo come funziona un’autenticazione di tipo Basic Auth.

Internet è ricco di buone guide per capire quali sono i passi da eseguire in questo caso (e.g. http://en.wikipedia.org/wiki/Basic_access_authentication). Come potete vedere nel link precedente, l’autorizzazione di tipo Basic Auth non fa niente di più che aggiungere alla http request rivolta al server per una specifica risorsa, le credentials dell’utente, ovvero la stringa “utente:password” codificata in base64.

La richiesta che dobbiamo spedire alla porta 80 del server Twitter.com per ottenere gli ultimi 20 elementi postati da un utente è la seguente:

GET /statuses/user_timeline.rss HTTP/1.0
Host: localhost
Authorization: Basic dXNlcjpwYXNzd29yZA==

E’ interessante notare che possiamo ottenere questi 20 elementi incapsulati in diversi formati; nella richiesta precedente vogliamo ottenere un risultato in formato rss ma è possibile sostituire .rss con .json, .atom o .xml

Nella parte authorization, indichiamo che si tratta di un’autorizzazione di tipo basic è di seguito alleghiamo la stringa ottenuta codificando in base64 le credentials dell’utente (in questo caso user:password).

Un primo modo per testare questo processo è utilizzare telnet (se lo avete sul vostro sistema!).

Aprite il terminale ed eseguite:

telnet twitter.com 80
Trying 168.143.162.116...
Connected to twitter.com.
Escape character is '^]'.
GET /statuses/public_timeline.rss HTTP/1.0
Host: localhost
Authorization: Basic (qui mettete le vostre credentials criptate)

Dopo aver inserito le vostre credentials dovete premere invio 2 volte e magicamente otterrete sulla console gli ultimi 20 post dell’utente.

Ora il punto è: come fare tutto ciò in erlang?

Beh, accediamo alla erlang shell tramite il comando erl.

Erlang (BEAM) emulator version 5.6.5  [async-threads:0]
[kernel-poll:false]

Eshell V5.6.5  (abort with ^G)
1> base64:encode("user:password").
<<"dXNlcjpwYXNzd29yZA==">>

Il modulo base64 mette a dispozione la funzione encode che data una stringa da come risultato un binary ottenuto codificando la stringa in base64; il contrario dell funzione encode è la funzione decode.

A questo punto non ci resta che aprire una connessione verso Twitter!

2> {ok, S1} = gen_tcp:connect("www.twitter.com", 80, [binary, {active, false}]).
{ok,#Port<0.426>}
3> gen_tcp:send(S1, <<"GET /statuses/user_timeline.rss HTTP/1.0\r\n">>).
ok
4> gen_tcp:send(S1, <<"Host: localhost\r\n">>).
ok
5> gen_tcp:send(S1, <<"Authorization: Basic dXNlcjpwYXNzd29yZA==\r\n\r\n">>).
ok
6> gen_tcp:recv(S1, 0).

Nella riga numero 2 utilizziamo il modulo gen_tcp per aprire una connessione verso twitter.com alla porta 80; alcune flags sono state settate in questa funzione: binary, che ci dice che i pacchetti sono ricevuti come binaries e la tupla {active, false} che indica che i pacchetti saranno recuperati per mezzo del comando gen_tcp:recv/2 o gen_tcp:recv/3.

Come potete notare eseguiamo un pattern matching tra il risultato di questa funzione e la tupla {ok, S1} in questo modo leghiamo il socket ottenuto alla variabile S1 (N.B. se l’operazione non va a buon fine la funzione non ritornerà la tupla precedente e qundi ci sarà un errore nel pattern matching!).

Il comando gen_tcp:sent(Socket, binary) manda verso il socket (nel nostro esempio S1) un binary a nostra scelta e restituisce sempre l’atom ok.

Dopo aver spedito i nostri 3 binaries (notare che l’ultimo contiene un”\r\n” per indicare l’ulteriore a capo) usiamo la funzione gen_tcp:recv(Socket, 0) che ritorna il risultato della nostra operazione di get nei confronti del web server. Il numero 0 indica che vogliamo ottenere tutto ciò che il server ha risposto in un unico bulk (A volte capita che il bulk di dati non funzioni (mi e’ capitato) quindi potete eseguire piu volte l’operazione gen_tcp:recv/n.); gen_tcp:recv/3 differisce dalla precedente funzione in quanto si può specificare un timeout in millisecondi come terzo argomento, se nulla è ricevuto dopo Timeout, la funzione ritorna una tupla del tipo {error, timeout} (vi consiglio http://erlang.org/doc/man/gen_tcp.html per maggiori informazioni!).

Come possiamo racchiudere tutto cio’ in un modulo?

Eccovi una breve soluzione!

-module(twitter).
-export([start/0]).

start() ->
        {ok, S} = gen_tcp:connect("www.twitter.com", 80, [binary, {active,true}]),
        gen_tcp:send(S, <<"GET /statuses/user_timeline.rss HTTP/1.0\r\n">>),
        gen_tcp:send(S, <<"Host: localhost\r\n">>),
        gen_tcp:send(S, <<"Authorization: Basic (vostre credentials)\r\n\r\n">>),
        Result = recv(S),
        io:format("Result is: ~p~n", [Result]).

recv(S) ->
        recv(S, []).
recv(S, Bulk) ->
        receive
                {tcp, S, Data} ->
                        recv(S, [Bulk|Data]);
                {tcp_closed, S} ->
                        list_to_binary(Bulk)
        end.

La prima cosa da notare e’ che abbiamo cambiato nella funzione di connessione la flag active da false a true. Come prima mandiamo i nostri binaries al server e dopo di cio’ eseguiamo la funzione recv(S) che puo’ ricevere due diversi tipi di dato: pacchetti tcp dal socket o un messaggio di chiusura del socket.

Nel primo caso non facciamo altro che iterare nuovamente la funzione di ricezione aggiungendo alla nostra lista dei dati ricevuti (che in partenza e’ vuota) un nuovo binary. Quando riceviamo il messaggio di chiusura del socket prendiamo la lista di binaries che abbiamo accumulato in ricezione e la convertiamo in un unico binary.

Ecco, piu o meno questo e’ quando ho da dire sull’argomento…probabilmente mi sono dimenticato qualcosa..o forse ho scritto delle castronerie! 😀

In futuro vorrei scrivere sul modulo http di erlang e cercare tramite tale modulo di fare cio’ che ho fatto in questo post tramite il modulo gen_tcp…ma non garantisco nulla!

Categories: Erlang Tags: , , , ,