Twitteando desde C#: Enviar un tweet

Después del post anterior, ya tenemos claro en qué terreno nos movemos para twittear desde C# y podemos empezar a hacer cosas más interesantes, como enviar un tweet.

Os recuerdo que si queréis interactuar con Twitter de forma segura, con garantías y aprovechando todas las opciones que ofrece, es mejor que recurráis a librerías como Twitterizer o TweetSharp. El objetivo de esta serie de posts no es hacer un cliente de Twitter completo.

El API REST para enviar un Tweet

Para enviar un tweet debemos usar el API REST para actualizar el estado, que es el nombre técnico de twittear. Permite hacer muchas cosas, como añadir imágenes, geolocalización, etc., pero en nuestro caso nos vamos a limitar a lo más básico: enviar un texto.

Enviar un texto es muy sencillo, sólo necesitamos hacer una petición POST a la url http://api.twitter.com/1/statuses/update.xml e incluir como cuerpo de la petición status=mensaje. Podemos elegir cómo queremos la respuesta cambiando el final de la url entre .xml, .json o el resto de formatos soportados por Twitter.

El código para hacer esto es tal que así:

var tweet = "Soy un tweet muy salao".EncodeRFC3986();

var request = (HttpWebRequest) WebRequest.Create("http://api.twitter.com/1/statuses/update.xml");
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";

var requestBody = Encoding.ASCII.GetBytes("status={0}".FormatWith(tweet));
using (var stream = request.GetRequestStream())
	stream.Write(requestBody, 0, requestBody.Length);

request.GetResponse();

Es importante destacar dos cosas:

  • Los datos que enviamos deben ir codificados siguiendo el RFC 3986. De eso se encarga el extension method EncodeRFC3986 que veremos al final del post. Se trata de una codificación muy similar a la que ofrece Uri.EscapeDataString (RFC 2396), pero con algunas diferencias.
  • El código anterior no funciona. Si intentamos lanzar esa petición, obtendremos un precioso error 401 No Autorizado, porque necesitamos usar la autorización OAuth que veíamos en el post anterior.

Autorizando la petición con OAuth

Aquí es donde la cosa se pone interesante. Para autorizar la petición con OAuth debemos seguir el protocolo descrito en el RFC 5849 (3 RFCs distintos en los últimos 3 párrafos, esto no puede ser nada bueno). Como a nadie le gusta leerse RFCs, vamos a ver qué tenemos que hacer.

Para autorizar la petición con OAuth, tenemos que añadir una cabecera HTTP de autorización como ésta:

Authorization: OAuth param1="value1",param2="value2",...

Los valores de los parámetros deben ir codificados conforme al RFC 3986 del que hablábamos antes y son los siguientes:

  • oauth_timestamp: fecha y hora en que se envía la petición, en formato Unix, es decir, segundos transcurridos desde el 1 de Enero de 1970.
  • oauth_nonce: debe ser un valor único dentro del mismo timestamp. Permite detectar peticiones duplicadas y evitar así ataques por repetición, es decir, que alguien intercepte el mensaje y lo vuelva a enviar.
  • oauth_signature_method: algoritmo usado para generar la firma criptográfica de la petición. En nuestro caso usaremos HMAC-SHA1.
  • oauth_consumer_key: es uno de los tokens que generamos en el post anterior y que permite identificar a la aplicación que está realizando la petición.
  • oauth_access_token: otro de los tokens que generamos en el post anterior. Este representa la autorización de acceso de la aplicación al recurso, en este caso, la cuenta de Twitter.
  • oauth_signature: es la firma criptográfica del mensaje, generada con el algoritmo indicado en oauth_signature_method. Generarla tiene su historia y lo veremos en detalle un poco más adelante.
  • oauth_version: versión del protocolo OAuth que estamos usando. Nosotros usaremos 1.0

Con todo esto se crearía una cabecera HTTP que habría que añadir a nuestro request:

request.Headers.Add("Authorization", authorizationHeaderValue);

Pero antes de poder hacer eso, nos falta el último paso, calcular la firma.

Firmando la petición

La firma se calcula sobre una cadena de la forma:

method&url&parameters

  • method: es el método HTTP de la petición. En este caso es POST, pero podría ser cualquiera: GET, PUT, etc.
  • url: es la url a la que lanzamos la petición, sin el QueryString. Debe estar codificada con el RFC3986.
  • parameters: es una cadena que incluye los parámetros OAuth que veíamos antes (excepto la firma, claro está) y los parámetros de la petición. Recordad que si la petición es un POST, los parámetros son lo que va en el cuerpo de la petición, y si la petición es un GET, los parámetros son los que van en la URL. Todos esos parámetros, los OAuth y los de la petición, los ordenamos alfabéticamente y generamos una cadena de la forma param1=value1¶m2=value2.... Esa cadena se codifica con el RFC 3986 y ya tenemos todo lo que hace falta firmar.

Los más observadores os habréis dado cuenta de que hasta ahora hay dos tokens que generamos en el post anterior y que no hemos usado hasta ahora: Consumer Secret y Access Token Secret. Estos tokens no viajan con el mensaje (deben permaner secretos, ¿recuerdas?) y son los que se usan como clave para la firma criptográfica del mensaje.

La clave de firma se forma de la siguiente manera:

ConsumerSecret&AccessTokenSecret

Tanto el ConsumerSecret como el AccessTokenSecret se tienen que codificar con el RFC 3986 y la firma se debe convertir a base64, con lo que el código sería algo así:

var signatureKey = "{0}&{1}".FormatWith(CONSUMER_SECRET.EncodeRFC3986(), ACCESS_TOKEN_SECRET.EncodeRFC3986());
var sha1 = new HMACSHA1(Encoding.ASCII.GetBytes(signatureKey));

var signatureBytes = sha1.ComputeHash(Encoding.ASCII.GetBytes(dataToSign.ToString()));
var signature = Convert.ToBase64String(signatureBytes);

Al recibir la petición, el servidor puede comprobar la validez de la firma usando los tokens públicos que hemos enviado y verificar que la aplicación y el acceso están realmente autorizados. Esto es lo que hace que sea importante incluir todos los parámetros en orden alfabético a la hora de generar la firma, porque es lo que garantiza que el servidor pueda saber qué hemos firmado exactamente (no es lo mismo firmar la cadena param1&param2 que param2&param1).

Juntando las piezas

Para lo poco que estamos haciendo, el código es un poco largo y farragoso, así que he dejado el código en este gist porque me da un poco de vergüenza ponerlo aquí.

Hay que reconocer que queda feo por culpa de todo el baile parámetros OAuth, firmas y demás, pero ya lo arreglaremos más adelante. De momento hemos cumplido el primer objetivo: enviar un tweet. En el próximo post veremos cómo acceder a los tweets.

7 comentarios en “Twitteando desde C#: Enviar un tweet

  1. Pingback: Twitteando desde C#: El API de Twitter y OAuth

  2. Elver Florez dijo:

    Buenas tardes,
    Viejo estoy intentando hacer un aplicacion en C# para enviar tweets pero me sale este error
    ‘string’ no contiene una definición de ‘EncodeRFC3986’ ni se encontró ningún método de extensión ‘EncodeRFC3986’ que acepte un primer argumento de tipo ‘string’ (¿falta una directiva de uso o una referencia de ensamblado?)

    igual me pasa con FormatWith, HMACSHA1, HttpWebRequest y WebRequest.

    le agradezco su colaboracion

  3. EncodeRFC3986 y FormatWith son extension methods. Puedes encontrarlos al final del código en este enlace: https://gist.github.com/2905028

    En cuanto a HMACSHA1, HttpWebRequest y WebRequest, necesitaras añadir las referencias a los assemblies que los contienen y los using correspondientes.

    De todas formas, tienes el código limpio, completo y listo para usarse en el post sobre TinyTwitter.

  4. Pues poner como hacer par enviar un tweet con imagen usando statuses/update_with_media

    Gracias

  5. No lo tengo muy claro, la verdad.

    Supongo que habría que modificar el método WriteRequestBody de la clase RequestBuilder para que, en el caso de que se incluya una imagen, se escriba la imagen en el body de la petición como parte de un mutipart/form-data, pero no tengo muy claro como se hace eso (la especificación está aquí: http://www.ietf.org/rfc/rfc2388.txt)

    Además no sé exactamente cómo afecta eso a la generación de la firma de firma OAuth. Si te animas a hacerlo, puedes forkear el código desde github (https://github.com/jmhdez/TinyTwitter) y partir de ahí.

    Si no quieres complicarte mucho y te da igual usar una dependencia externa, yo usaría Twitterizer.

Comentarios cerrados.