#GoTWar Les robots Twitter

Récupération et traitements de tweets grâce à des robots




La GoTwar ?

Cet article fait suite au développement de la GoTwar, un dispositif web assurant la promotion de la saison 5 de Game of Thrones. Tous les détails de ce projet ici : La GoTwar.

gotwar




Les robots Twitter

Durant l'opération GoTwar, ce sont + de 63.000 tweets qui ont été récupérés et traités ainsi que + de 8.500 tweets mentions envoyés via le compte de @canalsat. Cet article sera un peu différent de d'habitude. Au lieu de présenter du code dans le détail, il présentera plutôt un peu de la logique qui se cache derrière la collecte, le traitement et l'envoi de ces milliers de tweets. Comme toujours chez Web and Cow, les exemples de code sont basés sur le framework CakePHP. Mais ils peuvent être compris sans forcément connaitre le Framework.


Quelques modèles

Voici un aperçu de la base de données de la GoTwar, au travers de 3 modèles.

Le modèle tweets, comme son nom l'indique enregistrait l'ensemble des tweets contenant le hashtag #gotwar. Voici un aperçu de ce qu'il contenait :



On a donc beaucoup d'informations sur le tweet en question, est ce qu'il contient des hashtags ? Des mentions ? Est ce un retweet ? Si oui, de quel tweet ? Etc.
Remarque : la clé primaire utilisée est directement l'id du tweet, qui est unique. Aucun besoin de créer une nouvelle clé primaire.

Toutes ces informations sont récupérables grâce à l'API de Twitter. Dès qu'un tweet est enregistré, son champ "traité" est à 0.




Ensuite, le modèle soldats, qui contenait les participants à la gotwar, hors commandants. Voici un aperçu de ce qu'il contenait :



Tous les champs n'ont pas été remis. On sauvegardait également l'image de profil de l'utilisateur, sa description, son nombre de followers, etc.
Remarque : Comme précédemment, pas besoin de créer une nouvelle clé primaire, le twitter_user_id est unique.




Et enfin, le modèle envois, qui contenait tous les tweets à envoyer via le compte @canalsat. Il est plus simple que les 2 précédents. Le voici dans sa totalité :



Ce modèle était créé de façon à être indépendant. Une fois une ligne insérée, plus besoin de faire appel à un autre modèle pour gérer l'envoi (on aurait notamment pu se passer du twitter_screen_name qu'on peut retrouver grâce au soldat_id). Le tweet_id est une donnée très importante. Il est en effet nécessaire de répondre à un tweet pour maximiser l'effet d'un tweet mention et être sûr qu'il apparaisse dans l'onglet "Tweets et réponses". Sinon, il va apparaitre dans le fil traditionnel, sera visible par tous directement et va potentiellement "pourrir" la timeline principale si on envoie plusieurs milliers de fois un tweet similaire.

Remarque : Le champ "texte" n'a pas besoin d'être limité à 140 caractères et pourrait même (presque) ne pas avoir de limite. En effet, s'il contient des URL, Twitter va automatiquement les réduire pour qu'elles prennent moins de caractères dans le tweet. Mais attention car il se peut, même en réduisant les URL que le texte fasse plus de 140 caractères et dans ce cas là le tweet ne partira pas. Il faut donc préalablement bien anticiper le contenu de ses tweets automatisés.


Planification des robots

Pour ce projet, 3 robots PHP ont été créés. Nous entendons par "robot" une fonction qui va se répéter dans le temps et faire toujours la même chose à des intervalles de temps régulier.

Le premier robot permettait donc de collecter les tweets. Il faisait appel à l'API REST de Twitter, via la fonctionnalité search/tweets. Nous avons travaillé sur un composant Twitter pour CakePHP permettant de parcourir et récupérer les tweets efficacement, en manipulant les paramètres since_id et max_id. Quelques informations supplémentaires dans la doc de Twitter.

Du coup, notre code ressemblait à ça :


// TweetsController.php

public function enregistreTweets() {

	// On cherche le tweet le plus récent en BDD 
	$tweetPlusRecent = $this->Tweet->find('first', array('field' => 'id', 'order' => array('tweet_date' => 'DESC')));

	// S'il y en a un, on prend son id
	if(!empty($tweetPlusRecent)) {
		$idTweetDepart = $tweetPlusRecent['Tweet']['id'];
	} else {
		// Premier Tweet à partir duquel on commence, à définir en amont
		$idTweetDepart = PREMIER_TWEET_ID;
	}

	// On récupère tous les nouveaux tweets depuis $idTweetDepart
	// qui correspondent au hashtag #gotwar
	$tweets = $this->TwitterComponent->getTweets($idTweetDepart, 'gotwar'); 

	foreach ($tweets as $tweet) {

			$nouveauTweet = array('Tweet' => array(
				'id' => $tweet['tweet_id'],
				'twitter_user_id' => $tweet['tweet_user_id'],
				'tweet_user_screen_name' => $tweet['tweet_user_screen_name'],
				'tweet_content' => $tweet['tweet_content'],
				'tweet_hashtags' => $tweet['tweet_hashtags'],
				'tweet_mentions' => $tweet['tweet_mentions'],
				'tweet_date' => $tweet['tweet_date'],
				'retweet' => $tweet['retweet'],
				'retweet_id' => $tweet['retweet_id'],
				'traite' => 0
			));

			// On enregistre les tweets, avec la champ "traité" à 0	
			$this->Tweet->create();
			$this->Tweet->save($nouveauTweet); 
	}
}

Ce robot était lancé toutes les 4 minutes, 24h/24 pendant toute la durée de l'opération #gotwar. Cela pour être sûr de ne rater aucun tweet (certains tweets ne sont plus accessibles via l'API au bout d'un certain temps) et pour avoir un effet temps réel sur les animations proposées sur le site dédié.


Le second robot permettait de traiter l'ensembles des tweets. Il était beaucoup plus complexe car il traitait les tweets de différentes façons selon les évènements à créer, selon les armées de la #gotwar, etc.

Néanmois, voici un bref aperçu de ce à quoi il pouvait ressembler :


// TweetsController.php

public function enregistreTweets() {

	// On récupère les tweets qui n'ont pas encore été traité
	$tweetsATraiter = $this->Tweet->find('all', array('conditions' => array('Tweet.traite' => 0), 'order' => array('Tweet.date' => 'ASC'), 'limit' => 500));

	foreach ($tweetsATraiter as $tweet) {

		// ...
		// Traitement du Tweet
		// ...

		// Passage en état traité du tweet
		$this->Tweet->id = $tweet['Tweet']['id'];
		$this->Tweet->saveField('traite', 1);
	}
}

En réalité, la fonction était vraiment plus complexe avec d'autres modèles associés et la génération de l'ensemble des évènements dédiés aux animations des batailles. Elle était lancée toutes les 5 minutes et traitait un maximum de 500 tweets pour éviter un temps d'exécution trop long.

Mais cette fonction faisait également le lien entre les tweets et les participants à la GoTwar, les soldats. Pour cela, elle faisait appel au modèle Soldat, via la fonction suivante :


// Soldat.php

public function nouveauSoldat($tweet) {

	$newSoldat = array('Soldat' => array(
		'id' => $tweet['tweet_user_id'],
		'twitter_screen_name' => $tweet['tweet_user_screen_name'],
		'tweet_count' => 1
	));

	$this->create();
	$this->save($newSoldat);

	// On crée un nouvel envoi
	$texte = 'Bienvenue Soldat ! Suis-nous pour +d\'info #GotWar #GOTVIP, ton profil est ici ' . URL_SITE_AVEC_PROFIL . $twitterUser['twitter_screen_name'] . ' ' . VISUEL_BIENVENUE;
	$this->Envoi->nouvelEnvoi($tweet['tweet_user_id'], $tweet['tweet_user_screen_name'], $texte, $tweet['id']);
}

La fonction nouvelEnvoi est intéressante. C'est elle qui va nous permettre de faire le lien avec notre dernier robot.


Ce troisième robot permettait donc d'envoyer des tweets mentions via le compte @canalsat, toujours grâce à notre composant dédié à Twitter. Voici un aperçu du code qu'il contenait :


// EnvoisController.php

public function mentions() {

	// On récupère les envois qui ont un statut à 0, c'est à dire non traité
	// On a défini en amont une limite de tweets maximum envoyés en un seul appel
	$envois = $this->Envoi->find('all', array('conditions' => array('Envois.statut' => 0), 'order' => array('Envois.id' => 'ASC'), 'limit' => LIMITE_TWEETS_ENVOIS));

	foreach ($envois as $envoi) {

		// On construit le tweet à envoyer
		$status = '@' . $envoi['Envois']['screen_name'] . ' ' . $envoi['Envois']['texte'];
		$tweetResponseId = $envoi['Envois']['tweet_id'];

		// On envoi le tweet
		// $envoyerMention contiendra alors un code de retour, 1 si tout s'est bien passé par exemple
		$envoyerMention = $this->TwitterComponent->envoyerUnTweet($tweetResponseId, $status);
		
		// On met à jour l'envoi 
		$this->Envoi->id = $envoi['Envois']['id'];
		$this->Envoi->saveField('statut', $envoyerMention);
	}
}

Ce robot était lancé toutes les 5 minutes, et pouvait envoyer jusqu'à 150 tweets par appel.


Récapitulatif

Nous avions donc :

  • Une collecte des tweets toutes les 4 minutes, qui récupérait tous les derniers tweets contenant le hashtag #gotwar. Grâce à notre composant Twitter, ce robot est capable de récupérer plusieurs milliers de tweets en quelques minutes.
  • Un traitement des tweets toutes les 5 minutes, personnalisé entièrement pour les mécaniques de la GoTwar, qui créait alors un scénario d'animations pour les 5 minutes à venir.
  • Un envoi de tweets mentions toutes les 5 minutes.

Tous ces robots étaient programmés avec des décalages de façon à éviter qu'ils ne se lancent en même temps. Cela pouvait être gênant pour les 2 premiers surtout. Il fallait en effet s'assurer de toujours collecter de nouveaux tweets avant de lancer un nouveau traitement !


Cet article est terminé, il se voulait volontairement différent des articles habituels. Le code présenté sert surtout à appuyer la réflexion, il ne peut pas forcément être utilisé en l'état. Surtout qu'il va manquer le Composant Twitter, que nous sommes actuellement en train de travailler pour pouvoir le proposer prochainement dans le Studio Web and Cow, comme nous avons pu le faire avec le Composant Facebook.

Un deuxième article présente les mécanismes javascript qui ont permis de réaliser les animations de la GoTwar.

En tous cas, n'hésitez pas à laisser votre avis et à poser des questions dans la zone de commentaires ci-dessous.





comments powered by Disqus





Vous avez un projet ? Contactez-nous.

Agence web basée à Rennes, nous avons aussi l'habitude de travailler à distance avec des gens situés à Paris, Lyon, Nantes, etc. Donc peu importe votre localisation ou l'avancement dans la réflexion de votre projet, laissez-nous vos coordonnées et nous vous recontacterons très vite pour en parler plus en détails.


4 bis allée du Bâtiment, 35000 Rennes
06.47.52.18.78     a.weill@webandcow.com