この記事で書かかないこと
LINE developerの登録や、アクセストークンの発行等々は色々なブログで共有されているので割愛。
今回はコードを中心とした解説をします。
※この記事は作成途中です。また更新します。(2018/11/11更新)
大まかな流れ
まず、LINE(bot)にメッセージが送られた時の大まかな流れの説明。
LINEにメッセージが送信されるとwebhook記載のURLにPOST送信でデータが送信されます。
それを最初に受け取る部分、それがdoPOST関数です。
そのため要はdoPOST関数に必要な処理さえ書き込めばそれで事足りるのです。
よく「おうむ返し」するものは作られていますよね。
これは、do POSTで送信されてきたJSONのmessage.typeが「text」であった時のtext部分を一度変数にでも入れて、replyしているだけです。
JSONのtypeについて
ここで、textでない場合はどうなるの?ってすぐに思われた人はいい感じです。
送られてきたJSONのtypeは数種類あります。
LINE Developerのメッセージイベントの確認はこちら
text:皆が良く打つメッセージ(文字列)
JSON:text
{
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
"type": "message",
"timestamp": 1462629479859,
"source": {
"type": "user",
"userId": "U4af4980629..."
},
"message": {
"id": "325708",
"type": "text",
"text": "Hello, world!"
}
}
sticker:スタンプ
JSON:sticker
{
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
"type": "message",
"timestamp": 1462629479859,
"source": {
"type": "user",
"userId": "U4af4980629..."
},
"message": {
"id": "325708",
"type": "sticker",
"packageId": "1",
"stickerId": "1"
}
}
image:画像
JSON:image
{
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
"type": "message",
"timestamp": 1462629479859,
"source": {
"type": "user",
"userId": "U4af4980629..."
},
"message": {
"id": "325708",
"type": "image"
}
}
audio:音声
JSON:audio
{
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
"type": "message",
"timestamp": 1462629479859,
"source": {
"type": "user",
"userId": "U4af4980629..."
},
"message": {
"id": "325708",
"type": "audio"
}
}
file:ファイル
JSON:file
{
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
"type": "message",
"timestamp": 1462629479859,
"source": {
"type": "user",
"userId": "U4af4980629..."
},
"message": {
"id": "325708",
"type": "file",
"fileName": "file.txt",
"fileSize": 2138
}
}
video:動画
JSON:video
{
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
"type": "message",
"timestamp": 1462629479859,
"source": {
"type": "user",
"userId": "U4af4980629..."
},
"message": {
"id": "325708",
"type": "video"
}
}
location:位置情報
JSON:location
{
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
"type": "message",
"timestamp": 1462629479859,
"source": {
"type": "user",
"userId": "U4af4980629..."
},
"message": {
"id": "325708",
"type": "location",
"title": "my location",
"address": "〒150-0002 東京都渋谷区渋谷2丁目21−1",
"latitude": 35.65910807942215,
"longitude": 139.70372892916203
}
}
これで全て網羅できてると思うのですが、何か他にあれば教えて下さい(^-^)
用は送られてきたmessage.typeが上記の場合の処理を記載すればスタンプであろうが、位置情報であろうが適切にreplyすることができます。
アイデア帳コード解説
ざっと処理の流れ
1.LINE Message APIにメッセージを送るmessage.type→text
※Message.typeがtext出ない場合は”処理できないよ!”ってreplyする。
2.textがhelpの場合、使い方をreplyする。
3.textがspreadの場合、GoogleSpreadSheetURLをreplyする。
4.textがideaの場合、ideaをreplyする。
5.testが上記以外の場合、ユーザが入力したtextをシートに書き込む。
少し細かく処理の流れ
2は、message.textを一度変数に入れ、helpと”==”か?って処理をして”TRUE”が返却されれば使い方をreplyすれば良いですね。
3は、2同様GoogleSpreadSheetURLをreplyすれば良いですね。
4は3同様。
5は、上記以外でいけそうです。
問題は、3と4のGoogleSpreadSheetのURL取得(3.1)と、GoogleSpreadSheetへの書き込み(4.1)、GoogleSpreadSheetのideaの取得(4.2)といったところでしょうか。
3.1はGoogleSpreadSheetのClass ServiceのgetURLメソッドを使用すれば大丈夫ですね。
ScriptApp.getService().getURL()
4.1、GoogleSpreadSheetへの書き込みはSpreadSheet名(id)で対象のSpreadSheetを取得して、SpreadSheetのシート名を取得して、そのシートのセル(◯、×)ってすればなんとかなりそうですね。
4.2、GoogleSpreadSheetのideaの取得は4.1同様、対象のSpreadSheetを取得して、SpreadSheetのシート名を取得まで同じで、選択対象範囲を指定してやればなんとかできそうですね。
では、実装内容を見ていきましょう。
まず、全貌から。
line.js
// Messaging APIのアクセストークン
var channel_access_token = 'LINE Message APIアクセストークン';
// 応答メッセージAPIのURL
var line_endpoint = 'https://api.line.me/v2/bot/message/reply';
// ユーザープロフィールAPIのURL
var line_endpoint_profile = 'https://api.line.me/v2/bot/profile';
// Main処理
function doPost(e) {
var json = JSON.parse(e.postData.contents);
var reply_token = json.events[0].replyToken;
// POSTのreplyTokenが'undefined'
if (typeof reply_token === 'undefined') {
return;
}
// POSTしたユーザのUserIDを取得する
var user_id = json.events[0].source.userId;
Logger.log(user_id);
// POSTしたユーザのメッセージ内容の取得
var user_message = json.events[0].message.text;
Logger.log(user_message);
// 変数宣言
var reply_messages;
var spreadSheet;
// メッセージ内容分岐
if (user_message === 'help') {
reply_messages = ['スプレッドシートにアクセスしたい場合は「spread」と入力してください。\nアイデアを思い出したくなったら「アイデア」と入力してください。あなたの過去のアイデアをランダムにお伝えします。\n使い方がわからなくなったら「help」と入力してみてください。'];
} else if (user_message === 'spread') {
spreadSheet = getSpreadSheet(user_id);
reply_messages = [spreadSheet.getUrl()];
} else if (user_message === 'idea') {
try {
spreadSheet = getSpreadSheet(user_id);
reply_messages = getRandomIdeas(spreadSheet);
} catch (ex) {
Logger.log(ex);
}
} else if (user_message === 'undefined') {
reply_messages = ['ゴメンナサイ、文字以外の情報には対応していません。\n使い方が知りたいときは「ヘルプ」と入力してみてください。'];
} else {
addToSpreadSheet(user_id, user_message);
reply_messages = ['アイデアが追加されました'];
}
var messages = reply_messages.map(function (v) {
return {'type': 'text', 'text': v};
});
UrlFetchApp.fetch(line_endpoint, {
'headers': {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer ' + channel_access_token
},
'method': 'post',
'payload': JSON.stringify({
'replyToken': reply_token,
'messages': messages,
}),
});
return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);
}
function getSpreadSheet(user_id) {
var sheet_id = PropertiesService.getScriptProperties().getProperty(user_id);
if (sheet_id == null) {
return createSpreadSheet(user_id);
} else {
try {
return SpreadsheetApp.openById(sheet_id);
} catch(e) {
return createSpreadSheet(user_id);
}
}
}
function getRandomIdeas(spreadSheet) {
var ideas = [];
var sheet = spreadSheet.getSheets()[0];
var rowNum = sheet.getLastRow();
if (rowNum < 1) {
ideas.push('ゴメンナサイ、この機能を使うには、ある程度の数のアイデアが必要です。アイデアをいくつか入力してみてください。');
} else {
ideas.push('あなたは以前、こんなアイデアを考えていました。');
var num = getRandom(rowNum);
ideas.push(getIdea(spreadSheet, num));
var num2
for (var i = 0; i < 100; i++) {
num2 = getRandom(rowNum);
if (num != num2) {
break;
}
}
ideas.push(getIdea(spreadSheet, num2));
}
return ideas;
}
function addToSpreadSheet(user_id, message) {
var today = new Date();
var spreadSheet = getSpreadSheet(user_id);
var sheet = spreadSheet.getSheets()[0];
sheet.appendRow([today, message]);
}
function createSpreadSheet(user_id) {
var spreadSheet = SpreadsheetApp.create("idea(" + getUserDisplayName(user_id) + ")");
var sheet = spreadSheet.getSheets()[0];
sheet.appendRow(['日時', 'メッセージ']);
PropertiesService.getScriptProperties().setProperty(user_id, spreadSheet.getId());
var file = DriveApp.getFileById(spreadSheet.getId());
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
sheet.setColumnWidth(1, 130);
sheet.setColumnWidth(2, 300);
return spreadSheet;
}
function getUserDisplayName(user_id) {
var res = UrlFetchApp.fetch(line_endpoint_profile + '/' + user_id, {
'headers': {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer ' + channel_access_token,
},
'method': 'get',
});
return JSON.parse(res).displayName;
}
function getRandom(num) {
return Math.ceil(Math.random() * (num - 1));
}
function getIdea(spreadSheet, num) {
var sheet = spreadSheet.getSheets()[0];
var time = Utilities.formatDate(sheet.getRange((num + 1), 1).getValue(), 'Asia/Tokyo', 'yyyy/MM/dd hh:mm a');
var message = sheet.getRange((num + 1), 2).getValue();
return "(" + time + ")\n" + message;
}
function getSpreadSheet(user_id) {
var sheet_id = PropertiesService.getScriptProperties().getProperty(user_id);
if (sheet_id == null) {
return createSpreadSheet(user_id);
} else {
try {
return SpreadsheetApp.openById(sheet_id);
} catch(e) {
return createSpreadSheet(user_id);
}
}
}
コード解説詳細
各種APIについて
line.js
// Messaging APIのアクセストークン
var channel_access_token = 'LINE Message APIアクセストークン';・・・①
// 応答メッセージAPIのURL
var line_endpoint = 'https://api.line.me/v2/bot/message/reply';・・・②
// ユーザープロフィールAPIのURL
var line_endpoint_profile = 'https://api.line.me/v2/bot/profile';・・・③
①LINE Message APIアクセストークン
これはXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=っていうLINE Developerログインページ記載の長いトークンです。
②応答メッセージAPIのURL
https://api.line.me/v2/bot/message/reply
上記がLINE Developer Document記載のAPIのURLになります。これはこういうものです。
③ユーザープロフィールAPIのURL
https://api.line.me/v2/bot/profile
上記がLINE Developer Document記載のAPIのURLになります。これもこういうものです。
コードについての解説は少しづつしていきます。
コメント