🙄

LINEのイベントを複数のwebhookでリッスンしてみた

2023/09/06に公開

はじめに

自動返信機能付きのchatbotをチャネルトークなどのadminツールで管理したい!
LINE側ではwebhookを1つしか指定できないという都合上、直接複数のwebhookでLINEのイベントを受け取るのは不可能です。
しかし、LINEで指定したwebhookで受け取った情報をそのまま別のwebhookへと横流しするという方法を取ることで、擬似的に複数のwebhookでリッスンすることが可能になります。

データの流れは以下のとおりです
(ユーザーからのメッセージ)

LINEサーバー

webhook1(自動返信)

webhook2(admin)

具体的な実装

まずはwebhook1で受け取った情報をwebhook2へ横流しするだけのコードを書いてみましょう。

Part1

import express from 'express';
import axios from 'axios';
import bodyParser from 'body-parser';


const app = express();


app.use(bodyParser.json());


app.post('/webhook', async (req, res) => {
    const { method, url, headers, body } = req;
    console.log({ method, url, headers, body });
  
    try {
      const { 'host': _, ...filteredHeaders } = headers

      const axiosHeaders = {
          ...filteredHeaders,
          'content-type': 'application/json charset=utf-8'
      }

      
      const response = await axios({
        method: method,
        url: 'https://example.com/webhook',
        headers: axiosHeaders,
        data: body,
      });
  
      console.log('Forwarded Data:', response.data);
      console.log('HTTP Status Code:', response.status);
      
      res.status(200).send('Data forwarded successfully');
    } 
    
    catch (error) {
      console.error('Error:', error);
      res.status(500).send('Failed to forward data');
    }
});


const PORT = 3000;

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

要するに、webhookで受け取ったリクエストを再現し、https://example.com/webhook に送っています。

node index.js
ngrok http 3000

で(ngrokのurl)/webhookをLINE側に指定してあげましょう。
これで、https://example.com/webhook にLINEからのイベントが送られるはずです。

Part2

自動返信などの機能を実装したい場合は以下のようにします。

...

async function handleEvent(event) {
 // 自動返信などの処理
 ...
}

const app = express();

app.use(bodyParser.json());

app.post('/webhook', async (req, res) => {
    const { method, url, headers, body } = req;
    console.log({ method, url, headers, body });
  
    try {
      const { 'host': _, 'content-length': __, 'transfer-encoding': ___, ...filteredHeaders } = headers;

      const axiosHeaders = { ...filteredHeaders, };
      
      const response = await axios({
        method: method,
        url: 'https://example.com/webhook',
        headers: axiosHeaders,
        data: body,
      });
  
      console.log('Forwarded Data:', response.data);
      console.log('HTTP Status Code:', response.status);
      
      const result = await Promise.all(req.body.events.map(handleEvent));
      res.json(result);
    } 
    
    catch (error) {
      console.error('Error:', error);
      res.status(500).send('Failed to forward data');
    }
});

handleEventの処理を追加しただけですね。

あとがき

実質的にLINE側で1つのwebhookしか指定できない問題は解決できました。
注意点として、今回の方法ではwebhookを介したものしか扱えないため、例えばbotからの自動返信の内容などはadminに表示されません。

Discussion