
Partager:
Ben est un développeur en seconde carrière qui a auparavant passé une décennie dans les domaines de la formation pour adultes, de l'organisation communautaire et de la gestion d'organisations à but non lucratif. Il a travaillé comme défenseur des développeurs pour Vonage. Il écrit régulièrement sur l'intersection du développement communautaire et de la technologie. Originaire de Californie du Sud et ayant longtemps vécu à New York, Ben réside aujourd'hui près de Tel Aviv, en Israël.
Recherche de mises à jour sur le Web et envoi d'alertes par SMS avec Ruby
Temps de lecture : 19 minutes
Nous sommes tous passés par là. Vous vous réveillez, c'est un nouveau jour, et vous avez besoin de connaître la réponse à la question la plus brûlante. C'est déjà le week-end ? Votre semaine a été tellement chargée en responsabilités professionnelles et personnelles. Tout ce dont vous avez envie, c'est de prendre quelques jours pour vous asseoir et vous détendre.
Pour répondre à cette question, vous pouvez bien sûr ouvrir l'application calendrier de votre téléphone ou demander à votre assistant personnel préféré. Mais pourquoi faire cela alors que vous pourriez créer une application qui vous enverrait un message texte à la place ?
Nous allons créer une application Ruby on Rails qui effectue les opérations suivantes :
Permet à la fois l'inscription de nouveaux abonnés et la possibilité de se désinscrire de la liste.
Récupère la réponse à notre question sur isittheweekend.com
Envoie quotidiennement la réponse de nos données à tous les destinataires inscrits.
Pour terminer, nous allons également créer une tâche Rake qui exécutera toutes ces tâches en même temps, et désigner son exécution une fois toutes les 24 heures.
Si vous préférez, vous pouvez également trouver une version entièrement fonctionnelle de cette application sur GitHub.
C'est parti !
Conditions préalables
Vonage API Account
To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.
This tutorial also uses a virtual phone number. To purchase one, go to Numbers > Buy Numbers and search for one that meets your needs.
Générer l'application Rails
La première chose à faire est de créer notre nouvelle application Rails. Depuis votre ligne de commande, exécutez ce qui suit :
Cela crée la structure de fichier nécessaire pour notre application Rails et définit la base de données par défaut à PostgreSQL.
Une fois que c'est fait, cd dans le répertoire qui a été créé. Avant d'installer nos dépendances, nous allons ajouter les gemmes supplémentaires que notre application utilisera.
Ouvrez le code dans votre éditeur de code préféré et naviguez jusqu'au fichier Gemfile. À l'intérieur de l'élément Gemfile ajoutez les gemmes suivantes :
gem 'nexmo'
gem 'watir'
gem 'webdrivers', '~> 4.0'
gem 'whenever', require: false
gem 'dotenv-rails'Nous utilisons la gem nexmo pour envoyer les mises à jour par SMS, les gemmes watir et webdrivers pour effectuer la requête HTTP vers un site avec un contenu JavaScript dynamique, la gem pour planifier la tâche Rake et la gem pour planifier la tâche Rake. whenever pour planifier la tâche Rake, et la gem pour gérer les variables d'environnement. dotenv-rails pour gérer les variables d'environnement.
Après avoir sauvegardé le Gemfilevous êtes prêt à exécuter bundle install à partir de la ligne de commande.
L'étape suivante consiste à créer notre schéma et nos modèles de base de données.
Créer le schéma et les modèles de la base de données
Maintenant que notre application Rails est créée et que ses dépendances sont installées, la tâche suivante consiste à créer le schéma de base de données correct pour héberger les données dont nous aurons besoin pour faire fonctionner notre application. Nous devons stocker les types d'informations suivants :
Destinataires : La liste des abonnés avec leurs numéros de téléphone.
DiffStorage : Copies des données du site web à comparer pour déterminer s'il y a eu un changement.
Nous utiliserons l'outil Rails generator pour créer les fichiers de migration, puis nous modifierons chacun d'entre eux.
Ces commandes créeront à la fois des fichiers de modèle dans app/models et des fichiers de migration dans db/migrate. Avant de valider ces modifications dans votre application, une fois les actions du générateur effectuées, inspectez les fichiers créés dans les deux répertoires pour vous assurer qu'ils sont corrects.
Plus précisément, dans les fichiers de migration, vous voulez vous assurer que chaque migration inclut t.timestampsqui ajoute un created_at et updated_at à la table. Vous devriez également voir les colonnes number et subscribed dans le fichier de migration Recipient avec les types string et booleanrespectivement. De même, vous devriez voir une colonne dans le fichier de migration DiffStorage pour website_data dont le type est défini sur text.
Les fichiers de modèle à l'intérieur de app/models doivent être vides, à l'exception des déclarations de classes et de leur héritage de ApplicationRecord.
Lorsque cela semble satisfaisant, il est temps d'exécuter rake db:migrate à partir de la ligne de commande. La commande affichera les résultats sur votre console, et si vous inspectez le fichier db/schema.rb vous pourrez voir le schéma que vous avez créé initialisé dans l'application.
Enfin, nous devons également créer Messenger et Scraper mais nous n'avons pas besoin d'une migration pour eux. Pour ce faire, nous lançons à nouveau le générateur Rails et y ajoutons un drapeau --migration=false au générateur :
Le moment est venu de définir la logique à l'intérieur des modèles.
Définition des modèles
Comme mentionné ci-dessus, nous avons quatre modèles qui sont responsables de domaines uniques de l'application :
DiffStorage: Vérifie les différences éventuelles dans les données du site webRecipient: Gestion de l'ajout et de la suppression d'abonnésMessenger: Gère l'envoi des SMSScraper: Responsable de la recherche de données sur le site web
Définition du modèle DiffStorage
Le modèle DiffStorage contiendra deux modèles de classe. L'un d'eux contiendra l'URL que nous scrappons. La seconde vérifiera s'il y a eu des changements depuis la dernière fois que le site web a été scrappé et invoquera les étapes suivantes de l'application lorsque les conditions seront remplies.
Tout d'abord, définissons l'URL dans sa propre méthode afin de créer un endroit unique où elle existe et peut être facilement modifiée si nous décidons de le faire plus tard :
def self.url
'http://isittheweekend.com'
endEnsuite, l'essentiel de ce modèle se trouve dans la méthode #check_last_record de la classe :
def self.check_last_record
today_answer = Scraper.call(self.url)
if DiffStorage.any?
yesterday_answer = DiffStorage.last
else
yesterday_answer = ''
end
Messenger.send_update_message(Recipient.all, yesterday_answer, today_answer)
endLa méthode ci-dessus appelle d'abord la méthode de la classe Scraper qui commencera à parcourir le site web pour obtenir l'instantané le plus récent et affecte ces données à la classe today_answer. Elle intègre ensuite l'étape suivante dans une instruction if demandant s'il existe des enregistrements dans DiffStorage. Si des enregistrements antérieurs y sont stockés, la méthode prend le plus récent et l'affecte à la classe yesterday_answer. S'il n'y a pas d'enregistrements précédents, une chaîne vide est assignée à yesterday_answer. Enfin, elle envoie les destinataires et les deux variables au modèle Messenger pour qu'il traite l'envoi du message.
Définition du modèle de racleur
Le modèle Scraper Le modèle sera chargé de collecter les données à partir de isittheweekend.com pour déterminer si c'est bien le week-end ou non. Le modèle aura quatre méthodes de classe et nous définirons chacune d'entre elles ici :
require 'nokogiri'
require 'webdrivers/chromedriver'
require 'watir'
class Scraper < ApplicationRecord
def self.call(url)
self.get_url(url)
end
def self.get_url(url)
doc = HTTParty.get(url)
browser = Watir::Browser.new :chrome, headless: true
browser.goto(url)
parsed_page ||= Nokogiri::HTML.parse(browser.html)
answer = parsed_page.css('h1#isit').text
self.check_text(answer)
end
def self.check_text(data)
if data == '' || data == nil
puts "There was no text received from the web scrape."
exit
else
puts "There was data in the text received from the web scrape."
self.store_text(data)
end
end
def self.store_text(text)
record = DiffStorage.new
record.website_data = text
if record.save
puts "Record Updated Successfully"
end
return record
end
endChaque action de l'acte de grattage est définie dans sa propre petite méthode afin de maintenir nos préoccupations séparées. La méthode #call est le point d'entrée de la classe. C'est elle qui est invoquée par d'autres méthodes en dehors d'elle. La méthode #get_url effectue la requête HTTP en simulant une requête du navigateur Chrome à l'aide de la bibliothèque Watir et l'analyse avec Nokogiri. La méthode #check_text vérifie si des données ont été obtenues. La méthode #store_text enregistre ces données dans la base de données.
Définir le modèle de messagerie
Dans le modèle Messenger contiendra tout le code nécessaire à l'envoi de la mise à jour quotidienne par SMS aux abonnés. Nous créerons une méthode qui enverra le message, une méthode qui composera le texte de réponse du week-end, une méthode qui rassemblera l'ensemble du message et une méthode qui gérera un message de confirmation si un abonné envoie une demande de suppression.
Tout d'abord, la méthode d'envoi du message de mise à jour :
def self.send_update_message(recipients, yesterday, today)
@client = Nexmo::Client.new(
api_key: ENV['NEXMO_API_KEY'],
api_secret: ENV['NEXMO_API_SECRET']
)
puts "Sending Message to Each Recipient"
recipients.each do |recipient|
if recipient.subscribed == true
client.sms.send(
from: ENV['FROM_NUMBER'],
to: recipient.number,
text: self.weekend_message(yesterday, today)
)
puts "Sent message to #{recipient.number}"
end
end
endLa valeur du paramètre text ci-dessus fait référence à une méthode de classe appelée #weekend_message. Cette méthode composera la chaîne de caractères pour la mise à jour du week-end en vérifiant si la journée d'aujourd'hui est identique à celle d'hier ou non :
def self.weekend_message(yesterday, today)
if today == yesterday
response = "Today is the same as yesterday, and the answer is #{today}."
elsif today =! yesterday
response = "Today is not the same as yesterday, the answer for today is #{today}."
else
response = 'Today and yesterday are both neither affirmative or positive. Are we in an alternative dimension of time and space?'
end
self.compose_message(response)
endEnsuite, la méthode contenant la HEREDOC avec le corps du message :
def self.compose_message(response)
<<~HEREDOC
Hello!
It is a new day, but is it a weekend day?
#{response}
To be removed from the list please respond with "1".
HEREDOC
endEnfin, la méthode permettant d'envoyer un message de confirmation de retrait :
def self.send_removal_message(to)
@client.sms.send(
from: ENV['FROM_NUMBER'],
to: to,
text: 'You have been successfully removed.'
)
endLe dernier modèle que nous devons définir avant de passer à l'étape suivante est le modèle Recipient modèle.
Définir le modèle de bénéficiaire
Ce modèle ne contient aucune des méthodes de sa classe. Le seul ajout que nous ferons à ce modèle est l'ajout de deux validations aux données des destinataires. Ces validations serviront de garde-fou lors de l'ajout de nouveaux numéros de téléphone dans la base de données. Nous vérifierons que a) un numéro est effectivement fourni dans les données et b) que le numéro n'est pas un doublon d'un enregistrement déjà existant. Pour effectuer ces validations, nous ajoutons deux lignes sous la définition de la classe :
class Recipient < ApplicationRecord
validates :number, presence: true
validates :number, uniqueness: true
end Créer le contrôleur et les routes
Nous sommes sur le point de terminer la construction de notre application ! La prochaine étape consiste à définir les actions du contrôleur qui dicteront le flux de l'application. Tout d'abord, générons le contrôleur en utilisant le générateur Rails en ligne de commande :
Cela créera un nouveau fichier de contrôleur vide dans app/controllers appelé weekend_checker_controller.rb et des fichiers de vue complémentaires dans app/views/weekend_checker. Nous ajouterons une vue d'index sous peu. Pour l'instant, nous allons nous concentrer sur le contrôleur.
Le contrôleur a besoin de trois actions correspondant à trois itinéraires : #index, #create et #event. La route #index sera la vue par défaut et la seule vue de notre site web. C'est là que les personnes pourront s'inscrire à la liste. L'itinéraire #create sera l'endroit où les nouveaux Numbers seront traités. Enfin, l'itinéraire #event sera l'endroit où l'application reçoit les données du webhook de l'API SMS, y compris les demandes de suppression, et les traite.
class WeekendCheckerController < ApplicationController
def index
end
def create
@recipient = Recipient.new(recipient_params)
if @recipient.save
flash[:notice] = "Phone number saved successfully."
else
flash[:alert] = "Form did not save. Please fix and try again."
end
redirect_to '/'
end
def event
if params[:text] == '1'
recipient = Recipient.find_by(number: params[:msisdn])
if recipient
if recipient.update(subscribed: false)
Messenger.send_removal_message(params[:msisdn])
end
end
end
puts params
head :no_content
end
private
def recipient_params
params.permit(:number, :subscribed)
end
end
Ces trois actions du contrôleur nécessitent trois routes correspondantes définies dans config/routes.rb:
Rails.application.routes.draw do
get '/', to: 'weekend_checker#index'
get '/webhooks/event', to: 'weekend_checker#event'
post '/recipient/new', to: 'weekend_checker#create'
endL'avant-dernier élément de notre code d'application est la création d'une vue de base pour la route / route.
Définition de la vue
Pour s'inscrire à la liste SMS, nous allons créer une vue accessible au niveau supérieur de l'URL qui contiendra un formulaire d'inscription.
Dans le dossier app/views/weekend_checker ajouter un fichier index.html.erb . Il contiendra le code suivant Il contiendra le code suivant :
<h2>Is It The Weekend? Get a Daily Text to Find Out!</h2>
<p>
This is a free service that will analyze <a href="http://isittheweekend.com">isittheweekend.com</a> and check for any updates once a day. If there is an update it will send you a text message at the number you provide.
</p>
<p>
To remove yourself from the SMS list, reply to the text message you receive with the number "1".
</p>
<p>
SMS messages are sent using the <a href="/home">Nexmo SMS API</a>.
</p>
<% flash.each do |type, msg| %>
<div>
<%= msg %>
</div>
<% end %>
<%= form_with model: @recipient, url: "/recipient/new" do |f| %>
<%= f.telephone_field :number, :placeholder => '12122222222' %>
<%= f.hidden_field :subscribed, value: true %>
<%= f.submit "Add Number" %>
<% end %>La dernière tâche de codage que nous avons à faire est de mettre en place notre nouvelle tâche Rake qui exécutera tout ce code et de configurer le fichier whenever pour qu'il exécute la tâche Rake une fois par jour.
Créer la tâche Rake et la planifier
Une fois de plus, nous allons utiliser un générateur Rails à partir de la ligne de commande pour créer le fichier pour notre tâche Rake. A partir de la ligne de commande, exécutez ce qui suit :
La tâche ci-dessus créera un fichier dans lib/tasks appelé scraper.rake. Lorsque nous l'ouvrirons dans notre éditeur de code, il ressemblera à ceci :
namespace :scraper do
desc "TODO"
task :check_site_update => :environment do
end
end
Redéfinissons l'élément desc avec une courte chaîne de caractères décrivant ce que cette tâche va faire : desc "Check Website for Any Updates". Ensuite, à l'intérieur du bloc task ajoutons la méthode de classe DiffStorage#check_last_record qui est le point d'entrée de tout le travail que nous avons créé précédemment :
namespace :scraper do
desc "Check Website for Any Updates"
task :check_site_update => :environment do
DiffStorage.check_last_record
end
end
Maintenant que notre tâche Rake est définie, nous devons enfin initialiser la gemme whenever et lui faire savoir que nous voulons que cette tâche soit exécutée une fois par jour. Pour ce faire, nous lançons d'abord la commande d'initialisation de la gem en ligne de commande :
La commande ci-dessus crée un fichier schedule.rb dans le dossier config/ dans le dossier Ajoutez le code suivant à ce fichier pour exécuter la tâche scraper:check_site_update quotidiennement :
every 1.day do
rake "scraper:check_site_update"
endMaintenant que le programme est créé, nous devons mettre à jour le fichier crontab sur notre machine pour qu'il soit au courant de cette nouvelle tâche. Nous le faisons en exécutant bundle exec whenever --update-crontab à partir de la ligne de commande. Une fois cela fait, la tâche est entièrement initialisée et configurée pour s'exécuter une fois par jour sur notre machine.
Le code de notre application est prêt. Il ne nous reste plus qu'à créer notre Account Nexmo, à obtenir nos identifiants API Nexmo et à provisionner un numéro de téléphone virtuel avec lequel nous enverrons les SMS quotidiens. Une fois que nous aurons ces informations, nous les ajouterons à notre application en tant que variables d'environnement.
Références et numéro de téléphone de l'API Nexmo
Pour créer un Account, naviguez vers le Nexmo Dashboard et suivez les étapes de l'enregistrement. Une fois l'inscription terminée, vous accéderez à votre tableau de bord.
Si vous ne l'avez pas encore fait, créez un fichier .env dans le répertoire de premier niveau de votre application et ajoutez-y vos fichiers NEXMO_API_KEY et NEXMO_API_SECRET à ce fichier. Les valeurs de ces éléments se trouvent en haut de la page du tableau de bord, sous l'en-tête Your API credentials en haut de la page du tableau de bord.
NEXMO_API_KEY=
NEXMO_API_SECRET=La prochaine tâche à effectuer dans le tableau de bord est de fournir un numéro de téléphone. Après avoir cliqué sur le lien Numbers dans la barre de navigation latérale, un menu déroulant apparaît. Une fois que vous avez sélectionné l'option Buy numbers puis cliquez sur le bouton Search vous verrez une liste de numéros possibles à acquérir.
Lorsque vous recherchez des Numbers par fonction, pays et type, il est recommandé de sélectionner le pays dans lequel vos utilisateurs seront basés, SMS pour les fonctions et Mobile pour le type.
Après avoir cliqué sur le bouton orange Buy pour le numéro que vous souhaitez acheter, vous pouvez ajouter ce numéro à votre fichier .env sous la forme d'une nouvelle variable appelée FROM_NUMBER:
NEXMO_API_KEY=
NEXMO_API_SECRET=
FROM_NUMBER=La dernière chose que nous devons faire dans notre tableau de bord est de fournir une URL accessible de l'extérieur comme webhook d'événement pour le numéro de téléphone. À des fins de développement, ngrok est un bon outil à utiliser, et vous pouvez suivre ce guide sur la façon de l'utiliser.
Dans le tableau de bord Numbers dans le menu déroulant de la barre de navigation latérale, une fois que vous aurez sélectionné Your numbers vous verrez votre numéro de téléphone nouvellement provisionné dans une liste de présentation. Après avoir cliqué sur l'icône de l'engrenage pour gérer ses propriétés, une boîte de dialogue de paramétrage s'affiche.
Dans l'exemple de la capture d'écran ci-dessus, vous devez remplacer le champ de texte Inbound Webhook URL par votre propre URL se terminant par /webhooks/event.
Voilà, c'est fait ! Notre code est finalisé et nos identifiants Nexmo sont prêts. À ce stade, vous êtes confronté à un choix pour l'exécution de votre application. Vous pouvez soit l'exécuter localement, soit la déployer chez un hébergeur externe, comme Heroku. Dans la dernière étape, nous verrons comment l'exécuter localement.
Si vous souhaitez le déployer pour une solution à plus long terme, vous pouvez visiter le dépôt dépôt GitHub et cliquer sur le bouton Deploy to Heroku en haut du README pour lancer le processus.
Exécution de l'application
Nous sommes maintenant prêts à exécuter notre toute nouvelle application ! Afin de l'exécuter localement, le webhook de l'événement Rails doit être accessible au monde extérieur en dehors de votre environnement local. Par exemple, si vous utilisez ngrok après avoir suivi ce guide alors l'application Rails et ngrok doivent fonctionner simultanément.
Pour démarrer l'application Rails, exécutez ce qui suit à partir de votre ligne de commande :
Vous pouvez ensuite naviguer dans le navigateur de votre choix jusqu'à localhost:3000. Vous verrez le formulaire d'inscription que vous avez créé. Remplissez-le avec votre numéro de téléphone et envoyez-le. Une fois la tâche Rake exécutée, vous devriez recevoir un SMS vous indiquant si c'est le week-end et si la journée d'aujourd'hui est différente de celle d'hier !
Prochaines étapes
L'application que nous avons construite, bien que fantaisiste, démontre le potentiel de l'utilisation du web scraping et du SMS pour créer une application qui délivre des mises à jour aux abonnés. Les cas d'utilisation potentiels d'une telle application sont innombrables. Que vous souhaitiez reproduire ce scénario exact ou porter le code pour votre propre cas d'utilisation, il y a encore plus à explorer sur ce sujet.
Pour en savoir plus sur les autres possibilités de SMS, consultez les ressources suivantes :
Partager:
Ben est un développeur en seconde carrière qui a auparavant passé une décennie dans les domaines de la formation pour adultes, de l'organisation communautaire et de la gestion d'organisations à but non lucratif. Il a travaillé comme défenseur des développeurs pour Vonage. Il écrit régulièrement sur l'intersection du développement communautaire et de la technologie. Originaire de Californie du Sud et ayant longtemps vécu à New York, Ben réside aujourd'hui près de Tel Aviv, en Israël.
