2023年2月28日火曜日

LINE公式アカウントから返信および受信した写真を保存する

 LINE Messaging APIを発行するためのWeb資格証明の作成方法について、以前の記事で紹介しています。折角なので、この記事で作成したWeb資格証明(静的識別子はLINE_CHANNEL_ACCESS_TOKEN)を使用したAPEXアプリケーションを作ってみます。

作成するAPEXアプリケーションに、以下の2つの操作を実装します。

  • LINE公式アカウントが受信したメッセージに返答する
  • LINE公式アカウントが受信した写真を保存する
LINE公式アカウントが受信したメッセージは、Webhookによって表LINE_MESSAGESに保存されます。この処理はすでに作成済みで、そのまま流用します。

作成したアプリケーションは以下のように動作します。LINE公式アカウントがメッセージを受信したことはAPEXアプリケーションでは検知しないため、手動でページをリロードしてレポートを再表示しています。

 

LINEアプリケーションの画面です。メッセージありがとうございます!とあるのは、LINE公式アカウントにデフォルトで設定されている応答メッセージです。


以下よりAPEXアプリケーションの作成手順を紹介します。

クイックSQLの以下のモデルから、受信した写真を保存する表LINE_IMAGESを作成します。
# prefix: line
images
    message_id vc20 /pk
    content blob
    content_type vc80
SQLワークショップユーティリティクイックSQLより、表の作成を行います。左ペインにモデルを記述し、SQLの生成SQLスクリプトを保存レビューおよび実行を順次クリックします。表LINE_IMAGESの作成までを実施し、APEXアプリケーションは作成しません。


LINE公式アカウントが受信したメッセージは、対話モード・レポートで表示します。以下のDDLを実行し、対話モード・レポートのソースとして使用するビューLINE_MESSAGES_Vを作成します。

create or replace view line_messages_v
as
select
cm.id, cm.is_valid, cm.received_date
,cm.source_type, cm.source_user_id, cm.reply_token
,cm.message_type, cm.message_text
,cm.message_id, cm.content_provider_type
,dbms_lob.getlength(ig.content) content, ig.content_type
from
(
select m.id, m.is_valid, m.received_date
,c.source_type, c.source_user_id, c.reply_token
,c.message_type, c.message_text
,c.message_id, c.content_provider_type
from line_messages m,
json_table(m.content, '$'
columns
(
source_type varchar2(20) path '$.events.source.type'
,source_user_id varchar2(100) path '$.events.source.userId'
,reply_token varchar2(100) path '$.events.replyToken'
,message_type varchar2(20) path '$.events.message.type'
,message_text varchar2(4000) path '$.events.message.text'
,message_id varchar2(100) path '$.events.message.id'
,content_provider_type varchar2(20) path '$.events.message.contentProvider.type'
)
) as c
where c.message_id is not null
) cm left outer join line_images ig
on cm.message_id = ig.message_id
/

1行のDDLなので、SQLコマンドに貼り付けて実行します。


アプリケーション作成ウィザードを起動し、空のアプリケーションを作成します。

名前LINE応答とします。それ以外は変更せず、アプリケーションの作成をクリックします。


アプリケーションが作成されたら、ページの作成を実行します。


 作成するページのタイプとして、対話モード・レポートを選択します。


ページ定義ページ番号とします。後ほど記述するコードにページ番号が含まれているため、ここでは必ず2を指定します。レポート・ページの名前LINEメッセージページ・モード標準とします。

フォーム・ページを含めるONにします。フォーム・ページ番号フォーム・ページ名受信メッセージフォーム・ページ・モードモーダル・ダイアログを選択します。受信したメッセージへの返信と受信した写真の保存は、フォームのページに実装します。

データ・ソース表/ビューの名前として、先ほど作成したビューLINE_MESSAGES_Vを選択します。

ナビゲーションブレッドクラムの使用ナビゲーションの使用ともにデフォルトのONを選択します。

へ進みます。


主キー主キー列1としてMESSAGE_ID(Varchar2)を選択します。

以上で、ページの作成をクリックします。


対話モード・レポートとフォームのページが作成されます。

作成された対話モード・レポートの列CONTENTを選択し、タイプBLOBのダウンロードに変更します。LINE公式アカウントが受信した写真を列CONTENTからダウンロードできるようにします。

BLOB属性表名LINE_IMAGESBLOB列CONTENT主キー列1MESSAGE_IDMIMEタイプ列CONTENT_TYPEを選択します。


ファイル名列を指定すると、写真をダウンロードする際にファイル名が付きます。LINE公式アカウントが受信した写真には名前が付いていないため、ファイル名列は指定していません。

ここで一覧されるメッセージはLINE公式アカウントが受信し、Webhookの処理で表に書き込まれます。フォームからメッセージを投入することは無いため、ボタンCREATE削除します。


以上で、受信したメッセージを一覧する対話モード・レポートはできました。

続いてページ番号3のフォームのページを変更します。

非表示になっている主キーのページ・アイテムP3_MESSAGE_IDを除いて、すべてのページ・アイテムを表示のみに変更します。


少しフォームを見やすくするため、ページ・アイテムP3_IS_VALIDP3_RECEIVED_DATEP3_SOURCE_TYPEP3_CONTENTP3_CONTENT_TYPEを選択し、レイアウト新規行の開始OFFに変更します。


返信する文字列を保持するページ・アイテムP3_REPLY_TEXTを作成します。

識別名前P3_REPLY_TEXTタイプとしてテキスト領域を選択します。ラベルReply Textとします。

返信するときだけページ・アイテムが表示されるように、サーバー側の条件としてタイプアイテム = 値を選択し、アイテムP3_MESSAGE_TYPEtextを指定します。


このフォームに実装する処理は返信写真の保存です。その処理に合わせてボタンを変更します。

最初にボタンDELETE削除します。


ボタンSAVEラベル保存に変更します。動作データベース・アクション- 選択 -に変更し、データベース・アクションとして未指定の状態にします。

サーバー側の条件タイプとしてアイテム = 値を選択します。アイテムP3_CONTENT_PROVIDER_TYPEを選択し、lineを指定します。


ボタンCREATEボタン名REPLYに変更し、ラベル返信とします。動作データベース・アクション- 選択 -に変更します。

サーバー側の条件タイプアイテム = 値を選択します。アイテムとしてP3_MESSAGE_TYPEを選択し、textを指定します。


左ペインでプロセス・ビューを開きます。これよりLINE Messaging APIを発行し、返信と写真の保存を行うプロセスを実装します。

ウィザードにより作成されたプロセスプロセス・フォーム受信メッセージ削除します。


プロセスを作成しダイアログを閉じるの上に配置します。

LINE Messaging APIのReply APIについては、以下を参照しています。応答メッセージは応答トークン(replyToken)を使って送信しています。ドキュメントによると応答トークンは1分以内で使用する必要がある、とのことなので実際にはボットなどによる自動返信に用いるもので、今回のサンプルのように人による返信の場合は、応答メッセージ以外を使用することになるでしょう。
https://developers.line.biz/ja/reference/messaging-api/#send-reply-message

Messaging APIのReply APIはメッセージとしてカウントされませんが、Push APIはカウントされます。
https://www.linebiz.com/jp/service/line-official-account/plan/

識別名前Replyとします。タイプとしてコードの実行を選択し、ソースPL/SQLコードとして以下を記述します。

declare
C_ENDPOINT constant varchar2(80) := 'https://api.line.me/v2/bot/message/reply';
l_request json_object_t;
l_request_clob clob;
l_messages json_array_t;
l_message json_object_t;
l_response_clob clob;
begin
l_request := json_object_t();
l_request.put('replyToken', :P3_REPLY_TOKEN);
l_message := json_object_t();
l_message.put('type','text');
l_message.put('text', :P3_REPLY_TEXT);
l_messages := json_array_t();
l_messages.append(l_message);
l_request.put('messages', l_messages);
l_request_clob := l_request.to_clob();
/* 返信 */
apex_debug.info(l_request_clob);
apex_web_service.clear_request_headers();
apex_web_service.set_request_headers('Content-Type','application/json',p_reset = false);
l_response_clob := apex_web_service.make_rest_request(
p_url => C_ENDPOINT
,p_http_method => 'POST'
,p_body => l_request_clob
,p_credential_static_id => 'LINE_CHANNEL_ACCESS_TOKEN'
);
if apex_web_service.g_status_code <> 200 then
raise_application_error(-20001, 'LINE Reply Error = ' || apex_web_service.g_status_code);
end if;
end;
view raw line_reply.sql hosted with ❤ by GitHub

サーバー側の条件ボタン押下時REPLYを指定します。


同様の手順にてプロセスSaveを作成します。ソースPL/SQLコードとして以下を記述します。LINE Messaging APIのドキュメントの以下を参照しています。
https://developers.line.biz/ja/docs/messaging-api/receiving-messages/#getting-content-sent-by-users

declare
C_ENDPOINT constant varchar2(80) := 'https://api-data.line.me/v2/bot/message/{messageId}/content';
l_url varchar2(200);
l_response_blob blob;
l_count number;
l_content_type varchar2(80);
begin
select count(*) into l_count from line_images where message_id = :P3_MESSAGE_ID;
if l_count > 0 then
return; -- すでに保存済みであれば何もしない。
end if;
/* データを取得する */
l_url := replace(C_ENDPOINT, '{messageId}', :P3_MESSAGE_ID);
-- apex_debug.info(l_url);
apex_web_service.clear_request_headers();
l_response_blob := apex_web_service.make_rest_request_b(
p_url => l_url
,p_http_method => 'GET'
,p_credential_static_id => 'LINE_CHANNEL_ACCESS_TOKEN'
);
if apex_web_service.g_status_code <> 200 then
raise_application_error(-20001, 'LINE Content Error = ' || apex_web_service.g_status_code);
end if;
/* Content-Typeヘッダーを取得する */
for i in 1..apex_web_service.g_headers.count
loop
if apex_web_service.g_headers(i).name = 'Content-Type' then
l_content_type := apex_web_service.g_headers(i).value;
exit;
end if;
end loop;
/* 取得したイメージを表LINE_IMAGESに保存する。 */
insert into line_images(message_id, content, content_type)
values(:P3_MESSAGE_ID, l_response_blob, l_content_type);
end;

サーバー側の条件ボタン押下時SAVEを指定します。


プロセスダイアログを閉じるを選択し、サーバー側の条件SAVE,REPLYに変更します。


以上でアプリケーションは完成です。アプリケーションを実行すると、記事の先頭のGIF動画のように動作します。

今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/line-messaging-api-sample.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。