1 はじめに
私、50歳で初めて子供ができたじじパパ。プログラミング初心者ですが、プログラミングで子育てがもっと楽しくならないか模索中。
1-1 夫が寝た後にを見るのが好き
テレビでミキティーが、学校の配布書類の管理が大変!!って言ってたから、なんか管理が楽になるアプリ作れないかなと。
1-2 奥さんにAIを使って欲しい
AIつよつよ夫婦になって、今後のキャリア形成に活かせたらと思い、AI利用を奥さんに勧めるもなかなか上手く行きません(^^;;
なんかAIで便利になるものを実際に作れたら、もっと興味を持ってくれるのでは??と考えました。
詳細はこちらに記載
2 書類管理アプリ調査
AI検索ツール Feloで検索。1分で調べて表にしてくれた。すごい。
詳細:Felo 学校配布書類について教えて
アプリ名 | 主な機能 | 料金 | 対応デバイス | 特徴 |
---|---|---|---|---|
おたよりクリップ | - プリント撮影・保存 - フォルダ分け - OCR(文字認識) - カレンダー連携 |
無料(広告非表示:160円) | iOS, Android | シンプルで使いやすい。OCR機能でプリント内容をテキスト化可能。 |
プリゼロ | - プリント撮影・保存 - カレンダー連携 - 家族での共有 - タグ付け |
無料(プレミアム:月額200円) | iOS, Android | カレンダー連携でスケジュール管理が簡単。家族全員で情報共有可能。 |
がくぷり | - プリント配信 - アンケート集計 - 出欠連絡 - カレンダー連携 |
学校が導入費用を負担 | iOS, Android, PC | 学校全体での導入を前提としたペーパーレス化サービス。事務作業の効率化に最適。 |
Evernote | - 書類保存 - OCR機能 - タグ付け - デバイス間同期 |
無料(プレミアム:月額1,100円) | iOS, Android, PC | 汎用性が高く、学校プリント以外の書類管理にも活用可能。 |
Microsoft OneNote | - 書類保存 - タグ付け - デバイス間同期 |
無料 | iOS, Android, PC | Microsoft Lensと連携して高品質なスキャンが可能。整理機能が柔軟。 |
tetoru | - 学校連絡配信 - 出欠連絡 - 保護者とのメッセージ機能 |
無料 | iOS, Android | 学校と保護者間の連絡を効率化するツール。小中学校向け。 |
正直、無料のアプリでも良さそうなのがあるな。でも自分で作ることに意味がある!はず。
3 記事の内容
学校からの配布書類を管理するアプリを作成したいのですが、難しいので以下記事のアプリ作成に挑戦しました。
• LINEとGASを連携させ、ユーザーのアップロードした画像を処理する方法
• GASを使って画像をDifyに送信する方法
• DifyでAIを活用し画像からデータを抽出し、Googleスプレッドシートに保存する方法
4 アプリの流れ (上記記事から引用)
4-1 利用イメージ
スマホで領収書を写メして、Lineから画像を送付すると、内容がGoogleのスプレッドシートに書き込まれ結果をLineで返してくれるというものです。
今後、学校配布書類管理に応用したいと思います。
配布書類をスマホ撮影
➡️Lineで画像送付
➡️自動で要約された文書と画像リンクが、Notionに保存
後で検索しやすくなれるようにしたいです
4-2 具体的な流れ
①ユーザーがLINEで領収書の写真をアップロード
②LINEからGASへwebhookが送信される
③GASがそのリクエストを受け取り、画像データをDifyに送信
④Difyを通してLLMにて画像を解析し、領収書の内容をJSON形式にする
⑤GASがそのJSONデータをGoogleスプレッドシートに保存し、画像もGoogleドライブに保存する
⑥GASが保存した内容をLINEに返す
5 アプリ作成
5-1 ②LINEからGASへwebhookが送信される(GoogleDriveに画像保存)
webhookとは
Feloで検索
Webhookとは?
Webhookは、**「特定のイベントが起きたときに、他のサービスやアプリに自動で通知を送る仕組み」**です。これにより、異なるシステム間でリアルタイムな連携が可能になります。
Webhookの仕組み
-
イベントの発生
例: システムでデータが更新されたり、新しい情報が追加されたりする。 -
通知の送信
Webhookは、指定されたURL(受信先)にHTTPリクエストを送信します。このリクエストには、イベントに関するデータが含まれています。HTTPリクエストとは?
HTTPリクエストは、インターネットで情報を取得するための「お願い」のようなものです。例えば、あなたがウェブサイトを見たいとき、ブラウザ(インターネットを見るためのアプリ)がサーバー(情報を持っているコンピュータ)に「このページを見せて!」と頼むのがHTTPリクエストです。 -
受信先での処理
通知を受け取ったサービスが、そのデータを使って次のアクションを実行します。
Webhookの例
-
チャット通知
例: SlackやMicrosoft Teamsに新しいメッセージを自動投稿。 -
Eメール送信
例: フォームに入力があった際に、管理者へメール通知。 -
データ連携
例: kintoneでデータが更新されたら、別のシステムにその情報を送信。
Webhookのメリット
-
リアルタイム性
イベントが発生した瞬間に通知が送られる。 -
自動化
手動で確認する必要がなく、効率的。 -
柔軟な連携
異なるサービス間で簡単にデータをやり取りできる。
Webhookの注意点
-
受信先の設定
Webhookを受け取るサービスが正しく設定されている必要があります。 -
セキュリティ
不正なリクエストを防ぐために、認証や署名の仕組みを導入することが推奨されます。
Webhookのイメージ
Webhookは、シーソーのように「片方で何かが起きたら、もう片方に通知が飛ぶ」仕組みと考えると分かりやすいです。
このようにWebhookは、システム間の連携をスムーズにし、効率的なワークフローを実現するための重要なツールです。
[1] https://dev.classmethod.jp/articles/teams-webhook-markdown-workflows/
[2] https://note.com/seika_usa/n/n9125e43e9f41
[3] https://github.com/pact-foundation/pact_broker/blob/master/lib/pact_broker/doc/views/webhooks.markdown
[4] https://fider.io/docs/using-webhooks
[5] https://zenn.dev/yukun369/articles/ac2150ec6ab9de
[6] https://devforum.zoom.us/t/incoming-webhook-markdown/27218
[7] https://api.slack.com/incoming-webhooks
[8] https://kintone-blog.cybozu.co.jp/developer/000283.html
[9] https://jamstack.jp/blog/netlifycms_to_microcms/
[10] https://help.zoho.com/portal/ja/kb/connect/developer-tools/webhooks/articles/%E5%8F%97%E4%BF%A1webhook%E3%81%AE%E9%AB%98%E5%BA%A6%E3%81%AA%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E5%BD%A2%E5%BC%8F
[11] https://help.zoho.com/portal/ja/kb/connect/developer-tools/webhooks/articles/%E5%8F%97%E4%BF%A1webhook%E3%81%AE%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E5%BD%A2%E5%BC%8F-15-11-2021
[12] https://stackoverflow.com/questions/66823432/ms-teams-webhook-with-markdown
[13] https://devforum.zoom.us/t/does-incoming-webhook-full-message-format-support-markdown/28685
[14] https://help.zoho.com/portal/en/kb/connect/developer-tools/webhooks/articles/advanced-message-formatting-in-incoming-webhooks
[15] https://qiita.com/ik-fib/items/b4a502d173a22b3947a0
[16] https://community.cisco.com/t5/adopting-webex/webex-teams-webhooks-and-markdown/m-p/4450152
[17] https://thoughtbot.com/blog/blog-in-markdown-deploy-with-webhooks
[18] https://birdie0.github.io/discord-webhooks-guide/other/discord_markdown.html
[19] https://blog.microcms.io/create_markdown_blog/
[20] https://blog.trainocate.co.jp/blog/output_nocode_022
コード解説 doPost関数
作成したLINE公式アカウントにメッセージ(画像)を送ると
指定したURL(このあと設定)にWebhookイベントオブジェクトを含む
HTTP POSTリクエストが送られてきます。
今回はPOSTリクエストを受け取るためにdoPost関数を使います。
以下cloudによる解説
1. doPost関数の定義部分
/**
* LINE webhook からのリクエスト受け取り処理
* @param {GoogleAppsScript.Events.DoPost} e
* @return {GoogleAppsScript.Content.TextOutput}
*/
function doPost(e) {
-
/**
から始まるブロックは「JSDoc」と呼ばれるドキュメンテーションコメントです - このコメントは関数の説明を記述する特別な形式です
- 1行目:この関数の概要説明
-
@param
:関数が受け取る引数の説明 -
@return
:関数が返す値の型の説明
-
function doPost(e)
:- Google Apps Script(GAS)の特別な関数名で、webhookのPOSTリクエストを受け取ります
-
e
は「event」の略で、LINEから送られてくるデータ全体を含むオブジェクトです - この関数名は変更できません(GASの規約)
2. データ存在チェック
if (!e) {
Logger.log('データがありません');
return ContentService.createTextOutput('データがありません');
}
-
if (!e)
:-
!
は「否定」を表す演算子です -
e
が以下の場合にtrueとなります:- null
- undefined
- 空文字列
- 0
- false
-
-
Logger.log()
:- GASのログ機能です
- 実行ログに「データがありません」と記録します
- デバッグ時に役立ちます
-
ContentService.createTextOutput()
:- GASの応答を作成するサービス
- プレーンテキストの応答を生成します
- LINEサーバーに返すレスポンスとなります
3. JSONデータのパース
const json = JSON.parse(e.postData.contents);
-
const
:再代入できない変数を宣言します -
e.postData
:POSTリクエストのデータを含むオブジェクト -
e.postData.contents
:- リクエストの本文(JSON形式の文字列)
- 例:
{ "events": [{ "type": "message", "replyToken": "12345...", "message": { "type": "image", "id": "789..." } }] }
-
JSON.parse()
:- JSON形式の文字列をJavaScriptオブジェクトに変換
- エラーが発生する可能性がある処理です(try-catchで囲むとより安全)
4. 必要なデータの取り出し
const replyToken = json.events[0].replyToken;
const message = json.events[0].message;
const messageType = message.type;
-
json.events[0]
:-
events
は配列で、複数のイベントを含む可能性があります -
[0]
で最初のイベントを取得
-
-
replyToken
:- LINEの応答に必要な一時的なトークン
- 有効期限は約30分
- このトークンを使って特定のメッセージに返信できます
-
message
:- 送られてきたメッセージの詳細情報を含むオブジェクト
- 画像の場合:
{ "type": "image", "id": "画像ID", "contentProvider": { "type": "line" } }
-
messageType
:- メッセージの種類を示す文字列
- 可能な値:
- "text"(テキスト)
- "image"(画像)
- "video"(動画)
- "audio"(音声)
- "file"(ファイル)
- "location"(位置情報)
- "sticker"(スタンプ)
5. リプライトークンの確認
if (typeof replyToken === 'underfined') {
return ContentService.createTextOutput('リプライトークンが見つかりません');
}
-
typeof
:変数の型を調べる演算子 - バグ修正:
'underfined'
→'undefined'
が正しい - このチェックの目的:
- replyTokenが存在しない場合のエラー処理
- LINE APIの仕様上、一部のイベントではreplyTokenが含まれない
6. メッセージタイプの確認と応答
if (messageType !== 'image') {
replyMessageToLine(replyToken, '画像を送ってください');
return;
}
-
!==
:厳密な不等価演算子- 値と型の両方を比較
-
!=
より安全
- 処理の流れ:
- メッセージが画像かチェック
- 画像でなければ「画像を送ってください」と返信
-
return
で関数を終了
7. 画像受信確認の応答
replyMessageToLine(replyToken, '画像を受け取れました');
-
replyMessageToLine
:- 別途定義が必要な関数
- LINE Messaging APIを使用して実際の返信を行う
- 実装例:
function replyMessageToLine(replyToken, text) { const url = 'https://api.line.me/v2/bot/message/reply'; const headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN }; const postData = { 'replyToken': replyToken, 'messages': [{ 'type': 'text', 'text': text }] }; const options = { 'method': 'post', 'headers': headers, 'payload': JSON.stringify(postData) }; UrlFetchApp.fetch(url, options); }
コード解説 応答返却(replyMessageToLine関数)
このコードをより詳しく解説していきます。
1. 応答返却(replyMessageToLine)関数の定義部分
/**
* LINEでリプライメッセージを投稿する
* @param {string} replyToken リクエストに含まれていたリプライトークン
* @param {string} messageText 送りたいメッセージ
* @return {UrlFetchApp.HTTPResponse} リクエスト結果のHTTPレスポンス
*/
function replyMessageToLine(replyToken, messageText) {
- JSDocによるドキュメント:
- 1行目:関数の目的
-
@param
:2つの引数の説明-
replyToken
:返信先を特定するための一時的なトークン -
messageText
:実際に送信するメッセージの内容
-
-
@return
:返り値の型の説明
- 引数:
-
replyToken
:前のコードで受け取ったLINEからのトークン -
messageText
:送信したいメッセージの文字列
-
2. LINE APIのURL設定
const url = LINE_URL + '/reply';
-
LINE_URL
:- 別の場所で定義された定数(通常は
https://api.line.me/v2/bot/message
) - LINEのMessaging APIのベースURL
- 別の場所で定義された定数(通常は
-
/reply
:- メッセージ返信用のエンドポイント
- 完成するURL:
https://api.line.me/v2/bot/message/reply
3. リクエストオプションの設定
const options = {
'headers': {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer ' + LINE_TOKEN,
},
'method': 'post',
'payload': JSON.stringify({
'replyToken': replyToken,
'messages': [{
'type': 'text',
'text': messageText,
}],
}),
}
-
headers
:HTTPヘッダー情報-
Content-Type
:- データ形式がJSONであることを指定
- 文字コードはUTF-8
-
Authorization
:- LINE APIの認証情報
-
Bearer
:トークン認証方式を示す接頭辞 -
LINE_TOKEN
:LINE Developersで取得したチャネルアクセストークン
-
-
method
:- HTTPメソッドの指定
-
post
:データを送信するための標準的なメソッド
-
payload
:送信するデータ-
JSON.stringify()
:JavaScriptオブジェクトをJSON文字列に変換 - データの構造:
{ 'replyToken': 'reply-token-xxx...', // 返信先の特定 'messages': [{ // メッセージの配列 'type': 'text', // メッセージタイプ 'text': '実際のメッセージ' // 送信内容 }], }
-
4. APIリクエストの実行と結果の処理
const response = UrlFetchApp.fetch(url, options);
return JSON.parse(response);
-
UrlFetchApp.fetch()
:- Google Apps Scriptの HTTP通信用メソッド
- 指定したURLにHTTPリクエストを送信
-
response
:- LINE APIからのレスポンス
- 成功時のレスポンス例:
{ "status": "ok" }
-
JSON.parse(response)
:- レスポンスをJavaScriptオブジェクトに変換
- 呼び出し元で結果を扱いやすくするため
6 まとめ
基本的に、参考記事通りにやって成功しました。
✅画像をスマホ撮影
✅Lineで画像送付
✅GoogleDriveに保存(自動)
✅Lineに「画像受け取れました」のメッセージ受取(自動)
以後、続きも記事にします。
◻︎受取画像をDifyに送付
◻︎DifyのLLMで画像のテキストを読み取り
◻︎上記をGoogle Spreadsheetに保存
そして、上記が完成したら、
◻︎Notion に情報管理
に挑戦していきたいと思います。