Posted at

LINE Botの通知イベントをAWS DynamoDBに保存する

image.png

LINE APIでメッセージを送信するためには、送信先のグループIDや、お友達のユーザーIDが必要ですが、一般アカウントの場合は、IDを調べるAPIがありません(グループメンバーのユーザーIDを取得するには、認証済みアカウントまたはプレミアムアカウントが必要)。

その代わり、フォローや、グループ参加のタイミングでWebhookが飛んでくるので、通知されるイベントオブジェクトに含まれるIDを覚えておくことで取り回しができます。

LINE APIのWebhookイベントで人の出入りで発生するイベントです。


  • フォローイベント

  • フォロー解除イベント

  • 参加イベント

  • 退出イベント

  • メンバー参加イベント

  • メンバー退出イベント

参考: Messaging APIリファレンス - LINE Developers

IDをDB化しておけば良さそうなので、WebhookイベントのタイミングでDynamoDBに保存する方法を試してみます。


前準備

LINE Developerアカウントのセットアップ方法、AWS Lambdaの使用方法は前回の記事を参考にしてください。

まずは、Webhookでどんな構造のイベントオブジェクトが送られているかをみるために、イベントをまるごとDynamoDBに保存してみます。


DynamoDBのテーブルを作成する

AWS DynamoDBダッシュボードで「テーブルの作成」をクリックします。

image.png

設定例:

項目
設定値

テーブル名
LineEvent

プライマリキー
id

その他
デフォルト


IAMでロールにDynamoDBのアクセス権を与える

AWSのIdentity and Access Management (IAM)ダッシュボードでLambdaのロールを開いて、インラインポリシーを追加します。

image.png

サービスに「DynamoDB」を指定し、とりあえずすべてのリソースを指定します。実際には対象のDynamoDBのARNを個別に指定したほうが良いと思います。

image.png

ポリシー名を設定し、ポリシーを作成します。

image.png

そうすると、Lambda関数のDesignerにDyanamoDBが表示されます。

image.png


Lambda関数からDynamoDBへ書き込む

DBの準備ができたので、LINE BotのWebhookイベントオブジェクトをまるごとDynamoDBに書き込んでみます。事前にスキーマ定義をしなくても、プライマリーキーさえあれば、あとは好きな項目を入れられます。


index.js

'use strict';

const AWS = require("aws-sdk");

// event handler
exports.handler = function handleEvent(event, context) {

const docClient = new AWS.DynamoDB.DocumentClient();
var params = {
"TableName": "LineEvent",
"Item": {
"id": event.requestContext.requestId,
"event": event,
"body": JSON.parse(event.body),
}
};
docClient.put(params, function (err, data) {
if (err) {
console.error('Unable to put item. Error JSON:', JSON.stringify(err, null, 2));
} else {
console.log('DeleteItem succeeded:', JSON.stringify(data, null, 2));
}
});
};


コードをLambda関数に保存したら、LINEアプリからbotをフォローしたり、メッセージを送信してみます。DynamoDBダッシュボードのテーブルの「項目」を表示すると、レコードが追加されていることがわかります。

image.png

ためしに、「follow」イベント開くと、各項目がツリーで表示されます。めっちゃ便利。

image.png


イベントに応じてテーブルを更新する

様子がわかったので、ユーザーDBとメッセージDBを作ってみます。


テーブルの作成

これまでと同様の手順で、DynamoDBダッシュボードで新規にテーブルを2つ作成します。

テーブル名
プライマリキー
プライマリキーの属性

LineUser
userId
文字列

LineMessage
messageId
文字列


Lambda関数のコード

follow, unfollow, messageイベントに対応してDynamoDBのテーブルにレコードを追加・削除するサンプルコードです。


index.js

'use strict';

const line = require('@line/bot-sdk');
const AWS = require("aws-sdk");

// create LINE SDK config from env variables
const config = {
channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN,
channelSecret: process.env.CHANNEL_SECRET,
};

// event handler
exports.handler = function handleEvent(event, context) {
const body = JSON.parse(event.body);
const eventType = body.events[0].type;
const userId = body.events[0].source.userId;
const timestamp = body.events[0].timestamp;
const replyToken = body.events[0].replyToken;

const docClient = new AWS.DynamoDB.DocumentClient();

let params = {};
switch (eventType) {
case "follow":
params = {
"TableName": "LineUser",
"Item": {
"userId": userId,
"timestamp": timestamp
}
};
docClient.put(params, function (err, data) {
if (err) {
console.error("Unable to add item. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("Added item:", JSON.stringify(data, null, 2));
}
});
break;

case "unfollow":
params = {
"TableName": "LineUser",
"Key": {
"userId": userId
}
};
docClient.delete(params, function (err, data) {
if (err) {
console.error("Unable to delete item. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("DeleteItem succeeded:", JSON.stringify(data, null, 2));
}
});
break;

case "message":
const messageId = body.events[0].message.id;
const messageType = body.events[0].message.type;
const messageText = body.events[0].message.text;
params = {
"TableName": "LineMessage",
"Item": {
"messageId": messageId,
"userId": userId,
"text": messageText,
"timestamp": timestamp
}
};
docClient.put(params, function (err, data) {
if (err) {
console.error("Unable to add item. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("Added item:", JSON.stringify(data, null, 2));
}

// おうむ返し
return lineEcho(context, messageType, messageText, replyToken);
});
break;
}
};

function lineEcho(context, type, text, replyToken) {
// ignore non-text-message event
if (type != 'text' || replyToken == '00000000000000000000000000000000') {
const lambdaResponse = {
statusCode: 200,
headers: { "X-Line-Status": "OK" },
body: '{"result":"connect check"}'
};
return context.succeed(lambdaResponse);
}

// create a echoing text message
const echo = { type: 'text', text: text };

// create LINE SDK client
const client = new line.Client(config);

// use reply API
return client.replyMessage(replyToken, echo);
}



さいごに

これでユーザーIDのDB化ができたので、LINE botアプリの取り回しが楽になると思います。

今後は、Android/iOSアプリからDynamoDBの内容を取り出すために、Web API化してみたいと思います。


参考