LoginSignup
6
5

More than 5 years have passed since last update.

サーバーレスアーキテクチャ(Google cloud function)でLine botを作る

Posted at

はじめに

やること

  • Google custom search engine(CSE)で画像を拾ってきて、LINE APIのpushメッセージでその画像を送る
  • Google cloud function(GCF)を使ってサーバーレスアーキテクチャで画像を拾ってくるLine botを動かす

この記事のスコープ外のこと

  • Google cloud platform(GCP)やCSEの基本的な使い方
    • 他にいっぱい記事があるのでGCPのアカウント登録やGCFのチュートリアルはすでに終えている前提として話を進める

Line Messagesing APIを使う

アカウント登録からチャンネル設定まで

基本公式ドキュメントを読んで進めていけば問題ありません。
最終的に今回使う情報は以下の部分です。

基本情報
チャンネル設定.png

メッセージ送受信設定
メッセージ送受信設定.png

その他
その他.png

pushメッセージを送ってみる

CSEで画像を拾ってきてそれを自分に対して送ってみる。
CSEの使い方はこちらの記事が参考になる。
今回は自分の好きな声優さんの画像を拾ってくることにする。なぜならそちらの方がモチベーションが上がるから(重要)。

  • packageインストール
$ mkdir test_project
$ cd test_project
$ npm install @line/bot-sdk --save
$ npm install request --save
$ npm install config --save
  • confファイル作成
    • channelAccessTokenとchannelSecretは、LINE developersのコンソールから取得できるアクセストークンとChannel secretである
    • google APIの方のkeyとcxはAPI keyと検索エンジンIDである
$ mkdir conf
$ vi conf/default.conf
conf/default.conf
{
  "lineAPI": {
      "channelAccessToken": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "channelSecret":      "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  },
  "googleAPI": {
      "key": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "cx":  "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  }
}
  • 画像をCSEから取得してLINEへメッセージをpushするスクリプト作成
    • 今回は画像を送るが、その他にも普通のテキストはもちろん、動画やスタンプ、位置情報等も送れるので公式ドキュメントのメッセージオブジェクトの項目を参照されたし
$ vi search_image.js
search_image.js
'use strict'

const line       = require('@line/bot-sdk');
const config     = require('config');
const httpClient = require('request')

const lineClient = new line.Client(config.lineAPI);

const url        = 'https://www.googleapis.com/customsearch/v1';
const searchWord = '立花理香';

function searchImage(searchWord) {

    return new Promise(function(func){

        var pageNumber  = Math.floor(Math.random() * 10) + 1;
        var imageNumber = Math.floor(Math.random() * 10);

        var queryStrings = {
            q: searchWord,
            key: config.googleAPI.key,
            cx:  config.googleAPI.cx,
            searchType: 'image',
            start: pageNumber
        }

        var params = {
            url: url,
            method: 'GET',
            json: true,
            qs: queryStrings
        }

        httpClient.get(params, function(err, res, body) {
            if(err){
                console.log(err);
            }
            try {
                func(res.body.items[imageNumber].link);
            }catch(e){
                console.log('Error: ' + e)
            }
        });

    });
};

function returnUrl(imageUrl){
    return imageUrl;
};

searchImage(searchWord)
    .then(function(imageUrl){
        var message = {
            "type":               "image",
            "originalContentUrl": imageUrl,
            "previewImageUrl":    imageUrl
        };
        lineClient.pushMessage("<Your Line user ID>", message);
    });

これを実行すると……

$ node search_image.js

こんな感じに画像が送られてきました。
push message.png

サーバーレスアーキテクチャでbotとして動かす

上記の声優画像検索スクリプトを少し改修してbotとして動かしてみます。

$ vi index.js
index.js
'use strict';

const line         = require('@line/bot-sdk');
const config       = require('config');
const httpClient   = require('request');

const lineClient   = new line.Client(config.lineAPI);

const googleSearch = 'https://www.googleapis.com/customsearch/v1';

function searchImage(searchWord) {

    return new Promise(function(func){

        var pageNumber  = Math.floor(Math.random() * 10) + 1;
        var imageNumber = Math.floor(Math.random() * 10);

        var queryStrings = {
            q:          searchWord,
            key:        config.googleAPI.key,
            cx:         config.googleAPI.cx,
            searchType: 'image',
            start:      pageNumber
        }

        var params = {
            url: googleSearch,
            method: 'GET',
            json: true,
            qs: queryStrings
        }

        httpClient.get(params, function(err, res, body) {
            if(err){
                console.log(err);
                return err;
            }
            try {
                func(res.body.items[imageNumber].link)
            } catch(e){
                console.log('Error: ' + e)
            }
        });

    });
};

function replyWithImage(event, imageUrl){
    console.log(imageUrl);
    var message = {
        "type":               "image",
        "originalContentUrl": imageUrl,
        "previewImageUrl":    imageUrl
    };
    lineClient.replyMessage(event.replyToken, message);
};

function handleEvent(event) {

    var searchWord = '立花理香';

    searchImage(searchWord)
        .then(function(imageUrl){
            replyWithImage(event, imageUrl);
        });

}

exports.webhook = function webhook(req, res) {
    Promise
        .all(req.body.events.map(handleEvent))
        .then((result) => res.json(result))
        .catch((error) => console.error(error));
}

これをGCFにアップロードします。処理に数分かかるかもしれません。

$ gcloud beta functions deploy webhook --trigger-http --source .
Deploying function (may take a while - up to 2 minutes).../

exportsしてgcloudコマンドでdeployした関数がGCF上から呼べるようになります。
完了すると以下のようなメッセージが出力されます。

...
availableMemoryMb: 256
entryPoint: webhook
httpsTrigger:
  url: https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/webhook
labels:
  deployment-tool: cli-gcloud
...

ここで出てきているurlをLine developersのコンソール画面上からWebhook URLに設定します。
設定が完了したら早速botを呼び出してみましょう。
今回書いたコードではなんでもいいので適当なメッセージを送ると画像を返してくれるはずです。
reply message.png
やったぜ。

課題

  • CSEで一度に拾ってこれる画像は100枚が限度、また無料枠は一日100リクエスト(それ以上は有料)
    • 画像を大量収集するのには向いてないかな
    • 調べるとBing APIを使ってる人の方が多いっぽい?
  • 拾ってきた画像がLINE APIのメッセージオブジェクトの制限に引っかかってるぽくて失敗する(ステータスコード400が返ってくる)時がある
    • originalContentUrlは最大画像サイズ:1024×1024、最大ファイルサイズ:1MB
    • previewImageUrlは最大画像サイズ:240×240、最大ファイルサイズ:1MB
6
5
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
6
5