4
2

More than 3 years have passed since last update.

[LINE bot] NowとHerokuで常時起動化させる

Last updated at Posted at 2020-04-14

この記事の内容

  • LINEボットの完成形、常時稼働化のやり方
  • そもそもモヤついていたところを自分なりにまとめてみた

完成品

(いじってたらなぜかアイコンが黒く怪しくなってしまった・・・どうやって戻すんだろう)
image.png

猿よりは賢いアプリを作った。(ただし、人間ほど完璧な回答はできない。)

  • ?を付けて聞くと文章で返します
  • ?がない時はwikipediaのリンクを返します
  • お猿さんが知らない単語のときは、、無視します。。

QRコード(お友達になってあげてね!)

※このアプリはこの後紹介するHerokuを使ってます。
smartmonky.png

作っていて思ったこと・・・

そもそもexpress,ngrok,now,herokuってそれぞれなんだろう。普通のサーバーと何が違うのだろう。

この辺りがモヤつくポイントだと思うので、自分なりの理解でまとめます。

さくらサーバー(phpを使う)

以前、レンタルサーバーにphpファイルを置いて、そこでwebhookを受けて処理をするように作り替えた。
常に動くLINEBOTにお引っ越し(レンタルサーバ+PHP編)

こんなイメージ。
レンタルサーバ.png

Express

Node.jsで利用できるwebアプリケーションフレームワーク。

const express = require('express');
const app = express();

requireでExpressを利用可能にし、expressのアプリケーションオブジェクトをappに格納する。
そうすることで、app.post()postリクエスト時の処理や、app.listen()サーバー起動などの処理が簡単に記述できる。

Expressでローカル環境にアプリケーションを構築することができるが、ローカル環境は基本的に外部に公開しないのでLINEbotからの外部とのやりとりができない。
express.png

ngrok

Expressを用いて作った手元のlocalhostにグローバルからアクセスできるようにするトンネリングツール。
設定等はこちらを参照。
ngrokのサブドメインを取得して、その/webhookに対してアクセスすることでngrokを通して、

app.post('/webhook', line.middleware(config), (req, res) => {....}

の処理を呼ぶことができる。
ただし、PCを閉じてしまうとこのngrokサーバも終了してしまい、常時動かすには不便。
ngrok.png

now

とてもデプロイが簡単なPaas。
サーバにアクセスされるとnow.jsonの設定の通り処理が実行される。
設定等はこちらを参照。
now.png

Heroku

Paas。Gitを使ってデプロイする必要がある。
今回公開しているアプリはHerokuを使っています。なので手順を下記に記載する。

初期化

npm init -y

必要なライブラリインストール

npm i body-parser express

.gitignoreファイルの作成

# Dependency directories
node_modules/

# Optional npm cache directory
.npm

1番上の階層にProcfileの作成

web: node index.js

git

初期化

git init

herokuアプリを追加する。アプリケーションネームはお好きな名前を決める。

heroku create アプリケーションネーム

add -> commit -> pushをする

git add --a
git commit -m "commit"
git push heroku master

Nowで常時化

Nowを使って常時動くようにします。先述の通りNowはGitも不要で非常に簡単です。

準備

下記にあるようにngrokで動いていたモノが、nowでは正しい挙動をしない時があったので、前回記事から少し修正をした。
LINE BOTで天気を返すサンプルがngrokで動いてnowで動かない件

コード

linebot-now.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: 'XXXXXXXXXXX',
    channelAccessToken: 'XXXXXXXXXXX'
};

const app = express();

app.get('/', (req, res) => res.send('Hello LINE BOT!(GET)')); //ブラウザ確認用(無くても問題ない)
app.post('/webhook', line.middleware(config), (req, res) => {
    // LINE上で入力された
    console.log(req.body.events[0].message.text);


    //ここの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);

async 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の前の一言

    await client.replyMessage(event.replyToken, {
      type: 'text',
      // text: event.message.text //実際に返信の言葉を入れる箇所
      text : mes 
    });
    // このreturnはポイント
    return getBody(event.source.userId,result,mes); //wiki APIで取得できたらプッシュメッセージ

  }else{
    var result = event.message.text;
    mes = result + 'のURL:'; //wikiのurlの前の一言
    await client.replyMessage(event.replyToken, {
      type: 'text',
      // text: event.message.text //実際に返信の言葉を入れる箇所
      text : mes 
    });
    // このreturnはポイント
    return getUrl(event.source.userId,result); //wiki APIで取得できたらプッシュメッセージ
  }
}

    const getBody = async (userId,word,message) => {

    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,
    });
}

(process.env.NOW_REGION) ? module.exports = app : app.listen(PORT);
console.log(`Server running at ${PORT}`);

Herokuで常時化

Herokuとは

参考
Herokuってなんなの?

ソースコード

linebot-heroku.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: 'XXXXXXXX',
    channelAccessToken: 'XXXXXXXX'
};

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(process.env.PORT || 8080);
// console.log(`Server running at ${PORT}`);

どんどん試そう

レンタルサーバを除いて、無料なのでお好きややり方でどんどん試していきましょう!

4
2
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
4
2