Construire avec l'API Twitter Répéter les tweets d'un groupe

Ce que vous allez créer

Ce tutoriel fait partie d'une série liée à l'API Twitter. Vous pouvez trouver le tutoriel original de Birdcage Twitter ici ou suivez ma page auteur suivre les derniers ajouts à la série. Ce tutoriel particulier repose sur Birdcage et les modèles de données et le code de la précédent tutoriel tempête tweet.

Si vous êtes un fabricant comme moi, vous utilisez souvent Twitter pour partager des détails sur vos créations. Il y a souvent plus à dire que vous ne pouvez contenir 140 caractères, et la plupart de vos abonnés ne voient même pas chaque tweet. Même s'ils voient quelque chose que vous avez posté, ils peuvent le préférer et l'oublier. Il est utile d’avoir un service qui partage régulièrement différents aspects de votre annonce. La nature du flux Twitter rend la répétition utile, dans des limites raisonnables; exagérer c'est spammy et ennuyeux.

Ce didacticiel s’appuie sur mon précédent article sur les tempêtes tweet pour vous montrer comment créer un service qui publie régulièrement une mise à jour de statut sélectionnée de manière aléatoire sur votre travail. Cela automatise la tâche de répétition et de création de variations dans le temps pour augmenter la probabilité que vos suiveurs Twitter s'engagent avec votre contenu..

N'oubliez pas que l'API Twitter a des limites sur le contenu répétitif. Vous obtiendrez plus de succès si vous proposez une grande variété de variantes et exécutez le service sur un compte que vous utilisez également manuellement pour partager un autre contenu. Twitter rejettera probablement les tweets répétitifs de bots purement marketing - et vous rencontrerez probablement cela pendant que vous testez.

Caractéristiques requises

Les exigences de base pour notre fonctionnalité sont les suivantes:

  • Laissez l'utilisateur écrire et stocker un "groupe" de tweets au sein d'un groupe.
  • Sur une base récurrente, sélectionnez au hasard un tweet du groupe à publier sur notre compte..
  • Publiez régulièrement ces éléments à des intervalles configurables par l'utilisateur avec un décalage temporel aléatoire.
  • Autoriser l'utilisateur à définir un nombre maximal de récurrences, par exemple. 100.
  • Autoriser l'utilisateur à réinitialiser les groupes pour redémarrer la répétition.

Construire sur l'infrastructure pour les groupes que nous avons construite dans le tutoriel sur les tempêtes de tweets ne nécessite qu'un nombre modeste de nouveau code supplémentaire pour les tweets récurrents.

Le modèle de base de données

Nous utiliserons la migration pour créer la table de groupe, qui est légèrement différente de celle utilisée dans le tutoriel sur les tempêtes de tweets:

./ app / protected / yiic migrer créer create_group_table

Le code ci-dessous construit le schéma. Notez la relation de clé étrangère pour lier le groupe à un compte Twitter spécifique:

tablePrefix = Yii :: app () -> getDb () -> tablePrefix; if ($ this-> tablePrefix <> ") $ this-> tableName = $ this-> tablePrefix.'group '; fonction publique safeUp () $ this-> before (); $ this-> createTable ($ this -> tableName, array ('id' => 'pk', 'account_id' => 'entier par défaut 0', 'nom' => 'chaîne par défaut NULL', 'slug' => 'chaîne par défaut NULL', 'type_groupe '=>' tinyint default 0 ',' stage '=>' entier par défaut 0 ',' created_at '=>' DATETIME NOT NULL DEFAULT 0 ',' modified_at '=>' TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ',' next_publish_time ' => 'INTEGER DEFAULT 0', 'interval' => 'TINYINT DEFAULT 0', 'interval_random' => 'TINYINT DEFAULT 0', 'max_repeats' => 'INTEGER DEFAULT 0', 'status' => 'tinyint default 0 ',), $ this-> MySqlOptions); $ this-> addForeignKey (' fk_group_account ', $ this-> tableName,' account_id ', $ this-> tablePrefix.'account', 'id', 'CASCADE', '' CASCADE '); fonction publique safeDown () $ this-> before (); $ this-> dropForeignKey (' fk_group_account ', $ this-> nom_table); $ this-> dropTable ($ this-> nom_table); 

Les nouveaux champs incluent next_publish_time, intervalle, interval_random, max_repeats, et statut.

Nous allons également utiliser la même table relationnelle appelée GroupStatus qui suit les tweets d’état au sein de chaque groupe:

tablePrefix = Yii :: app () -> getDb () -> tablePrefix; if ($ this-> tablePrefix <> ") $ this-> tableName = $ this-> tablePrefix.'stat_groupe '; fonction publique safeUp () $ this-> avant (); $ this-> createTable ($ this -> tableName, array ('id' => 'pk', 'group_id' => 'INTEGER NOT NULL', 'status_id' => 'INTEGER default 0',), $ this-> MySqlOptions); $ this-> addForeignKey ('fk_group_status_group', $ this-> tableName, 'id_groupe', $ this-> préfixe_table.'group ',' id ',' CASCADE ',' CASCADE '); $ this-> addForeignKey (' fk_group_status_status ', $ this-> tableName, 'status_id', $ this-> tablePrefix.'status ',' id ',' CASCADE ',' CASCADE '); public function safeDown () $ this-> avant (); $ this- > dropForeignKey ('fk_group_status_group', $ this-> nomTable); $ this-> dropForeignKey ('fk_group_status_status', $ this-> nomTable); $ this-> dropTable ($ this-> nomTable);

Construire le code

Utilisez le didacticiel Tweet Storm pour voir comment utiliser Gii de Yii pour créer le code d'échafaudage du contrôleur de groupe, ainsi que les modèles correspondants. GroupStatus.

Voici à quoi ressemble le formulaire "Créer un groupe":

Voici le code d'affichage du formulaire. Notez que JQuery affiche et masque les paramètres supplémentaires lorsque l'utilisateur sélectionne un type de groupe récurrent (par opposition à un groupe de tempêtes tweet):

beginWidget ('bootstrap.widgets.TbActiveForm', array ('id' => 'group-form', 'enableAjaxValidation' => false,)); ?> user-> hasFlash ('no_account')) $ this-> widget ('bootstrap.widgets.TbAlert', array ('alerts' => array (// configurations par type d'alerte 'no_account' => array ('block' => true, 'fade' => true, 'closeText' => '×'),),)); ?> 

Champs avec * sont requis.

errorSummary ($ model); ?> 'Créer un groupe avec quel compte:')); echo CHtml :: activeDropDownList ($ model, 'account_id', Account :: model () -> getList (), array ('empty' => 'sélectionner un compte')); ?> textFieldRow ($ model, 'name', array ('class' => 'span5', 'maxlength' => 255)); ?> 'Type de groupe:')); ?> dropDownList ($ model, 'group_type', $ model-> getTypeOptions ()); ?>

Horaire de publication ou heure de début:
Cliquez sur le champ ci-dessous pour définir la date et l'heure.

widget ('ext.jui.EJuiDateTimePicker', array ('modèle' => $ modèle, 'attribut' => 'next_publish_time', 'language' => 'en', 'mode' => 'datetime', // ' datetime 'ou' time '(' datetime 'par défaut)' options '=> array (' dateFormat '=>' M j, aa ',' timeFormat '=>' hh: mm tt ', //' hh: mm tt 'default' alwaysSetTime '=> true,),)); ?>

Choisissez des options pour votre méthode récurrente (facultatif):
'Récurrent: choisissez un intervalle:')); echo CHtml :: activeDropDownList ($ modèle, 'intervalle', Status :: modèle () -> getIntervalList (false), array ('vide' => 'Sélectionnez un intervalle')); ?> «Nombre maximal de postes répétés: ')); echo CHtml :: activeDropDownList ($ model, 'max_repeats', Status :: model () -> getMaxRepeatList (), array ('empty' => 'Sélectionnez un nombre maximal')); ?> 'Choisissez une période de randomisation pour vos intervalles:')); echo CHtml :: activeDropDownList ($ model, 'interval_random', Status :: model () -> getRandomList (), array ('empty' => 'Sélectionnez un intervalle')); ?>

widget ('bootstrap.widgets.TbButton', array ('buttonType' => 'submit', 'type' => 'primaire', 'label' => $ model-> isNewRecord? 'Create': 'Save', ) ?>
endWidget (); ?>

Voici à quoi ressemble la page d'index du contrôleur de groupe une fois que vous avez ajouté des groupes:

Voici le code qui exécute la vue d'index. Tout d'abord, l'action d'index du contrôleur de groupe:

 / ** * Gère tous les modèles. * / fonction publique actionIndex () $ model = new Group ('search'); $ model-> unsetAttributes (); // efface toutes les valeurs par défaut if (isset ($ _GET ['Groupe'])) $ modèle-> attributs = $ _GET ['Groupe']; $ this-> render ('admin', array ('model' => $ model,));  

Ensuite, le code de la vue admin, qui utilise le contrôleur Yii Gridview:

breadcrumbs = array ('Groupes' => array ('index'), 'Manage',); $ this-> menu = array (array ('label' => 'Ajouter un groupe', 'url' => array ('create')),); Yii :: app () -> clientScript-> registerScript ('search', "$ ('. Search-button'). Cliquez sur (function () $ ('. Search-form'). Toggle (); return false ;); $ ('. formulaire de recherche'). submit (function () $ .fn.yiiGridView.update ('groupe-grille', data: $ (this) .serialize ()); return faux; ); "); ?> 

Gérer des groupes de tweets

widget ('bootstrap.widgets.TbGridView', array ('id' => 'group-grid', 'dataProvider' => $ model-> search (), 'filter' => $ model, 'columns' => tableau (array ('header' => 'Account', 'name' => 'account_id', 'value' => array (Account :: model (), 'renderAccount'),), 'name', 'slug', array ('header' => 'Type', 'name' => 'group_type', 'value' => array (Group :: model (), 'renderGroupType'),), array ('name' => 'status ',' header '=>' Status ',' value '=> array ($ model,' renderStatus '),),' stage ', array (' htmlOptions '=> array (' width '=>' 150px ') , 'class' => 'bootstrap.widgets.TbButtonColumn', 'header' => 'Options', 'template' => 'manage update delete', 'boutons' => tableau ('manage' => array ('options' => array ('title' => 'Manage'), 'label' => '',' url '=>' Yii :: app () -> createUrl ("groupe / vue", array ("id" => $ data-> id)) ',),),), // bouton de fin tableau),)); ?>

Vous pouvez accéder à la page Vue du groupe en cliquant sur l'icône "liste" la plus à gauche d'un groupe individuel. À partir de là, vous pouvez ajouter des tweets individuels:

Le code de composition est presque identique à celui du référentiel Birdcage. Une fois que vous avez ajouté une poignée de tweets de statut à votre groupe, vous obtenez un résultat similaire à celui-ci:

Plus vous pouvez fournir de volume et de variété de tweets, plus vous serez en mesure de republier des tweets automatisés avec succès sans vous heurter aux limitations intégrées de Twitter concernant les robots..

Les groupes récurrents ne commencent pas à tweeter tant que vous ne les avez pas activés. Notez les options de menu de droite dans l'image ci-dessus. Les groupes récurrents courent jusqu'à ce que le nombre d'étapes atteigne le nombre maximal de tweets publiés. L'utilisateur peut également réinitialiser un groupe pour recommencer le processus..

Voici le code pour les opérations d'activation et de réinitialisation:

 public function activate ($ group_id) // crée une action pour publier la tempête en arrière-plan $ gp = Group :: model () -> findByPK ($ group_id); if ($ gp-> status == self :: STATUS_PENDING ou $ gp-> status == self :: STATUS_TERMINATED) $ gp-> status = self :: STATUS_ACTIVE; else $ gp-> status = self :: STATUS_TERMINATED; $ gp-> save ();  public function reset ($ group_id) // crée une action pour publier la tempête en arrière-plan $ gp = Group :: model () -> findByPK ($ group_id); if ($ gp-> status == self :: STATUS_TERMINATED ou $ gp-> status == self :: STATUS_COMPLETE) $ gp-> status = self :: STATUS_ACTIVE; $ gp-> stage = 0; // remise à zéro de l'étape $ $ gp-> next_publish_time = time () - 60; // réinitialisé il y a une minute $ gp-> save (); 

Publication des tweets récurrents

le DaemonController La méthode index est appelée par cron en arrière-plan. Ceci appelle le modèle du groupe processusRécours méthode. Cela parcourt chaque compte à la recherche de groupes en retard..

fonction publique processRecurring () // boucle sur les utilisateurs de l'application Birdhouse (généralement 1) $ users = User :: model () -> findAll (); foreach ($ utilisateurs en tant que $ utilisateur) $ utilisateur_id = $ utilisateur ['id']; echo 'Utilisateur:'. $ utilisateur ['nom d'utilisateur']; lb (); // boucle dans les comptes Twitter (peut être multiple) $ accounts = Account :: model () -> findAllByAttributes (array ('user_id' => $ user_id)); foreach ($ comptes en tant que $ compte) $ account_id = $ account ['id']; echo 'Compte:'. $ compte ['nom_écran']; lb (); $ this-> publishRecurring ($ compte);  // boucle du compte final // boucle de l'utilisateur final

Ensuite, nous appelons publierRecours. Nous utilisons des étendues nommées pour rechercher des groupes récurrents dont next_publish_time est en retard. Nous traitons par compte afin de minimiser le nombre de connexions Twitter OAuth nécessaires. Les portées sont montrées plus bas.

 fonction publique publishRecurring ($ account) // traiter tous les groupes actifs en retard $ groups = Group :: model () -> in_account ($ account ['id']) -> recur () -> active () -> en retard () -> findAll (); if (count ($ groups)> 0) // établit la connexion à Twitter une fois pour chaque compte $ twitter = Yii :: app () -> twitter-> getTwitterTokened ($ account ['oauth_token'], $ compte [' oauth_token_secret ']); // traite chaque statut en retard pour chaque groupe ($ groupes en tant que $ groupe) // examine le type // sélectionne un statut aléatoire $ status = Status :: model () -> in_specific_group ($ group-> id) -> find (array ('order' => 'rand ('. rand (1,255). ')')); echo $ status-> tweet_text; lb (); // tweet it $ tweet_id = Status :: model () -> postTweet ($ twitter, $ status); // vérifie le niveau maximum si ($ group ['stage']> = $ group ['max_repeats']) $ group ['status'] = self :: STATUS_COMPLETE; $ group ['next_publish_time'] = 0;  else // set next_publish time - il est correct d'utiliser la méthode du modèle d'état $ group ['next_publish_time'] = Status :: model () -> getNextRecurrence ($ group);  $ group ['stage'] + = 1; // sauvegarde les données du groupe mis à jour dans la base de données $ updated_group = Group :: model () -> findByPk ($ group ['id']); $ updated_group-> stage = $ group ['stage']; $ updated_group-> next_publish_time = $ group ['next_publish_time']; $ updated_group-> status = $ group ['status']; $ updated_group-> save ();  // fin de la boucle de groupe // fin si groupes> 0 

Voici les étendues ActiveRecord que nous utilisons pour rechercher les groupes en retard:

fonction publique scopes () return array ('active' => array ('condition' => 'status ='. self :: STATUS_ACTIVE,), 'recur' '=> array (' condition '=>' group_type = '. self :: GROUP_TYPE_RECUR,), 'overdue' => array ('condition' => 'next_publish_time < UNIX_TIMESTAMP(NOW())', ), );  // custom scopes public function in_account($account_id=0)  $this->getDbCriteria () -> mergeWith (array ('condition' => 'account_id ='. $ account_id,)); return $ this; 

Lorsque nous trouvons un groupe qui doit être mis à jour, nous utilisons ce code pour sélectionner au hasard un tweet d'état et mettre à jour le message. next_publish_time:

// sélectionne un statut aléatoire $ status = Status :: model () -> in_specific_group ($ group-> id) -> find (array ('order' => 'rand ('. rand (1 255).)) ) echo $ status-> tweet_text; lb (); // tweet it $ tweet_id = Status :: model () -> postTweet ($ twitter, $ status); // vérifie le niveau maximum si ($ group ['stage']> = $ group ['max_repeats']) $ group ['status'] = self :: STATUS_COMPLETE; $ group ['next_publish_time'] = 0;  else // set next_publish time - il est correct d'utiliser la méthode du modèle d'état $ group ['next_publish_time'] = Status :: model () -> getNextRecurrence ($ group);  $ group ['stage'] + = 1;

Nous calculons la récurrence en combinant l'intervalle de retard et un décalage temporel aléatoire qui permet aux tweets d'apparaître dans des fuseaux horaires différents et des périodes légèrement différentes pour atteindre différents utilisateurs:

 fonction publique getNextRecurrence ($ status) // calcule la prochaine heure récurrente pour poster $ start_time = time (); if ($ status ['interval' '] == self :: STATUS_INTERVAL_HOUR) $ hours = 1;  else if ($ status ['interval' '] == self :: STATUS_INTERVAL_THREEHOUR) $ hours = 3;  else if ($ status ['intervalle'] == self :: STATUS_INTERVAL_SIXHOUR) $ hours = 6;  else if ($ status ['interval' '] == self :: STATUS_INTERVAL_HALFDAY) $ hours = 12;  else if ($ status ['intervalle'] == self :: STATUS_INTERVAL_DAY) $ hours = 24;  else if ($ status ['interval' '] == self :: STATUS_INTERVAL_TWODAY) $ hours = 48;  else if ($ status ['intervalle'] == self :: STATUS_INTERVAL_THREEDAY) $ hours = 72;  else if ($ status ['intervalle'] == self :: STATUS_INTERVAL_WEEK) $ hours = 168;  $ start_time + = ($ hours * 3600); $ ri = $ this-> getRandomInterval ($ status ['interval_random']); if (($ start_time + $ ri)0) $ ri = rand (1, $ ri); $ ri = $ ri * 60; // fois # de secondes si (rand (1,100)> 50) $ ri = 0 - $ ri; return $ ri;  

Les résultats affichés au fil du temps ressembleront à ceci:

Améliorations futures

Vous voudrez peut-être même uniformiser la distribution des tweets en enregistrant la fréquence utilisée et en choisissant parmi les éléments les moins répétés à chaque itération.

Vous pouvez également souhaiter permettre aux groupes d’avoir un suffixe de hashtags pouvant être sélectionnés de manière aléatoire pour faire varier le contenu lorsque vous tweetez..

En clôture

J'espère que vous avez trouvé cela intéressant et utile. Encore une fois, vous pouvez trouver le tutoriel original de Birdcage sur Twitter ici ou suivre ma page d’auteur pour suivre les derniers ajouts à la série des API de Twitter.. 

N'hésitez pas à poster vos propres corrections, questions et commentaires ci-dessous. Je suis particulièrement intéressé par vos améliorations. J'essaie de rester engagé avec le fil de discussion. Vous pouvez également me joindre sur Twitter @reifman ou m'envoyer un email directement.