前回こんな記事を書きました。
田村ゆかりさん公式サイトの通知を LINE BOT API で作ってみた。 #yukarin

この記事では友達追加をしてもらった際にメッセージを送ってもらって
mid(ユーザーID)を保存するという処理だったのですが、友人が
「友達追加時にもCallbackされるからメッセージ送らなくてもmid保存できるよ」と教えてもらいました。

 

試しに中身を覗いてみた

{
"result": [
{
"content": {
"message": null,
"opType": 4,
"params": [
"",
null,
null
],
"reqSeq": 0,
"revision": 350
},
"createdTime": 1461579375186,
"eventType": "138311609100106403",
"from": "",
"fromChannel": ,
"id": "",
"to": [
""
],
"toChannel": 1462173514
}
]
}

注目するのは “opType”: 4 です。
友達追加時は 4
ブロック時は 8 が返ってきます。

LINE Developers にも記載されてました…。

FireShot Capture 42 - LINE Developers - BOT API - API refere_ - https___developers.line.me_bot-api

 

友達追加時に、midを保存。
ブロック時にmidを削除といったことが可能ですね。

ということでPHPで友達追加時に相手のmidを保存するコードは以下に。

<?php
/**
* $otType は 登録時とブロック時にリクエストが飛んで来る。
* 4 = 友だち追加
* 8 = ブロック
*/
$f_mids = file_get_contents('./mids');
$content = file_get_contents('php://input');
$content = json_decode($content);
$opType = $content->result[0]->content->opType;
$mid = trim($content->result[0]->from);
/*
* 最初の登録
*/
if ($opType === 4 && strpos($f_mids, $mid) === false) {
$text = "ご登録が完了しました。\r\n田村ゆかりさん公式サイトが更新される度にLINEでお知らせいたします。";
$response_format_text = ['contentType' => 1, "toType" => 1, "text" => $text];
$post_data = [
"to" => [$mid],
"toChannel" => "1383378250",
"eventType" => "138311608800106203",
"content" => $response_format_text
];
toPost($post_data);
file_put_contents('mids', $mid . "\r\n", FILE_APPEND | LOCK_EX);
die();
}

この一々保存するの面倒なので一斉送信できるAPIを公開してくだしゃい

 

ふとコードを書き直してる時にセキュリティやばくね?ってなって思ってた時に署名とかこないの?って思ったら署名乗ってくるみたいですね…。
Qiitaとか9割方、署名確認してないです。

 

LINEからのアクセスか検証する

LINEからのアクセスの場合、ヘッダーに X-LINE-CHANNELSIGNATURE があり
これをbase64デコードしたもの + LINEきたらjsonの内容を LINE BUSINESS CENTER で確認できる ChannelSecret をキーとしたHMAC方式SHA256アルゴリズムのハッシュ値が
合致すればOKということです。

/* LINE からのアクセスか検証 */
$channel_secret = "<チャンネルシークレット>";
$headers = getallheaders();
$content = file_get_contents('php://input');
if (base64_decode($headers['X-LINE-CHANNELSIGNATURE']) === hash_hmac('sha256', $content, $channel_secret, true)) {
$f_mids = file_get_contents('./mids');
$opType = $content->result[0]->content->opType;
$mid = trim($content->result[0]->from);
} else {
die();
}

 

ちなみに nginx だとPHPの getallheaders 関数が使えないので

function getallheaders()
{
$headers = '';
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[strtoupper(str_replace(' ', '-', ucwords(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}

よく見るこんなのを使います。