LoginSignup
25
24

More than 5 years have passed since last update.

2時間でできるサーバーレスLINEBot(送信元識別子取得編)

Posted at

モチベ

  • 高校とか大学の頃の写真が最近見ると面白い
  • けど、毎日見つけてくんのめんどくさい

    • 毎日定時に写真を貼ってくれるBotが欲しい
  • AWSをいい加減触り始めたい

  • サーバ立てるんめんどくさい&挫折率高め

    • こんな記事を発見
    • Lambdaでサーバー構築が簡単に!!!

tl;dr

目指すものはBotを含むグループ・ルームにおいて、ある特定の単語が出た場合、S3に配置してある画像からランダムに画像を1枚取得し、Botが写真を返信すること

  1. LINEサーバからLambdaに発火
  2. RDSからURLを取得
  3. LINEサーバへURLを含んだPOSTを送る
  4. アプリにS3の画像が投稿される

てな流れですかねえ

また、LINEBotを作る上でネックになる、送信元識別子の取得も合わせてできるように実装していきます

構成図(完成予定)

Untitled Diagram.png

この記事ではその第一弾として、とりあえずLambdaを使って、疎通確認・色々弄ってみたいと思います

構成図(現状)

Untitled Diagram-2.png

LINEのBotアカウント取得

まずはこちらで、事業者アカウントを登録しましょう。

職種などなど聞かれますが、だいたい適当でok(細かいところは覚えてない、、なんせアカウント作ったのは去年なので,,,)
筆者の職種は美容系にしました

そうすると、「アカウントリスト」画面のページ下部に「ビジネスアカウントを作成する」があるのでこちらへGO
スクリーンショット 2017-08-31 0.37.26.png

すると、アカウントが提供するサービスの種類を聞かれますが、ここはMessagingAPIの下部の小さな「Developer Trialを始める」を選択しましょう

apapapap.png

こうすると、アカウントのプランがDeveloperTrialになります。一般的なフリープランとの大きな違いは、

developper フリー
pushAPI OK NG
追加可能友達数 50 制限なし

趣味で作るぐらいなら、DeveloperTrialで十分ですね

アカウント名・画像・職種などを入力して、申し込みます
その後、この部分忘れがちですが、LINE@MANAGERに移動してください

すると、こんな画面に出くわすはずです(アカウント設定>MessagingAPI設定)
スクリーンショット 2017-08-31 0.52.46.png

そのまま進んでいきましょう
これで、晴れて、Botアカウント作成が完了しました

ここで、LINEサーバとLambdaをつなげるための、CHANNEL_ACCESS_TOKENをメモしておきましょう
スクリーンショット 2017-09-01 14.26.12.png

Lambda関数作成 & Lambda => LINEサーバへの接続設定

次に、Lambdaでサーバーレスなサーバーを作って、Lambda => LINEサーバへの接続設定も併せて行っていきたいと思います。

AWS Lambda を使用すれば、サーバーのプロビジョニングや管理なしでコードを実行できます。課金は実際に使用したコンピューティング時間に対してのみ発生し、コードが実行されていないときには料金も発生しません。Lambda を使用すれば、実質どのようなタイプのアプリケーションやバックエンドサービスでも管理を必要とせずに実行できます。コードさえアップロードすれば、高可用性を実現しながらコードを実行およびスケーリングするために必要なことは、すべて Lambda により行われます。コードは、他の AWS サービスから自動的にトリガーするよう設定することも、ウェブやモバイルアプリケーションから直接呼び出すよう設定することもできます。

  1. AWSのConsoleからLambdaを選択
  2. 関数の作成
  3. 設計図:一から作成
  4. トリガー:APIGateway
  5. API名:{任意},セキュリティ:オープン,デプロイされるステージ:prod
  6. 名前:{任意},説明:{任意}:ランタイム:python3.6

今回は、python3.6を使います(筆者が最近好んでるのでw)

実は、LINE のMessageinAPIを使って、特定のユーザー・グループ・ルームに対して何かしようとすると、送信先識別子というのがつきまとってきます。これは、

Webhookイベントの送信元ユーザ、グループ、及びトークルームの識別子を、送信先識別子として指定できます。
LINEアプリで使用されているLINE IDは指定できません。

とのことらしいです。botを開発しているユーザーの自分の送信先識別子はLINEDevelopersに書いてあるにしても、他のユーザー・グループ・ルームの識別子は全くわかりません。

なので、まず始めにLambdaで実装するコードは、Botに対して送信して来たグループ・ユーザーの送信先識別子を返すということにします

以下、私が作ったsampleコードです


import urllib.request, json, os, ast

def lambda_handler(event, context):

    # LINEサーバへ叩く準備
    url = "https://api.line.me/v2/bot/message/push"
    method = "POST"
    headers = {
        'Authorization': os.getenv("CHANNEL_ACCESS_TOKEN"),
        'Content-Type': 'application/json'
    }

    # 文字列をdict型へparse
    apiParam = ast.literal_eval(event['body'])
    source = apiParam['events'][0]['source']

    sourceList = []
    for key, value in source.items():
        sourceList.append({"type": "text", "text": key + "#" + value})

    obj = {
        # ルームからきたrequestはルームへ返す。他は、発信者へ返す。
        "to": source["roomId"] if "roomId" in source else source["userId"],
        "messages": sourceList
    }
    json_data = json.dumps(obj).encode("utf-8")

    # httpリクエストを準備してPOST
    request = urllib.request.Request(url, data=json_data, method=method, headers=headers)
    with urllib.request.urlopen(request) as response:
        response_body = response.read().decode("utf-8")
    return 0

ハンドラ:index.lambda_handler
ロール:カスタムロールの作成
=> ここで、ポリシーテンプレートにS3オブジェクトの読み取り専用アクセス権限を加えてください。他は適当に作りましょう。

また、環境変数部分には以下を設定

CHANNEL_ACCESSS_TOKEN:{先ほどLINEの画面でメモしたCHANNEL_ACCESS_TOKEN}

これを貼って保存しましょう

※ここで、テストをしても、実際のLINEサーバからのレスポンスではないので、正しくテストできません。

LINEサーバ => Lambdaへの接続設定

最後に、LINEサーバからLambdaへの接続設定を行なっていきます

先ほど作成したLambdaのトリガー項目から、(下図参照)

スクリーンショット 2017-09-02 22.17.31.png

URLを拾って来ます、筆者はMethod:AMYのため、このURLをGETでもPOSTでも叩くと、Lambdaに書かれたコードが実行されるということです

では、この発火するためのURLをLINEサーバに設定します。

LINEDevelopersにログインし、Webhook URLに設定します

スクリーンショット 2017-09-02 22.22.58.png

この際に、必ず、ポート番号443をつけましょう。弾かれるみたいです(試してはいない)

これで、晴れてLINEBotの完成です

LINEDevelopersのQRコードから友達登録して、適当なルーム・1to1で何か送信してみましょう
こんな感じで表示されるはずです
こいつらが、送信先識別子です、この場合、私自身の識別子と、送信したルームの識別子が表示されています

スクリーンショット 2017-09-02 22.56.19.png

tips1 外部ライブラリは使えないの?

Lambdaでは、コードを書いて乗っけるだけなので、初期状態では基本的なライブラリしか使えません。
なので、LambdaからAPIを叩くときには、urllib2が使えずに、urllibをごり押しで使いました
実はzip化したコードとライブラリ一式をアップロードすると、ライブラリがつかえますが、それは次回紹介します

tips2 なんでもう一回parseしてるの?

LINE API Referenceをざっと読むと、
Webhookのrequestは以下のように書いてありました。

{
  "events": [
      {
        "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
        "type": "message",
        "timestamp": 1462629479859,
        "source": {
             "type": "user",
             "userId": "U206d25c2ea6bd87c17655609a1c37cb8"
         },
         "message": {
             "id": "325708",
             "type": "text",
             "text": "Hello, world"
          }
      }
  ]
}

対して、Lambdaの引数は、以下

def lambda_handler(event, context):

引数eventの中にこいつらが含まれています。

{
    ~~~~~略~~~~~~~
    "body": "{"events":[{"type":"message","replyToken":"cd8477b1e2844fb7ac535ba8cfb7e604","source":{"userId":"Ue49159d045ba254087865fb1c09ce0e7","type":"user"},"timestamp":1504100774524,"message":{"type":"text","id":"6623088374597","text":"fewgvlkwgw"}}]}"
    ~~~~~略~~~~~~~
}

なななんと、文字列で格納されているんですね、、、dict型だと思って、どハマりした、、、、

なので、

apiParam = ast.literal_eval(event['body'])

というふうに、parseしてるんです

所感

  • サーバレスだからどハマりする要素が少ない
  • せっかくLINE developersアカウントでpushAPIが使えるのに使ってない
  • pythonの無駄を削ぎ落とした感
    • ||orとか、phpでいうarray_key_exist()がpythonでは A in Bなどから感じられる

次回予告

次回は
RDS&S3連動編ということで、「プリーズ」と送ったら、RDS(S3)の中からランダムな写真をresponseするものを作っていきます

25
24
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
25
24