従来のLINE BOT APIが廃止予定ということで
新たに発表されたLINE Messaging APIに移行しました。
作ったものはこんな感じ普通です。
LINE Messaging API は管理画面から背景色とかアカウント設定できる項目はめっちゃ増えてて
LINE BOT APIよりは個性的なものが作れそう(^o^)
LINE Messaging API
誰でも手軽に叩けるAPIです。
BOTも作成できるし、管理画面も用意されてて分かりやすい。
無料で作成するBOTはユーザーの発言に対しての返信はできる(REPLY_MESSAGE) が
BOTから直接メッセージを送信(PUSH_MESSAGE)するのは月2万ぐらいかかる。
しかし友達登録上限50人という制限付きBOTでは REPLY_MESSAGE & PUSH_MESSAGE
どちらも使える。
従来のLINE BOT APIと変わったことはそんなことなくて
移行難易度はそんなに高くないと思います。
Webhookの設定
今回実装にあたって少しだけハマったところは
Webhookの設定で、CloudflareのSSL機能を使ったサーバーを指定すると
Webhookが飛んでこないという仕様だった。
LINE BOT APIでは使えたんですけどね、いずれか使えるようにはなりそう。
ちなみにLet’s Encryptでは可能です。
実装
今回もPHPです。
特に理由はありません。楽だからです。
callback
<?php require_once './lib/callback_class.php'; /* LINE */ $CHANNEL_SECRET = ''; $CHANNEL_ACCESS_TOKEN = ''; /* TWITTER */ $CONSUMER_KEY = ''; $CONSUMER_SECRET = ""; $ACCESS_TOKEN = ""; $ACCESS_TOKEN_SECRET = ""; $json_input = file_get_contents('php://input'); $yows = new YOWS($CHANNEL_SECRET, $CHANNEL_ACCESS_TOKEN, $json_input); if ($yows->type === "unfollow") { $yows->unRegister($yows->userid); } elseif ($yows->type === "follow" && $yows->checkRegister($yows->userid) === 0) { $profile = $yows->getProfile($yows->userid); $yows->register($yows->userid); $yows->pushMessage($yows->userid, "登録ありがとうございます ??3 hearts?\n\n・田村ゆかり公式サイト\n・ファンクラブサイト\nの更新をお知らせ致します??light bulb?\n\nなお、当アカウントをブロックすることで利用の停止ができます ??content?"); } elseif ($yows->type === "follow" && $yows->checkRegister($yows->userid) === 1) { $yows->pushMessage($yows->userid, "既に登録済みです。"); } else { die(); }
<?php require_once 'twitteroauth/twitteroauth.php'; class YOWS { private $CHANNEL_SECRET = ''; private $CHANNEL_ACCESS_TOKEN = ''; public $header = []; public $data, $userid, $type = ''; public function __construct($CHANNEL_SECRET, $CHANNEL_ACCESS_TOKEN, $data) { $this->CHANNEL_SECRET = $CHANNEL_SECRET; $this->CHANNEL_ACCESS_TOKEN = $CHANNEL_ACCESS_TOKEN; $this->data = $data; $this->getallheaders(); $this->checkSignature(); $this->setVariables(); } private function getallheaders() { $header = ''; foreach ($_SERVER as $name => $value) { if (substr($name, 0, 5) == 'HTTP_') { $header[strtoupper(str_replace(' ', '-', ucwords(str_replace('_', ' ', substr($name, 5)))))] = $value; } } $this->header = $header; } public function checkSignature() { if (base64_decode($this->header["X-LINE-SIGNATURE"]) === hash_hmac('sha256', $this->data, $this->CHANNEL_SECRET, true)) { $this->data = json_decode($this->data, true); } else { die(); } } private function setVariables() { foreach ($this->data as $row) { $this->type = $row[0]['type']; $this->userid = $row[0]['source']['userId']; } } /** * @param $USERID * @return int 0=not register / 1=registerd */ public function checkRegister($USERID) { try { $pdo = new PDO('mysql:dbname=; host=', '', '', array(PDO::ATTR_EMULATE_PREPARES => false)); $sql = "SELECT * FROM line WHERE userid='${USERID}'"; $stmt = $pdo->query($sql); foreach ($stmt as $row) { $query_result = $row['id']; } empty($query_result) === true ? $result = 0 : $result = 1; return $result; } catch (PDOException $e) { $this->debug('Error:' . $e->getMessage()); die(); } } public function register($USERID) { try { $pdo = new PDO('mysql:dbname=; host=', '', '', array(PDO::ATTR_EMULATE_PREPARES => false)); $stmt = $pdo->prepare('INSERT INTO line (userid, date) VALUES (:register_userid, :date)'); $stmt->execute(array(':register_userid' => $USERID, ':date' => $this->getDate())); } catch (PDOException $e) { $this->debug('Error:' . $e->getMessage()); die(); } } public function unRegister($USERID) { try { $pdo = new PDO('mysql:dbname=; host=', '', '', array(PDO::ATTR_EMULATE_PREPARES => false)); $stmt = $pdo->prepare('DELETE FROM line WHERE userid = :delete_userid'); $stmt->execute(array(':delete_userid' => $USERID)); } catch (PDOException $e) { $this->debug('Error:' . $e->getMessage()); die(); } } private function getDate() { return date("Y-m-d H:i:s"); } public function pushMessage($USERID, $msg) { $format_text = [ "type" => "text", "text" => $msg ]; $post_data = [ "to" => $USERID, "messages" => [$format_text] ]; $header = array( 'Content-Type: application/json', 'Authorization: Bearer ' . $this->CHANNEL_ACCESS_TOKEN ); $ch = curl_init('https://api.line.me/v2/bot/message/push'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data)); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); $result = curl_exec($ch); curl_close($ch); } public function debug($string) { error_log(print_r($string, true), 3, '/var/log/nginx/error.log'); } }
LINEからのアクセスかどうかは従来のLINE BOT APIと同じで
public function checkSignature() { if (base64_decode($this->header["X-LINE-SIGNATURE"]) === hash_hmac('sha256', $this->data, $this->CHANNEL_SECRET, true)) { $this->data = json_decode($this->data, true); } else { die(); } }
こんな感じで署名の確認ができる。
Push(BOTからユーザーへメッセージ送信)
<?php require_once 'lib/push_class.php'; $URL = "http://www.tamurayukari.com/"; $old_url = file_get_contents('old_url'); /* LINE */ $CHANNEL_ACCESS_TOKEN = ''; $yows = new Push($URL, $CHANNEL_ACCESS_TOKEN); $latest_content = $yows->crawler->filter('div#news_table td a')->text(); $latest_url = $yows->crawler->filter('div#news_table td a')->attr('href'); if($latest_url !== $old_url) { $msg = "【サイト更新通知BOTよりお知らせ】\r\n公式サイトが更新されました!\r\n\r\n【タイトル】\r\n{$latest_content}\r\n\r\n詳しくはこちらへ!\r\n${latest_url}"; foreach ($yows->getUserid() as $userid) { $yows->pushMessage($userid, $msg); } file_put_contents('old_url', $latest_url); }
<?php require_once 'goutte/vendor/autoload.php'; use Goutte\Client; class Push { private $CHANNEL_ACCESS_TOKEN, $url = ''; public $client, $crawler = ''; public function __construct($url, $CHANNEL_ACCESS_TOKEN) { $this->client = new Client(); $this->url = $url; $this->CHANNEL_ACCESS_TOKEN = $CHANNEL_ACCESS_TOKEN; $this->crawler = $this->client->request('GET', $this->url); } public function getUserid() { try { $array_userid = []; $pdo = new PDO('mysql:dbname=yukarinotification; host=database.cma0jldtuyey.us-west-2.rds.amazonaws.com', 'luis', 'luism8526', array(PDO::ATTR_EMULATE_PREPARES => false)); $stmt = $pdo->query("SELECT * FROM line"); foreach ($stmt as $row) { $array_userid[] = $row['userid']; } return $array_userid; } catch (PDOException $e) { /** ERROR HANDLING */ die(); } } public function pushMessage($USERID, $msg) { $format_text = [ "type" => "text", "text" => $msg ]; $post_data = [ "to" => $USERID, "messages" => [$format_text] ]; $header = array( 'Content-Type: application/json', 'Authorization: Bearer ' . $this->CHANNEL_ACCESS_TOKEN ); $ch = curl_init('https://api.line.me/v2/bot/message/push'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data)); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); $result = curl_exec($ch); curl_close($ch); } }
LINEにリクエストを投げるときはCHANNEL_ACCESS_TOKEN
をヘッダーに載せるだけ。
LINE DeveloperからサーバーのIPをホワイトリストに登録する必要はあります。
今回の話に関係はないですけどGoutte便利だし何より速い。
メッセージを送信する関数は
public function pushMessage($USERID, $msg) { $format_text = [ "type" => "text", "text" => $msg ]; $post_data = [ "to" => $USERID, "messages" => [$format_text] ]; $header = array( 'Content-Type: application/json', 'Authorization: Bearer ' . $this->CHANNEL_ACCESS_TOKEN ); $ch = curl_init('https://api.line.me/v2/bot/message/push'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data)); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); $result = curl_exec($ch); curl_close($ch);
特筆することなし!
戯言
Webhookに関してはイベント内容がJSON見れば一発で分かるようになったので楽でした。
LINE BOT APIでは type 1 が友達追加時で~ type3 がユーザーからのメッセージとかで
分かりにくかった。
あとはドキュメントも日本語が用意されて、各言語のSDKも用意されているので
比較的容易に移行(作成)できました。
ソースコードはこちら
rluisr/yukari-line-botA – github