Streamer – .Net / Unity access to the Twitter Streaming API

I’ve been messing around with the streaming API for Twitter, where you can grab filtered, realtime tweets. Do I want to use this in a game? Yes. Do I know what kind of game would it would be useful for? No.

EDIT: Now on GitHub for less painful use

While I figure that out though, I thought I’d release my library into the wild. I couldn’t find any decent .net libraries for Twitter streaming, or Unity It’s a little unfinished, but it does allow you to filter the stream by location or certain words, and parses the JSON quickly into a much easier to use form. You can connect with OAuth if you register your app on dev.twitter.com, or just provide your twitter login info. At some point I’ll expand it to give access to the general API so you post/receive tweets on individual accounts or search the history and so forth.

Here’s what I’ve used it for so far, you can look at where tweets are coming from (or where they’re not coming from) and see what tag is trending in each location. Download, run for a while, and see what happens! (sorry, no webplayer due to Unity security restrictions)

OSX download

Win download

Anyway, here are the main couple of classes:

– TwitterAccess.cs provides all the connectivity. Make a new TwitterAccess object, assign some query parameters(say, filter all tweets with the word “Bieber” in them), then connect. As tweets arrive they’ll be put in the Tweets queue for you to have your way with them. At the top is the stuff you’ll need to fill out if you want to use OAuth, ie, your consumer token/secret and, for now, your own account token/secret (until I set it up to use OAuth properly). You can instead fill out your username/password and connect with basic auth.

[sourcecode language=”csharp” collapse=”true”]

/*

* TwitterAccess.cs – Eddie Cameron

* ——-

* Controls access to the Twitter API for your .Net app

*

* Make sure the OAuth tokens/secrets are set up, or provide your username and password

*

* On a new TwitterAccess object, provide some QueryParameters, connect, and any matching tweets will show up in the "tweets" queue

* ——-

*/

using System;

using System.Net;

using System.IO;

using System.Threading;

using System.Text;

using System.Collections.Generic;

#if FOR_UNITY

using UnityEngine;

#endif

public class TwitterAccess

{

private const string streamFilterURL = "https://stream.twitter.com/1/statuses/filter.json";

private const string streamSampleURL = "https://stream.twitter.com/1/statuses/sample.json";

private string consumerKey = "YOUR_CONSUMER_KEY"; // register on dev.twitter.com to get

private string consumerSecret = "YOUR_CONSUMER_SECRET"; // these for your app

private const string signatureMethod = "HMAC-SHA1";

private const string version = "1.0";

public bool isAuthed;

private string token = "YOUR_TOKEN"; // generate an access token for yourself

private string tokenSecret = "YOUR_TOKEN_SECRET"; // at dev.twitter.com

// for the streaming API, you can use basic authentication…for now…

private string username;

private string password;

public enum ApiMethod{ StreamFilter, StreamSample };

public ApiMethod apiMethod = TwitterAccess.ApiMethod.StreamFilter;

private Dictionary<string, TwitterQuery> queries;

private Queue<string> tweetStringQueue; // this holds tweets as they arrive

private int maxInQueue = 2048;

private DecodeStream tweetParser;

public Queue<Tweet> tweets; // this holds processed tweets

// network backoff times

int networkErrors = 0;

int httpRetryTime = 10000;

// information about the API connection

private HttpWebResponse currentResponse;

private Thread currentConnection;

private StreamReader currentReader;

private bool connectionIsOpen = false;

private Thread connectingThread;

private WebRequest connectingRequest;

// URL encode strings, since Twitter doesn’t like .Net URL encoding

private static string UrlEncode( string toEncode )

{

StringBuilder toRet = new StringBuilder();

string acceptedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";

foreach (char c in toEncode )

{

if ( acceptedChars.IndexOf( c ) != -1 )

toRet.Append( c );

else

toRet.Append(‘%’ + string.Format( "{0:X2}", (int)c ) );

}

return toRet.ToString();

}

public TwitterAccess()

{

tweetStringQueue = new Queue<string>();

queries = new Dictionary<string,TwitterQuery>();

isAuthed = token != "" && tokenSecret != "";

tweetParser = new DecodeStream( tweetStringQueue );

tweets = tweetParser.tweets;

tweetParser.isParsingPaused = true;

}

// To add: user auth via OAuth

// Add and remove parameters (to add more)

public void AddQueryParameter( TwitterQuery query )

{

if ( queries.ContainsKey( query.GetKey() ) )

queries[query.GetKey()].MergeQuery( query );

else

queries.Add( query.GetKey(), query );

}

public void RemoveQueryParameter( TwitterQuery query )

{

if ( queries.ContainsKey( query.GetKey() ) )

{

TwitterQuery inQueries = queries[query.GetKey() ];

inQueries.RemoveQuery ( query );

if ( inQueries.GetParameter() == "" )

queries.Remove( query.GetKey() );

}

}

// For the streaming API, you can provide a user/password instead of OAuthing

public void BasicAuthUserPassword( string username, string password )

{

this.username = username;

this.password = password;

}

// Connect with already given parameters

public void Connect( bool useBasicAuth )

{

// if attempting connection, cancel

if ( connectingThread != null )

connectingThread.Abort ();

// translate parameters to string values

string[,] parameterStrings = new string[queries.Count,2];

StringBuilder postString = new StringBuilder();

int i = 0;

foreach ( TwitterQuery query in queries.Values )

{

string key = UrlEncode( query.GetKey() );

string parameter = UrlEncode ( query.GetParameter() );

parameterStrings[i,0] = key;

parameterStrings[i,1] = parameter;

i++;

postString.Append ( key + "=" + parameter + "&" );

}

postString = postString.Remove ( postString.Length – 1, 1 );

// make and authorise webrequest

connectingRequest = WebRequest.Create( streamFilterURL );

if ( useBasicAuth )

connectingRequest.Credentials = new NetworkCredential( username, password );

else if ( isAuthed )

connectingRequest.Headers.Add ( HttpRequestHeader.Authorization, MakeAuthorisation( parameterStrings ) );

else

{

#if FOR_UNITY

Debug.Log ( "Not yet authorised via OAuth, provide user/password or access tokens" );

#else

Console.WriteLine ( "Not yet authorised via OAuth, provide user/password or access tokens" );

#endif

return;

}

// start connection thread

ParameterizedThreadStart newThreadStart = new ParameterizedThreadStart( StartStream );

connectingThread = new Thread( newThreadStart );

connectingThread.Start ( postString.ToString () );

}

// disconnect streams if there are any

public void Disconnect()

{

if ( connectionIsOpen )

{

currentResponse.Close ();

currentReader.Close ();

currentConnection.Abort();

}

if ( connectingThread != null )

connectingThread.Abort ();

tweetParser.isParsingPaused = true;

}

private void StartStream( object postString )

{

Console.WriteLine( "Starting connection to " + connectingRequest.RequestUri );

// try to get stream

HttpWebResponse newResponse = GetNewConnection ( (string)postString );

Console.WriteLine( "Have response. Starting stream" );

// disconnect old thread if there was one, streaming API only allows one connection per user

if ( connectionIsOpen )

{

currentConnection.Abort();

currentReader.Close ();

currentResponse.Close ();

}

// save current connection

connectionIsOpen = true;

currentConnection = Thread.CurrentThread;

currentResponse = newResponse;

while ( true )

{

currentReader = new StreamReader( newResponse.GetResponseStream(), UTF8Encoding.UTF8 );

// start reading

ReadFromCurrentResponseStream ();

currentResponse.Close ();

// for streaming, restart when response ends

#if FOR_UNITY

Debug.Log ( "Response ended, trying to reconnect in " + ( networkErrors * 250 / 1000f ).ToString ( "F2" )  );

#else

Console.WriteLine ( "Response ended, trying to reconnect in " + ( networkErrors * 250 / 1000f ).ToString ( "F2" )  );

#endif

Thread.Sleep ( networkErrors * 250 );

networkErrors++;

currentResponse = GetNewConnection ( (string)postString );

}

}

// Read from response and add to queue

private void ReadFromCurrentResponseStream()

{

if ( currentReader == null )

return;

tweetParser.isParsingPaused = false;

// read new stream into tweet queue

while ( !currentReader.EndOfStream )

{

string line = currentReader.ReadLine ();

if ( line == "n" ) // ignore keepalive statuses

continue;

tweetStringQueue.Enqueue( line );

if ( tweetStringQueue.Count > maxInQueue )

tweetStringQueue.Dequeue();

}

}

// connect to API and get response object

private HttpWebResponse GetNewConnection( string postString )

{

HttpWebResponse response = null;

// keep trying until successful

while ( response == null )

{

try

{

// add parameters to webrequest

byte[] postData = UTF8Encoding.UTF8.GetBytes ( (string)postString );

connectingRequest.Method = "POST";

connectingRequest.ContentLength = postData.Length;

connectingRequest.ContentType = "application/x-www-form-urlencoded";

Stream contentStream = connectingRequest.GetRequestStream();

contentStream.Write ( postData, 0, postData.Length );

contentStream.Close ();

response = (HttpWebResponse)connectingRequest.GetResponse ();

}

catch ( WebException e )

{

int waitTime;

if ( e.Status == WebExceptionStatus.ProtocolError ) // http level error

{

HttpWebResponse errorResponse = (HttpWebResponse)e.Response;

#if FOR_UNITY

Debug.Log (  errorResponse.StatusDescription + ". Trying again in " + ( httpRetryTime / 1000f ).ToString ( "F2" ) );

#else

Console.WriteLine (  errorResponse.StatusDescription + ". Trying again in " + ( httpRetryTime / 1000f ).ToString ( "F2" )  );

#endif

waitTime = httpRetryTime;

httpRetryTime *= 2;

}

else // network level error

{

#if FOR_UNITY

Debug.Log (  e.Message + ".Trying again in " + ( networkErrors * 250 / 1000f ).ToString ( "F2" ) );

#else

Console.WriteLine ( e.Message + ".Trying again in " + ( networkErrors * 250 / 1000f ).ToString ( "F2" ) );

#endif

waitTime = networkErrors * 250;

networkErrors++;

}

Thread.Sleep ( waitTime );

}

}

return response;

}

private string MakeAuthorisation( string[,] queryParameters )

{

// make nOnce

StringBuilder no = new StringBuilder( 32 );

string alphanumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";

System.Random random = new System.Random();

for ( int i = 0; i < 32; i++ )

no.Append( alphanumeric[ random.Next ( alphanumeric.Length ) ] );

// set timestamp

string timeStamp = ( (int)( System.DateTime.UtcNow – new System.DateTime( 1970, 1, 1 ) ).TotalSeconds ).ToString();

// sort all the parameters to be signed

SortedDictionary<string, string> properties = new SortedDictionary<string, string>();

for( int i = 0; i < queryParameters.GetLength( 0 ); i++ )

properties.Add ( queryParameters[i,0], queryParameters[i,1] );

properties.Add ( UrlEncode ( "oauth_consumer_key" ), UrlEncode( consumerKey ) );

properties.Add ( UrlEncode( "oauth_nonce" ), no.ToString () );

properties.Add ( UrlEncode( "oauth_signature_method" ), UrlEncode( signatureMethod ) );

properties.Add ( UrlEncode( "oauth_timestamp" ), timeStamp );

properties.Add ( "oauth_token", UrlEncode ( token ) );

properties.Add ( "oauth_version", UrlEncode ( version ) );

// make parameter string

StringBuilder parameters = new StringBuilder();

foreach( KeyValuePair<string, string> kv in properties )

parameters.Append ( kv.Key + "=" +  kv.Value + "&" );

parameters.Remove ( parameters.Length – 1, 1 );

string postParameters =  "POST&" + UrlEncode ( streamFilterURL ) + "&" + UrlEncode ( parameters.ToString () );

// sign parameter string

byte[] sigBase = UTF8Encoding.UTF8.GetBytes (postParameters);

MemoryStream ms = new MemoryStream();

ms.Write (sigBase, 0, sigBase.Length );

byte[] key = UTF8Encoding.UTF8.GetBytes ( UrlEncode ( consumerSecret ) + "&" + UrlEncode( tokenSecret ) );

System.Security.Cryptography.HMACSHA1 sha1 = new System.Security.Cryptography.HMACSHA1( key );

byte[] hashBytes = sha1.ComputeHash ( sigBase );

string signature = System.Convert.ToBase64String( hashBytes );

// construct auth header

string header = "OAuth " + "oauth_consumer_key="" + UrlEncode ( consumerKey ) + "",oauth_nonce="" + UrlEncode ( no.ToString() )

+ "",oauth_signature="" + UrlEncode ( signature ) + "",oauth_signature_method="" + UrlEncode ( signatureMethod )

+ "",oauth_timestamp="" + UrlEncode ( timeStamp ) + "",oauth_token="" + UrlEncode ( token )

+ "",oauth_version="" + UrlEncode ( version ) + """;

return header;

}

}

[/sourcecode]

– TwitterQuery.cs Base class for all Twitter queries. Inherit from this class to write your own

[sourcecode language=”csharp” collapse=”true”]</pre>
/* TwitterrQuery.cs – Eddie Cameron

* ——

* Base for all Twitter API query parameters

* ——

*/

using System.Text;

using System.Collections.Generic;

public abstract class TwitterQuery

{

public abstract string GetKey(); // twitter URL name for this query

public abstract string GetParameter(); // to return URL string with parameters

public abstract void MergeQuery( TwitterQuery toMerge ); // combine toMerge parameters into this query

public abstract void RemoveQuery( TwitterQuery toRemove ); // remove toRemove parameters from this query if they are there

}

[/sourcecode]

– QueryLocation.cs

For adding bounding-box location queries

[sourcecode language=”csharp” collapse=”true”]

/* QueryLocations.cs – Eddie Cameron

* ——

* Base for all queries to filter by location (Twitter location parameter)

* ——

*/

using System;

using System.Text;

using System.Collections.Generic;

// location query – specified by bounding boxes to return results from

public class QueryLocations : TwitterQuery

{

private List<Coordinates[]> locationsToQuery;

public QueryLocations()

{

locationsToQuery = new List<Coordinates[]>();

}

public QueryLocations( Coordinates southwestCorner, Coordinates northeastCorner )

{

locationsToQuery = new List<Coordinates[]>();

AddLocationBoundingBox( southwestCorner, northeastCorner );

}

public bool AddLocationBoundingBox( Coordinates southwestCorner, Coordinates northeastCorner )

{

// Check is a valid box

if ( northeastCorner.lng <= southwestCorner.lng )

return false;

if ( northeastCorner.lat <= southwestCorner.lat )

return false;

locationsToQuery.Add ( new Coordinates[]{ southwestCorner, northeastCorner } );

return true;

}

public List<Coordinates[]> GetLocations()

{

return locationsToQuery;

}

public override string GetKey()

{

return "locations";

}

public override string GetParameter()

{

if ( locationsToQuery.Count == 0 )

return "";

StringBuilder sb = new StringBuilder();

foreach( Coordinates[] locationBox in locationsToQuery )

{

foreach( Coordinates coord in locationBox )

sb.Append ( coord.lng.ToString ( "F1" ) + "," + coord.lat.ToString( "F1" ) + "," );

}

sb.Remove ( sb.Length – 1, 1 );

return sb.ToString ();

}

public override void MergeQuery (TwitterQuery toMerge)

{

if ( toMerge is QueryLocations )

{

locationsToQuery.AddRange( ((QueryLocations)toMerge ).GetLocations() );

}

}

public override void RemoveQuery (TwitterQuery toRemove)

{

if ( toRemove is QueryLocations )

{

foreach( Coordinates[] coord in ((QueryLocations)toRemove ).GetLocations() )

locationsToQuery.Remove( coord );

}

}

}

public struct Coordinates

{

private float _lng;

public float lng

{

get { return _lng; }

set {

if ( value < -180 )

_lng = -180;

else if ( value > 180 )

_lng = 180f;

}

}

private float _lat;

public float lat

{

get { return _lat; }

set {

if ( value < -90 )

_lat = -90;

else if ( value > 90 )

_lat = 90;

}

}

public Coordinates( float lng, float lat )

{

this._lng = lng;

this._lat = lat;

}

}

[/sourcecode]

– QueryTrack.cs

For adding filters based on strings (Twitter track parameter)

 [sourcecode language=”csharp” collapse=”true”]</div>
<div>

using System.Text;

using System.Collections.Generic;

&nbsp;

public class QueryTrack : TwitterQuery

{

private List<string> stringsToTrack;

&nbsp;

public QueryTrack()

{

stringsToTrack = new List<string>();

}

&nbsp;

public QueryTrack( string withTag )

{

stringsToTrack = new List<string>();

stringsToTrack.Add ( withTag );

}

&nbsp;

public List<string> GetTrackStrings()

{

return stringsToTrack;

}

&nbsp;

public bool AddTrack( string track )

{

if ( track.ToCharArray().Length > 60 )

return false;

else

stringsToTrack.Add( track );

&nbsp;

return true;

}

&nbsp;

public override string GetKey()

{

return "track";

}

&nbsp;

public override string GetParameter()

{

StringBuilder sb = new StringBuilder();

foreach( string s in stringsToTrack )

sb.Append( s + "," );

sb.Remove( sb.Length – 1, 1 );

return sb.ToString();

}

&nbsp;

public override void MergeQuery( TwitterQuery toMerge )

{

if ( toMerge is QueryTrack )

stringsToTrack.AddRange( ( (QueryTrack)toMerge ).GetTrackStrings() );

}

&nbsp;

public override void RemoveQuery( TwitterQuery toRemove )

{

if ( toRemove is QueryTrack )

{

foreach( string t in ((QueryTrack)toRemove ).GetTrackStrings() )

stringsToTrack.Remove( t );

}

}

}

&nbsp;

[/sourcecode]

– DecodeTweets.cs

TwitterAccess will create this, pass it the JSON strings direct from the Twitter feed, and get back Tweet objects

[sourcecode language=”csharp” collapse=”true”]</pre>
/* DecodeStream.cs – Eddie Cameron

* ——

* Decodes JSON objects into tweets

* ——

*/

using System;

using System.Threading;

using System.Collections;

using System.Collections.Generic;

&nbsp;

#if FOR_UNITY

using UnityEngine;

#endif

&nbsp;

public class DecodeStream

{

private Queue<string> tweetStringQueue; // ref to raw Twitter json

&nbsp;

private Thread parseThread;

public bool isParsingPaused;

&nbsp;

public Queue<Tweet> tweets; // access this to get tweets

public double curLimit;

&nbsp;

// set up with reference to direct queue from TwitterAccess

public DecodeStream( Queue<string> tweetStringQueue )

{

this.tweetStringQueue = tweetStringQueue;

tweets = new Queue<Tweet>();

&nbsp;

parseThread = new Thread( new ThreadStart( Parse ) );

parseThread.Start ();

}

&nbsp;

// stop any JSON procesing

public void Stop()

{

if ( parseThread != null )

parseThread.Abort ();

}

&nbsp;

// process all tweets in tweetStringQueue

void Parse()

{

while ( true )

{

while ( !isParsingPaused && tweetStringQueue.Count > 0 )

{

string newTweetStr = tweetStringQueue.Dequeue();

&nbsp;

try

{

JSONObject newObj = new JSONObject( newTweetStr );

&nbsp;

if ( newObj.HasProperty( "text" ) )

{

Tweet newTweet = new Tweet( newObj );

tweets.Enqueue ( newTweet );

}

else if ( newObj.HasProperty ( "limit" ) )

curLimit = newObj.GetProperty( "limit" ).GetProperty ( "track" ).n;

else

Console.WriteLine ( "Unrecognised (valid) JSON object in stream" );

}

catch

{

Console.WriteLine( "Invalid object in json string" );

}

}

&nbsp;

Thread.Sleep ( 100 );

}

}

}

&nbsp;

// To be expanded as needed

public class Tweet

{

public string status = "";

&nbsp;

private JSONObject user;

public string userName = "";

&nbsp;

private JSONObject location;

public Coordinates[] coords; // single entry means point, multiple means bounding box

&nbsp;

private JSONObject entities;

public string[] tags = new string[1];

&nbsp;

// Make tweet object from JSON

public Tweet( JSONObject jsonTweet )

{

if ( jsonTweet.type != JSONObject.Type.OBJECT )

throw new System.Exception( "Non-valid JSON object" );

&nbsp;

status = jsonTweet.GetProperty ( "text" ).str;

&nbsp;

user = jsonTweet.GetProperty ( "user" );

&nbsp;

JSONObject name = user.GetProperty ( "screen_name" );

userName = name.str;

&nbsp;

location = jsonTweet.GetProperty( "geo" );

if ( location.type == JSONObject.Type.NULL )

location = jsonTweet.GetProperty( "coordinates" );

// to add parsing for ‘place’ tagged tweets

&nbsp;

if ( location.type != JSONObject.Type.NULL )

coords = GetLocationData( location );

&nbsp;

entities = jsonTweet.GetProperty ( "entities" );

JSONObject hashtags = entities.GetProperty( "hashtags" );

if ( hashtags.type != JSONObject.Type.NULL && hashtags.props.Count > 0 )

{

tags = new string[ hashtags.props.Count ];

for ( int i = 0; i < tags.Length; i++ )

tags[i] = hashtags.props[i].GetProperty ( "text" ).str;

}

else

tags = new string [0];

}

&nbsp;

// extract coords from location object

public static Coordinates[] GetLocationData( JSONObject locationObj )

{

try

{

if ( locationObj.HasProperty( "coordinates" ) )

{

List<JSONObject> coords = locationObj.GetProperty ( "coordinates" ).props;

if ( coords.Count % 2 != 0 )

throw new System.Exception( "Wrong coordinate setup?" );

&nbsp;

Coordinates[] toRet = new Coordinates[ coords.Count / 2 ];

for ( int i = 0; i < coords.Count; i += 2 )

{

toRet[i] = new Coordinates( (float)coords[i+1].n, (float)coords[i].n );

}

return toRet;

}

&nbsp;

throw new System.Exception( "wrong geo type" );

&nbsp;

}  catch ( System.Exception e )

{

Console.WriteLine( "Location error:" + e.Message );

return new Coordinates[2];

}

}

}

&nbsp;
<pre>

[/sourcecode]

– JSONObject.cs

Parses JSON strings to structured objects, based on a script by Matt Schoen

[sourcecode language=”csharp” collapse=”true”]

#define FROMTWITTER

&nbsp;

/* JSONObject.cs – Eddie Cameron

* ——

* A rather simple JSON string to object parser

* ——

* Based on JSONObject, Copyright Matt Schoen 2010, from http://www.unifycommunity.com/wiki/index.php?title=Json

* http://www.opensource.org/licenses/lgpl-2.1.php

* ——

*/

&nbsp;

using System.Collections.Generic;

using System.IO;

using System;

&nbsp;

public class JSONObject

{

const int MAX_DEPTH = 1000;

const string INFINITY = ""INFINITY"";

const string NEGINFINITY = ""NEGINFINITY"";

public enum Type { NULL, STRING, NUMBER, OBJECT, ARRAY, BOOL }

&nbsp;

public Type type;

public bool b;

public double n;

public string str;

public List<string> keys;

public List<JSONObject> props;

&nbsp;

public bool HasProperty( string prop )

{

return keys.Contains ( prop );

}

&nbsp;

public JSONObject GetProperty( string prop )

{

if ( type == Type.OBJECT || type == Type.ARRAY )

{

int propInd = keys.IndexOf ( prop );

if ( propInd >= 0 && propInd < props.Count )

return props[ propInd ];

}

&nbsp;

return null;

}

&nbsp;

public JSONObject( string str )

{

if ( str == "" )

{

type = Type.NULL;

return;

}

&nbsp;

if( str.Length > 0)

{

if ( string.Compare(str, "true", true) == 0)

{

type = Type.BOOL;

b = true;

}

else if(string.Compare(str, "false", true) == 0)

{

type = Type.BOOL;

b = false;

}

else if(str == "null")

{

type = Type.NULL;

}

else if(str == INFINITY)

{

type = Type.NUMBER;

n = double.PositiveInfinity;

}

else if(str == NEGINFINITY)

{

type = Type.NUMBER;

n = double.NegativeInfinity;

}

else if(str[0] == ‘"’)

{

type = Type.STRING;

this.str = str.Substring(1, str.Length – 2);//.Replace( @"", "" );

}

else

{

try

{

// check for number type

n = System.Convert.ToDouble(str);

type = Type.NUMBER;

}

catch(System.FormatException)

{

int token_tmp = 0;

/*

* Checking for the following formatting (www.json.org)

* object – {"field1":value,"field2":value}

* array – [value,value,value]

* value – string   – "string"

*     – number – 0.0

*     – bool      – true -or- false

*     – null      – null

*/

switch(str[0])

{

case ‘{‘:

type = Type.OBJECT;

keys = new List<string>();

props = new List<JSONObject>();

break;

case ‘[‘:

type = Type.ARRAY;

props = new List<JSONObject>();

break;

default:

type = Type.NULL;

return;

}

&nbsp;

int depth = 0;

bool openquote = false;

bool inProp = false;

for ( int i = 1; i < str.Length; i++ )

{

#if !FROMTWITTER

// Twitter feeds may have these characters in their statuses

if(str[i] == ” || str[i] == ‘t’ || str[i] == ‘n’ || str[i] == ‘r’) {

i++;

continue;

}

#else

if ( str[i] == ” )

{

if ( str.Length >= i & str[i+1] == ‘"’ )

{

i++;

continue;

}

}

#endif

&nbsp;

if(str[i] == ‘"’)

openquote = !openquote;

else if( str[i] == ‘[‘ || str[i] == ‘{‘ )

depth++;

&nbsp;

if(depth == 0 && !openquote)

{

if( str[i] == ‘:’ && !inProp )

{

inProp = true;

try {

keys.Add( str.Substring( token_tmp + 2, i – token_tmp – 3));

} catch {  }

token_tmp = i;

}

if( str[i] == ‘,’ )

{

inProp = false;

props.Add(new JSONObject(str.Substring(token_tmp + 1, i – token_tmp – 1)));

token_tmp = i;

}

if(str[i] == ‘]’ || str[i] == ‘}’)

{

if ( str[i – 1] != ‘[‘ && str[i-1] != ‘{‘ )

props.Add(new JSONObject(str.Substring(token_tmp + 1, i – token_tmp – 1)));

}

}

&nbsp;

if(str[i] == ‘]’ || str[i] == ‘}’)

depth–;

}

}

}

}

}

}

[/sourcecode]

And that’s all you need! Here’s an example script to show you one way to use it:
(for .Net)
[sourcecode language=”csharp” collapse=”true”]</div>
using System;

&nbsp;

namespace Streamer

{

class MainClass

{

public static void Main ( string[] args )

{

// Create twitteraccess object

TwitterAccess access = new TwitterAccess();

&nbsp;

// Get all tweets with a location tag

QueryLocations world = new QueryLocations( new Coordinates( -180f, -90f ), new Coordinates( 180f, 90f ) );

access.AddQueryParameter( world );

&nbsp;

// and with the word "bieber" in them

access.AddQueryParameter( new QueryTrack( "bieber" ) );

&nbsp;

access.Connect ( false );

&nbsp;

while ( true )

{

if ( access.tweets.Count > 0 )

{

Tweet newTweet = access.tweets.Dequeue();

Console.WriteLine( newTweet.userName  + ": " + newTweet.status );

}

}

}

}

}

&nbsp;

&nbsp;

[/sourcecode]

(for Unity)

[sourcecode language=”csharp” collapse=”true”]

/* TwitterStream.cs – Eddie Cameron

* ——

* Example script to use Streamer

* ——

*/

&nbsp;

using UnityEngine;

using System.Collections;

using System.Collections.Generic;

&nbsp;

public class TwitterStream : MonoBehaviour

{

public static Queue<Tweet> tweetQueue;

&nbsp;

private TwitterAccess twitterAccess;

&nbsp;

void Awake()

{

tweetQueue = new Queue<Tweet>();

&nbsp;

// Make new twitteraccess object

twitterAccess = new TwitterAccess();

tweetQueue = twitterAccess.tweets;

&nbsp;

// add a query (in this case, any location tagged tweet)

QueryLocations allLocations = new QueryLocations( new Coordinates( -180, -90 ), new Coordinates( 180, 90 ) );

twitterAccess.AddQueryParameter( allLocations );

&nbsp;

StartNewStream();

}

&nbsp;

public void StartNewStream()

{

// connect using OAuth

twitterAccess.Connect ( false );

&nbsp;

StartCoroutine ( PrintTweets() );

}

&nbsp;

private IEnumerator PrintTweets()

{

while ( true )

{

// print tweets as they appear in the queue, one per frame

if ( tweetQueue != null && tweetQueue.Count > 0 )

Debug.Log ( "Decoded tweet: " + tweetQueue.Dequeue ().status );

&nbsp;

yield return 0;

}

}

&nbsp;

void OnApplicationQuit()

{

twitterAccess.Disconnect ();

}

}

[/sourcecode]

I’ll put this up on GitHub soon (DONE!), and update as I use it. Of course, in the meantime everyone is free to use and modify as they see fit. If you make a game out of it, I’d love to hear what you do, I’m sure there’s a whole bunch of really interesting game ideas involving the world’s collective realtime thoughts.

2 thoughts on “Streamer – .Net / Unity access to the Twitter Streaming API”

Comments are closed.