はじめに
こちらはRaspberry Pi と LINE messaging API で作る監視カメラシリーズ第12弾です。
LINEからラズパイを制御するために必要なLINEのWebhook機能を作ります。
今回のコードでは、LINEで「おはよう」と送ると「おはようございます」と返信、
「こんにちは」と送ると「こんにちは!」と返信する機能を作成します。
LINE Webhookとは
ざっくり解説ですが、
Webhook(ウェブフック) とは、「あるシステムで特定のイベント(出来事)が発生した際に、別のシステムへ自動的に通知(データ)を送信する仕組み」です。
そして、LINE Webhook とは、ユーザーのアクション(友だち追加、メッセージ送信など)が発生した際に、そのイベント情報をLINEプラットフォームから指定した外部システム(ボットサーバーなど)へ自動的に通知(POSTリクエスト)する仕組みのことです。
メッセージ返信の流れ
ここではcpp-httplibで次の機能を作成し、メッセージ返信を実現します。
- POSTリクエストでLINE APIにメッセージを送信する機能(第9弾と同じような機能です)
- そして、Webhook URLにアクセスされたらメッセージを確認し、
1.のメッセージ送信を呼び出して返信をするWebサーバーとしての機能です。(第10弾を使ってローカルから外部に公開します)
つまり、LINEに「おはよう」と送ると
LINE APIから「おはようというイベントだよー」とURLにアクセスが来ます、そしたら「なんのイベントかなー」と中身を確認します、そして「おはようだったらおはようございますって送ろー」と送信する感じです。
→これでLINEでは「おはようございます」と返信されたようになります。
今回はシリーズ第8弾で取得するチャネルアクセストークンと、第10弾でのURLを使用します。
準備
LINE Webhookを使用する場合は、次の設定が必要です。
LINE Developers の設定
ログインしたらチャネルが表示されていると思いますので、使用しているチャネルを選択し、項目の「Messaging API設定」を選択します。
トップ > (プロバイダー名) > (チャネル名) > Messaging API設定
この状態です。
・Webhook URLを第10弾でのURLを使い、写真のようなURL/webhookにして更新します。(https://xxxx.xxxx.xxの部分を自分のURLに変更してください。)
※NgrokのURLが更新された場合は、都度変更が必要です。
・Webhookの利用を有効にします。
LINE Official Account Managerの設定
ログインしたら、チャネルを選択します。
・Webhookを有効にします。
・応答メッセージは無効にします。
※LINEの標準システムが勝手に「メッセージありがとうございます!」と返信してしまい、プログラム側の返信が無視されることがあります。
以上で設定は完了です。
LINEに応答するコードです。
line_botディレクトリに移動し
cd /home/pi/projects/line_bot
main.cppを作成します
vim main.cpp
テキストエディタに次のコードを貼り付けます。
ここを変更するの部分はご自分のチャネルアクセストークンに置き換えてください。
#define CPPHTTPLIB_OPENSSL_SUPPORT // APIへの返信時にHTTPSクライアントとして動作するために必要
#include "httplib.h"
#include <iostream>
#include <string>
#include "nlohmann/json.hpp" // nlohmann/jsonを使用
using namespace httplib;
using json = nlohmann::json;
const std::string CHANNEL_ACCESS_TOKEN = "ここを変更する"; // チャンネルアクセストークン
const int WEBHOOK_PORT = 8080;
// (1) LINE Messaging APIのReply Messageを送信する関数 (クライアント機能)
void send_reply_message(const std::string& reply_token, const std::string& text) {
// HTTPSクライアントとしてLINE APIのホストに接続
Client cli("https://api.line.me");
// 返信用JSONデータの作成
json reply_json = {
{"replyToken", reply_token},
{"messages", {
{
{"type", "text"},
{"text", text}
}
}}
};
std::string post_data = reply_json.dump();
// 必須ヘッダー: 認証トークンとContent-Type
Headers headers = {
{"Authorization", "Bearer " + CHANNEL_ACCESS_TOKEN}
};
// Reply APIを呼び出して返信を送信
auto res = cli.Post("/v2/bot/message/reply", headers, post_data, "application/json");
if (res && res->status == 200) {
std::cout << " > LINEへ返信しました: " << text << std::endl;
} else {
std::cerr << " > 返信に失敗。HTTP ステータス: " << (res ? std::to_string(res->status) : "接続エラー") << std::endl;
if (res) std::cerr << " > レスポンス内容: " << res->body << std::endl;
}
}
// (2) Webhookリクエストを処理するハンドラ関数 (サーバー機能)
void handle_webhook(const Request& req, Response& res) {
// ----------------------------------------------------
// 1. リクエストボディの解析
// ----------------------------------------------------
json req_json;
try {
req_json = json::parse(req.body);
} catch (json::parse_error& e) {
std::cerr << "JSON解析エラー: " << e.what() << std::endl;
res.set_content("Bad Request", "text/plain");
res.status = 400;
return;
}
std::cout << "Webhookリクエストを受信しました。処理を開始します。" << std::endl;
// ----------------------------------------------------
// 2. メッセージイベントの抽出と処理
// ----------------------------------------------------
// リクエストに含まれるすべてのイベントをループ
for (const auto& event : req_json["events"]) {
// イベントタイプが「メッセージ」かつ、メッセージタイプが「テキスト」であるか確認
if (event.contains("type") && event["type"] == "message" &&
event.contains("message") && event["message"].contains("type") && event["message"]["type"] == "text") {
std::string user_message = event["message"]["text"];
std::string reply_token = event["replyToken"];
std::cout << " > ユーザーメッセージ: [" << user_message << "]" << std::endl;
// ------------------------------------------------
// 3. 応答ロジックの実装
// ------------------------------------------------
if (user_message == "おはよう") {
send_reply_message(reply_token, "おはようございます");
} else if (user_message == "こんにちは") {
send_reply_message(reply_token, "こんにちは!");
} else {
// その他のメッセージには何も返信しない、またはデフォルトの応答
std::cout << " > 定義されていないメッセージのため、返信しません。" << std::endl;
}
}
}
// LINEプラットフォームに対して、リクエストを正常に受け取ったことを必ず伝える (ステータス200)
res.set_content("OK", "text/plain");
res.status = 200;
}
// (3) メイン関数
int main() {
Server svr;
// Webhookの受付パスとハンドラを設定
svr.Post("/webhook", [](const Request& req, Response& res) {
handle_webhook(req, res);
});
std::cout << "===================================================" << std::endl;
std::cout << "LINE Webhook サーバーをポート " << WEBHOOK_PORT << " で起動します。" << std::endl;
std::cout << "ngrok で外部に公開し、LINE DevelopersにURLを設定してください。" << std::endl;
std::cout << "===================================================" << std::endl;
// サーバーを起動し、リクエストを待ち受け
if (!svr.listen("0.0.0.0", WEBHOOK_PORT)) {
std::cerr << "サーバーの起動に失敗しました。ポートが使用中かもしれません。" << std::endl;
return 1;
}
return 0;
}
ESCキーを押して、:wqを入力してEnterで保存して閉じます。
buildディレクトリに移動し、
cd build
makeコマンドでビルドします。
make
ここで、実行の前に、別のターミナルを開き、ラズパイにssh接続してからngrokを起動してください。
ngrok http 8080
元のターミナルに戻り、実行ファイルのmain_appを./で実行します。
./main_app
LINEで「おはよう」や「こんにちは」と送って、返答を確認しましょう。
こちらはサーバーが待ち続けるため、処理の中断はctrキー + cを押してください。
サーバーの停止は全ての機能を合わせる時に実装します。
さいごに
webhook機能を使えばLINEの特定の文字列に反応してラズパイの制御を行えるので、単純なモード変更などの遠隔操作する手段として使用できます。
次に作りたいロボットカーでも何かに使えないか考えていると楽しいです。

