[RAILS] Comment créer une fonction de messagerie LINE avec Ruby

introduction

J'ai eu l'opportunité de développer une fonction d'échange de messages entre un système écrit en Ruby et LINE, un utilisateur généraliste, donc je partagerai les connaissances que j'ai acquises à cette époque.

J'ai également fait une démo d'une application fonctionnelle utilisant Rails, alors voyez ici pour des détails d'implémentation détaillés. Je pense que le contenu de l'article entrera en douceur si vous le demandez.

Quoi faire

Un administrateur système (ci-après dénommé «administrateur») et un utilisateur général (ci-après dénommé «utilisateur») créent une fonction d'échange de messages texte via LINE. Étant donné que le système a besoin de connaître les informations LINE de l'utilisateur pour pouvoir communiquer, nous implémenterons également la connexion LINE sur le navigateur.

Après avoir lié les informations LINE de l'utilisateur, lorsque l'utilisateur envoie un message au compte officiel LINE lié à l'administrateur, le message sera envoyé au système par Webhook et sauvegardé dans la base de données à partir de là. L'image est à peu près comme le montre la figure ci-dessous. Untitled Diagram (1).png

L'administrateur peut également envoyer des messages LINE aux utilisateurs via le système. Lorsque vous envoyez un SMS depuis le système, vous pouvez envoyer un message à l'utilisateur cible depuis le compte officiel LINE de l'administrateur via le programme. Une fois envoyé, le message sera enregistré dans la base de données. L'image est la suivante. Untitled Diagram (2).png

Travaux requis à l'avance

Créer un canal pour l'API LINE

Si l'administrateur n'a pas de canal API LINE, il ne sera pas possible de communiquer avec les utilisateurs via LINE, alors créez-le. Faisons-le depuis LINE Developer console. La chaîne dont vous avez besoin pour créer cette fois

Il y en a deux.

L'API de messagerie agit comme un compte LINE officiel. En guise de mise en garde, définissons le ** mode de réponse sur "Bot" **. En mode chat, je peux communiquer depuis le gestionnaire de compte LINE officiel, mais je ne peux pas recevoir de webhooks. Par conséquent, il est nécessaire de l'utiliser en mode Bot car il y a un problème que le système ne peut pas garder un journal de l'interaction. Pour plus de détails sur la façon de le créer, cliquez ici [https://developers.line.biz/ja/docs/messaging-api/getting-started/#%E3%83%81%E3%83%A3%E3%83%8D% E3% 83% AB% E3% 81% AE% E4% BD% 9C% E6% 88% 90) S'il vous plaît. La connexion LINE est littéralement un canal pour utiliser la connexion LINE.

Après la création, créez les données dans la table admins de la base de données. À l'origine, je pense qu'il est bon de s'enregistrer depuis l'écran, mais il est fait pour le porter sur le côté et le piquer directement depuis la console (lors de la mise dans la DB avec le code de production, il est recommandé de le mettre crypté par souci de sécurité. Faire)

  create_table "admins", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", force: :cascade do |t|
    t.integer "line_messaging_id", null: false #API de messagerie "ID de canal"
    t.string "line_messaging_secret", null: false #API de messagerie "Secret de canal"
    t.string "line_messaging_token", null: false #API de messagerie "Jeton d'accès au canal"
    t.integer "line_login_id", null: false #"ID de chaîne" de la connexion LINE
    t.string "line_login_secret", null: false #Connexion LINE "Channel Secret"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

Définir diverses URL

La prochaine fois, je devrai définir des URL du côté console de LINE, donc je vais m'enregistrer. Plus précisément, c'est comme suit.

Il y a certaines choses à prendre en compte lors de l'enregistrement de cette URL. Il a une URL

--https uniquement --localhost non disponible

C'est le but. A ce rythme, le développement local semble difficile. Cependant, en utilisant ngrok, vous pouvez publier temporairement l'URL locale vers l'extérieur, de sorte que vous pouvez confortablement développer localement en enregistrant l'URL créée avec lui. Tu peux le faire. C'est très pratique.

Après avoir créé le chemin de la requête POST pour l'URL Webhook et l'avoir défini sur la console, vérifiez la requête via le bouton «Vérifier». Assurez-vous d'obtenir 200 réponses. Pour plus d'informations [ici](https://developers.line.biz/ja/docs/messaging-api/building-bot/#%E3%82%B3%E3%83%B3%E3%82%BD%E3% 83% BC% E3% 83% AB% E3% 81% A7% E3% 83% 9C% E3% 83% 83% E3% 83% 88% E3% 82% 92% E8% A8% AD% E5% AE% 9A% E3% 81% 99% E3% 82% 8B) S'il vous plaît.

Maintenant que les préparatifs du développement sont terminés, je voudrais continuer à le développer.

Lier le compte LINE de l'utilisateur au système

Tout d'abord, je voudrais créer une fonction de connexion LINE pour mettre les informations LINE de l'utilisateur nécessaires pour envoyer et recevoir des messages dans la base de données. Cette information LINE est l '"ID utilisateur". Veuillez noter qu'il ne s'agit pas de l'ID de LIGNE utilisé dans la recherche d'ID.

Le flux de connexion est [ici](https://developers.line.biz/ja/docs/line-login/integrate-line-login/#%E3%83%AD%E3%82%B0%E3%82% Ce sera A4% E3% 83% B3% E3% 81% AE% E3% 83% 95% E3% 83% AD% E3% 83% BC), mais si je l'écris à nouveau

  1. Dirigez l'utilisateur du système vers l'URL d'autorisation pour la connexion LINE en utilisant les paramètres de requête requis
  2. L'écran de connexion LINE s'ouvre dans le navigateur, et l'utilisateur se connecte et est authentifié.
  3. Redirigez les utilisateurs de la plate-forme LINE vers le système via redirect_uri. A ce moment, une chaîne de requête comprenant le code d'autorisation et "état" est également envoyée au système.
  4. Après avoir validé l '«état», le système utilise le code d'autorisation pour demander un jeton d'accès à partir de l'URL du point de terminaison https://api.line.me/oauth2/v2.1/token.
  5. La plateforme LINE valide la demande du système et renvoie un jeton d'accès.
  6. Appelez l'API sociale en fonction du jeton d'accès pour obtenir l'ID utilisateur et le stocker dans la base de données.

Regardons le code qui crée l'URL d'autorisation pour l'étape 1.

app/controllers/auth_controller.rb


class AuthController < ApplicationController
  def index
    admin = Admin.find(params[:admin_id])

    state = SecureRandom.hex(32)
    session[:state] = state
    redirect_to Line::Api::Oauth.new(admin).auth_uri(state)
  end
end

/ admin /: admin_id / auth redirige vers l'URL d'autorisation en fonction des informations du compte de connexion LINE de l'administrateur cible. Étant donné que l'état est nécessaire pour les mesures CSRF, un nombre aléatoire est généré côté application. La valeur est stockée en session (utilisée dans le traitement post-autorisation) et transmise à Line :: Api :: Oauth # auth_uri. Jetons un coup d'œil au code qu'il contient.

app/models/line/api/oauth.rb


AUTH_URI = 'https://access.line.me/oauth2/v2.1/authorize'

def auth_uri(state)
  params = {
    response_type: 'code',
    client_id: @admin.line_login_id,
    redirect_uri: callback_uri,
    state: state,
    scope: 'openid',
    prompt: 'consent', #Option pour vous assurer d'autoriser l'authentification LINE
    bot_prompt: 'aggressive' #Une fois connecté, un écran apparaîtra vous demandant si vous souhaitez vous faire des amis avec le compte officiel auquel vous vous êtes lié.
  }

  # NOTE: https://developers.line.biz/ja/docs/line-login/integrate-line-login/#making-an-authorization-request
  "#{AUTH_URI}?#{params.to_query}"
end

Voir ici pour la signification détaillée des paramètres. Personnellement, le paramètre bot_prompt est pratique, et après la connexion à LINE, l'écran d'ajout d'amis du compte officiel correspondant est également affiché, j'ai donc pensé qu'il serait pratique d'amener des messages entre eux.

Lorsque vous cliquez sur l'URL, vous serez redirigé vers l'écran de connexion comme indiqué dans l'image ci-dessous.

スクリーンショット 2020-06-16 1.52.14.png

Lorsque la connexion est terminée et l'autorisation terminée, vous serez redirigé vers l'URL de rappel de la connexion LINE que vous avez définie précédemment. Maintenant que nous nous dirigeons vers / admin /: admin_id / callback, regardons le code correspondant.

app/controllers/callback_controller.rb


class CallbackController < ApplicationController
  def index
    admin = Admin.find(params[:admin_id])

    #Lancer une exception si les états sont différents
    raise Line::InvalidState unless params[:state] == session[:state]

    line_user_id = Line::Api::Oauth.new(admin).line_user_id(params[:code])
    User.create!(line_user_id: line_user_id)

    render plain: 'Coopération LINE terminée!', status: :ok
  end
end

Les mesures CSRF sont prises en comparant la valeur de l'état qui vient après l'autorisation avec la valeur de l'état de la session qui a été précédemment entrée. Après cela, obtenez l'ID utilisateur LINE. Une fois obtenu, stockez-le dans la base de données. Jetons un coup d'œil au code d'acquisition de l'ID utilisateur.

app/models/line/api/oauth.rb


def line_user_id(code)
  verify_id_token(access_token_data(code))['sub']
end

def access_token_data(code)
  req_body = {
    grant_type: 'authorization_code',
    code: code,
    redirect_uri: callback_uri, # NOTE:Elle est comparée à l '"URL de rappel" définie dans la console du canal de connexion LINE.
    client_id: @admin.line_login_id,
    client_secret: @admin.line_login_secret
  }

  # NOTE: https://developers.line.biz/ja/docs/line-login/integrate-line-login/#get-access-token
  res = conn.post do |req|
    req.url '/oauth2/v2.1/token'
    req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
    req.body = req_body
  end
  JSON.parse(handle_error(res).body)
end

def verify_id_token(access_token_data)
  req_body = {
    id_token: access_token_data['id_token'],
    client_id: @admin.line_login_id
  }
  # NOTE: https://developers.line.biz/ja/reference/social-api/#verify-id-token
  res = conn.post do |req|
    req.url '/oauth2/v2.1/verify'
    req.body = req_body
  end
  JSON.parse(handle_error(res).body)
end

ʻAccess_token_data` accède à API d'acquisition de jetons d'accès pour renvoyer des informations, puis À l'API sociale API de vérification de jeton d'identification et réussissez, vous obtiendrez votre identifiant d'utilisateur.

Une chose à garder à l'esprit est que l'API d'acquisition de jetons d'accès transmet redirect_uri, qui devrait être exactement le même que le redirect_uri défini dans l'URI d'autorisation. Vous ne pouvez pas faire grand-chose, mais pour diverses raisons, si vous voulez passer quelque chose comme paramètre de requête à redirect_uri lors de la création d'une URL d'autorisation et l'obtenir avec un rappel, l'API d'acquisition de jeton d'accès comprenant ce paramètre de requête Ne pas le transmettre à redirect_uri entraînera une erreur.

Recevoir le message de l'utilisateur sur le système

Maintenant que l'utilisateur a pu se connecter avec le compte officiel LINE de l'administrateur, je souhaite recevoir le message LINE envoyé par l'utilisateur dans le système suivant. Étant donné que l'URL du chemin de / admin /: admin_id / webhook est spécifiée dans l'URL du Webhook de l'API de messagerie, la demande sera envoyée chaque fois qu'un événement (ajouter un ami, bloquer un message, etc.) se produit dans le compte officiel. Devenir. Ainsi, si l'événement qui a envoyé le message à partir de cette demande && la personne qui l'a envoyé est l'ID utilisateur LINE ** enregistré dans la base de données, si vous enregistrez le message dans la base de données, les messages LINE de l'utilisateur seront collectés. Vous pouvez. Le code du contrôleur Webhook est ci-dessous.

app/controllers/webhook_controller.rb


class WebhookController < ApplicationController
  protect_from_forgery with: :null_session

  def create
    admin = Admin.find(params[:admin_id])

    bot = Line::Api::Bot.new(admin)
    body = request.body.read
    raise Line::InvalidSignatureError unless bot.validate_signature?(body, request.env['HTTP_X_LINE_SIGNATURE'])

    events = bot.parse_events_from(body)
    events.each do |event|
      case event
      when Line::Bot::Event::Message
        Line::SaveReceivedMessage.new(admin).call(event)
      end
    end

    render plain: 'success!', status: :ok
  end
end

Étant donné que la demande provient de l'extérieur, invalidez le jeton CSRF. Vous devez également faire Vérification de la signature pour déterminer si la demande provient de la plate-forme LINE. C'est un peu compliqué, mais c'est fait avec un gem appelé line-bot-sdk-ruby, alors utilisons-le.

Après vérification, déterminez à quoi ressemble l'événement. Je vais l'utiliser car la gemme analysera également les informations d'événement du corps de la requête. Je pense qu'il est simple de brancher des événements avec une déclaration de cas. Puisque Line :: Bot :: Event :: Message est l'événement lorsque le message arrive, le processus d'enregistrement du message est inséré à ce moment. Le code est ci-dessous.

app/models/line/save_received_message.rb


module Line
  class SaveReceivedMessage
    def initialize(admin)
      @admin = admin
    end

    def call(event)
      user = User.find_by(line_user_id: event['source']['userId'])

      resource = MessageText.new(content: event.message['text'])
      Message.create!(sendable: user, receivable: @admin, resource: resource) if user.present?
    end
  end
end

Voir ici pour le contenu de l'objet événement.

Enfin, vérifiez le fonctionnement. iOS_の画像.png Lorsque vous envoyez un message

スクリーンショット_2020-06-16_13_02_20.png Vous pouvez voir que la demande est ignorée et que l'enregistrement est enregistré. Le système peut maintenant recevoir le message LINE de l'utilisateur et le mettre dans la base de données.

Envoyer un message du système à l'utilisateur

Ensuite, nous examinerons la transmission des messages Système-> LIGNE de l'utilisateur, qui est l'opposé de la précédente. Vous n'avez pas besoin d'utiliser Webhook, appuyez simplement sur API pour envoyer un message push et vous avez terminé.

app/models/line/save_sent_message.rb


module Line
  class SaveSentMessage
    def initialize(admin)
      @admin = admin
    end

    def call_with_text(user:, text:)
      user = User.find_by(line_user_id: user.line_user_id)

      if user.present?
        Line::Api::Push.new(@admin).call_with_text(user: user, text: text)
        resource = MessageText.new(content: text)
        Message.create!(sendable: @admin, receivable: user, resource: resource)
      end
    end
  end
end

app/models/line/api/push.rb


module Line::Api
  class Push < Base
    def call_with_text(user:, text:)
      call(user: user, resource: Message::Text.new([text]))
    end

    private

    def call(user:, resource:)
      req_body = {to: user.line_user_id}.merge(resource.request_body)
      # NOTE: https://developers.line.biz/ja/reference/messaging-api/#send-push-message
      res = conn.post do |req|
        req.url '/v2/bot/message/push'
        req.headers['Content-Type'] = 'application/json'
        req.headers['Authorization'] = "Bearer #{@admin.line_messaging_token}"
        req.body = req_body.to_json
      end

      handle_error(res)
    end
  end
end

C'est un contrôle de fonctionnement. Portez-le sur le côté et frappez l'accord de la console.

スクリーンショット_2020-06-18_8_13_23.png Un enregistrement a été ajouté.

iOS_の画像__1_.png

Un message a été envoyé à LINE!

à la fin

Avec ce qui précède, il est possible de communiquer entre l'utilisateur et le système et de laisser le contenu. Je pense que ce sera plus compliqué dans un vrai système, mais j'espère que cela vous aidera à mettre en œuvre quelque chose: priez:

Recommended Posts

Comment créer une fonction de messagerie LINE avec Ruby
Apprendre Ruby avec AtCoder 13 Comment créer un tableau à deux dimensions
Comment implémenter TextInputLayout avec la fonction de validation
Facile à créer LINE BOT avec Java Servlet
Comment faire un pot ombré
Faisons un Bot LINE avec Ruby + Sinatra - Partie 2
Faisons un Bot LINE avec Ruby + Sinatra - Partie 1
[Java] Comment rompre une ligne avec StringBuilder
Java pour jouer avec Function
Java - Comment créer JTable
Comment ajouter la fonction ActionText
Comment utiliser le retour Ruby
[Ruby] Comment commenter
Comment numéroter (nombre) avec html.erb
Comment mettre à jour avec activerecord-import
Ruby: Comment utiliser les cookies
[Rails] Comment faire des graines
Comparaison de l'écriture des fonctions de rappel (Java, JavaScript, Ruby)
[Facile] Comment formater automatiquement les fichiers Ruby erb avec vsCode
Je veux créer une fonction avec kotlin et java!
Comment créer une application à l'aide de Tensorflow avec Android Studio
Comment gérer différentes versions de rbenv et Ruby
Je veux créer un bouton avec un saut de ligne avec link_to [Note]
Comment démarrer avec Slim
Comment itérer indéfiniment en Ruby
Je souhaite ajouter une fonction de navigation avec ruby on rails
Comment créer un fichier jar sans dépendances dans Maven
Comment créer un conteneur Java
Comment installer Ruby via rbenv
Comment créer une application avec ruby on rails (en supposant que l'environnement a été construit)
Comment entourer n'importe quel caractère avec "~"
Comment utiliser Ruby on Rails
Comment créer un pilote JDBC
Comment installer Bootstrap dans Ruby
Comment écrire Ruby en une seule ligne Résumé par débutant
Comment utiliser mssql-tools avec Alpine
Comment créer un écran de démarrage
Comment créer un plug-in Jenkins
Comment faire un projet Maven
Comment ajouter la fonction de suppression
[Pour les débutants] Comment obtenir le nom de la ligne de chemin de fer différée Ruby
Apprendre Ruby avec AtCoder 11 Comment recevoir une entrée standard souvent utilisée
Comment utiliser la méthode Ruby inject
Comment créer un tableau Java
Comment utiliser MinIO avec la même fonction que S3 Utiliser docker-compose
[Android] Comment créer un fragment de dialogue
Comment exécuter l'IRB de Ruby (Ruby interactif)
Comment démarrer Camunda avec Docker
J'ai essayé de créer une fonction de groupe (babillard) avec Rails