2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

常に動くLINEBOTにお引っ越し(レンタルサーバ+PHP編)

Posted at

#今回のモチベーション
前回、こちらの記事を参考にWikipedia APIを使った、調べものLINE botを作った。

前回の記事
https://qiita.com/shima-07/items/2322598ca5a40cfee47b

だが、

  • ngrokを立ち上げている時しか使えないから普段使えない。
  • いざ、ngrokを立ち上げるとアドレスが変わってしまうため、Messaging API settingsのwebhook URLを毎度変えないと動かない。

うーん。。。

ngrok立ち上げるのめんどくさい! 常に使えるようにしないと意味ないじゃん!

と思ったわけです。だから『常に動くようにしよう!』が今回の動機です。

今回やったこと

    1. まずは now を試してみた
    1. さくらのレンタルサーバでやることにした
    1. jsで書いていたものをPHPに書き直した

最終的にはPHP化してさくらサーバに載っけました。

1. まずは now を試してみた

このあたりを参考に進めてみる。

さすが良記事!サクサク進むぜと思いながら最後までいきデプロイ完了!
簡単だったなあと思いながら、Webhook URLに入れて「Verify」をクリック・・・・

image.png

う。。まあよくある。

(1時間ほど立ち向かう)
色々と試すが私の手におえないと判断して諦める。

2. さくらのレンタルサーバでやることにした

この記事を発見!
http://blog.hetabun.com/line-bot-php-sakura

これ通りやることで、「こんにちは」に対して「こんにちは!」と元気よく返してくれるボットができました。

3. jsで書いていたものをPHPに書き直した

ここからが本番。前回あげた下記jsのコードと同じような振る舞いをPHPで書いていく。
PHPももちろん初心者である。

再掲

server.js
'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。

LINEからのメッセージ.php
//ユーザーからのメッセージ取得
$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"}; のような書き方をして、、当然何も取れず。

結論、下記のような取り方でできた。

wikipediaAPIからURLやbody取得部分.php
        $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ではどうやってみたらいいかわからずわざわざこんなことしました。。。 )
image.png

check_data.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に返す。
分岐部分.php
    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
            ];
      }
    }

サンプル

全ソースコード

linebot.php
<?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を使ったお引っ越しもやってみようと思います。

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?