Alertes cryptomonnaies par SMS avec JavaScript, Node.js et Twilio

June 12, 2018
Rédigé par
Steve Senkus
Contributeur
Les opinions exprimées par les contributeurs de Twilio sont les leurs

Suivre les cryptomonnaies via des alarmes SMS à l'aide de JavaScript, Node.js et Twilio

Bitcoin, Ethereum, Ripple, Bitcoin Cash, Dogecoin, Litecoin – la liste des cryptomonnaies est infinie, et il ne se passe pas une journée sans que de nouvelles technologies émergent. Dans ce tutoriel, nous allons créer une application qui envoie des SMS à chaque fois que la valeur marchande d'une cryptomonnaie dépasse un seuil de prix. Les investisseurs en cryptomonnaies peuvent s'installer confortablement, laisser faire le marché et attendre que des opportunités se présentent au lieu de suivre les cours de façon compulsive !

Pour suivre le tutoriel

Pour les besoins de ce tutoriel, j'ai mis en place un référentiel avec le code complet hébergé sur Github : https://github.com/ssenkus/coin-sms-alert. Vous pourrez construire tout ce dont vous aurez besoin en suivant les étapes de ce post, mais si vous voulez voir à n'importe quel stade à quoi ressemble le projet terminé, vous pouvez cloner ce référentiel à partir de la commande suivante dans votre interface de ligne de commande ou votre terminal :

git clone git@github.com:ssenkus/coin-sms-alert.git

Pour chaque étape de ce tutoriel, vous pouvez basculer sur une branche possédant le code complet. Par exemple, exécuter la commande git checkout step-1-simple-express-app dans le répertoire racine du projet vous donnera le code exact que ce tutoriel vous demande d'écrire. (Ne vous inquiétez pas, je vous donnerai les noms de branches spécifiques à chaque étape.) Après avoir basculé sur la branche, exécutez npm install pour télécharger tout package NPM dont vous avez besoin pour l'étape du tutoriel.

Si vous êtes bloqué, vous pouvez exécuter et consulter le code de la branche pour voir un exemple qui fonctionne.

REMARQUE : pour exécuter cette application Node.js depuis la ligne de commande, utilisez npm start afin de lancer l'application et Ctrl+C pour l'arrêter.

Ce dont vous aurez besoin

Avant de commencer, vous devrez installer certains outils et logiciels sur votre ordinateur.

  • Node.js : installez la version LTS pour votre système d'exploitation. Dans ce tutoriel, j'ai utilisé la version 8.9.4.
  • MongoDB : pour mon développement local sur Mac OS X, j'ai utilisé Homebrew pour mon instance MongoDB locale. Vous pouvez choisir parmi un certain nombre d'options et de systèmes d'exploitation ici. Installez la dernière version la plus stable (3.6.4 au moment où j'écris cet article).
  • Compte Twilio : créez un compte Twilio si vous n'en avez pas déjà un. (C'est gratuit !)
  • Postman : un outil permettant de tester des API. Dans ce tutoriel, nous interagissons assez souvent avec notre API personnalisée. Il nous sera donc utile de pouvoir générer et modifier des requêtes HTTP à la volée.
  • (Facultatif) Git : à mesure que vous suivrez ce tutoriel, vous pourrez, si vous le souhaitez, vérifier votre code source par rapport au référentiel associé hébergé sur GitHub. Trouvez une option d'installation ici.
  • (Facultatif) Robo 3T (anciennement Robomongo) : si vous travaillez régulièrement avec MongoDB, l'utilisation de la ligne de commande pour exécuter des requêtes sur des collections de bases de données peut être fastidieuse. Installez Robo 3T si vous préférez avoir une interface graphique pour une représentation visuelle de votre base de données MongoDB.

Configurer votre compte Twilio

Si vous ne l'avez pas encore fait, inscrivez-vous pour obtenir un compte Twilio gratuit. Une fois que vous disposerez d'un compte, vous pourrez créer votre premier projet. Si vous avez déjà un compte Twilio, rendez-vous depuis votre navigateur à l'adresse https://www.twilio.com/console/projects/create et créez un projet.

création de nouveau projet dans Twilio

Donnez un nom au projet (par exemple, coin-sms-alert), puis cliquez sur le bouton Create Project (Créer un projet).
Une fois votre projet créé, vous serez redirigé vers une nouvelle console Twilio. Cliquez sur Manage Phone Numbers (Gérer les numéros de téléphone) :

informations du projet

Cliquez sur Get Started (Démarrer), puis sur le bouton Get your first Twilio phone number (Obtenir votre premier numéro de téléphone Twilio).

interface des numéros de téléphone

Lorsque la boîte de dialogue de confirmation s'affiche, cliquez sur Choose this Number (Choisir ce numéro), puis enregistrez le numéro de téléphone à un endroit facilement accessible. Vous devrez vous assurer que le numéro de téléphone a la possibilité d'envoyer des SMS :

confirmation d'achat de numéro de téléphone

confirmation d'achat de numéro

Pour tester votre nouveau numéro de téléphone, suivez ce tutoriel de démarrage rapide et assurez-vous que vous pouvez envoyer un SMS à votre téléphone portable. Il est important que vous enregistriez quelque part votre Account SID, votre token d'autorisation (Auth Token) et votre numéro de téléphone avec fonction SMS activée. Vous en aurez besoin plus tard dans ce post pour envoyer des SMS dans votre appli.

Créer une app de notification pour cryptomonnaies

Créons tout d'abord une base solide pour notre projet. Nous allons construire une API que nous utiliserons par la suite pour configurer les alertes.

Créer une app Express simple

En suivant cette étape de l'article :

git checkout step-1-simple-express-app; npm install

Notre première étape consiste à créer un simple serveur Node.js à l'aide du puissant framework d'application Web Express. Il constituera la base de notre app et traitera dans un premier temps les requêtes API simples pour être ensuite élargi à mesure que nous ajouterons des fonctionnalités.

À l'aide de votre terminal de ligne de commande, créez un répertoire nommé coin-sms-alert pour notre projet, puis passez dans ce répertoire.

mkdir coin-sms-alert; cd coin-sms-alert

Ensuite, initialisez un projet Node.js, ce qui créera un fichier package.json. Pour plus de simplicité, nous utiliserons l'indicateur -y dans notre commande pour ignorer toutes les invites de commande et utiliser les valeurs par défaut.

npm init -y

Un fichier package.json devrait apparaître dans votre répertoire racine.

Configurons un simple serveur Node.js et Express pour écouter nos requêtes. Nous allons devoir installer le module express à partir de npm :

npm install -save express

Créez un nouveau fichier nommé server.js avec le code suivant :

// server.js
const express = require('express');
const app = express();

// Requests to http://localhost:3000/ will respond with a JSON object
app.get('/', (req, res) => {
   res.json({success: true});
});

// Start listening on port 3000 to start receiving requests
app.listen(3000, () => {
   console.log('Example app listening on port 3000!')
});

Lancez le projet avec la commande suivante :

npm start

REMARQUE : npm start recherche server.js par défaut. Vous pouvez également exécuter node server.js pour le même résultat.

Vous devriez voir ceci dans votre terminal :

application démarrée dans le terminal

Rendez-vous depuis votre navigateur préféré sur l'URL :http://localhost:3000. Vous devriez voir quelque chose qui ressemble à ceci :

capture d'écran de localhost

Bien, nous avons maintenant une base très minime sur laquelle nous allons pouvoir construire notre app. Notre prochaine étape consiste à obtenir le cours des cryptomonnaies.

Récupérer les données des cryptomonnaies depuis l'API CoinMarketCap

En suivant cette étape de l'article :

git checkout step-2-retrieving-cryptocurrency-data; npm install

capture d'écran de CoinMarketCap

CoinMarketCap est un site populaire auprès des courtiers en crypto monnaies du fait de ses données complètes. Heureusement pour nous, une API pouvant être utilisée dans nos applications est fournie : https://coinmarketcap.com/api.

Nous pouvons maintenant configurer une simple requête au point de terminaison d'API afin de récupérer depuis CoinMarketCap les 100 cours de crypto-monnaies les plus élevés, au format JSON.

Nous aurons besoin du module reques de npm :

npm install --save request

Ensuite, ajoutez un point de terminaison d'API au fichier server.js et testez-le pour vous assurer qu'il fonctionne :

// server.js

const express = require('express');
const bodyParser = require('body-parser');
const request = require('request');
const app = express();

// Use body-parser as middleware so that we have JSON data available on Request objects
app.use(bodyParser.json({type: 'application/json'}));

// Requests to http://localhost:3000/ will respond with a JSON object
app.get('/', (req, res) => {
   res.json({success: true});
});

// Requests to http://localhost:3000/api/coins will trigger a request to CoinMarketCap API,
// respond with a JSON object with coin prices, and log a message to the console.
app.get('/api/coins', (req, res) => {
   request.get('https://api.coinmarketcap.com/v1/ticker/', (err, response, body) => {
       let coinData = JSON.parse(body);

       console.log(coinData);
       res.json(coinData);
   });
});

// Start listening on port 3000 to start receiving requests
app.listen(3000, () => {
   console.log('Coin Alert app listening on port 3000!')
});

Exécutez npm start dans votre terminal et rendez-vous depuis votre navigateur à l'adresse http://localhost:3000/api/coins. Un tableau JSON devrait apparaître à la fois dans votre navigateur et votre terminal, comme suit :

json affiché dans le navigateur

Félicitations, nous avons maintenant quelques données avec lesquelles nous amuser.

Ajouter une alerte avec Underscore.js

En suivant cette étape de l'article :

git checkout step-3-simple-alarm; npm install

Ensuite, nous allons créer une alerte très simple pour détecter les moments où une cryptomonnaie atteint un seuil d'alerte donné.

Nous utiliserons une bibliothèque d'utilitaires JavaScript très populaire appelée Underscore qui va nous aider à parcourir et filtrer les données d'API que nous appelons depuis l'API CoinMarketCap. Installez underscore via npm :

npm install --save underscore

Créez un répertoire appelé models, puis créez un fichier dans ce répertoire nommé alarm.js avec le code suivant :

// models/alarm.js
const _ = require('underscore');

class Alarm {

   constructor(data) {
       // put data object onto Alarm object
       _.extend(this, data);
   }

   // Returns a boolean based on whether alarm threshold has been exceeded
   isTriggered(latestCoinData) {
       let isAlarmTriggered = false;
       switch (this.thresholdDirection) {
           case 'under':
               isAlarmTriggered = latestCoinData.price_usd < this.priceUsdThreshold; break; case 'over': default: isAlarmTriggered = latestCoinData.price_usd > this.priceUsdThreshold;
               break;
       }
       return isAlarmTriggered;
   }
}

module.exports = Alarm;

La classe Alarm est très simple, dans la mesure où elle prend ses propriétés d'objet dans son constructeur. La méthode isTriggered est utilisée pour comparer les seuils d'alerte au cours actuel d'une crypto monnaie. Elle renvoie true si le cours a atteint ou dépassé le seuil, et false dans le cas contraire.

Maintenant, modifiez server.js pour importer notre nouveau modèle d'alerte, codez une alerte en dur et vérifiez si elle a été déclenchée. Pour des raisons de simplicité, définissez une alerte qui se déclenche lorsque le cours du Bitcoin dépasse les 1 000 $ :

// server.js

const express = require('express');
const bodyParser = require('body-parser');
const request = require('request');
const _ = require('underscore');
// Import our new Alarm model
const Alarm = require('./models/alarm');

const app = express();


app.use(bodyParser.json({type: 'application/json'}));

// Requests to http://localhost:3000/ will respond with a JSON object
app.get('/', (req, res) => {
   res.json({success: true});
});

// Requests to http://localhost:3000/api/coins will trigger a request to CoinMarketCap API,
// respond with a JSON object with coin prices, and log a message to the console if Bitcoin is above $1000.
app.get('/api/coins', (req, res) => {
   request.get('https://api.coinmarketcap.com/v1/ticker/', (err, response, body) => {
       let coinsData = JSON.parse(body);

       // Hardcode an Alarm with a low value to ensure alarm will trigger
       let alarm = new Alarm({
           coinId: 'bitcoin',
           priceUsdThreshold: 1000.00,
           thresholdDirection: 'over'
       });

       // Find Bitcoin's data object inside response collection
       let latestCoinData = _.findWhere(coinsData, { id: alarm.coinId});

       //  Log the alarm data to the console if the threshold is crossed
       if (latestCoinData && alarm.isTriggered(latestCoinData)) {
           console.log(`* ALARM * ${alarm.coinId}: $${latestCoinData.price_usd} is ${ alarm.thresholdDirection} threshold $${alarm.priceUsdThreshold}`);
       }
       // Return a JSON object of the CoinMarketCap API response
       res.json(coinsData);
   });
});

// Start listening on port 3000 to start receiving requests
app.listen(3000, () => {
   console.log('Coin Alert app listening on port 3000!')
});

Lancez l'app via le terminal :

npm start

Rendez-vous depuis votre navigateur à l'adresse http://localhost:3000/api/coins afin de voir les dernières données de l'API CoinMarketCap. Le message de la console devrait s'afficher, confirmant que notre alerte a été déclenchée.

alerte affichée dans la console

Maintenant que nous disposons de données à jour sur les cryptomonnaies et d'une alerte fonctionnelle, il est temps de commencer à enregistrer ces données dans une base de données.

Enregistrer et récupérer les alertes avec MongoDB

En suivant cette étape de l'article :

git checkout step-4-save-alarm-mongodb; npm install

Intégrez MongoDB à notre app afin de stocker nos données d'alerte et de les récupérer à chaque fois que nous extrayons les données des cours des cryptomonnaies depuis l'API CoinMarketCap. Nous allons également refactoriser et structurer notre code en modules distincts dans un souci de testabilité et de clarté.

MongoDB est une base de données NoSQL flexible qui est un choix populaire pour les applications telles que la nôtre. Avant de commencer, assurez-vous que MongoDB est installée et en cours d'exécution. Sur la ligne de commande, exécutez :

mongod

Voici ce que vous devriez voir dans votre terminal lorsque MongoDB est en cours d'exécution :

execution de mongo db dans la console

Ensuite, installez le client mongodb dans notre app Node.js :

npm install --save mongodb

Créez dans votre répertoire racine un dossier nommé dataAccess, puis créez un fichier nommé mongoClientWrapper.js. Nous pouvons encapsuler le code client MongoDB dans un module afin de pouvoir configurer la chaîne de connexion, le nom de la base de données, les collections et toute autre fonctionnalité dont nous avons besoin.

// mongoClientWrapper.js
const mongoClient = require('mongodb').MongoClient;

let db = null;
let dbConnectionString = 'mongodb://localhost:27017';
let dbName = 'coin-sms-alert';

// Connect to our MongoDB database and make database client available for use
exports.initialize = (done) => {
   if (db) return process.nextTick(done);

   console.log('Connecting to mongo database: ' + dbConnectionString);

   mongoClient.connect(dbConnectionString, (err, connectedDb) => {
       if (err) {
           console.log('Couldn\'t connect to mongo database', err);
           return done(err);
       }

       db = connectedDb.db(dbName);
       return done();
   });
};

// Close our MongoDB connection for shutdown
exports.dispose = (done) => {
   if (db) {
       console.log('Closing connection to mongo database: ' + dbConnectionString);
       var tempDb = db;
       db = null;
       tempDb.close((err, result) => {
           if (err) {
               console.log('Error closing connection to mongo database', err);
               return done(err);
           }
           console.log('Database connection closed');
           return done();
       });
   } else {
       return process.nextTick(done);
   }
};

exports.getDb = () => {
   return db;
};

exports.alarms = () => {
   return db.collection('alarms');
};

Pour que notre API reste gérable, commencez à configurer des routes que notre application exposera comme son API. Créez un répertoire nommé routes dans votre répertoire racine. Dans le répertoire routes, nous allons créer trois fichiers.

Tout d'abord, nous devons importer tous les modules de routes et les enregistrer dans Express. Créez un fichier nommé routes.js avec le contenu suivant :

const coinDataRoutes = require('./coinDataRoutes');
const alarmRoutes = require('./alarmRoutes');

// Configure all routes here
exports.configure = (app) => {

   // Test URL
   app.get('/', (req, res) => {
       res.json({success: true});
   });

   // Add routes to the express app object
   coinDataRoutes.configure(app);
   alarmRoutes.configure(app);

};

Ensuite, créez un module qui va permettre la création d'un routage RESTful pour le traitement des opérations CRUD sur les données d'alerte. Ajoutez à un fichier nommé alarmRoutes.js ce qui suit :

const alarmRepo = require('../dataAccess/repos/alarmRepository');

// Alarm CRUD (CREATE - READ/RETRIEVE - UPDATE - DELETE) routes
exports.configure = (app) => {
   // RESTful routing
   app.post('/api/alarm', createAlarm);
   app.get('/api/alarm', getAlarms);

};

// Create an alarm and respond with JSON alarm object
function createAlarm(req, res, done) {
   const alarmData = req.body;

   alarmRepo.createAlarm(alarmData, (err, alarm) => {
       return res.json(alarm);
   });
}

// Retrieve all alarms and return a JSON array
function getAlarms(req, res, done) {

   alarmRepo.getAlarms((err, alarms) => {
       return res.json(alarms);
   });

}

Enfin, créez coinDataRoutes.js, un module qui gère la récupération des données des cryptomonnaies depuis l'API externe CoinMarketCap :

const _ = require('underscore');

const coinDataRepo = require('../dataAccess/repos/coinDataRepository');
const alarmRepo = require('../dataAccess/repos/alarmRepository');

// Coin routes
exports.configure = (app) => {
   // RESTful coin routes
   app.get('/api/coins', getCoinData);

};

// Get latest coin data, get all created alarms,
// determine if any alarms have thresholds that have been crossed,
// return coin data in JSON format
function getCoinData(req, res, done) {

   coinDataRepo.getCoinData((err, coinsData) => {

       alarmRepo.getAlarms((err, alarms) => {

           alarms.forEach((alarm) => {

               let latestCoinData = _.findWhere(coinsData, {id: alarm.coinId});

               if (latestCoinData && alarm.isTriggered(latestCoinData)) {
                   console.log(`* ALARM * ${alarm.coinId}: $${latestCoinData.price_usd} is ${ alarm.thresholdDirection} threshold $${alarm.priceUsdThreshold}`);
               }

           });

           res.json(coinsData);
       });

   });

}

Nos routes étant établies, nous allons maintenant utiliser un modèle de conception simplifié permettant l'accès aux données persistantes dans MongoDB et leur manipulation, ainsi que l'interaction avec des API tierces via des requêtes HTTP.

Créez dans le répertoire dataAccess un répertoire nommé repos. Dans ce répertoire, nous allons créer deux fichiers :
Tout d'abord, créez alarmRepository.js. Ce répertoire disposera de deux méthodes : getAlarms qui permet de récupérer toutes les alertes, et createAlarm qui permet d'en créer.

const Alarm = require('../../models/alarm');
const db = require('../../dataAccess/mongoClientWrapper');

// Insert an alarm document record in MongoDB alarms collection
exports.createAlarm = (alarmData, done) => {
   const collection = db.alarms();

   collection.insertOne(alarmData, (err, alarm) => {
       done(err, alarm);
   });
};

// Retrieve all alarms from MongoDB alarm collection, convert all to Alarm model objects
exports.getAlarms = (done) => {
   const collection = db.alarms();

   collection.find({}).toArray((err, results) => {
       let alarms = results.map((result) => {
           return new Alarm(result)
       });

       done(err, alarms);
   });
};

Ensuite, créez coinDataRepository.js. Ce répertoire traite les requêtes API vers l'API CoinMarketCap :

const request = require('request');

// Make a GET request to CoinMarketCap's API to retrieve latest cryptocoin prices
exports.getCoinData = (done) => {

   request.get('https://api.coinmarketcap.com/v1/ticker/', (err, response, body) => {

       let coinsData = JSON.parse(body);

       done(err, coinsData);
   });

};

Maintenant que tous ces fichiers ont été créés, nous devons refactoriser notre fichier server.js principal pour utiliser nos nouveaux modules :

const express = require('express');
const bodyParser = require('body-parser');

const routes = require('./routes/routes');
const mongoClientWrapper = require('./dataAccess/mongoClientWrapper');
const app = express();

// Use body-parser as middleware so that we have JSON data available on Request objects
app.use(bodyParser.json({type: 'application/json'}));

// Start listening on port 3000 to start receiving requests
app.listen(3000, () => {
   console.log('Coin Alert app listening on port 3000!');

   // Initialize our MongoDB client
   mongoClientWrapper.initialize(() => {
       // Set up routes
       routes.configure(app);
   });

});

À présent, lançons l'app et testons notre dernier code ! Exécutez npm start. Vous devriez voir un message dans votre console indiquant que l'app est en cours d'exécution.

Avec l'ajout de notre routage RESTful, nous aurons besoin d'un moyen d'envoyer aussi bien des requêtes POST que des requêtes GET. Postman est un excellent outil à cette fin. Démarrez Postman, puis créez vos requêtes comme indiqué dans les images ci-dessous :

  • GET http://localhost:3000/api/alarm : cela renverra un tableau JSON contenant tous nos objets d'alerte. Au départ, ce tableau sera vide ; nous créerons des alertes dans notre prochaine requête.
capture d&#x27;écran postman
  • POST http://localhost:3000/api/alarm : à partir d'un objet au format JSON contenant des données d'alerte et d'un en-tête Content-Type, cette requête créera une alerte qui sera stockée dans MongoDB. Après avoir exécuté cette requête, recherchez dans votre base de données coin-sms-alert une collection d'alertes avec un document enregistré :
capture d&#x27;écran postman

capture d&#x27;écran postman

capture d&#x27;écran mongo db
  • GET http://localhost:3000/api/coins : cette requête interroge l'API de CoinMarketCap afin d'obtenir les dernières données sur les cryptomonnaies. Une fois les données récupérées, notre code effectue une itération sur toutes les alertes enregistrées et vérifie si des seuils ont été franchis.
capture d&#x27;écran postman

logs node dans la console
capture d&#x27;écran postman

Tout cela a représenté beaucoup de travail, mais le résultat est là. Nos données d'alerte étant maintenant enregistrées de manière persistante et vérifiées par rapport à l'API CoinMarketCap, nous pouvons passer à la partie passionnante de ce tutoriel : l'envoi d'alertes sur le cours des cryptomonnaies directement vers notre téléphone !

Envoyer par SMS des alertes sur le cours des cryptomonnaies

En suivant cette étape de l'article :

git checkout step-5-send-sms-alarms; npm install

L'API Twilio nous permet d'envoyer des SMS et des MMS via une interface simple. Un package officiel est disponible sur npm (https://www.npmjs.com/package/twilio) et fera de l'envoi de SMS un jeu d'enfant !

Au début de ce post, vous devriez avoir exécuté avec succès le tutoriel de démarrage rapide et noté vos Account SID Twilio, token d'autorisation et numéro de téléphone Twilio. Vous devrez également avoir accès à un numéro de téléphone personnel pouvant recevoir des SMS.
Au lieu de coder en dur ces identifiants, nous utiliserons un package npm appelé dotenv pour configurer l'environnement de notre app. (Si vous souhaitez en savoir plus sur dotenv et son utilisation, consultez ce tutoriel sur le blog de Twilio pour de plus amples informations.)

Installez le package dotenv :

npm install --save dotenv

Ensuite, créez dans votre répertoire racine un fichier nommé .env. Il s'agit du fichier par défaut utilisé par dotenv pour la configuration des variables d'environnement :

TWILIO_ACCOUNT_SID=yourAccountSid
TWILIO_AUTH_TOKEN=yourAuthToken
FROM_PHONE_NUMBER=yourTwilioPhoneNumber
TO_PHONE_NUMBER=yourPersonalSmsEnabledPhoneNumber

REMARQUE : si vous suivez ce tutoriel avec le référentiel GitHub, vous aurez un fichier nommé .env.template. Copiez le contenu de ce fichier dans un fichier nommé .env et saisissez vos identifiants.
Installez le module twilio avec npm :

npm install --save twilio

Créez un répertoire appelé services dans le répertoire racine de votre projet, puis un fichier nommé smsService.js. Ce fichier de service gérera toutes les interactions avec l'API Twilio, facilitant ainsi son intégration dans notre app :

const dotenv = require('dotenv');
const Twilio = require('twilio');

const config = dotenv.config().parsed;
const client = new Twilio(config.TWILIO_ACCOUNT_SID, config.TWILIO_AUTH_TOKEN);


exports.sendSms = (bodyMessage, done) => {

   let message = {
       to: config.TO_PHONE_NUMBER,
       from: config.FROM_PHONE_NUMBER,
       body: bodyMessage
   };

   client.messages.create(message, (err, message) => {
       if (err) return done(err);

       return done(null, message);
   });

};

Créez dans le répertoire routes un fichier nommé smsRoutes.js, qui utilisera notre smsService.js nouvellement créé. Nous allons mettre en place une route pour tester notre application, http://localhost:3000/api/sms/test, à laquelle nous pourrons accéder via un navigateur et qui nous permettra de confirmer que le projet est correctement configuré :

const smsService = require('../services/smsService');

// SMS-related routes
exports.configure = (app) => {
   // Test route to ensure API successfully sends a SMS alarm to our phone
   app.get('/api/sms/test', sendSmsTest);

};

// Send an SMS test message
function sendSmsTest(req, res, done) {

   smsService.sendSms('This is a test message from our Node.js App!', () => {
       console.log('Successfully sent a SMS message');

       res.json({
           success: true
       });
   })

}

Ajoutez cette route au fichier routes.js :

const coinDataRoutes = require('./coinDataRoutes');
const alarmRoutes = require('./alarmRoutes');
const smsRoutes = require('./smsRoutes');

// Configure all routes here
exports.configure = (app) => {

   // Test URL
   app.get('/', (req, res) => {
       res.json({success: true});
   });

   // Add routes to the express app object
   coinDataRoutes.configure(app);
   alarmRoutes.configure(app);
   smsRoutes.configure(app);

};

Lancez votre app avec npm start, puis utilisez un navigateur ou Postman pour envoyer une requête GET à http://localhost:3000/api/sms/test. L'app devrait répondre avec du JSON, et vous devriez recevoir un message texte sur votre téléphone.

capture d&#x27;écran postman

capture d&#x27;écran de message reçu

Maintenant que nous avons vérifié que les SMS sont bien envoyés, il est temps de les intégrer à notre API existante. Ouvrez coinDataRoutes.js et refactorisez le code pour utiliser notre nouveau smsService. Il est probablement plus facile de supprimer le contenu du fichier et de coller à la place ce qui suit :

const _ = require('underscore');

const coinDataRepo = require('../dataAccess/repos/coinDataRepository');
const alarmRepo = require('../dataAccess/repos/alarmRepository');
const smsService = require('../services/smsService');

// Coin routes
exports.configure = (app) => {
   // RESTful coin routes
   app.get('/api/coins', getCoinData);

};

// Get latest coin data, get all created alarms,
// determine if any alarms have thresholds that have been crossed,
// return coin data in JSON format
function getCoinData(req, res, done) {

   coinDataRepo.getCoinData((err, coinsData) => {

       alarmRepo.getAlarms((err, alarms) => {

           alarms.forEach((alarm) => {

               let latestCoinData = _.findWhere(coinsData, {id: alarm.coinId});

               if (latestCoinData && alarm.isTriggered(latestCoinData)) {

                   let message = `* ALARM * ${alarm.coinId}: $${latestCoinData.price_usd} is ${ alarm.thresholdDirection} threshold $${alarm.priceUsdThreshold}`;

                   smsService.sendSms(message, () => {
                       console.log(message);
                   });

               }

           });

           res.json(coinsData);
       });

   });

}

Assurez-vous que des alertes sont créées, de préférence avec des seuils suffisamment bas pour les déclencher, en envoyant une requête GET à http://localhost:3000/api/alarm

capture d&#x27;écran postman

Relancez l'app avec npm start et effectuez une requête GET à http://localhost:3000/api/coins. Vous devriez recevoir un SMS si des seuils d'alerte ont été dépassés.

capture d&#x27;écran de téléhpone

Super ! À chaque fois que nous lançons manuellement une requête à http://localhost:3000/api/coins, nous vérifions nos alertes et envoyons des alertes par SMS. Mais est-ce que ce ne serait pas mieux si notre app pouvait vérifier périodiquement le cours des cryptomonnaies et envoyer ces alertes sur notre téléphone ?

Planifier les tâches avec Agenda

En suivant cette étape de l'article :

git checkout step-6-task-scheduling; npm install

Lors de l'étape précédente, nous avons pu nous envoyer des SMS d'alerte à chaque fois que nous lancions des requêtes à http://localhost:3000/api/coins . C'est très bien pour une preuve de concept, mais nous pouvons refactoriser un peu plus encore notre code de façon à planifier l'exécution de cette tâche à intervalles réguliers.

npm héberge un excellent module de planification des tâches appelé agenda : http://agendajs.com/. Ce module présente un cas d'usage similaire à cron sur les systèmes de type Unix ou au Planificateur de tâches sous Windows. Ce que l'on veut fondamentalement, c'est que notre application fasse une chose selon un intervalle spécifié (chaque minute, heure, jour, à un jour spécifique de la semaine, etc.), ou à un moment précis dans le temps. agenda est un module très flexible que nous pouvons utiliser pour configurer une tâche récurrente dans l'optique de comparer nos alertes aux derniers cours des cryptomonnaies issus de l'API CoinMarketCap.

Installez le package agenda avec npm :

npm install --save agenda

agenda nécessite une base de données MongoDB et une chaîne de connexion pour stocker des informations sur les tâches planifiées. Nous avons codé cela en dur lors d'une étape précédente, mais en tant que développeurs nous devrions nous soucier de la sécurité et déplacer notre configuration en dehors de notre code d'application. Mettez à jour .env comme suit :

TWILIO_ACCOUNT_SID=yourAccountSid
TWILIO_AUTH_TOKEN=yourAuthToken
FROM_PHONE_NUMBER=yourTwilioPhoneNumber
TO_PHONE_NUMBER=yourPersonalSmsEnabledPhoneNumber
MONGODB_CONNECTION_STRING=mongodb://localhost:27017
MONGODB_DATABASE=coin-sms-alert

Ensuite, mettez à jour le fichier mongoClientWrapper.js pour utiliser nos nouvelles valeurs d'environnement :

const mongoClient = require('mongodb').MongoClient;
const dotenv = require('dotenv');

let config = dotenv.config().parsed;
let dbConnectionString = config.MONGODB_CONNECTION_STRING;
let dbName = config.MONGODB_DATABASE;

let db = null;
let connectedClient = null;

// Connect to our MongoDB database and make database client available for use
exports.initialize = (done) => {
   if (db) return process.nextTick(done);

   console.log('Connecting to mongo database: ' + dbConnectionString);

   mongoClient.connect(dbConnectionString, (err, client) => {
       if (err) {
           console.log('Couldn\'t connect to mongo database', err);
           return done(err);
       }

       db = client.db(dbName);
       connectedClient = client;
       return done();
   });
};

// Close our MongoDB connection for shutdown
exports.dispose = (done) => {
   if (db) {
       console.log('Closing connection to mongo database: ' + dbConnectionString);
       db = null;

       connectedClient.close((err, result) => {
           if (err) {
               console.log('Error closing connection to mongo database', err);
               return done(err);
           }
           console.log('Database connection closed');
           return done();
       });
   } else {
       return process.nextTick(done);
   }
};

exports.getDb = () => {
   return db;
};

exports.alarms = () => {
   return db.collection('alarms');
};

exports.agendaJobs = () => {
   return db.collection('agendaJobs');
};

Créez un répertoire appelé jobs, puis créez deux fichiers nommés jobs.js et checkAlarmsJob.js.

jobs.js gère l'initialisation, l'enregistrement et l'arrêt des tâches utilisées dans l'app :

const Agenda = require('agenda');

const db = require('../dataAccess/mongoClientWrapper');
const checkAlarmJob = require('./checkAlarmJob');

const mongoConnectionString = `${process.env.MONGODB_CONNECTION_STRING}/${process.env.MONGODB_DATABASE}`;

let agenda = null;


exports.start = () => {
   // configure Agenda
   agenda = new Agenda({
       db: {
           address: mongoConnectionString
       },
       processEvery: 'one minute'
   });

   // Register event listener for when agenda starts up
   agenda.on('ready', () => {
       // Delete the jobs collection on startup
       deleteJobsCollection(() => {

           // Set up our job with agenda
           checkAlarmJob.register(agenda);
           // Job will run every minute
           checkAlarmJob.setSchedule('one minute');

           // Start agenda
           agenda.start(() => {
               console.log('Started jobs');
           });

       });
   });

   // error event listener
   agenda.on('error', (err) => {
       console.log('Error with Agenda!', err);
   });
};

// Gracefully stop errors
exports.stop = (done) => {
   agenda.stop(() => {
       console.log('Successfully shut down jobs');
       done();
   });
};

// Delete the MongoDB jobs collection
function deleteJobsCollection(done) {
   db.agendaJobs().drop(done);
}

checkAlarmsJob.js est une refactorisation du code que nous avons précédemment utilisé dans notre coinDataRepository.js. Nous déplaçons le code afin que notre application soit plus modulaire et plus facile à tester et à comprendre.

const _ = require('underscore');

const coinDataRepo = require('../dataAccess/repos/coinDataRepository');
const alarmRepo = require('../dataAccess/repos/alarmRepository');
const smsService = require('../services/smsService');

const jobName = 'Check Alarm Job';
let agendaInstance = null;

// Register our job with the agenda instance
exports.register = (agenda) => {
   agendaInstance = agenda;

   // Define what this job will do
   agendaInstance.define(jobName, () => {
       console.log(`'${jobName}' executed at ${new Date()}`);

       // Request coin data from the CoinMarketCap API
       coinDataRepo.getCoinData((err, coinsData) => {
           // Retrieve all alarms from our database
           alarmRepo.getAlarms((err, alarms) => {
               // Iterate through all alarms
               alarms.forEach((alarm) => {
                   let latestCoinData = _.findWhere(coinsData, {id: alarm.coinId});

                   // If the alarm's coin is found and the alarm is triggered...
                   if (latestCoinData && alarm.isTriggered(latestCoinData)) {
                       let message = `* ALARM * ${alarm.coinId}: $${latestCoinData.price_usd} is ${ alarm.thresholdDirection} threshold $${alarm.priceUsdThreshold}`;

                       // ...send a SMS message!
                       smsService.sendSms(message, () => {
                           console.log(message);
                       });
                   }
               });
           });
       });
   });
};

// Set schedule
exports.setSchedule = (timeInterval) => {
   agendaInstance.every(timeInterval, jobName);
};

Notre app est quasiment prête pour la production ! Ajoutons quelques mises à jour à server.js pour arrêter notre application en douceur :

const express = require('express');
const bodyParser = require('body-parser');

const routes = require('./routes/routes');
const mongoClientWrapper = require('./dataAccess/mongoClientWrapper');
const jobs = require('./jobs/jobs');
const app = express();

// Use body-parser as middleware so that we have JSON data available on Request objects
app.use(bodyParser.json({type: 'application/json'}));

// Start listening on port 3000 to start receiving requests
app.listen(3000, () => {
   console.log('Coin Alert app listening on port 3000!');

   // Initialize our MongoDB client
   mongoClientWrapper.initialize(() => {
       // Set up routes
       routes.configure(app);
       jobs.start();
   });

   // Process terminal signal event listeners
   process.on('SIGINT', shutDown);
   process.on('SIGTERM',shutDown);
});

// Gracefully shut down
function shutDown() {
   console.log('\nShutting down!');
   // Stop all running jobs
   jobs.stop(() => {
       // safely clean up mongodb connection
       mongoClientWrapper.dispose(() => {
           process.exit(0);
       });

   });

}

Lancez l'app à l'aide de npm start. Vous devriez recevoir au moins un SMS sur votre téléphone. Vous devriez également voir des messages de console vous informant que la tâche a été exécutée, ainsi que des alertes dont le seuil a été déclenché.

capture d&#x27;écran de la console

REMARQUE : si vous ne recevez pas de SMS, vous devrez peut-être vérifier les derniers cours des cryptomonnaies afin de créer des alertes qui seront déclenchées avec succès.

Pour arrêter l'app, utilisez Ctrl+C dans votre terminal. Vous devriez voir des messages indiquant que l'app est en cours d'arrêt, que la connexion à MongoDB a été fermée et que toutes les tâches ont été arrêtées.

capture d&#x27;écran de la console

Nous avons réussi !

Félicitations ! Ensemble, nous avons créé avec succès une API très utile et automatisé nos vérifications des cours des crypto-monnaies. Nous avons utilisé toute la flexibilité de Node.js et les capacités de l'API Twilio pour créer une application utile et concrète qui peut tous nous aider à devenir crypto millionnaires ! Merci d'avoir suivi ce tutoriel, ne ratez pas notre prochain article de suivi où nous créerons une UI (interface utilisateur) de tableau de bord pour notre app d'alerte avec React.js !

Steve Senkus est un développeur de logiciels passionné œuvrant sous le soleil de San Diego, Californie. Lorsqu'il n'est pas en train de bronzer ou d'échanger à la volée des cryptomonnaies, vous pouvez le joindre sur Twitter (@stevesenkus) ou par e-mail à l'adresse steve.senkus@gmail.com.