<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Corunet. El Blog &#187; php</title>
	<atom:link href="http://blog.corunet.com/tag/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.corunet.com</link>
	<description>Web development, usability and more</description>
	<lastBuildDate>Thu, 03 Nov 2011 18:30:49 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Twitter alerts: using twitter streaming API for fun and profit</title>
		<link>http://blog.corunet.com/twitter-alerts-using-twitter-streaming-api/</link>
		<comments>http://blog.corunet.com/twitter-alerts-using-twitter-streaming-api/#comments</comments>
		<pubDate>Fri, 23 Oct 2009 12:25:31 +0000</pubDate>
		<dc:creator>David Pardo</dc:creator>
				<category><![CDATA[English]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[Twitter]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://blog.corunet.com/?p=39</guid>
		<description><![CDATA[Twitter is a wonderful service, but, until now, you have to subscribe to some websites to be alerted when a selected word (maybe your trademark) is tweeted. We&#8217;ll try to develop a service that filters the tweeter api, stores the interesting ones in our database, and show them in the browser in real time. If [...]]]></description>
			<content:encoded><![CDATA[<h2><img class="alignleft size-thumbnail wp-image-74" title="twitter_alerts" src="http://blog.corunet.com/wp-content/uploads/2009/10/twitter_alerts-150x150.png" alt="twitter_alerts" width="150" height="150" /></h2>
<p>Twitter is a wonderful service, but, until now, you have to subscribe to some websites to be alerted when a selected word (maybe your trademark) is tweeted. We&#8217;ll try to develop a service that filters the tweeter api, stores the interesting ones in our database, and show them in the browser in real time.</p>
<p>If you want to try it, <a href="twitter-alerts-using-twitter-streaming-api/#video">watch it in action</a>, grab<a href="/twitter-alerts-using-twitter-streaming-api/#thecode"> the code</a> or read on&#8230;</p>
<p><strong>NOTE</strong>: Now you have to use https:// instead of http:// for it to work.</p>
<p><span id="more-39"></span></p>
<h2>What?</h2>
<p>We&#8217;ll take twitter real time results for a given word (or words) and visualize them in a browser window, like monitter.com, but on our own servers and a bit more automatic. This is going to be useful to get our own alerts and work with them.</p>
<h2>How?</h2>
<p>We are going to use the twitter stream API to get the comments containing a given word. Since the results are given in Json format, we&#8217;ll need to filter the data, take the interesting bits and store them. From the other side, we&#8217;re going to write a bit of javascript to read the data from the server into a web page using ajax. We could use other technologies, like comet, to push the data to the browser, but, while it would be a much cleaner implementation, I think I&#8217;m not ready to write about that yet (check this space <img src='http://blog.corunet.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> ). We need to decouple the reading of the stream from twitter and the serving to our clients because we can only have a single active  instance of the twitter streaming api at a given time, and we&#8217;re going to leave the listening proccess on for a long time using a daemon/service approach. On top of that, we want to store the tweets in a database for later perusal and data mining.</p>
<h2>The tools</h2>
<p>We are trying to make a useful system. To do this, we&#8217;ll need some tools:</p>
<ul>
<li>HTTP server:I&#8217;m currently using <a href="http://apache.org" target="_blank" onclick="pageTracker._trackPageview('/outgoing/apache.org?referer=');">Apache</a>, but any one would be OK</li>
<li>Server side programming language: This time, PHP. While probably not as elegant or fast as Python or as cool as Ruby, gets the work done and it&#8217;s available in almost all web hosting plans. And the documentation is pretty extensive.</li>
<li>A database to store all the info</li>
<li>Client side programming: We are going to use HTML, javascript and the jQuery javascript library to simplify AJA(X) programming and for the effects.</li>
<li>A twitter acount: Yours own is OK but maybe you want to create a new one for this kind of tasks. Write down your user and password, we are going to need them soon.</li>
<li>The twitter API. Twitter people are so kind they have developed a restful API free for everyone to use. And it&#8217;s sub-zero cool.</li>
</ul>
<h2>The Twitter streaming API</h2>
<p>Twitter has published a Streaming API that&#8217;s described as “The Twitter Streaming API allows near-realtime access to various subsets of Twitter public statuses”. In fact, this is jus what we need. You can read all the documentation at <a href="https://twitterapi.pbworks.com/Streaming-API-Documentation" onclick="pageTracker._trackPageview('/outgoing/twitterapi.pbworks.com/Streaming-API-Documentation?referer=');">https://twitterapi.pbworks.com/Streaming-API-Documentation</a>, but I&#8217;ll try to take the interesting parts for this project so you don&#8217;t need to yet.</p>
<p>We are going to use just one method (status/filter) to get results including one or several words. This method can return a stream of data in xml or json formats, has to be called using POST and can get some parameters. You can use it from the command line if you have access to some kind of unix in the following way:</p>
<p><code>curl -d 'track=google' https://stream.twitter.com/1/statuses/filter.json -uuser:password</code></p>
<p>Where user and password are your twitter credentials.</p>
<p>It should return something like:</p>
<p class="toggle_code"><a href="/uploads/code/response.json" onclick="jQuery('#code_0').toggle('slow');return false">response.json</a></p>
<div  style="display:none" id="code_0">
<pre class="brush: js">{&quot;text&quot;:&quot;RT @gillesguillemin: Google FINALLY Releases AS3 Player for YouTube http://bit.ly/yKgaz&quot;,
&quot;favorited&quot;:false,&quot;in_reply_to_user_id&quot;:null,&quot;in_reply_to_screen_name&quot;:null,&quot;source&quot;:&quot;web&quot;,&quot;geo&quot;:null,
&quot;in_reply_to_status_id&quot;:null,&quot;user&quot;:{&quot;friends_count&quot;:52,&quot;screen_name&quot;:&quot;imrahil&quot;,&quot;verified&quot;:false,
&quot;profile_background_color&quot;:&quot;9ae4e8&quot;,&quot;favourites_count&quot;:8,&quot;notifications&quot;:null,&quot;profile_text_color&quot;:&quot;000000&quot;,
&quot;description&quot;:&quot;&quot;,&quot;location&quot;:&quot;Poland&quot;,&quot;time_zone&quot;:&quot;Warsaw&quot;,&quot;profile_link_color&quot;:&quot;0000ff&quot;,&quot;following&quot;:null,
&quot;profile_background_image_url&quot;:&quot;http://s.twimg.com/a/1255558003/images/themes/theme1/bg.png&quot;,
&quot;profile_sidebar_fill_color&quot;:&quot;e0ff92&quot;,&quot;protected&quot;:false,&quot;url&quot;:&quot;http://flex.imrahil.com&quot;,
&quot;geo_enabled&quot;:false,&quot;profile_background_tile&quot;:false,&quot;name&quot;:&quot;Jarek&quot;,&quot;profile_sidebar_border_color&quot;:&quot;87bc44&quot;,
&quot;profile_image_url&quot;:&quot;http://a3.twimg.com/profile_images/76229939/avatar5_normal.jpg&quot;,&quot;id&quot;:5393872,
&quot;statuses_count&quot;:48,&quot;utc_offset&quot;:3600,&quot;created_at&quot;:&quot;Sun Apr 22 07:02:54 +0000 2007&quot;,&quot;followers_count&quot;:31},
&quot;id&quot;:4887052323,&quot;truncated&quot;:false,&quot;created_at&quot;:&quot;Thu Oct 15 12:10:57 +0000 2009&quot;}

{&quot;text&quot;:&quot;Google Wave\u2019s Little Secret: It Already Works On The iPhone&quot;,&quot;favorited&quot;:false,
&quot;in_reply_to_user_id&quot;:null,&quot;in_reply_to_screen_name&quot;:null,
&quot;source&quot;:&quot;&lt;a href=\&quot;http://www.atebits.com/\&quot; rel=\&quot;nofollow\&quot;&gt;Tweetie&lt;/a&gt;&quot;,&quot;geo&quot;:null,
&quot;in_reply_to_status_id&quot;:null,&quot;user&quot;:{&quot;friends_count&quot;:52,&quot;screen_name&quot;:&quot;Jwizzman&quot;,
&quot;verified&quot;:false,&quot;profile_background_color&quot;:&quot;000000&quot;,&quot;favourites_count&quot;:1,&quot;notifications&quot;:null,
&quot;profile_text_color&quot;:&quot;663B12&quot;,&quot;description&quot;:&quot;&quot;,&quot;location&quot;:&quot;Amsterdam, The Netherlands&quot;,
&quot;time_zone&quot;:&quot;Amsterdam&quot;,&quot;profile_link_color&quot;:&quot;1F98C7&quot;,&quot;following&quot;:null,
&quot;profile_background_image_url&quot;:&quot;http://a3.twimg.com/profile_background_images/20711299/background.png&quot;,
&quot;profile_sidebar_fill_color&quot;:&quot;DAECF4&quot;,&quot;protected&quot;:false,&quot;url&quot;:&quot;http://www.julianprofas.com&quot;,
&quot;geo_enabled&quot;:false,&quot;profile_background_tile&quot;:false,&quot;name&quot;:&quot;Julian Profas \uf8ff&quot;,
&quot;profile_sidebar_border_color&quot;:&quot;C6E2EE&quot;,
&quot;profile_image_url&quot;:&quot;http://a3.twimg.com/profile_images/458268969/foto_avatar_2_normal.png&quot;,
&quot;id&quot;:15727906,&quot;statuses_count&quot;:645,&quot;utc_offset&quot;:3600,&quot;created_at&quot;:&quot;Mon Aug 04 21:08:08 +0000 2008&quot;,
&quot;followers_count&quot;:79},&quot;id&quot;:4887052409,&quot;truncated&quot;:false,&quot;created_at&quot;:&quot;Thu Oct 15 12:10:58 +0000 2009&quot;}
</pre>
</div>
<p>Until you exit it (with CTRL+C) . This is a Json stream and can be read and parsed by several means. In fact, it&#8217;s eval-uable javascript code that we could read from the browser. But right now, we&#8217;re going to use a server-side language to read it and work with the interesting parts.</p>
<h2>Reading Twitter stream</h2>
<p>As I told you in the tools section, we are going to use PHP as our server side language. For the first part, the reading of the twitter stream, we don&#8217;t even need a web server, since we can run it from the command line. And if we run it from the command line, we can convert it to a kind of daemon/service and leave it on for a long time. But first, some code:</p>
<p class="toggle_code"><a href="/uploads/code/basicstreaming.php" onclick="jQuery('#code_1').toggle('slow');return false">basicstreaming.php</a></p>
<div  style="display:none" id="code_1">
<pre class="brush: php">&lt;?php
	$opts = array(
		&#039;http&#039;=&gt;array(
			&#039;method&#039;	=&gt;	&quot;POST&quot;,
			&#039;content&#039;	=&gt;	&#039;track=flickr&#039;,
		)
	);
	$context = stream_context_create($opts);
	$instream = fopen(&#039;http://USERNAME:PASSWORD@stream.twitter.com/1/statuses/filter.json&#039;,&#039;r&#039; ,false, $context);
	while(! feof($instream)) {
		if(! ($line = stream_get_line($instream, 20000, &quot;\n&quot;))) {
			continue;
		}else{
			//print_r(json_decode($line));
			$tweet = json_decode($line);
			print $tweet-&gt;{&#039;text&#039;}.&quot;\n&quot;;
			flush();
		}
	}
?&gt;
</pre>
</div>
<p>This is (almost) the simplest code that delivers what we want. A php formatted stream of tweets that include our marked word. If you run it from your command line, it&#8217;ll show something like:</p>
<p class="toggle_code"><a href="/uploads/code/sample_tweets.php" onclick="jQuery('#code_2').toggle('slow');return false">sample_tweets.php</a></p>
<div  style="display:none" id="code_2">
<pre class="brush: php">stdClass Object
(
    [favorited] =&gt;
    [in_reply_to_user_id] =&gt;
    [text] =&gt; Multiplexing and switching - sci.electronics.design | Google Groups: Multiplexing and switc.. http://bit.ly/m4nIq http://bit.ly/o0gO4
    [created_at] =&gt; Tue Oct 20 09:32:08 +0000 2009
    [geo] =&gt;
    [in_reply_to_screen_name] =&gt;
    [source] =&gt; &lt;a href=&quot;http://twitterfeed.com&quot; rel=&quot;nofollow&quot;&gt;twitterfeed&lt;/a&gt;
    [in_reply_to_status_id] =&gt;
    [user] =&gt; stdClass Object
        (
            [notifications] =&gt;
            [profile_text_color] =&gt; 1c0a1c
            [url] =&gt; http://www.managedserviceproviders.biz
            [time_zone] =&gt; Arizona
            [created_at] =&gt; Thu Aug 20 20:54:47 +0000 2009
            [profile_link_color] =&gt; 29ccf5
            [profile_background_image_url] =&gt; http://s.twimg.com/a/1255997062/images/themes/theme9/bg.gif
            [description] =&gt; Telecom Professional who enjoys helping businesses set up and manage their communications including voice, data and social media.
            [profile_sidebar_fill_color] =&gt; 361da3
            [profile_background_tile] =&gt;
            [following] =&gt;
            [profile_sidebar_border_color] =&gt; 0d0f12
            [geo_enabled] =&gt;
            [statuses_count] =&gt; 737
            [followers_count] =&gt; 237
            [protected] =&gt;
            [profile_image_url] =&gt; http://a3.twimg.com/profile_images/424262573/istock_000008181854xsmall-150x150_1__normal.jpg
            [location] =&gt; Phoenix, AZ
            [friends_count] =&gt; 236
            [name] =&gt; Jennifer Springston
            [verified] =&gt;
            [profile_background_color] =&gt; 1A1B1F
            [screen_name] =&gt; inTELEgentMSP
            [id] =&gt; 67421923
            [utc_offset] =&gt; -25200
            [favourites_count] =&gt; 0
        )

    [truncated] =&gt;
    [id] =&gt; 5015099767
)
stdClass Object
(
    [text] =&gt; 逛了一下午的教务处、鼓浪听涛、豆瓣和Google，寻找厦大比较能说的老师。结果发现大部分老师都不在漳州校区开课。
    [created_at] =&gt; Tue Oct 20 09:32:12 +0000 2009
    [in_reply_to_status_id] =&gt;
    [source] =&gt; &lt;a href=&quot;http://www.hellotxt.com/&quot; rel=&quot;nofollow&quot;&gt;HelloTxt&lt;/a&gt;
    [truncated] =&gt;
    [favorited] =&gt;
    [in_reply_to_user_id] =&gt;
    [user] =&gt; stdClass Object
        (
            [profile_background_image_url] =&gt; http://s.twimg.com/a/1255724203/images/themes/theme3/bg.gif
            [url] =&gt; http://www.jiakon.com
            [created_at] =&gt; Sat Jun 02 07:59:20 +0000 2007
            [profile_sidebar_fill_color] =&gt; E3E2DE
            [profile_background_tile] =&gt;
            [profile_sidebar_border_color] =&gt; D3D2CF
            [description] =&gt; Education,Student,Mathematics,Edubuntu
            [geo_enabled] =&gt;
            [statuses_count] =&gt; 2379
            [followers_count] =&gt; 186
            [friends_count] =&gt; 168
            [following] =&gt;
            [verified] =&gt;
            [profile_background_color] =&gt; EDECE9
            [favourites_count] =&gt; 8
            [protected] =&gt;
            [profile_image_url] =&gt; http://a1.twimg.com/profile_images/26783462/IMG0017A_normal.jpg
            [location] =&gt; Xiamen China
            [notifications] =&gt;
            [profile_text_color] =&gt; 634047
            [name] =&gt;  Xu Tom
            [time_zone] =&gt; Beijing
            [screen_name] =&gt; jiakon
            [id] =&gt; 6518512
            [utc_offset] =&gt; 28800
            [profile_link_color] =&gt; 088253
        )

    [id] =&gt; 5015100624
    [geo] =&gt;
    [in_reply_to_screen_name] =&gt;
)
stdClass Object
(
    [favorited] =&gt;
    [in_reply_to_user_id] =&gt;
    [text] =&gt; Google Wave&#039;s Best Use Cases - Wave - Lifehacker: http://bit.ly/1nCqlM
    [created_at] =&gt; Tue Oct 20 09:32:12 +0000 2009
    [geo] =&gt;
    [in_reply_to_screen_name] =&gt;
    [source] =&gt; &lt;a href=&quot;http://apiwiki.twitter.com/&quot; rel=&quot;nofollow&quot;&gt;API&lt;/a&gt;
    [in_reply_to_status_id] =&gt;
    [user] =&gt; stdClass Object
        (
            [notifications] =&gt;
            [profile_text_color] =&gt; 3C3940
            [url] =&gt;
            [time_zone] =&gt; Alaska
            [created_at] =&gt; Sun Apr 19 05:22:52 +0000 2009
            [profile_link_color] =&gt; 0099B9
            [profile_background_image_url] =&gt; http://s.twimg.com/a/1255980505/images/themes/theme4/bg.gif
            [description] =&gt; Enrich you websurfing experience! Discover interesting and amazing websites now.
            [profile_sidebar_fill_color] =&gt; 95E8EC
            [profile_background_tile] =&gt;
            [following] =&gt;
            [profile_sidebar_border_color] =&gt; 5ED4DC
            [geo_enabled] =&gt;
            [statuses_count] =&gt; 4233
            [followers_count] =&gt; 5997
            [protected] =&gt;
            [profile_image_url] =&gt; http://a1.twimg.com/profile_images/146406726/webdiscover_normal.jpg
            [location] =&gt; Webverse
            [friends_count] =&gt; 5735
            [name] =&gt; Web Explorer
            [verified] =&gt;
            [profile_background_color] =&gt; 0099B9
            [screen_name] =&gt; Web2Discover
            [id] =&gt; 33139096
            [utc_offset] =&gt; -32400
            [favourites_count] =&gt; 0
        )

    [truncated] =&gt;
    [id] =&gt; 5015100766
)
</pre>
</div>
<p>Let&#8217;s look at the code:</p>
<p>We create an $opts array (in fact an array of arrays) that contain the parameters. In this particular case, we&#8217;re using two, the POST method and the search line (track=google). Then, we can treat the twitter stream as a file, using <em>stream_context_create</em> and <em>fopen</em> and just start reading lines. Each line is going to be a JSON encoded tweet, similar to what we&#8217;ve seen when we called the API from command line. Since we want to use the contents as easily as possible, we&#8217;ll need the <em>json_decode</em> function to parse it into PHP objects, print them and call <em>flush</em> just in case we&#8217;re calling the script from a browser.</p>
<h2>Storing the results</h2>
<p>The best way to store the results for later perusal is a database. I&#8217;m using MySQL but any other database should be OK. To be able to store the data, we need to create a database with a single table.</p>
<p>We are only going to store the following data:</p>
<ul>
<li>Text: This is the twetter status. 140 chars max.</li>
<li>User screen name: The screen name of the poster. This is needed to create the link to twitter</li>
<li>Id: A unique id for the tweet. It&#8217;s a sequential number, so, we can order the tweets acording to this, and use it along with the user screen name to built a link back to twitter, and use it as our primary key.</li>
<li>Followers count: The number of people that are going to receive the tweet in their inboxes. We are using it to style the real-time viewer. Since my primal intention is to watch a trademark, I care about the number of people that are watching the messages.</li>
<li>The time of the tweet: basically for filtering purposes. We are going to store our server time to avoid lengthy conversions.</li>
</ul>
<p>We could store several other fields, and a complete solution should probably take into account that you can have some different tweet types, but for the time, these four fields should suffice.</p>
<p>To create the table, we can run the following SQL script from the server:</p>
<p class="toggle_code"><a href="/uploads/code/createtable.sql" onclick="jQuery('#code_3').toggle('slow');return false">createtable.sql</a></p>
<div  style="display:none" id="code_3">
<pre class="brush: sql">CREATE DATABASE twitter_alerts;

CREATE TABLE `twitter_alerts`.`tweets` (
`id` BIGINT UNSIGNED NOT NULL ,
`text` VARCHAR( 150 ) NOT NULL ,
`screen_name` VARCHAR( 255 ) NOT NULL ,
`followers_count` INT NOT NULL ,
`created_at` DATETIME NOT NULL ,
PRIMARY KEY ( `id` )
) ENGINE = InnoDB;

GRANT ALL PRIVILEGES ON twitter_alerts.tweets TO &#039;twitter_alerts&#039;@&#039;localhost&#039; IDENTIFIED BY &#039;somepasword&#039;;
</pre>
</div>
<p>After that, we will have a single table database waiting for us to fill it with tweets&#8230; Let&#8217;s go:</p>
<p class="toggle_code"><a href="/uploads/code/storing_tweets_in_the_database.php" onclick="jQuery('#code_4').toggle('slow');return false">storing_tweets_in_the_database.php</a></p>
<div  style="display:none" id="code_4">
<pre class="brush: php">&lt;?php

	$opts = array(
		&#039;http&#039;=&gt;array(
			&#039;method&#039;	=&gt;	&quot;POST&quot;,
			&#039;content&#039;	=&gt;	&#039;track=twitter&#039;,
		)
	);
	//We&#039;re going to store the data in the database, so, let&#039;s open a connection:
	$db = mysql_connect(&#039;localhost&#039;, &#039;twitter_alerts&#039;, &#039;somepasword&#039;);
	mysql_select_db(&#039;twitter_alerts&#039;, $db);

	$context = stream_context_create($opts);
	while (1){
		$instream = fopen(&#039;http://USERNAME:PASSWORD@stream.twitter.com/1/statuses/filter.json&#039;,&#039;r&#039; ,false, $context);
		while(! feof($instream)) {
			if(! ($line = stream_get_line($instream, 20000, &quot;\n&quot;))) {
				continue;
			}else{
				$tweet = json_decode($line);
				//Clean the inputs before storing
				$id = mysql_real_escape_string($tweet-&gt;{&#039;id&#039;});
				$text = mysql_real_escape_string($tweet-&gt;{&#039;text&#039;});
				$screen_name = mysql_real_escape_string($tweet-&gt;{&#039;user&#039;}-&gt;{&#039;screen_name&#039;});
				$followers_count = mysql_real_escape_string($tweet-&gt;{&#039;user&#039;}-&gt;{&#039;followers_count&#039;});
				//We store the new post in the database, to be able to read it later
				$ok = mysql_query(&quot;INSERT INTO tweets (id ,text ,screen_name ,followers_count, created_at) VALUES (&#039;$id&#039;, &#039;$text&#039;, &#039;$screen_name&#039;, &#039;$followers_count&#039;, NOW())&quot;);
				if (!$ok) {echo &quot;Mysql Error: &quot;.mysql_error();}
				flush();
			}
		}
	}
?&gt;
</pre>
</div>
<p>It&#8217;s as ugly as sin but it works. If you run it from the command line, it should start storing tweets in your database and keep on until you stop it. So, our database is starting to fill with tweets concerning our desired word. Now we need to be able to navigate them&#8230;</p>
<h2>Creating the code from the server side</h2>
<p>Now we need to publish the tweets in our browser. To do that, we need a small PHP script that returns the tweets when called. If we call it with a parameter <em>start</em> it&#8217;ll return all the tweets with an id bigger than that. Otherwise, it&#8217;ll return the last ten tweets stored in our database. To do that, we will use two different queries, the first one to return the last ten results, and the second one to return all results since the given <em>id</em>. We use subqueries (SELECT from SELECT) to get the results in our wished order.</p>
<p class="toggle_code"><a href="/uploads/code/server.php" onclick="jQuery('#code_5').toggle('slow');return false">server.php</a></p>
<div  style="display:none" id="code_5">
<pre class="brush: php">&lt;?php
	//We are going to need a database connection:
	$db = mysql_connect(&#039;localhost&#039;, &#039;twitter_alerts&#039;, &#039;somepasword&#039;);
	mysql_select_db(&#039;twitter_alerts&#039;, $db);
	//Now, two possibilities: if we don&#039;t have a start parameter, we print the last ten tweets.
	//Otherwise, we print all the tweets with IDs bigger than start, if any
	$start = mysql_real_escape_string($_GET[&#039;start&#039;]);
	if(! $start){
		$query = &quot;SELECT * FROM (SELECT * FROM tweets ORDER BY id DESC LIMIT 0,10) AS last_ten ORDER BY id ASC&quot;;
	}else{
		$query = &quot;SELECT * FROM (SELECT * FROM tweets WHERE id&gt;&quot;.$start.&quot; ORDER BY id DESC LIMIT 0,10) AS new_tweets ORDER BY id ASC&quot;;
	}

	$result = mysql_query($query);
	$data = array(); //Initializing the results array

	while ($row = mysql_fetch_assoc($result)){
		array_push($data, $row);
	}
	$json = json_encode($data);
	print $json;
?&gt;</pre>
</div>
<p>We are going to poll this code every ten seconds and refresh the tweets list to show the most recent ones, using javascript, and the output format will be JSON.</p>
<h2>Writing a front-end</h2>
<p>Since, as <a href="http://wall.org" target="_blank" onclick="pageTracker._trackPageview('/outgoing/wall.org?referer=');">Larry Wall</a> said, one of the cardinal virtues of a programmer is lazyness, we are going to use jQuery  to construct the interface and the business logic. And we&#8217;re not even serving it, but linking from the Google CDN, as <a href="http://encosia.com/2009/10/11/do-you-know-about-this-undocumented-google-cdn-feature/" target="_blank" onclick="pageTracker._trackPageview('/outgoing/encosia.com/2009/10/11/do-you-know-about-this-undocumented-google-cdn-feature/?referer=');">Dave Ward posted</a> in his wondeful blog.</p>
<p>So, let&#8217;s start with the HTML:</p>
<p class="toggle_code"><a href="/uploads/code/barebones.html" onclick="jQuery('#code_6').toggle('slow');return false">barebones.html</a></p>
<div  style="display:none" id="code_6">
<pre class="brush: html">&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xml:lang=&quot;gl&quot; lang=&quot;gl&quot;&gt;
&lt;head&gt;
		&lt;title&gt;Twitter alert viewer&lt;/title&gt;
		&lt;meta http-equiv=&quot;Content-type&quot; content=&quot;text/html;charset=UTF-8&quot; /&gt;
		&lt;script src=&quot;http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js&quot;&gt;&lt;/script&gt;
		&lt;style type=&quot;text/css&quot;&gt;
				body{
						background-color:#ccccff;
						font-family: georgia, serif;
				}
				.hidden{
						height:0px;
						overflow:hidden;
				}
				#tweets{
						margin:0 auto;
						width:400px;
				}
				#tweets div{
						border:1px solid silver;
						margin:5px;
						padding:5px;
						background-color:#fff;
				}
		&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
		&lt;div id=&quot;tweets&quot;&gt;
				&lt;h1&gt;Twitter alert viewer&lt;/h1&gt;
				&lt;a href=&quot;#&quot; onclick=&quot;clearTimeout(timeOut)&quot;&gt;Pause&lt;/a&gt;
				&lt;a href=&quot;#&quot; onclick=&quot;poll()&quot;&gt;Run&lt;/a&gt;
		&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
</div>
<p>Just some styles, one script link and a body containing one div, two links and a header. Simple, eh?</p>
<p>We need to add some javascript in the middle of the code to connect to the server:</p>
<p class="toggle_code"><a href="/uploads/code/logic.js" onclick="jQuery('#code_7').toggle('slow');return false">logic.js</a></p>
<div  style="display:none" id="code_7">
<pre class="brush: js">
var last = &#039;&#039;;
var timeOut;

function getTweets(id){
		$.getJSON(&quot;server.php?start=&quot;+id,
		function(data){
				$.each(data, function(count,item){
						addNew(item);
						last = item.id;
				});
		});
}

function addNew(item){
		if($(&#039;#tweets div.tweet&#039;).length&gt;9){ //If we have more than nine tweets
				$(&#039;#tweets div.tweet:first&#039;).toggle(300);//remove it form the screen
				$(&#039;#tweets div.tweet:first&#039;).removeClass(&#039;tweet&#039;);//and it&#039;s class
				$(&quot;#tweets div:hidden&quot;).remove(); //sweeps the already hidden elements
		}
		$(&#039;#tweets&#039;).append(renderTweet(item, &#039;hidden&#039;));
}

function renderTweet(item){
		importanceColor=getImportanceColor(item.followers_count);
		return &#039;&lt;div class=&quot;tweet&quot; id=&quot;&#039;+item.id+&#039;&quot;&gt;&#039;+
		&#039;&lt;strong&gt;&lt;a href=&quot;http://twitter.com/&#039;+item.screen_name+&#039;&quot; style=&quot;color:&#039;+importanceColor+&#039;&quot;&gt;&#039;+
		item.screen_name+&#039;&lt;/a&gt;&lt;/strong&gt;&lt;span class=&quot;text&quot;&gt;&#039;+
		item.text
		+&#039;&lt;/span&gt;&lt;span class=&quot;created_at&quot;&gt;&lt;br /&gt;&lt;a href=&quot;http://twitter.com/&#039;+
		item.screen_name+&#039;/status/&#039;+item.id+&#039;&quot;&gt;&#039;+
		item.created_at+&#039;&lt;/span&gt;&lt;/div&gt;&#039;;
}

function getImportanceColor(number){
		rgb = 255-Math.floor(16*(Math.log(number+1)+1)); //should return about 0 for 0 followers and 255 for 4million (Ashton Kutchner? Obama?)
		return &#039;rgb(&#039;+rgb+&#039;,0,0)&#039;;
}

function poll(){
		timeOut = setTimeout(&#039;poll()&#039;, 200);//It calls itself every 200ms
		getTweets(last);
}

$(document).ready(function() {
		poll();
});</pre>
</div>
<p>Let&#8217;s see&#8230; This is probably the most complex part of the article, so, I&#8217;ll try to go slow and explain every function:</p>
<h3>getTweets(id)</h3>
<p>This function calls the server using the getJSON jQuery method. Then, it takes each response line and calls addNew with it. If we call it with an <em>id</em> parameter, it&#8217;ll ask the server for all tweets with <em>id</em>s greater than that. Otherwise, it will grab the last ten tweets.</p>
<h3>addNew(item)</h3>
<p>It takes an item (a tweet) as input. It hides the first tweet, remove it&#8217;s &#8216;tweet&#8217; class, appends a new tweet at the bottom and shows it. It calls the renderTweet function to get the tweet in HTML format.</p>
<h3>renderTweet(item)</h3>
<p>Just one line to call getImportanceColor() and a return with the HTML code. It&#8217;s a bit long but that&#8217;s because we&#8217;re adding a couple of links to the tweet to be able to visit the original one.</p>
<h3>getImportanceColor(number)</h3>
<p>It takes a number of followers and returns a rgb color that will be between total black, for people without followers, and total red, for Ashton Kutchner. It uses logarithms to scale between the two extremes, because there are 6 orders of magnitude between the extremes. We will use it to paint (it) black the twitters with few followers and red the twitter stars.</p>
<h3>poll()</h3>
<p>This is the timeout function that calls itself every 200ms and gets the new tweets.</p>
<p>The last block just starts the polling as soon as the document is loaded.</p>
<h2>The Result</h2>
<p><a name="video"></a><object type="application/x-shockwave-flash" width="425" height="344" data="http://www.youtube.com/v/96JEBQk8vig&fs=1&rel=0&hd=1&showinfo=0"><param name="movie" value="http://www.youtube.com/v/96JEBQk8vig&fs=1&rel=0&hd=1&showinfo=0"></param><param name="allowFullScreen" value="true"></param><param name="wmode" value="transparent" /></object></p>
<p>This is a small screen capture of my browser visiting the HTML/javascript page while running storing_tweets_in_the_database.php. It&#8217;s watching the word &#8216;twitter&#8217; and, as you can see, it&#8217;s running too fast for the human eye -at least mine -, but since we are keeping all the data in our database, it&#8217;s not lost forever  <img src='http://blog.corunet.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h2>Limits</h2>
<p>Right now, because of the Twitter API limits, just one instance of the watching process can be run at once. Anyhow, you can write several words, separated by commas, and Twitter will return results for all of them.This code should not be used in production, since there are almost no security checks to avoid missuse. If you want to use it in a machine open to the public, you should check -twice- every input for missbehaviour.</p>
<h2>The code</h2>
<p><a name="thecode"></a></p>
<ol>
<li><a href="/uploads/twitter_watch/twitter_watch.zip">Download the code</a> and unzip it into a folder in your local webserver</li>
<li>Edit config.php to add your twitter login data and the words you want to watch</li>
<li>Create the database and the table with the SQL code above</li>
<li>Run watch.php and leave it running for as long as you wish.</li>
<li>Visit http://localhost/thefolderwhereyouunzippedthecode/ and watch the tweets coming.</li>
</ol>
<h2>Further work</h2>
<p>Obviously, this is just a sample. It can be made much better looking, and we could even analyze the tweets and tweet back a response to any questions concerning our keywords. The watch module should be daemonized or converted to a service to be left unatended. The HTML page could be able to filter between two dates and so on. Keep on watching. We&#8217;ll try to keep on posting this kind of contents.</p>
<h2>Shameless plug</h2>
<p>I&#8217;m part of <a href="http://coru.net/" onclick="pageTracker._trackPageview('/outgoing/coru.net/?referer=');">Corunet</a>, a web agency in Spain, that can deliver consistent good results in all kind of internet projects. You can visit our website <a href="http://coru.net/" onclick="pageTracker._trackPageview('/outgoing/coru.net/?referer=');">http://coru.net/</a> or contact me at david@corunet.com if you have any special needs <img src='http://blog.corunet.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>You can follow me on twitter as<a href="http://twitter.com/dei_biz/" onclick="pageTracker._trackPageview('/outgoing/twitter.com/dei_biz/?referer=');"> @dei_biz</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.corunet.com/twitter-alerts-using-twitter-streaming-api/feed/</wfw:commentRss>
		<slash:comments>28</slash:comments>
		</item>
	</channel>
</rss>

