#今回のモチベーション
前回、こちらの記事を参考にWikipedia APIを使った、調べものLINE botを作った。
前回の記事
https://qiita.com/shima-07/items/2322598ca5a40cfee47b
だが、
- ngrokを立ち上げている時しか使えないから普段使えない。
- いざ、ngrokを立ち上げるとアドレスが変わってしまうため、Messaging API settingsのwebhook URLを毎度変えないと動かない。
うーん。。。
ngrok立ち上げるのめんどくさい! 常に使えるようにしないと意味ないじゃん!
と思ったわけです。だから『常に動くようにしよう!』が今回の動機です。
今回やったこと
-
- まずは now を試してみた
-
- さくらのレンタルサーバでやることにした
-
- jsで書いていたものをPHPに書き直した
最終的にはPHP化してさくらサーバに載っけました。
1. まずは now を試してみた
このあたりを参考に進めてみる。
さすが良記事!サクサク進むぜと思いながら最後までいきデプロイ完了!
簡単だったなあと思いながら、Webhook URLに入れて「Verify」をクリック・・・・
う。。まあよくある。
(1時間ほど立ち向かう)
色々と試すが私の手におえないと判断して諦める。
2. さくらのレンタルサーバでやることにした
この記事を発見!
http://blog.hetabun.com/line-bot-php-sakura
これ通りやることで、「こんにちは」に対して「こんにちは!」と元気よく返してくれるボットができました。
3. jsで書いていたものをPHPに書き直した
ここからが本番。前回あげた下記jsのコードと同じような振る舞いをPHPで書いていく。
PHPももちろん初心者である。
再掲
'use strict';
const express = require('express');
const line = require('@line/bot-sdk');
const PORT = process.env.PORT || 3000;
// 追加
const axios = require('axios');
const config = {
channelSecret: '作成したBOTのチャンネルシークレット',
channelAccessToken: '作成したBOTのチャンネルアクセストークン'
};
const app = express();
app.get('/', (req, res) => res.send('Hello LINE BOT!(GET)')); //ブラウザ確認用(無くても問題ない)
app.post('/webhook', line.middleware(config), (req, res) => {
//ここのif文はdeveloper consoleの"接続確認"用なので後で削除して問題ないです。
if(req.body.events[0].replyToken === '00000000000000000000000000000000' && req.body.events[1].replyToken === 'ffffffffffffffffffffffffffffffff'){
res.send('Hello LINE BOT!(POST)');
console.log('疎通確認用');
return;
}
Promise
.all(req.body.events.map(handleEvent))
.then((result) => res.json(result));
});
const client = new line.Client(config);
function handleEvent(event) {
if (event.type !== 'message' || event.message.type !== 'text') {
return Promise.resolve(null);
}
let mes = ''
// console.log(event.message.text);
if(event.message.text.indexOf('?') > -1){
// ?を含んでいる場合にはwikiで検索したものを出して、含んでない場合はurlを返す
var str = event.message.text;
var result = str.split( '?' ).join( '' ); //?を取り除く処理
mes = result + 'の説明:'; //wikiのbodyの前の一言
getBody(event.source.userId,result); //wiki APIで取得できたらプッシュメッセージ
}else{
var result = event.message.text;
mes = result + 'のURL:'; //wikiのurlの前の一言
getUrl(event.source.userId,result); //wiki APIで取得できたらプッシュメッセージ
}
return client.replyMessage(event.replyToken, {
type: 'text',
text : mes
});
}
const getBody = async (userId,word) => {
const res = await axios.get('http://wikipedia.simpleapi.net/api?keyword='+ encodeURIComponent(word) + '&output=json');
const item = res.data;
// console.log(item);
await client.pushMessage(userId, {
type: 'text',
text: item[0].body,
});
}
const getUrl = async (userId,word) => {
const res = await axios.get('http://wikipedia.simpleapi.net/api?keyword='+ encodeURIComponent(word) + '&output=json');
const item = res.data;
// console.log(item);
await client.pushMessage(userId, {
type: 'text',
text: item[0].url,
});
}
app.listen(PORT);
console.log(`Server running at ${PORT}`);
## 処理の整理
- LINEからのメッセージを受け取る
- そのメッセージをWikipedia APIに渡して結果を受け取る
- メッセージによってLINE側に返却するものを変える
- ?があるときはurlを返す
- ?がないときはbodyを返す
LINEからのメッセージを受け取る
参考にした記事中にあった下記の$text
で取れているからそれはOK。
//ユーザーからのメッセージ取得
$json_string = file_get_contents('php://input');
$jsonObj = json_decode($json_string);
$type = $jsonObj->{"events"}[0]->{"message"}->{"type"};
//メッセージ取得
$text = $jsonObj->{"events"}[0]->{"message"}->{"text"};
//ReplyToken取得
$replyToken = $jsonObj->{"events"}[0]->{"replyToken"};
そのメッセージをWikipedia APIに渡して結果を受け取る
ここが一番ハマった。
jsではres.data.item[0].body
の構造で取れていたので、
同じノリで $value = $res->{"data"}->{"item"}[0]->{"body"};
のような書き方をして、、当然何も取れず。
結論、下記のような取り方でできた。
$keyword = mb_convert_encoding($text, "UTF-8", "auto");
$res = file_get_contents('http://wikipedia.simpleapi.net/api?keyword=' . $keyword . '&output=json');
$jsonwiki_decode = json_decode($res,true);
// 0番目の物だけを抽出する。もっとたくさん抽出したいときはここを変更する
$jsonwiki = $jsonwiki_decode[0];
//欲しい項目だけの配列にする
$wikidata = array(
'url' => $jsonwiki["url"],
'body' => $jsonwiki["body"]
);
$URL = $wikidata["url"];
$body = $wikidata["body"];
流れとしては、
- LINEから受け取ったキーワードをAPIのkeywordとして渡せるようにエンコードする
- それをAPIに渡し
$res
として取得する - json_decodeして配列にする
- そのキーワードに対しての一番先頭の回答を取得するため[0]を取得する
- それに対して
$wikidata
として必要な項目だけ取得する - LINEに返したいものは、
$URL = $wikidata["url"]
や$body = $wikidata["body"]
として取得できる
補足
上記 $res
にどんなものが入っているか見るために下記のようなものをページを作って見てました。( jsはconsole.log()で気軽に見えたけどphpではどうやってみたらいいかわからずわざわざこんなことしました。。。 )
<html>
<head>
<title> test </title>
</head>
<body>
<form method="POST" action="show.php">
キーワード:
<input type="text" name="name" size="15" />
<input type="submit" name="submit" value="送信" />
</form>
<?php
if($_REQUEST['submit'] != null){
$input = $_REQUEST[name];
//$textのなかに'?'が含まれている場合
$text = str_replace('?', '', $input);
$keyword = mb_convert_encoding($text, "UTF-8", "auto");
$res = file_get_contents('http://wikipedia.simpleapi.net/api?keyword=' . $keyword . '&output=json');
$jsonwiki_decode = json_decode($res,true);
$jsonwiki = $jsonwiki_decode[0];
$wikidata = array(
'url' => $jsonwiki["url"],
'body' => $jsonwiki["body"]
);
$URL = $wikidata["url"];
$body = $wikidata["body"];
print('$input: '.$input.'-----');
print('$text: '.$text.'-----');
print('$keyword: '.$keyword.'-----');
print('$res: '.$res.'-----');
print('$jsonwiki_decode :'.$jsonwiki_decode.'-----');
print('$jsonwiki :'.$jsonwiki .'-----');
print('$wikidata :'.$wikidata .'-----');
print('URL: '.$URL.'-----');
print('body: '.$body.'-----');
}
?>
</body>
</html>
メッセージによってLINE側に返却するものを変える
- ?がある場合はURLをLINEに返す。また、Wikipedia APIに渡すときには?を取り除く。
- ?がない場合はBodyをLINEに返す。
if(strpos($text,'?') !== false){
//$textのなかに'?'が含まれている場合
$text = str_replace('?', '', $text);
$keyword = mb_convert_encoding($text, "UTF-8", "auto");
$res = file_get_contents('http://wikipedia.simpleapi.net/api?keyword=' . $keyword . '&output=json');
$jsonwiki_decode = json_decode($res,true);
// 0番目の物だけを抽出する。もっとたくさん抽出したいときはここを変更する
$jsonwiki = $jsonwiki_decode[0];
//欲しい項目だけの配列にする
$wikidata = array(
'url' => $jsonwiki["url"],
'body' => $jsonwiki["body"]
);
$URL = $wikidata["url"];
$body = $wikidata["body"];
// メッセージ部分
$response_format_text = [
"type" => "text",
"text" => $URL
];
}else{ // ?が含まれないときの処理
$keyword = mb_convert_encoding($text, "UTF-8", "auto");
$res = file_get_contents('http://wikipedia.simpleapi.net/api?keyword=' . $keyword . '&output=json');
$jsonwiki_decode = json_decode($res,true);
// 0番目の物だけを抽出する。もっとたくさん抽出したいときはここを変更する
$jsonwiki = $jsonwiki_decode[0];
//欲しい項目だけの配列にする
$wikidata = array(
'url' => $jsonwiki["url"],
'body' => $jsonwiki["body"]
);
$URL = $wikidata["url"];
$body = $wikidata["body"];
// メッセージ部分
$response_format_text = [
"type" => "text",
"text" => $body
];
}
}
サンプル
できたー レンタルサーバ上で動いたー!
— yuta kawashima (@y_kawashima_) March 29, 2020
前回のはngrokでやってたから普段使えなかったけど、これでいつでも使える!
#protoout pic.twitter.com/HznZSciExJ
全ソースコード
<?php
$accessToken = 'アクセストークン';
//ユーザーからのメッセージ取得
$json_string = file_get_contents('php://input');
$jsonObj = json_decode($json_string);
$type = $jsonObj->{"events"}[0]->{"message"}->{"type"};
//メッセージ取得
$text = $jsonObj->{"events"}[0]->{"message"}->{"text"};
//ReplyToken取得
$replyToken = $jsonObj->{"events"}[0]->{"replyToken"};
//メッセージ以外のときは何も返さず終了
if($type != "text"){
exit;
}
if($type == "text"){
if(strpos($text,'?') !== false){
//$textのなかに'?'が含まれている場合
$text = str_replace('?', '', $text);
$keyword = mb_convert_encoding($text, "UTF-8", "auto");
$res = file_get_contents('http://wikipedia.simpleapi.net/api?keyword=' . $keyword . '&output=json');
$jsonwiki_decode = json_decode($res,true);
// 0番目の物だけを抽出する。もっとたくさん抽出したいときはここを変更する
$jsonwiki = $jsonwiki_decode[0];
//欲しい項目だけの配列にする
$wikidata = array(
'url' => $jsonwiki["url"],
'body' => $jsonwiki["body"]
);
$URL = $wikidata["url"];
$body = $wikidata["body"];
// メッセージ部分
$response_format_text = [
"type" => "text",
"text" => $URL
];
}else{ // ?が含まれないときの処理
$keyword = mb_convert_encoding($text, "UTF-8", "auto");
$res = file_get_contents('http://wikipedia.simpleapi.net/api?keyword=' . $keyword . '&output=json');
$jsonwiki_decode = json_decode($res,true);
// 0番目の物だけを抽出する。もっとたくさん抽出したいときはここを変更する
$jsonwiki = $jsonwiki_decode[0];
//欲しい項目だけの配列にする
$wikidata = array(
'url' => $jsonwiki["url"],
'body' => $jsonwiki["body"]
);
$URL = $wikidata["url"];
$body = $wikidata["body"];
// メッセージ部分
$response_format_text = [
"type" => "text",
"text" => $body
];
}
}
$post_data = [
"replyToken" => $replyToken,
"messages" => [$response_format_text]
];
$ch = curl_init("https://api.line.me/v2/bot/message/reply");
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, array(
'Content-Type: application/json; charser=UTF-8',
'Authorization: Bearer ' . $accessToken
));
$result = curl_exec($ch);
curl_close($ch);
おわりに
APIから返ってきた値をいい感じで取ってくるところでだいぶハマりました。
JSONデータの扱い方にもっと慣れないとなあー
次はHerokuを使ったお引っ越しもやってみようと思います。