Creating a Twitter app with Twilex

Written by Erika Heidi on Thursday July 17, 2014 - Permalink - Category: PHP - Tags: PHP, API, Twitter, Silex, TTools, Twilex - Lang: eng

Twilex is a new open source project I put together, in order to serve as base for some Twitter apps I plan to create. It basically provides a base structure, on top of Silex and Flint, using TTools as Twitter API wrapper, to help creating simple Twitter apps. I started organizing this for a raffle system on Twitter, and ended up open sourcing a skeleton to easily bootstrap different types of Twitter apps. 

So in this post I will guide you through the process of creating a Twilex app, using the idea that I mentioned before. It's just a very simple raffle system based on retweets for a given tweet ID, similar to what they have at twitterdraw

Getting Started

The first thing we're going to do is create the app skeleton using Composer.

$ composer create-project twilex/twilex-standard rafflebird 0.1.4

Now go to the application folder to configure it. We need to create a config.yml file inside app/config:

$ cd rafflebird
$ cp app/config/config.yml.dist app/config/config.yml

Edit the config.yml file to configure the application settings. This includes the API keys from your Twitter app:

config:
    name: Rafflebird
    author: Erika Heidi
    description: Simple raffler for twitter
    tags: twitter, raffle

twitter.keys:
    api_key: REPLACE_WITH_API_KEY
    api_secret: REPLACE_WITH_API_SECRET

locale: en_GB

​bundles.path: "%root_dir%/src"

active_bundles:
    - "%root_dir%/src/ExampleBundle"

http_cache.cache_dir: "%root_dir%/app/cache/http"
routing.resource: "%root_dir%/app/config/routing.yml"

Running the App with Vagrant

Now let's see the base application running. I'm using Vagrant for this.

$ vagrant up

After a few minutes, when the provision finishes, point your browser to 192.168.13.37 and you shall see the application up and running:

You can login with Twitter to see some information about your user:

Creating the RaffleBird bundle

Let's create a bundle to start working in our app. I'll create a bundle named RaffleBird.

$ php app/console bundle-create Rafflebird

If you check the src directory now, you'll se a structure like this:

We still need to activate the new bundle, and include its custom routes to the main routing file.

Edit app/config/config.yml and include the new bundle in the "active_bundles" array:

active_bundles:
    - "%root_dir%/src/RaffleBird"
    - "%root_dir%/src/ExampleBundle"

Edit app/config.routing.yml and include the routing file from the bundle:

rafflebird_main:
    resource: "src/RaffleBird/Resources/config/routing.yml"

example_main:
    resource: "src/ExampleBundle/Resources/config/routing.yml"
    
twilex_main:
    resource: "vendor/twilex/twilex/src/Twilex/Resources/config/routing.yml"

Note that I placed the new bundle in the top on both files. The first bundle in the top has the highest precedence, so the default routes and templates on RaffleBird will overwrite the ExampleBundle ones.

Now, if you go to your browser again and reload the page, you shall see something diferent:

Now we can proceed with the app development inside the RaffleBird bundle.

Creating the RaffleController

We'll need a route for executing the raffle. We could create some new actions in the DefaultController, but in this way we keep things more organized. Initially, let's just get the tweet_id as a route parameter (let's use /raffle/tweet_id as route) and return a simple text response.

<?php

namespace RaffleBird\Controller;

use Flint\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Twilex\Application;

class RaffleController extends Controller
{
    public function indexAction($tweet_id, Application $app)
    {
        return new Response($tweet_id);
    }
}

Now add a new route for this action by editing src/RaffleBird/Resources/config/routing.yml:

RaffleBird_default:
    path: /
    defaults: { _controller: "RaffleBird\Controller\DefaultController::indexAction" }

RaffleBird_raffle:
    path: /raffle/{tweet_id}
    defaults: { _controller: "RaffleBird\Controller\RaffleController::indexAction" }

You should now be able to access the application at '/raffle/123456789' and see the "id" echoed.

Now let's get some information about the tweet_id provided. We'll request that info from the Twitter API, using the helper method getTweet, from the ttools object. Our controller now looks like this:

public function indexAction($tweet_id, Application $app)
{
    $ttools = $app['ttools'];
    $tweet = $ttools->getTweet($tweet_id);

    echo "<pre>";
    var_dump($tweet);
    echo "</pre>";
    
    return new Response($tweet_id);
}

Now, try to access this route (the number is a real tweet ID): /raffle/489020866827919360 . You shall see a dump with extensive info about this tweet, including how many retweets it has, and the complete object representing the author of the tweet. But we need a list telling us who retweeted this, so we can pick a random user from the list to be the winner.

TTools doesn't have a helper method to get the list of users who retweeted a given tweet, but we can easily get this with a generic request to the Twitter API. There's an endpoint to get only the IDs of users who retweeted a given tweet, exactly what we need - after getting one random ID we can make a new request to get information about the winner. More info:  /statuses/retweeters/ids .

Note: API limitation - the REST API only provides you the latest 100 retweets from a given tweet. Althought the documentation leads you to believe you will be able to get all of them using the pagination cursor, it's a well-known fact that you are only able to request the most recent 100 retweets. Yeah it sucks, I know, but... theres nothing we can do about it.

Our controller now will request the list of retweeters and dump the resulting array:

public function indexAction($tweet_id, Application $app)
{
    $ttools = $app['ttools'];
    $retweeters = $ttools->get('/statuses/retweeters/ids.json', array('id' => $tweet_id));

    var_dump($retweeters);

    return new Response($tweet_id);
}

Now if you reload the page, you'll see something like this:

array(5) { ["ids"]=> array(38) { [0]=> int(50684214) [1]=> int(1480353349) [2]=> int(27491296) [3]=> int(19185780) [4]=> int(262572918) [5]=> int(195555022) [6]=> int(821167496) [7]=> int(47411637) [8]=> int(58537977) [9]=> int(352473555) [10]=> int(12162742) [11]=> int(51442248) [12]=> int(13513932) [13]=> int(37644697) [14]=> int(24018881) [15]=> int(871121113) [16]=> int(2558757163) [17]=> int(23605053) [18]=> int(14513358) [19]=> int(116045774) [20]=> int(127279220) [21]=> int(17672182) [22]=> int(85314937) [23]=> int(215052925) [24]=> int(517404551) [25]=> int(278509985) [26]=> int(236382419) [27]=> int(1257598033) [28]=> int(602257380) [29]=> int(90021813) [30]=> int(8842002) [31]=> int(8486682) [32]=> int(29384003) [33]=> int(101760859) [34]=> int(1617089666) [35]=> int(2233586576) [36]=> int(139073430) [37]=> int(14844642) } ["next_cursor"]=> int(0) ["next_cursor_str"]=> string(1) "0" ["previous_cursor"]=> int(0) ["previous_cursor_str"]=> string(1) "0" } 489020866827919360

So, we have the IDs of everyone (max. 100 users) who retweeted this specific tweet. 

Now, we just need to choose a random ID from this array, and get the information about this user to show who won the raffle. The refactored indexAction method below will do the trick:

public function indexAction($tweet_id, Application $app)
{
    $ttools = $app['ttools'];
    $retweeters = $ttools->get('/statuses/retweeters/ids.json', array('id' => $tweet_id));
    $ids = $retweeters['ids'];

    $winner = $ids[array_rand($ids)];
    $winner_info = $ttools->getProfile(array('id' => $winner));

    # debug winner info
    #var_dump($winner_info);

    $message = sprintf('And the winner is: <a href="http://twitter.com/%s">%s</a>', $winner_info['screen_name'], $winner_info['name']);

    return new Response($message);
}

Reload the page and you'll get a new random winner each time!

Wasn't that easy? Now it's just a matter of creating a nice front-end to show the raffle result. :) This was just a quick example of what we can do with Twilex. I hope this helps someone. Cheers!

comments powered by Disqus