LoginSignup
0
0

More than 3 years have passed since last update.

千葉県Go To EAT向けに店舗検索BOT(AI LINE BOT)を作った話(5)【配信サーバ構築】

Posted at

千葉県Go To EAT向けに店舗検索BOT(AI LINE BOT)を作った話(4)【AIモデル作成】の続きです。

この記事にて、LINE Messaging APIの登録、配信サーバの構築を行います。

LINE Messaging APIの利用

まずは、LINE Developersに登録します。
既存のLINEアカウントを持っていれば、無料で登録できます。

LINE Developers

登録完了後、プロバイダを作成します。作成ボタンを押下します。
Devhome.png

devpro作成.png

チャネル設定画面から、Messaging APIを選択します。

チャネル作成.png

各チャネルの項目に任意の値を入力し、設定を保存します。

チャネル設定.png

これでチャネル作成は完了です。
チャネル画面.png

AWSサーバの構築

pythonで作ったAIモデルを動作させるためのサーバをAWS上に確保します。
まずは、AWSのユーザを登録します。
ユーザはあらかじめ以下を参考に作成して下さい。

◆参考AWSアカウント作成手順

今回は、「無料」がコンセプトなので、無料枠が適用されるt.micro(Amazon linux2)を使います。

インスタンス作成画面からインスタンスを作成します。
インスタンス作成.png
OSにAamazon Linux2 AMIを選択します。
AMZLIN.png
インスタンスタイプにt2.microを選択します。
t2micro.png
確認画面からインスタンス作成ボタンを押下します。
kakunin.png

数分経つと、インスタンスが作成されます。
LINE Messageing APIとサーバはhttps通信する必要なため、このタイミングでセキュリティグループを設定しておきます。左ペイン「セキュリティグループ」を選択し、443のインバウンドアクセスを可能としておきます。
SC.png

ドメインの取得

https通信を可能とするためには、秘密鍵と証明書を発行する必要があります。
そのために、無料のドメインプロバイダに登録して、フリードメインを取得します。
以下のページを参考にしました。

◆参考:freenomでドメインの取得

Let's Encryptによる秘密鍵/証明書の発行

以下のサイトを参考に秘密鍵/証明書を無料で発行します。

参考:無料でHTTPS化できる「Let's Encrypt」をやってみた ※install.sh付き

Flask&https通信化

以下のページを参考にサーバ側でflask&https通信するプログラムを作成しました。
プログラムには、スマホから受け取った検索ワードを作成済のdoc2vecモデルに読み込ませ、結果を返却しています。
◆参考:Flask を HTTPS 化する。

プログラム格納先
https://github.com/Haradasn/linebot

python_deamon.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import MeCab
import gensim.models.doc2vec as doc2vec
from gensim import models
from gensim.models.doc2vec import TaggedDocument
from flask import Flask, request, abort
import ssl
import pandas as pd
from linebot import (
   LineBotApi, WebhookHandler
)
from linebot.exceptions import (
   InvalidSignatureError
)
from linebot.models import (
   MessageEvent, TextMessage, TextSendMessage,
)
model = models.Doc2Vec.load('/opt/doc2vec.model')
df = pd.read_csv('/opt/database.csv', header=None)
def trim_doc(doc):
    lines = doc.splitlines()
    valid_lines = []
    is_valid = False
    horizontal_rule_cnt = 0
    break_cnt = 0
    for line in lines:
        if horizontal_rule_cnt < 2 and '-----' in line: 
            is_valid = horizontal_rule_cnt == 2
            continue
            break_cnt += 1
            is_valid = break_cnt != 3
            continue
        break_cnt = 0
        valid_lines.append(line)
    return ''.join(valid_lines)

def split_into_words(doc):
    mecab = MeCab.Tagger("-Ochasen")
    valid_doc = doc
    lines = mecab.parse(valid_doc).splitlines()
    words = []
    #print(lines)
    for line in lines:
        chunks = line.split('\t')
        if len(chunks) > 3 and (chunks[3].startswith('形容詞') or (chunks[3].startswith('名詞') and not chunks[3].startswith('名詞-数'))):
            words.append(chunks[0])
    return words

# 似た文章を探す
def search_similar_texts(words,df):
    x = model.infer_vector(words)
    most_similar_texts = model.docvecs.most_similar([x],topn=3)
    text=[[0 for i in range(4)] for j in range(3)]
    for idx,row in enumerate(df.iterrows()):
        for i,similar_text in enumerate(most_similar_texts):    
            if df[df.columns[4]][idx] == similar_text[0]:
                text[i][0] = similar_text[0]
                text[i][1] = df[df.columns[7]][idx]
                if df[df.columns[2]][idx] == "〇":
                    text[i][2] = "◆紙クーポン使用  :可"
                else:
                    text[i][2] = "◆紙クーポン使用  :不可"
                if df[df.columns[3]][idx] == "〇":
                    text[i][3] = "◆電子クーポン使用 :可"
                else:
                    text[i][3] = "◆電子クーポン使用 :不可"
                break
    return text
    #for similar_text in most_similar_texts:
    #    print()

# 似た単語を探す
def search_similar_word(words):
    #print(words)
    for word in words:
        #print(word)
        #print(word + ':')
        for result in model.most_similar(positive=word, topn=10):
            print(result[0])
app = Flask(__name__)
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.load_cert_chain('/etc/letsencrypt/live/tsurushi.tk/fullchain.pem', '/etc/letsencrypt/live/tsurushi.tk/privkey.pem')
line_bot_api = LineBotApi('#LINE APIトークンを記載する')
handler = WebhookHandler('#LINE Developersに書いてあるシークレットを記載する。')



@app.route("/callback", methods=['POST'])
def callback():
   # get X-Line-Signature header value
   signature = request.headers['X-Line-Signature']

   # get request body as text
   body = request.get_data(as_text=True)
   app.logger.info("Request body: " + body)

   # handle webhook body
   try:
       handler.handle(body, signature)
   except InvalidSignatureError:
       print("Invalid signature. Please check your channel access token/channel secret.")
       abort(400)

   return 'OK'
@app.route('/')
def hello():
    return 'Hello World!'

@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    #print("event.message.text=",event.message.text)
    words = split_into_words(event.message.text)
    text = []
    text = search_similar_texts(words,df)
    #print("text",text[0])
    line_bot_api.reply_message(
       event.reply_token,
       [
       TextSendMessage(text="1.こちらはいかがでしょうか\uDBC0\uDC78\n"+text[0][1]+"\n"+text[0][2]+"\n"+text[0][3]),
       TextSendMessage("2.こちらはいかがでしょうか\uDBC0\uDC9D\n"+text[1][1]+"\n"+text[1][2]+"\n"+text[1][3]),
       TextSendMessage("3.こちらはいかがでしょうか\uDBC0\uDC90\n"+text[2][1]+"\n"+text[2][2]+"\n"+text[2][3])
       ]
    )
if __name__ == "__main__":
   app.run(host='0.0.0.0', port=443,ssl_context=context, threaded=True)
#    app.run(host='0.0.0.0',port=80)

LINE DevelopersへのURLの設定

最後に、先に取得していたドメインのURLを、LINE Developersの「Webhook URL」に登録します。
hol.png

検証ボタンを押して、「成功」の文字が出たら、LINE Message APIとサーバの疎通は完了です!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0