Showing posts with label twitter API. Show all posts
Showing posts with label twitter API. Show all posts

Tuesday, June 19, 2012

The @FindMyPlane bot

A few days ago I wrote about the Genexus Challenge developer edition and my first Smart Devices app, Find my Plane. Once I get, what I think is, a good idea in my head I get around it a lot to improve it in every possible way. But this is not the case, sort of.

Right after I deployed the app to the Apple’s app store, I thought “how can I advertise it, without paying for advertising of course”, I needed people to find out about the app and download it, and a good word about it would be awesome too. So I thought about social networks… Facebook is for friends and I already told them to download it (did I mentioned it’s free?!) so twitter came to mind… but what can I do to promote my app from twitter, other than twitting about it of course.

So I thought of the @FindmyPlane bot and this post will tell you how I did it, not that it’s rocket science, but I found an interesting use of Windows Azure’s Worker Roles*. Wait, what?! you’re gonna tell people how to build a twitter spammer bot? No, let me get into that.

The @FindMyPlane twitter account works like this. You send a twit to @FindMyPlane with your flight number and find my plane will answer that tweet with useful info about it, the same kind of info you’d get in the Find My Plane app, but of course, only the info that fits in 140 characters.

So this is how it works. There’s a worker role (called Receiver) that every ten seconds access the twitter api looking for mentions for the @FindMyPlane account. Once it gets the list of tweets (if any of course) it saves them to a Table from Azure Storage and saves the latest tweet id in a Queue (also from Azure Storage) so the next time it just asks from that tweet on.

This is pretty much the code:

string lastTweet = "";
string previousMessageId = "";
foreach (CloudQueueMessage message in Queue.GetAllMessages(Constants.AZURE_STORAGE_ACCOUNT, Constants.AZURE_STORAGE_KEY, Constants.AZURE_QUEUE))
{
previousMessageId = message.Id;
lastTweet = message.AsString;
}

bool first = true;
foreach (Status status in Mentions.GetMentions(fmp, lastTweet))
{
try
{
Table.Insert(Constants.AZURE_STORAGE_ACCOUNT, Constants.AZURE_STORAGE_KEY, Constants.AZURE_TABLE, TweetEntity.FromStatus(status).ToString());
}
catch { }

if (first)
{
lastTweet = status.Id;
first = false;
}
}

if (!first)
{
Queue.DeleteMessage(Constants.AZURE_STORAGE_ACCOUNT, Constants.AZURE_STORAGE_KEY, Constants.AZURE_QUEUE, previousMessageId);
Queue.CreateMessage(Constants.AZURE_STORAGE_ACCOUNT, Constants.AZURE_STORAGE_KEY, Constants.AZURE_QUEUE, lastTweet);
}




And there’s a second Worker Role (called Replier) that every ten seconds queries the table where the tweets were saved for those that have not been replied yet and don’t have errors. For every tweet I try to get info of the flight number sent, if I do find info, I reply the tweet with that info and update the record on the table as replied. If I can’t find info, let’s say you tweeted “@FindMyPlane is awesome!” I update the record as ‘with errors’. This is just a way for me to know when I couldn’t reply because of an error on the system or because what I got was not a valid flight number.



Code here:



string query = "Replied eq 'False' and Error eq 'False'";
foreach (TableEntity entity in Table.Query(Constants.AZURE_STORAGE_ACCOUNT, Constants.AZURE_STORAGE_KEY, Constants.AZURE_TABLE, query))
{
string flightNumber = entity["Text"].ToUpper().Replace("@FINDMYPLANE ", "");
try
{
FlightInfo info = FlightStatus.GetFlightStatus(flightNumber);
string tweet = string.Format("@{0} {1}", entity["UserScreenname"], info);
Update.UpdateStatus(tweet, fmp, entity["Id"]);
entity["Replied"] = "True";

Table.UpdateEntity(Constants.AZURE_STORAGE_ACCOUNT, Constants.AZURE_STORAGE_KEY, Constants.AZURE_TABLE, entity);
}
catch
{
try
{
entity["Error"] = "True";
Table.UpdateEntity(Constants.AZURE_STORAGE_ACCOUNT, Constants.AZURE_STORAGE_KEY, Constants.AZURE_TABLE, entity);
}
catch
{
Table.DeleteEntity(Constants.AZURE_STORAGE_ACCOUNT, Constants.AZURE_STORAGE_KEY, Constants.AZURE_TABLE, entity.PartitionKey, entity.RowKey);
}
}
}



Cool uh?!



Here’s how all this works together



image



(*) for a good read on the Azure platform, Worker Roles and Storage (Table, Queues & Blobs) go to: http://bit.ly/SGAzure



You can download the ‘Find my Plane’ for Android and for iPhone.

Read Full Post

Wednesday, June 30, 2010

Introducing shelltwit



Some time ago I talked about a command line twitter client I was developing. Today I can proudly say it is finished… for now Smile
And I decided to upload the code to Codeplex GitHub for a few reasons. First of all it uses xAuth with .net with no extra library. Some may be asking “why would you want to do that if there are hundreds of libraries out there?”… well yeah but… no I don’t have an answer for that… I guess I just wanted to give it a try.
Also, while developing this tool I encountered some problems that some people does not ever find, specially people from English speaking countries. The problem appears when you want to twit say… “Peñarol Campeón!”. It took me a little while to discover the right encoding and as I said before, there’s not much written about it. (For more info take a look here)
And finally I wanted to share the code in case some soul out there wanted to give me a hand with the rest of the API. So far this tool only updates the status, but I’m also building a library (cleverly called shelltwitlib) where I’m intending to add every twitter API method.
As a bonus, if you use the shelltwitlib (since it works with xAuth) you’ll be able to have your tool displayed on your status like in the picture shown below.
ViaShelltwit
Want more? Bit.ly integration is also available Smile

works-on-my-machine-starburst_3_thumb[1]

Read Full Post

Monday, May 17, 2010

Encoding strings for the twitter API

gentleface.com free icon set Some time ago, I started to work on shelltwit a command line twitter updater (it only updates your status), but I didn’t want to use any existing library because I wanted to learn how to work directly to twitter API.

I looked around for some examples, the Twitter API doc is not well updated or complete, so it is not easy to start coding right away, you need to read a lot first (I hate when that happens). I found a good sample from Shannon Whitley called Twitter xAuth with .net. I started up with that code but I found an issue with international characters, like á, é or ñ, which kept me from posting about #Peñarol. So I started to hunt the bug, looked around online, went to the Twitter API user group and found out that there are a lot of issues with international characters. I found people form Brazil, Russia and Japan complaining about it. Apparently most libraries were written by english speaking developers so very few encounter the issue.

Now I can happily say that found the issue so I thought about posting the solution here.

Encoded strings (your twitter status) must be made to UTF8 according to RFC3986 and there’s no native .net function that does that, so after some researching I came up with an algorithm that does exactly that. So I hope it helps some one else.

static string UNRESERVED_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";

//http://en.wikipedia.org/wiki/Percent-encoding
//http://www.w3schools.com/tags/ref_urlencode.asp see 'Try It Yourself' to see if this function is encoding well
//This should be encoded according to RFC3986 http://tools.ietf.org/html/rfc3986
//I could not find any native .net function to achieve this
/// <summary>
///
Encodes a string according to RFC 3986
/// </summary>
/// <param name="value">
string to encode</param>
/// <returns></returns>
public static string EncodeString(string value)
{
StringBuilder sb = new StringBuilder();
foreach (char c in value)
{
if (UNRESERVED_CHARS.IndexOf(c) != -1)
sb.Append(c);
else
{
byte[] encoded = Encoding.UTF8.GetBytes(new char[] { c });
for (int i = 0; i < encoded.Length; i++)
{
sb.Append('%');
sb.Append(encoded[i].ToString("X2"));
}
}
}
return sb.ToString();
}

Read Full Post