LINE Massaging API 応答メッセージの送信
投稿日: 2024年10月29日
前回の続編です。
データベースに必要な情報の登録が出来たので、登録したグループやユーザーな必要なURLと合言葉(ポケモンの名前)をLINEで送信します。
メッセージ送信する方法は5つあります。
応答メッセージ
プッシュメッセージ:1対1
マルチキャストメッセージ:1対多(ユーザーID指定)
ナローキャストメッセージ:1対多(絞り込み配信)
ブロードキャストメッセージ:1対多(すべての友だち)
大きく分けると2つ。
ユーザーからのメッセージやアクションに応答するというのは、webhookのイベントが発生した時と解釈しました。
今回は参加イベントの発火に応答して返信したいので、応答メッセージで実装するのかなと考えました。
公式にリクエストの仕方が詳しく書いてあります。
ポイントとなるのは応答トークンを利用することかなと思います。
webhookイベントオブジェクトにreplyTokenが含まれていて、それをリクエストボディに含める必要があります。
下記、公式のサンプルコードです。
curl -v -X POST https://api.line.me/v2/bot/message/reply \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {channel access token}' \
-d '{
"replyToken":"nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
"messages":[
{
"type":"text",
"text":"Hello, user"
},
{
"type":"text",
"text":"May I help you?"
}
]
}'
Shell知らないですけど、見たら何となくわかりますね。
method
はPOST
。
エンドポイントはhttps://api.line.me/v2/bot/message/reply
headers
にContent-Type: application/json
とAuthorization
にBearer
スペース挟んでchannel access tokenが必要。
リクエストボディにreplyToken
とmessages
という配列が必要。
messages
はtype
とtext
という文字列のプロパティが必要。こんなところっぽいです。
文字の強調の仕方が正しいかはよくわかりませんw
公式にとにかく詳しく書いていますのでご安心ください。
では、出来上がったコードをぺたっと貼ります。
前回と重複ありますのでご了承ください!
webhookのエンドポイントURLに設定しているところからです。
import { NextRequest, NextResponse } from "next/server";
import { WebhookRequest } from "./_types/WebhookRequest";
import { buildPrisma } from "@/app/_utils/prisma";
import { sendMassage } from "./_utils/sendMassage";
import { pokeApi } from "./_utils/pokeApi";
import { randomBytes } from "crypto";
export const POST = async (req: NextRequest) => {
const prisma = await buildPrisma();
try {
const body: WebhookRequest = await req.json();
const events = body.events;
const joinEvent = events.find(event => event.type === "join");
if (!joinEvent)
return NextResponse.json(
{
message: "参加イベントじゃない",
},
{ status: 200 }
);
const { groupId, roomId, userId } = joinEvent.source;
const lineId = groupId || roomId || userId;
if (!lineId) throw new Error("IDの取得が出来ませんでした");
//既存のIDじゃないか確認
const room = await prisma.room.findUnique({
where: {
roomUrlId: lineId,
},
});
if (room)
return NextResponse.json(
{
message: "登録済のroomid",
},
{ status: 200 }
);
//合言葉の生成
const pokeName = await pokeApi();
//URLの生成
const buffer = randomBytes(16);
const roomUrlId = buffer.toString("hex");
await prisma.room.create({
data: {
adminUserId: process.env.ADMIN_USER as string,
lineId,
roomUrlId,
password: pokeName,
},
});
await sendMassage(joinEvent.replyToken, roomUrlId, pokeName);
return NextResponse.json(
{
message: "success",
},
{ status: 200 }
);
} catch (e) {
if (e instanceof Error) {
console.log(e.message);
return NextResponse.json({ error: e.message }, { status: 400 });
}
}
};
実際に送信しているのがこちら。
import { fetcher } from "../../_utils/fetcher";
import { SendMassageResponse } from "../_types/SendMassageResponse";
import { SendMessageRequest } from "../_types/SendMessageRequest";
export const sendMassage = async (
replyToken: string,
roomId: string,
password: string
) => {
const endpoint = "https://api.line.me/v2/bot/message/reply";
const options = {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CHANNEL_ACCESS_TOKEN}`,
},
body: {
replyToken,
messages: [
{
type: "text",
text: `予定を登録するページのURL:${process.env.NEXT_PUBLIC_APP_URL}/room/${roomId}\n合言葉:${password}`,
},
],
},
};
try {
const resp = await fetcher<SendMassageResponse, SendMessageRequest>(
endpoint,
options
);
return resp.sentMessages;
} catch (error) {
console.error(`Error fetching data for ID ${roomId}:`, error);
throw new Error("LINE応答に失敗しました");
} finally {
}
};
headersのAuthorizationに設定しているchannel access tokenですがこちらは、LINE developerで確認できます。
channel access tokenもいくつかあります
今回は長期を使いました。
LINE developerのMessaging API設定の画面の一番下にあります。
これを環境変数に設定しています。
こちらはwebhookイベントオブジェクトに含まれている今回のイベントを識別するトークンというイメージです。
このイベントに対して応答メッセージを送る際に使用する応答トークン
応答メッセージの時にしか使わないようです。
こちらはリクエストボディに必ず含めないといけないです。
const joinEvent = events.find(event => event.type === "join");
//中略
await sendMassage(joinEvent.replyToken, roomUrlId, pokeName);
joinEvent.replyTokenをsendMessagie.tsに渡して送信時に使用しています。
body: {
replyToken,
messages: [
{
type: "text",
text: `予定を登録するページのURL:${process.env.NEXT_PUBLIC_APP_URL}/room/${roomId}\n合言葉:${password}`,
},
],
},
messagesは一つであろうと必ず配列なので注意しましょう。
これで公式アカウントを友だち登録したり、グループのメンバーに追加した時に、
までの実装が出来ました!
しょうもないことで私は時間溶かしましたが、意外とハードル低いですよね・・!
また通知が必要なことがあり、今回の情報に加えてデータベースに登録必要な気がするので、もう少しドキュメント見ながら最初に登録すべき項目は見極めていきたいと思います(多分行き当たりばったりになると思いますがw)!