2021-03-29
On poursuit notre découverte des APIs Twitch avec ce troisième article qui traitera de l'API REST fournie par le célèbre service de streaming vidéo. Aujourd'hui on va développer une alerte qui affichera (presque) en temps réel les nouveaux followers de notre chaîne Twitch, toujours à l'aide des langages JavaScript, HTML et CSS.
Pour le code de cet article, on repart de celui écrit dans l'article précédent (que vous pouvez retrouver sur mon Github), que l'on va compléter au fur et à mesure.
--------------------------------------------------------------------------------
📝️ Note:
--------------------------------------------------------------------------------
Cet article fait partie d'une série consacrée à la découverte des APIs Twitch :
Création d'applications et intégration à OBS
Afficher des alertes pour les nouveaux followers !
--------------------------------------------------------------------------------
Pour développer nos alertes, on va intensivement utiliser un concept présent depuis la norme ES2015 de JavaScript : les promises (ou promesses en français).
Pour faire court, les promesses vont nous permettre de chaîner facilement des appels à des fonctions asynchrones (lorsque l'on fait une requête HTTP par exemple) et de gérer une file d'attente pour nos notifications (pour ne pas risquer d'en afficher plusieurs simultanément).
Lorsqu'une fonction retourne une promesse, elle retourne un objet dont le contenu sera rempli une fois la tâche asynchrone terminée. Cet objet permet, via sa méthode .then(), de renseigner une fonction a exécuter ensuite, lorsque la tâche se sera terminée avec succès. La fonction ainsi renseignée recevra en paramètre la valeur de retours de la fonction précédente.
On peut également renseigner une fonction à appeler lorsque tout ne se passe pas comme prévu à l'aide de la méthode .catch(). La fonction passée à .catch() recevra alors l'erreur rencontrée.
Voici un exemple de chaîne de promise pour expliciter un peu la description ci-dessus :
twitch.getUserId("flozz_") .then(twitch.getNewFollowers) .then(afficherNouveauxFollowers) .catch(function(error) { console.log("Quelque chose s'est mal passé :", error); });
Je ne vous en dis pas plus sur les Promises, ce n'est pas le sujet principal de cet article, mais je vous invite à aller lire un peu de documentation à leur sujet :
Les promesses en JavaScript (Zest de Savoir)
Utiliser les promesses (Mozilla Developer Network)
Comme vous le savez certainement, une API REST s'exploite en faisant des requêtes HTTP (souvent on parle aussi de requêtes AJAX). Je vais utiliser l'API Fetch de JavaScript pour réaliser ces requêtes, mais comme pour les helpers de l'article précédent, je ne vais pas vous expliquer tout cela en détails : ça dépasse le scope de cet article.
Je vais donc créer une fonction qui effectue tout le travail, et vous expliquer comment elle s'utilise, sans en détailler le contenu :
const request = { // [Promise] Download (GET) a JSON from the given URL getJson: function(url, params=null, headers={}) { // ... }, };
Cette fonction prend de 1 à 3 paramètres :
Pour finir, cette fonction retourne une Promise qui, si elle est résolue, fournira la réponse de l'API sous la forme d'un objet.
Exemple d'utilisation :
request.getJson("http://example.org/data.json") .then(function(data) { console.log("Réponse de l'API :", data); }) .catch(function(error) { console.error("Une erreur est survenue :", error); });
Pour notre premier appel à l'API de Twich, on va commencer par récupérer notre ID. En effet, nous aurons besoin de fournir cet identifiant numérique lors de la plupart de nos appels à l'API.
La première étape est de trouver quel endpoint de l'API peut nous fournir cette information. Après quelques recherches dans la documentation, on trouve ce qu'il nous faut :
Maintenant que l'on sait tout ça on peut se remettre les mains dans le code, en commençant par ajouter l'autorisation manquante à notre variable SCOPES :
const SCOPES = ["user:read:email"];
--------------------------------------------------------------------------------
⚠️ Warning
--------------------------------------------------------------------------------
ATTENTION : Étant donné qu'on demande de nouvelles autorisations, il nous faut demander un nouveau token, et donc repasser par la phase d'authentification et d'autorisation de l'application, comme expliqué dans l'article précédent.
--------------------------------------------------------------------------------
On peut à présent écrire une fonction twitch.getUserId() qui nous permettra de récupérer notre ID Twitch depuis notre nom d'utilisateur :
const twitch = { // ... getUserId: function(userName) { const params = helpers.getUrlParams(); // On fait appel à la fonction que je vous ai présentée plus tôt // pour faire des requêtes HTTP return request.getJson("https://api.twitch.tv/helix/users", { // Paramètres à passer dans l'URL (?login=trucuche) "login": userName, }, { // Entêtes supplémentaires pour la requête // ID de notre application "client-id": CLIENT_ID, // Le token obtenu après la phase d'authentification / authorisation de l'application. // ATTENTION : Ce token s'écrit sous la forme "Bearer XXXXXXX". "Authorization": `Bearer ${params["access_token"]}`, }) .then(function(result) { // On retourne la partie qui nous intéresse de la réponse. return result.data[0].id; }); }, };
On peut à présent tester tout ça rapidement dans notre navigateur. Pour cela, appuyez sur F12 afin d'ouvrir la console et tapez le code suivant :
twitch.getUserId("flozz_").then(console.log)
Si tout s'est bien passé, Twitch devrait vous retourner l'ID de l'utilisateur demandé, soit 92454370 pour moi :
Capturé d'écran de l'exécution de twitch.getUserId() dans la console du navigateur
Maintenant qu'on sait faire un appel API et qu'on sait comment récupérer son ID Twitch, on peut aller récupérer la liste de ses derniers followers.
Encore une fois on trouve notre bonheur en fouillant dans la doc :
On peut donc écrire une fonction twitch.getLastFollowers() qui récupère nos derniers followers :
const twitch = { // ... getLastFollowers(userId) { const params = helpers.getUrlParams(); return request.getJson("https://api.twitch.tv/helix/users/follows", { to_id: userId, }, { "client-id": CLIENT_ID, "Authorization": `Bearer ${params["access_token"]}`, }).then(function(data) { return data.data; }); }, }
Cette fonction nous retournera la liste des 20 derniers followers, classés du plus récent au plus ancien.
Pour tester tout ça, on peut encore une fois reprendre notre navigateur et y écrire le code suivant :
twitch.getLastFollowers(92454370).then(console.log)
On peut également chaîner cette fonction avec la précédente pour ne pas avoir à écrire l'ID à la main :
twitch.getUserId("flozz_") .then(twitch.getLastFollowers) .then(console.log)
Voici un aperçu du résultat :
Capturé d'écran de l'exécution de twitch.getLastFollowers() dans la console du navigateur
Bon, on arrive à récupérer nos derniers followers, c'est cool, mais nous ce dont on a besoin pour afficher nos alertes, ce sont les NOUVEAUX followers.
Pour faire cela, rien de bien compliqué : on va écrire une fonction qui va retenir l'ID des derniers followers, ce qui nous permettra de déterminer si une nouvelle personne a suivi la chaîne depuis le dernier appel à cette fonction.
Si on traduit ça sous forme de code, ça donne ceci :
const twitch = { // ... _lastFollowersIds: null, getNewFollowers: function(userId) { // On appelle notre fonction qui récupère les derniers followers return twitch.getLastFollowers(userId) .then(function(followers) { // On gère le cas où la chaîne n'aurait pas encore de followers du tout if (followers.length == 0) { twitch._lastFollowersIds = []; return []; } // Premier appel à cette fonction : on remplit la liste des // derniers followers if (twitch._lastFollowersIds === null) { twitch._lastFollowersIds = []; for (const i in followers) { twitch._lastFollowersIds.push(followers[i].from_id); } return []; } const result = []; // On parcours la liste des derniers followers et on met de côté // les petits nouveaux... :) for (const i in followers) { // On arrête de parcourir la liste lorsque l'on // rencontre un follower déjà connu if (twitch._lastFollowersIds.includes(followers[i].from_id)) { break; } // On met de côté le nouveau follower result.push(followers[i]); // ... et on oublie pas d'enregistrer son ID dans la liste // des followers connus twitch._lastFollowersIds.push(followers[i].from_id); } return result; }); }, }
--------------------------------------------------------------------------------
📝️ Note:
--------------------------------------------------------------------------------
NOTE : Dans une première version de cette fonction, écrite en live sur Twitch, je conservais uniquement l'ID du dernier follower. Ça fonctionne bien la plupart du temps, mais si le follower arrête de suivre la chaîne une fois l'alerte le concernant passée, le script perd tout ses repères et va considérer les 20 derniers followers comme étant nouveaux... ce qui est quelque peu embêtant puisqu'on part pour 3 minutes et 40 secondes de notifications... 😅️
--------------------------------------------------------------------------------
Cette fois-ci ça va être plus compliqué de tester : on va devoir exécuter en boucle le code suivant dans la console de notre navigateur jusqu'à ce que quelqu'un suive la chaîne :
twitch.getUserId("flozz_") .then(twitch.getNewFollowers) .then(console.log)
Et voici un exemple de résultat si quelqu'un suit la chaîne entre deux appels à la fonction :
Capturé d'écran de l'exécution de twitch.getNewFollowers() dans la console du navigateur
Maintenant que nos appels API sont au point, on va passer à l'implémentation de l'alerte en elle-même. On va commencer par rajouter un peu de HTML et de CSS à notre projet.
Dans index.html :
<div class="alert" id="alert-follower"> <img src="./follower.gif" alt="" /> <div class="alert-text"> <span id="alert-follower-name">Trucmuche</span> just followed the channel! </div> </div> <audio src="./alert.ogg" preload="auto" id="alert-sound"></audio>
Dans style.css :
.alert { opacity: 0; } .alert.visible { opacity: 1; }
Je vous ai mis ici le minimum de code nécessaire à la compréhension pour pas que ça soit trop long. Vous retrouverez bien sûr le code complet de cet exemple sur Github.
Il ne nous reste plus qu'à écrire un peu de JavaScript pour afficher / masquer cette alerte... Mais juste avant, ça, on va ajouter un nouvel helper dont on aura besoin :
const helpers = { // ... wait: function(seconds) { return new Promise(function(resolve, reject) { setTimeout(resolve, seconds * 1000); }); }, }
Ce helper est tellement simple que je vous ai laissé le code cette fois-ci. Il s'agit simplement d'une fonction qui retourne une promise qui sera résolue au bout du nombre de secondes demandées. Le but est simplement de « mettre en pause » notre programme pour laisser aux gens le temps de voir nos alertes avant de la masquer.
Passons à présent à la fonction affichant l'alerte :
const alerts = { // Ici on a une Promise résolue qui nous servira de file d'attente pour // nos alertes. _queue: Promise.resolve(), // Rajoute une alerte de nouveau follower à la file d'attente. newFollower(name) { const divAlertFollower = document.getElementById("alert-follower"); const spanAlertFollowerName = document.getElementById("alert-follower-name"); const audioAlertSound = document.getElementById("alert-sound"); // Affiche l'alerte function _show() { // On met à jour le nom du nouveau follower spanAlertFollowerName.innerText = name; // On rend l'alerte visible en lui ajoutant la classe "visible" divAlertFollower.classList.add("visible"); // On joue le son de l'alerte audioAlertSound.play(); } // Masque l'alerte function _hide() { // On cache l'alerte en supprimant la classe "visible" divAlertFollower.classList.remove("visible"); } // On rajoute une chaîne de Promise à la file d'attente. alerts._queue = alerts._queue .then(_show) // On affiche l'alerte .then(helpers.wait.bind(null, 10)) // On attend 10 secondes .then(_hide) // On masque l'alerte .then(helpers.wait.bind(null, 1)); // On attend 1 seconde // | (pour laisser le temps à l'alerte // | de se masquer avant d'enchaîner // | avec la suivante) }, };
--------------------------------------------------------------------------------
📝️ Note:
--------------------------------------------------------------------------------
NOTE sur la méthode .bind() :
La méthode .bind() retourne une fonction avec le contexte (this) et des paramètres préremplis. Ici on s'en sert pour préremplir la durée de l'attente. On aurait également pu se passer de l'appel à cette méthode en « wrappant » l'appel à helpers.wait() dans une fonction anonyme :
.then(function() { helpers.wait(10); })
Pour plus d'informations sur la méthode .bind(), vous pouvez consulter sa documentation sur MDN.
consulter sa documentation sur MDN
--------------------------------------------------------------------------------
Si on veut tester notre alerte, la méthode est toujours la même, on ouvre la console JavaScript du navigateur, et on appelle notre fonction. On peut même essayer de l'appeler plusieurs fois de suite afin de s'assurer qu'une seule notification est bien affichée à la fois :
alerts.newFollower("Trucmuche"); alerts.newFollower("JeanKevin");
Voici ce que ça donne chez moi :
./videos/video_alert_demo.webm
On est presque au bout, il ne nous reste plus qu'une seule chose à implémenter : le polling. Rien de bien compliqué, il s'agit juste d'écrire une fonction qui va aller interroger en boucle l'API de Twitch pour récupérer les nouveaux followers :
function newFollowerPolling(userId) { function _displayNewFollowers(newFollowers) { for (let i in newFollowers) { alerts.newFollower(newFollowers[i].from_name); } } // On récupère les nouveaux followers twitch.getNewFollowers(userId) .then(_displayNewFollowers); // On rajoute un petit timer qui rappelera cette fonction dans 15 secondes // /!\ On prend bien soin de lui fournir le paramètre userId à l'aide de bind() ! setTimeout(newFollowerPolling.bind(null, userId), 15 * 1000); }
Et pour terminer, on met à jour notre fonction main() comme ceci :
const TWITCH_CHANNEL = "flozz_"; function main() { if (!twitch.isAuthenticated()) { twitch.authentication(); } else { twitch.getUserId(TWITCH_CHANNEL) .then(newFollowerPolling); } } window.onload = main;
Le code est terminé, il ne reste plus qu'à intégrer cette page web dans OBS comme on l'a vu dans le premier article... À un détail près cependant.
Lorsque cette page sera intégrée dans le browser d'OBS, on va se retrouver bloqués sur un page d'authentification de Twitch.
Demande d'authentification de Twitch dans le Browser d'OBS
Il y a alors 2 possibilités :
La première est d'ouvrir l'application dans un navigateur, de vous identifier et de recopier dans OBS l'URL de la page avec le token obtenu après authentification. Je n'ai par contre aucune idée de la durée de validité dudit token.
L'autre solution est de passer le navigateur d'OBS en mode « interactif » et de s'identifier directement dedans. Pour ce faire,
Menu contextuel de la source OBS Linux Browser
Il ne vous reste plus qu'à vous identifier dans la fenêtre qui apparait :
Fenêtre interactive de la source OBS Linux Browser
Et voilà, on a implémenté nos propres alertes pour afficher les nouveaux followers de la chaîne !
Comme d'habitude, vous retrouvez les sources complètes de l'exemple de cet article sur Github :
J'avais à l'origine développé ces alertes en live sur ma chaîne Twitch. Vous pourrez retrouver la VOD issue de ces lives sur ma chaîne YouTube :
À bientôt pour le prochain article de la série qui traitera de l'API PubSub de Twitch, qui nous permettra entre autres de récupérer en temps réel les nouveaux subscribers (abonnés payants) et d'interagir avec les points de chaînes !
--------------------------------------------------------------------------------