コードも何も読めないど素人プログラマーが慶應のポータル"K-LMS"の通知をLINEとG-mailに送るようになるまで
※ラジオverを聞きたい方はこちらから。
前半部分
今回は、慶應義塾大学のポータル「K-LMS(lms.keio.jp)」のログインを自動化し、課題ページをスクショして、LINEとGmailに自動通知する仕組みを作った話をしたいと思います。
実質的に課題が出た瞬間に通知するシステムになります。
前半:技術的な話
後半:ここに至るまでの経緯や自己啓発的な話
どちらか片方でも面白いのですが、両方読むとより深く楽しめる内容になっています。プログラム初心者でもわかりますし、エンジニアの方向けにも細かく解説している記事になります。
Githubにもアップロードしたので気になる方はこちらかもご覧ください。
このプロダクトとやり取りできるChatGPTも用意したので、気になる人はここで質問してみてください!
💡 結論:使った技術
今回使用した技術は以下の通りです。
Playwright(Go言語で記述)
Gemini API(1日50回まで)
LINE Messaging API(1日10通まで)
タスクスケジューラ
コスト
0円です。
今のところ1か月以上運用しておりますが、コストは一切かかっておりません。Gemini APIも無料枠に収まるように1日50回に制限していますし、LINE messaging APIも通知が必要分送れるように1日10通に制限しています。
所要時間
20秒です。
20秒であなたの端末に、新しく出た課題の通知が届きます。
実際のターミナル画面の結果をお見せします。
2025/11/21 00:05:56 🚀 K-LMS監視を開始します...
2025/11/21 00:05:59 🌐 アクセス中: https://lms.keio.jp/
2025/11/21 00:06:03 ⏳ ダッシュボード待機中...
2025/11/21 00:06:04 🔍 新ハッシュ: 937ae244ee
2025/11/21 00:06:04 🟥 変更検知!スクショを撮ります
2025/11/21 00:06:05 📸 スクリーンショットを保存しました
2025/11/21 00:06:05 🔍 Geminiで課題情報を抽出中...
2025/11/21 00:06:11 💬 LINE送信中...
2025/11/21 00:06:11 ✅ LINE送信完了
2025/11/21 00:06:11 📧 Gmail送信中...
2025/11/21 00:06:16 ✅ Gmail送信完了
2025/11/21 00:06:16 🎉 全工程完了
⚙️ K-LMS 自動監視システムの全体フロー
Playwright(Goで記述) が K-LMS に自動ログイン
└ state.json(Cookie)を使って高速ログインダッシュボードのテキストを取得し、ハッシュ化して保存
└ 内容が前回と同じなら何もしないで終了
└ 差分があったときだけ次の処理へGemini OCR を起動
└ ダッシュボードをスクショ
└ Gemini API で課題名・期限を文字起こし
└ 前回課題の文字起こしと比較し差分を確認。差分なしなら終了。データ処理・ファイル生成
└ 過去に通知済の課題を判定し、新規課題のみ抽出
└ 新規課題のみをカレンダー登録用ファイル(.ics)として生成通知処理
Gmail:画像+課題内容+.icsファイルを送信
LINE:課題内容のみテキストで送信(API経由)
タスクスケジューラで 9:00〜17:00 の間、1分ごとに自動実行
└ 面倒なターミナル画面を逐一出さない
└ 課題が設置されて20秒~1分20秒で通知できるリアルタイム通知

ファイル構成

ファイルの細かい役割は以下の通りです。
K-LMS-Go/
├── K-LMS.exe # 🤖 ロボット本体(これを実行するだけで動く)
├── main.go # 🧠 司令塔(全体の指揮、フォルダ整理、重複チェックを行う)
│
├── internal/ # 🧰 道具箱(専門家チームの待機所)
│ ├── browser/ # 🕵️ 偵察部隊 (Playwright)
│ │ └── browser.go # K-LMSに潜入し、ダッシュボードを撮影してくる
│ │
│ ├── ocr/ # 👓 解析班 (Gemini 2.5)
│ │ └── gemini.go # 画像から文字を読み取り、科目リストと照合してデータを整える
│ │
│ ├── ics/ # 📅 工作班 (iCalendar)
│ │ └── generator.go # スマホのカレンダーに登録できる「.icsファイル」を作成する
│ │
│ ├── storage/ # 📚 書記係 (DB管理)
│ │ └── manager.go # 「どの課題を通知済みか」を記録し重複通知を防ぐ
│ │
│ └── notify/ # 📨 通信兵 (LINE / Gmail)
│ └── notify.go # LINEに速報を、Gmailにファイルを添付して送信する
│
├── data/ # 🗄️ 資料室(設定や履歴データの保管場所)
│ ├── courses.json # 科目名辞書(表記ゆれ補正用の正解リスト)
│ ├── sent_history.json # 送信済みリスト(重複防止用のブラックリスト)
│ └── last-run.txt # 前回ハッシュ(変更検知用)
│
├── logs/ # 📝 航海日誌
│ └── run-log.txt # いつ何が起きたか、エラー内容などを記録
│
├── .env # 🔑 機密書類(パスワードやAPIキーなどの極秘情報)
└── state.json # 🎫 通行手形(ログイン状態を維持する魔法のチケット)
ーーー以下が実際の通知の様子ですーーー



意識したポイントー技術選定フェーズ
まずはどんな技術を使って具現化しようか、の話をします。
大学のポータルサイトであるK-LMS(lms.keio.jp) に毎度ログインして課題を取得するのが面倒だな、ということからこのプロジェクトは始まりました。
そこで自動化する方法はないのかと考え、浮かんだ戦略が2つありました。
① ManusなどのAIエージェントによる操作
② K-LMSのアクセストークンを使用する
作戦1:ManusなどのAIエージェントによる操作
当初はManusというノーコード自動化ツールを使おうと考えていました。
以前、インターン先でCRMツール(Salesforce)の自動処理を試したとき、Manusを使ってログインやデータ編集までは成功した経験があったからです。
しかし、今回は最終的にManusの使用を断念しました。
理由は次の3つです。
(1) ManusにSSOのIDとパスワードを教えたくなかった
SSOとは「一つのIDとパスワードで大学の様々なサービスを使用できる仕組み」のことです。
慶應に関するあらゆる情報が1つのIDとパスワードで統合的に管理されているということです。
慶應ではこのSSOを通じて、以下のようなツールにアクセスできます:
K-LMS、gakuji.keio.jp、G-mail、Google Drive、Microsoft Office(Word・Excel・Teams、Zoom、Box、Slackなど
誤解を招くので比喩を使うのは好きではないのですが、マンションの管理人が持っている、マスターキーに近いです。この鍵(IDとパスワード)さえあればどんな部屋(G-mail、Onedrive、Slack、Zoomなど)にも入れる、ということです。
もし仮にManusを使う場合、自然言語でこう指示する必要があります:
「○○にログインして××の情報を取得して。IDとパスワードは以下の通り。IDはxxxx@keio.jp。パスワードはXxxXX。」
これがManus側のサーバーに保存されるわけです。一般に外部サービスを経由してSSO資格情報を扱うことは危険です。
この認証情報が外部に流出すれば、見知らぬところで不正にアクセスされてしまいます。
最近Xを見ていて、LINEのアクセストークンなどをプロンプトに含ませて指示することでニュースやAI生成動画を公式アカウントから送信した、という投稿を見かけましたが、私は推奨しません。
個人で楽しむ分にはよいと思いますが、会社で使うのは絶対にNGです。
閑話休題。
では今回の仕組みだと、なぜ外部流出を防げるのか。
それは.env(ドットエンブ)というローカルファイル(手元の端末に保存されているファイルのこと)に認証情報を安全に置いておくからです。
これは環境変数ファイルで、APIキーやパスワードを外部に出さずに扱うためのものです。ここにドットエンブファイルのサンプルコードを掲載しておきます。
# セキュリティ情報ー絶対.envファイルは公開しないこと
KEIO_USER=
KEIO_PASS=
GEMINI_API_KEY=
LINE_TOKEN=
LINE_USER_ID=
# --- Gmail ---
SMTP_USER=
SMTP_PASS=具体的には
・keio.jpのIDおよびパスワード
・Gemini APIキー
・LINE TOKENとUSER ID
・G-mailのアカウントと、アプリパスワード
です。
それぞれ説明します。
keio.jpのIDとパスワードは、当然ログインのために必要です。
Gemini APIキーは
保存したスクリーンショットを文字起こしするために使います。
LINEトークンとUSER IDについては、自作したLINE公式アカウントから送信するために必要です。
G-mailのアカウントについては送信先として必要。
G-mailのアプリパスワード は「メール専用の合鍵」です。
16桁のランダムな英字(例: xxxx xxxx xxxx xxxx)から構成されています。
できることはメールを送るだけなので、流出時のリスクもかなり小さいです。
最後に、AIが最近話題なので、Gemini APIキーについて説明します。
これは、簡潔に言えばGeminiを起動しなくても、Geminiを使用することができる、ということです。AIの開発者側が発行してくれる、これもまた鍵のようなものです。
一例をあげると
・Gemini
・Open AI
・Deepseek
・Groq(X社)
などたくさんあります。モデルによってできることが違います。
例えば
チャットでの応答しかできないモデル、画像も理解して応答するモデル、画像を生成できるモデル、AI音声を生成できるモデル、など様々です。
当然のことながらできることが増えるとコストも高くなります。
使用コストはトークンによって決まります。
トークンとは何なのか。
簡単にいうとAIが処理を行う上での単位、になります。
文字数と、タスクの重さで決まります。
日本語の場合、だいたい1トークンは1-2文字に相当します。
Gemini-2.5-flash-latestモデル(以下flashと略)ができることは以下の通りで、
・文字起こし
・チャット
・画像の読み取り、解析
・音声の解析
今回はこの文字起こしを使うことにしました。
実際に私のプロジェクトの場合、1回の文字起こしには370トークンほど使っています。
(2) 汎用性が低い
たとえManusでログインが自動化できても、再利用性や展開性が低いです。
今回は「自分の手で開発すること」そのものに価値を置いていました。
もしManusで完結したら、
「Manusにこう指示したらログインできたよ!」
それは開発ではなく、プロンプト作成(指示出し)にすぎません。今回は「自分で構築した仕組み」として形に残したかったのです。
(3) クレジット課金制
Manusは有料クレジット制で、ログインや複雑な操作には多くのクレジットを消費します。画面がうまく動かないと、自動で再試行(リロードなど)を行うため、実行コストも上がります。"ただkeio.jpにログインして課題を取得するだけで4,000円"は、さすがに非効率です。
もし使うなら、AIエージェントが「課題を理解して回答する」といったレベルまで進化させる場合ですが、慶應では授業ごとに生成AIの使用が制限されており、実装は難しいため断念しました。
作戦2:K-LMSのアクセストークンを使用する
慶應のSSOには「外部アクセストークン」という非常に便利な仕組みもあります。
lms.keio.jpにアクセスしてアカウントからK-LMSのアクセストークンの生成ができます。

アクセストークンを発行することができます。
これは一種のAPIキー(社員証のようなもの)で、lms.keio.jpにログインせずにデータ取得が可能になります。
APIキーがあれば、コースや課題を見れたり、期限や自分の提出状況を確認することができます。ただし、課題の提出や、授業の登録などはできません。
しかし、このアクセストークンにも2つの制約があります:
有効期限が1時間
再発行時にOSネイティブポップアップが出る
🔐 1時間制限の理由
安全性とサーバー負荷軽減のためにこれを採用しています。
安全性、について細かくお話しします。
アクセストークンは「持っているだけで権限が認められる鍵」です。
つまり、持ち主の確認をせずにアクセスできてしまうため、フリーWi-Fiなどを通して流出すれば、誰でもK-LMSから情報を取得できますし、大学側もそれを関知できません。
細かいことをいうと、アクセストークンを使ってできることは、あくまで課題や登録科目の情報の"読み取り"に過ぎないので、流出した場合も悪用される範囲はある程度限られますが、当然リスクはあるので、1時間に制限されています。
また、慶應は1学年7000人程度ですから、4学年、そして院生や教授など関係者を含めれば3万人程度の大規模な組織になります。できる限り、アクセスを最小限にするためにも、アクセストークンは1時間に限定しています。
こうなると、私たちが1時間おきにアクセストークンを更新するプログラムを作らないといけないので結構面倒です。
⚠️ OSネイティブポップアップ問題
アクセストークン発行時(再発行時も)、「生成しますか?」という確認ダイアログがブラウザではなくOS(Windows)側で表示されます。

このような画面が出ます。
Playwrightについては追って話すので、なんとなく、「人間が操作しないと無理な画面だ」ととらえてもらえれば問題ないです。
K-LMSにある厄介すぎる2つの問題
K-LMSには2つの大きな問題があります。
1つ目:K-LMSでは、教授が課題を出した瞬間にK-LMS側から通知がでません。厳密にいえば課題の通知システムは存在しているものの、これは1週間1度ほど不定期に実行され、全く使い物になりません。
こちらが実際の様子です。

そのため学生によくあるのが、
K-LMS上のオンラインリアクションペーパーで出席を提出しなければいけない場合、大抵が短い時間(例えば授業内の15分間など)にしか提出できないので、その間にひたすらブラウザを開き、リロードし続け、課題が出たら出席の課題を提出する、というものです。
これは非常に不便です。
あくまで推測ですが、管理者、教育的観点から考えると
新規課題ができた瞬間に通知するシステムを設けてしまうと、
学生が授業に一切出席せずとも課題を提出できてしまいます。
それを防ぐ目的があるのかもしれません。
2つ目:カレンダー登録機能が微妙
https://lms.keio.jp/calendar にアクセスして「カレンダーフィード」をクリックすると、このようにiCalフィードを取得という画面が表示されます。
これは簡単に言うと、GoogleカレンダーやiPhoneのカレンダーなどに簡単に登録できるファイルをダウンロードできる、ということです。
これで課題の登録ができるのですが、、、微妙なんです。

というのも、登録した日付よりも前の課題まで登録されてしまいます。
12/03に実行したとしたら、11月の頭の分からカレンダーが登録されてしまいます。これでは、課題を提出したのか、それとも提出し忘れてしまっているのかよくわかりません。
このデフォルト機能も非常に残念です。
話を戻して、
Manus → セキュリティ的に危険
アクセストークン → 自動操作できない画面がある
ということで、最終的にPlaywrightによる完全自動ログインを採用しました。
本題:Playwrightとは — 「ブラウザを人間のように動かすロボット」
ここで登場するのがPlaywright(プレイライト)。
Microsoftが2020年に開発したヘッドレスブラウザ操作ライブラリです。
難しい単語がどんどん出てきますね。丁寧に解説します。
ヘッドレスとはどういう意味か。
簡単にいうと画面(GUI)がいらない、俗な言い方をすれば、裏で勝手にブラウザを操作してくれるということです。
みなさんが通常検索をかけるときはchromeの画面が開かれた状態で検索をかけますよね。進むボタンやアドレスバーがある、いつもの画面です。
これをヘッドフル、と言います。
でもそれがありません。ヘッド”レス”、です。
こうすることでボタンの用意やCSS画面の描画などが不要になり、高速に処理することができます。
話を戻して、Playwrightというのは
「プログラム言語(JavaScriptやGoなど)で指示を出して、ブラウザをリモート操作できる仕組み」です。
ではコードに何を記述しているのでしょうか。
わかりやすくいうと、このように記述しています。
「まずはlms.keio.jpのページにアクセスして」
「次はこのブロックをタップして」
「クリックしたらここにIDを入力して」
この記述方法はウェブページの構造を使って記述しています。
F11キーやCtrl+Shift+Iなどでブラウザのデベロッパーツールを開いてみてください。


このように、WEbページというのは様々なブロックが階層化されて構成されています。
だいたい、よく入力する中心的なブロックには名前が割り当てられているので、それを一つずつ確認して、記述していきます。
例えば先ほどの 「まずはlms.keio.jpのページにアクセスして」 というのは
// K-LMSへアクセス
log.Println("🌐 アクセス中: https://lms.keio.jp/")
if _, err := page.Goto("https://lms.keio.jp/", playwright.PageGotoOptions{
WaitUntil: playwright.WaitUntilStateDomcontentloaded,
}); err != nil {
return nil, fmt.Errorf("ページ遷移エラー: %v", err)
}
このように記述されます。
一つずつ、K-LMSのページをデベロッパーツールで確認していき、
特定のブロックが見つかったら、それをタップしてもらうようなコードを記述する、という感じです。
勘違いしやすいポイントーPlaywrightは何も考えていない
勘違いしやすいポイントをお伝えします。
この時点ではAIは全く動いていません。AIが画面を認識して、この画面がでてきたからこうしよう!などと考えて操作しているわけではありません。
これを実行してくれるのはManus、ChatGPT AtlasやCommetのようなツールです。
AIブラウザは今めまぐるしく発展していますし、今後誰もがAIブラウザを使うことになるでしょう。今は技術にキャッチアップしたい、最新技術が好きな人のみが使っていますが、ChromeやSafariに標準装備される日もそう遠くないでしょう。
当然みなさんも、lms.keio.jpをAIブラウザが搭載されたツールで開くことになるでしょう。
だからこそ先にお伝えすると、Commetでは実は課題の提出ができます。
課題ページを開いて、サイドバーにアシスタントを出現させて自然言語ベースで指示すると、課題を自動で解き、提出までさせられます。
となれば、このような開発は不要で、結局AI搭載ブラウザに淘汰されて終わりなのではないか、という意見も重々承知しています。
私はプログラムが一切わからない人間でした。一つでも何か学びになると思い、今回このプログラムに取り組みました。
素人プログラマーの学んだこととしては、
プログラミングというのはすべてを記述しないと意味がなく、暗黙知、や勘、"なんとなく"といった観念と真逆の位置に存在しているということを学びました。
だから、
・ユーザー名を入力するブロックがどこか?
・そこに何を記述するか?
・そのあと何をするのか?(次へボタンを押すなど)
このように、すべて内容を理解したうえで、決められたプログラミング言語、というルールのもとに記述しなければいけない、ということです。
意識したポイントー操作・文字起こしフェーズ
・Cookieが保存される、Playwrightという選択
・コンパイラ言語であるGoで記述
・ハッシュ化で不必要に文字起こしさせない
解説します。
Cookieが保存されるPlaywrightという選択
このプロジェクトの当初はGoogleが開発したPuppeteerで実行していました。しかしこれではCookieが保存されないので、却下しました。
Cookie(クッキー)とは、Webサイトが あなたのブラウザの中に一時的に保存する小さなデータ のことです。
K-LMS でいうと、
・「誰がログインしているか」
・「どのセッションが有効か」
といった情報をブラウザ側に記録するために使われています。
なぜCookieが必要なのか、というともし Cookie がなければ、ページを移動するたびに毎回ログインし直す必要がある からです。
Cookie を持っているということは「この人は既にログイン済みですよ」といういわば“通行証”を持っている状態です。
Cookieが使えれば、いきなり本題のダッシュボード画面に遷移できます。
ショートカットできる感じですね。
Playwrightはそれができるので、不用意なログイン操作や入力操作が一部なくなることで処理が高速、かつ安定になりました。
厳密にいうと、 K-LMS のCookieはセキュリティの都合で半日程度で失効するので、その際は再度ログインが必要になります。
コンパイラ言語であるGoで記述
最初は使い慣れたJavaScript(Node.js)で開発していましたが、最終的に Go言語で全て書き直しました。
実はGoにした理由は3つあるのですが、ここでは1つだけ紹介します。
このツールは「1分に1回起動して、終わったらすぐ終了する」を繰り返します。 Node.jsは実行のたびにJavaScriptエンジンを立ち上げるため、どうしても遅いです。 Go言語は機械語に直接翻訳されているため、起動が爆速です。とにかく、爆速です。ほかの2つの理由については追って話します。
不必要に文字起こしさせないーハッシュ化
冒頭でお話しした流れを抜粋します。
2.ダッシュボードのテキストを取得し、ハッシュ化して保存
└ 内容が前回と同じなら何もせず終了
└ 差分があったときだけ次の処理へ進む
3.差分があった場合:Gemini OCR を起動
└ ダッシュボードをスクショ
└ Gemini API で課題名・期限を文字起こし
要は差分があったときだけ、文字起こし、というのが肝です。
前回と課題の内容が同じなのに、通知が来ていたら正直迷惑ですよね。
だから、課題が前回確認した時よりも変更されていたら、そのときだけ文字起こしする、という設定にしたのです。
では、差分がある、というのはどのようにして把握するのでしょうか。
この話はとても面白いので、この箇所だけでも見てください。
要するに、
“元のページの文章の内容をすべて固定長の文字列に変換して、1つの不可逆な指紋” にしている。
ということです。これをhash(ハッシュ化)と言います。
ハッシュ化とは?

hash 化しているのは
const dashboardContent = await page.innerText('body');
const newHash = sha256(dashboardContent)Canvas ダッシュボードに書かれているテキストすべて です。
授業名や課題名、課題の期限など、すべてのテキスト情報を64桁に圧縮させています。
SHA-256というハッシュ関数 で、次の性質を持っています。
・一方向性(不可逆)
元の文章 → ハッシュにすることは簡単
ハッシュ → 元の文章を復元することは数学的に不可能
・衝突耐性
異なる文章が同じハッシュになる確率は天文学的に低い(ほぼゼロ)
・微小な変化に超敏感
元の文章の1文字だけ変えても、ハッシュは完全に別物の64桁になる。
この特徴のおかげで、
「ダッシュボードのどこか1文字でも変わったら検知できる」という 最強の差分検知システム が成立するのです。
具体例:SHA-256でのハッシュ化を見てみる
元文章がHelloのとき
185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969
SHA-256ではこのように表示されます。
では、helloと1文字だけ変えた場合はどうなるのか?
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
完全に別物になります。
とっても面白いですよね。
1文字違うだけで、全く違う、でも64桁の値になるのです。
だから “差分検知” できるのです。
差分検知できると、さらに良いことが起きます。
Gemini APIを必要な時だけ呼び出せる、からです。
このプロジェクトを行ううえで、プロダクトをすべて無料で利用するために気を付けるべき注意点は2つあります。
・Gemini-2.5-flash-latest API(1日や1分ごとの利用制限あり)
・LINE messaging API(月に200通まで)
Gemini-2.5-flashモデルの使用制限で今回考えるのは
1日あたりのリクエスト数: 250回
これです。
1日250回のリクエストが多いのか、少ないのか、現実的な計算をしてみましょう。
課題が出た瞬間に通知してもらいたい場合、1分で1回文字起こしをする必要があります。そうなると
250÷60≒4
なので、4時間ちょっとしか毎分定期実行できません。
授業の課題は、授業時間の9:00~18:00ころに出るので、さすがに足りません。
実行頻度を落とすのも考えましたが、却下しました。
課題によっては提出可能時間が5分、10分、15分などかなり短く制限されているものがあります。10分に1回だった場合に、その課題を検知できない可能性があるのです。これでは使い物になりません。
だからこそ、1分に1回実行できるプログラムを作る、そこにプライドを持って開発に取り組みました。
ということで
とりあえずスクショ・文字起こし
↓
LINEやG-mailに送信
上記のGemini APIを無駄打ちしてしまう単純な手順ではなく、
テキストページを64文字の値に直す
↓
直した値を前回と比較
↓
差分があるときのみスクショ・文字起こし
このように一工夫挟むことで、Gemini APIの使用制限を抑えることができました。とはいえ、ハッシュ化が暴走すると危険なので、Gemini APIは1日50回までに制限しています。
ハッシュ化だけでは実は不十分ー文字起こし結果の差分でも見る
ハッシュ化の魅力を語りましたが、実際のところ、正確性は99%ほどで、ときどき課題が変更されていないのに通知が来てしまうことがあります。
これはK-LMSのUIに依存しており、仕方がありません。
K-LMSのダッシュボード画面にメンテナンスの通知が出たり、1日経てば日付部分が変わるので、前回実行時と課題内容は同じでも、ページ全体のハッシュ値で見ると全く異なり、これによって文字起こし・スクショが実行されてしまいます。
実際に実行してみると、1日たつと日付部分が変わるので確実にダッシュボード部分のハッシュ値が変わります。
そのため、前回課題の文字起こし結果との差分も検知することにしました。
そうすることで、仮にハッシュ値が異なっていたとしても文字起こし結果が同じであれば、通知送信フェーズに行かない、処理を終了する、という仕組みを新たに設けました。
そして、文字起こし結果を正確にするためにも、
dataフォルダーにcourses.jsonという、私が登録している授業をあらかじめすべて用意しておきました。
そうすることで予測変換を利用することができます。
具体的な教授名は出せないので、まったく別の漢字を出しますが、例えばスクリーンショットするとつぶれやすい感じがありますよね。
健、鵬、愛、鴨、藤など。
結局Gemini APIは画像に対して処理を行うので、つぶれた字の文字起こしには不向きです。
だから、あらかじめ授業を登録しておき、その中から一番近いものを決めてもらうようにしました。
またそれぞれのAPI自体にも使用制限を設けました。
(APIに使用制限を設けるのは開発者であれば当然のことかもしれませんが、なにぶん私は文系出身で一切そういった常識を理解しておりませんでした。)
カレンダー登録の仕組みーボタン一つであなたのカレンダーに課題を登録できる
単にLINEとG-mailの通知が来るだけでなく、カレンダーにボタン1つで追加できるシステムまで開発しました。
実際の写真はプライバシーの関係でお見せできないのですが、サンプルファイルの写真をお見せします。

API地獄からの脱出:なぜ「.icsファイル」生成なのか?
当初はGoogle Calendar APIを使って自動登録しようと考えていましたが、OAuth認証の複雑さや、個人開発でのメンテナンスコスト(トークン管理など)を考えると、少し大掛かりすぎると感じました。
画像の保存先をGoogle Drive にしていたときにGoogle 関連のAPIは制限もあったり、認証が切れたりするので面倒だ、というのは痛いほど理解していたので、絶対にGoogle系のAPIを使わずにカレンダーに追加できるシステムを作ろうと思っていました。
そこで採用したのが「.ics(iCalendar)ファイルを生成してメールに添付する」というローテクかつ堅実なアプローチです。 これなら認証周りの実装は一切不要。ユーザー(自分)はGmailに届いた添付ファイルをスマホでワンタップするだけで、カレンダーに予定を追加できます。
「自動で勝手に入る」よりも「確認してワンタップで入れる」方が、誤登録も防げてUXとしても優れていると判断しました。
実装のポイント
Go言語でこの機能を実装するにあたり、以下の3つのステップを踏みました。
1. Geminiの出力を「構造化データ」へ
これまではGeminiに「通知用の文章」を直接作らせていましたが、カレンダー登録には正確な「日付」や「タイトル」が必要です。 そこで、プロンプト(命令)を変更し、JSON形式でデータを受け取るように改良しました。
Before: 「統計学の課題が1月20日までです」という文章
After: {"course": "統計学", "title": "課題1", "deadline": "2026-01-20 23:59"} というデータ
2. 重複登録の防止(ハッシュ管理)
実はここでもハッシュを使用しています。
K-LMSは未提出課題を何度でも表示するため、そのままでは実行するたびに同じ予定がカレンダーに増殖してしまい、被ってしまいます。
これを防ぐため、簡易的な重複排除システムを実装しました。
課題情報(授業名・課題名・期限)からSHA-256ハッシュを生成してID化。
data/sent_history.json に送信済みのIDを記録。
「初見のID」を持つ課題だけを .ics ファイルに書き出し、2回目以降はスキップする(LINE通知のみ送る)。
これにより、「リマインドは何度でも送るが、カレンダー登録ファイルは最初の1回だけ」という挙動を実現しました。
3. 標準ライブラリで.ics生成
Go言語には強力な標準ライブラリがあるため、外部ライブラリを使わずにテキスト処理だけで .ics ファイルを生成できました。strings.Builder を使い、iCalendar形式の仕様に沿ってテキストを組み立て、schedule.ics として出力。これをGmail送信機能に渡して添付させています。
これにより、「Go言語 × 生成AI × ローテク(メール添付)」 という組み合わせで、セキュアかつメンテナンスフリーなカレンダー連携機能が完成しました。 APIの仕様変更に怯えることもなく、スマホ標準の機能を活かしたスマートな運用ができています。
通知の仕組みーLINEとG-mailに文字起こし結果を送るには?
さて、文字起こしが完了した後はそれをLINEとG-mailに通知しなければなりません。G-mailの場合は、スクショ画像も送信します。
それぞれ説明します。
今回使ったのは
🟢 LINE:LINE Messaging API(Web経由)
🔴 Gmail:SMTP(メールサーバー経由)
この2つになります。
LINE Messaging APIについて
LINEへの通知は 「API(エーピーアイ)」 を使っています。 これは、「Webサイトのフォームに入力して送信ボタンを押す」 のと同じ感覚です。仕組みとしては プログラムが https://api.line.me/... というLINEの窓口(URL)に対して、「このメッセージを送って!」とデータを投げつける(HTTPリクエスト)だけで完了します。LINE社の受付カウンターに書類を提出する感じです。それ以降の処理は勝手にしてくれる、という感じですね。
そのための許可証としてLINE_TOKENやLINE_USER_IDが存在します。
Gmail:SMTP について
Gmailへの通知は、「SMTP(エスエムティーピー)」 という世界標準のプロトコル(通信の決まりごと)を使っています。これはWebサイトを見る仕組みとは違い、「メールソフト(OutlookやApple Mail)がやっていること」 をGo言語でそのまま再現しています。
仕組み
Goプログラムが、Googleの郵便局(smtp.gmail.com)のドアをノックする。
「アプリパスワード」を見せて本人確認をする。
手紙(画像付きメール)を渡して「配送お願いします」と頼む。
意識したポイントー通知フェーズ
なぜG-mailに画像とテキストを送信し、LINEにはテキストのみを送信したのか?
これには理由が2つあります。
・1日の送信制限のないG-mailに画像を送るべき
・LINEにmessaging APIを経由して画像を送信するのは面倒
以上2つです。
まず、SMTPを経由してG-mailに送信するのに重要な送信制限などはありませんでした。一方LINE messaging APIは1か月で200通と制限があります。
文字起こし結果と画像を送る場合、これは2通分にカウントされます。
そうなると、1日3回通知が来てしまうと、30日で180回とかなり制限を圧迫します。であれば、制限のないほうを選びますよね。
次に、LINE messaging APIを経由して画像を送信するのは非常に面倒です。
どれだけ面倒かお話しします。
当初LINEに画像を送信するために、GAS×Google Drive APIを使って処理していました。
仕組みはこのような感じです。
スクショ→指定したGoogle DriveフォルダにAPI経由でアップロード→GASが指定フォルダを1分ごとに定期監視→新規フォルダが上がったら、それを送信
というシステムです。見るだけで反吐が出ます。
まずGoogle Drive APIが面倒。GCPでの設定、クォータ制限、OauthクライアントIDの更新など、すべてが面倒です。
それからGASが1分おきにしか監視できません。これは致命的なミスです。
なぜか。
タスクスケジューラの起動<スクショ・GASの監視含めた一連の動作
になるからです。
要するに全部の処理に1分以上かかってしまう、ということです。
タスクスケジューラで1分おきに設定した意味がありません。
タスクスケジューラで1分ごとに実行する
これら一連のシステムを最後にタスクスケジューラでまとめ、
9:00-17:00の1分おきに実行するようにしました。

ここでの工夫は2点。
・ターミナルを開かずに実行すること
・スリープ時でもスリープを解除して実行すること
です。
1つ目について。
headlessとはいえ基本的にターミナルは起動してしまいますから、はっきり言って邪魔です。これが1分に1回起動してしまっては困ります。
これをGo言語において
-ldflags "-H=windowsgui"と記述するだけでターミナル画面を開かないでよくなりました。
ターミナル画面が開かれなくなると、実行されているか心配になるかと思います。こちらについては run-log.txtというファイルに実行日時を出力させているので、問題がある場合はここから見れます。
1分おきに実行されているのがわかりますね。

また、ターミナルを表示させないので、何らかの工程でエラーが起きた時も心配です。これもしっかりG-mailに通知するシステムを設計しました。
エラーが起きたらそのエラーが起きた部分で通知する。安全対策もばっちりです。
2つ目について
Windowsではタスクスケジューラで実行させる際に、パソコンがスリープ時でもスリープを解除して実行すること、を設定することができます。

この場合、パソコンがスリープ状態でもWi-FIがつながっていれば勝手に起きて、自動で実行してくれます。
配布・他ユーザーの利用まで考える
最後の話です。Go言語で記述した残り2つの理由はここにあります。
配布して、ほかのユーザーに利用してもらうことを考えました。
配布の革命(シングルバイナリ)
Node.js: 相手に「Node.js入れて」「npm installして」と頼み、数万ファイルの node_modules を抱える必要がありました。
Go: 「この .exe 1個で動くよ」 で終了。
必要なライブラリも全て1つのファイルに「お弁当」のように詰め込まれるため、依存関係地獄から解放されます。
当初はJavascriptで記載していたので、後述する仲の良い友人のプログラマーから面倒すぎる、とひどく指摘されました。
K-LMS-Auto-Loginの持つ2つの弱点
弱点は2つあります。
・ネットワーク接続がないと実行できない
・Macユーザーは利用不可
1点目について。
当然ながら、ローカルでchromeを開き、諸々の操作を行うので、ネットワークにつながっていないと実行されません。クラウドサーバーにおいての常時実行は慶應の管理者側から制限をされたので、不可能です。
そうなると、残された手段はテザリングくらいしかありません。これはPC自体のネットワークがつながっていなくてもスマホの回線を共有する形で、接続すれば問題ありません。最近はデータ通信の制限がないプランなどもあるので、そういう人であれば、PCとスマホをテザリングしてしまえば、移動中でも通知が来ます。
2点目について。
こちらも残念ながらセキュリティの問題できびしかったです。
Githubに公開し、友人のプログラマーで、Macユーザーの彼に試してもらったのですが、ダウンロードができませんでした。
せっかく開発したのにMacユーザーが使えないプログラムとなってしまい申し訳ありません。私なりに全力を尽くしました。
おわりに

開発に関する話は以上です。SSOという「諸刃の剣」をローカルで安全に扱うために、Playwrightを中心としたローカル完結型の自動化システムを構築しました。
初心者でも理解できるように作ったつもりなので、もし間違いや改善点があればぜひ教えてください。
次回の後編では、
「この仕組みを作るに至った経緯」や「自分の思考の変化」について、
もう少し人間的な側面からお話しします。
後半部分
後編|「コードがわからない文系」がK-LMS自動化に至るまでの思考とプロセス
はじめに
さらっと内容をお伝えすると、今年の10月にパソコンを起動したら慶應義塾大学のK-LMSという学習ポータルサイトに自動でログインして、課題のページをスクショしてLINEやG-mailに自動で送信する、というシステムを開発しました。完全に個人での開発で開発期間は8日程度です。前提としては私はコードの読み書きは全くできません。プログラミングの0.1%程度しか理解していません。今回の後編では、コードも全く知らない文系の人間がどのような思考で、ここまでたどり着いたのか、自己啓発的な話をします。
結論ー開発がそこそこできた理由
・定期的にプログラムに触れていた
・AIと根気よくチャットし続けた
・プログラマーの友達と高めあった
・10点で20点でも良いからアウトプットし続けた
結論としてはこれに収まると思います。
どの点も才能というよりは日頃の意識、少しの行動力で変わる部分が大きいと思うので、1点ずつお話しできればと思います。この話がなにか皆さんに対してきっかけや気づきを与えられる文章になることを願っています。
GASとの出会いと最初の成果
私がいわゆるプログラム、に触れたのは2025年の2月頃です。
きっかけはSNSで有名なUsutakuさんのMIchikusaというオンラインコミュニティに入ったことでした。ここでGoogle Apps Script(通称GAS)という存在を知りました。私はいまは所属していないのですが、コミュニティとして非常に面白いのでもし良かったらご覧ください。
一応リンクを掲載しておきます。
https://aimokuyokai.com/
GASとは?
簡潔に言うと、Googleが作ったJavascriptに近いプログラム言語で、これをG-mailやGoogle form、Google Spread Sheet、使い方によってはLINEやSlackといったツールとも連携できます。
プログラムは当然かけないので、AIを使ってプログラムを書く、ということを知りました。ちょうどサークルでセミナーの参加者に一斉にメールを送信する、という機会があったので実際にChatGPTにGASのコードを書かせて、とりあえずGASの編集画面に張り付けて実行してみました。
すると、参加者40名ほどに送信したいメールを一斉に送ることができ、予約送信までできました。さらには送信した場合は送信済、と記載。
1度送信した「送信済」の人には二度送らない設定などもでき、はじめてGASの強さ、魅力に気が付きました。
もしGASの存在に気づいていなかったら、きっと手作業で全員にメールを送っていたでしょう。そう考えると今でも恐ろしいです。
インターンでの実践と3つの挑戦
2回目にGASに触れたのはインターン先でした。
当時は大学受験塾でインターンをしておりました。
そこではSpreadsheetを使って生徒様の学習計画表を作成・管理しているなど、多くがGoogle関連のアプリケーションを多くを使っていました。
そこで私は
「以前GASを使ってメールを一斉送信したことがあります。コードはそこまで読めませんが、GASは使ったことがあります」
というと、上司から
「今これくらい自動化したいことあるから、AI使っても何でもよいからこのあたりの業務を自動化してもらいたい」
ということで3つほど業務をもらいました。GASを使って挑戦したのと、その結果は以下の通りです。
・学習計画表の自動作成→成功
・生徒とコーチのマッチング→成功
・Salesforceへの自動入力、参照→失敗
細かい点でいうとSlackの特定のチャンネルの情報を定期的にGoogle Docsに出力→社内Notebook LMへの同期、なども行っておりました。
学習計画表の自動作成のアプローチ
学習計画表の自動作成は話すとかなり長くなるので、
簡潔に話すと、
各科目のレベル帯に沿った参考書が記載されたスプレッドシートを用意
↓
入塾する生徒様の学習レベルをアクティブシート上で選択
↓
実行ボタン
↓
参考書のシートを参照し、生徒様が受講する全科目の参考書リストを新規シートとして出力
という感じです。
これまで手入力を行っていた、元のフォーマットがあったので、そちらに際限なく近づけるつもりで行いました。そのため、カレンダー設定や色塗りの設定などもすべて模倣しました。業務改善率は1800%程度です。
これまで90分程度かかっていた作業が5分でできるようになりました。
AIとの対話で詰めた要件定義
結論2点目の「AIとひたすらチャットし続けた」ともつながるのですが、ここでも私はコードが一切かけなかったので、ひたすらAIとチャットし続けました。いわゆる要件定義、に近いです。
1点、誤解を防ぐためにも言いたいのはただ闇雲にチャットし続けたわけではありません。できる限り最短のチャットで実装できるように工夫しました。言語化です。
半年前のAIではスプレッドシートを投げただけで、課題を認識し、自律的に考えて○○すべきだ、とまで結論を出せるほどAIは優秀ではありませんでした。(少なくともGemini で何度も試しましたが、不可能だったと思います)
そのため、自分自身で、GASのコードを出してもらうためにも、何を自動化してもらいたいのか、それをひたすら言語化しました。
一番優先度の高いこと、とりあえずできるか試したいことを徹底的に言語化しました。それから○○ができるなら△△もしてもらいたい、という形で指定もしました。色の選定もカラーコードで記述するほど細かく指定しました。
元のフォーマットがあるので、このセルの色はカラーコード#xxxxxx で、サイズは〇 ptで、というようにです。ただそれでも苦戦しました。全科目出力して、と指定して出されたコードを実行すると1科目しか出力されなかったり、指定したセル以外の部分まで真っ黄色になってしまったり、思った通りに行かないことの連続でした。
開発だけで月に60~70時間くらいかけてしまったと思います。その多くがGemini 2.5 proの回答を待ち、コードを貼り付けて実行する。理想の結果とどう違ったのかを言語化してAIに対してフィードバック。
当時はタイピングするのも面倒だったので音声入力AIのAqua Voiceを使って最短で入力していました。そうこうしていくうちに、どのようにAIに指示だしすればよいのか、またフィードバック時に何をすればよいのか、というのも徐々にわかってきて、AIの扱いに慣れてきました。
マッチング機能とSalesforceの壁
同様に、生徒様とコーチのマッチングや、Salesforceの自動化なども取り組みました。生徒様とコーチのマッチングは既存のマッチングアプリとほとんどシステムが変わりません。生徒様の希望条件を、コーチの情報と照らしあわせて数値化してランキング順に表示、というものでした。
そのため開発には30~40時間で済んだと思います。Salesforceは顧客データを管理するCRMツールで、こちらはとても面倒でした。難しいのですが、Oauth 2.0認証という非常にセキュリティの強い認証が必要で、これを突破しないと自動制御することができません。
結局どこでうまくいっていなかったのか原因はわからず、途中で頓挫してしまいました。
ただ、この中で一度だけManusを使って入力を自動化させたことがありました。これが後々のK-LMSの入力の自動化につながってきます。GASと巡り会ってから、Gemini とチャットし続けてついにインターン先のDXに貢献しました。これがだいたい5~6か月くらいの期間での話です。
コードを読めないことがデバッグ修正であだとなる
反省点も当然ありました。
コードが読めないことがすべての原因です。コードが読めないと何が起こるのか。プログラムを実行するたびにログを出力させていたのですが、そのログの意味が分からない。
そうなると、エラーログまでGeminiに送り付けて、これはどういうことだ、そして何をすればよいのか、とアドバイスをもらう。回答の生成までが長いわりにエラーログが実はたいしたことなくて、実行する関数が違っただけ、一部分にカンマが記載されていなかっただけということもありました。もしエラーログの内容を理解していたら、一部を手作業で治せたはずです。
ここがロスになってしまいました。とはいえ、これまでほとんどプログラムに触れてこなかった文系出身で、AIをかじっただけの人間が最短でプログラムを実装するにはこれしか手段がなかったとも思っています。
途中で興味を持って図書館でGASに関する本も読みましたがあまりにも退屈でした。当然基礎、基本から書かれているので、スプレッドシートのA列とB列に書かれているテキストをまとめたものをC列に出力する、スプレッドシートの特定のセルを検索して一致するセルが見つかったら色を付ける、などです。私がしたいのはそんなことではない、と。
私が学習したいのはもっと複雑で、階層化されていて、会社の業務の改善にそのままつながるようなことだからです。根気よくやることだけには自信があり、音を上げることなくあきらめずにチャットを続けました。
切磋琢磨できる友人の存在
GASのプログラムを本格的に書き始めたころに、3点目の”プログラマーの友達と切磋琢磨した”、につながる、その友達と出会いました。
きっかけはサークルで、彼は自分でウェブサイトを1から立ち上げてブログ記事の運営を行っている人でした。本物のプログラマーです。
週に1度か2週に1度ほどあって、今はこんなプログラムを作っているなど、お互いの進捗を語り合いました。彼はウェブサイトの作成に続いて、次はアプリケーションの作成に動き出しました。
はじめはサークルの連絡や管理を簡潔に行うアプリケーションを作成したのちに、次はブログで扱っていたゲームの攻略に関するアプリケーションを作成しました。後者のアプリは実際にGoogle Play StoreとApple Storeに公開しており、当然私が第一号のダウンローダーです。
彼もCursorというバイブコーディングAIを使って開発していましたが、おそらく彼のほうが何倍もコードの意味を理解してプログラムしていたと思います。
10月:K-LMS通知機能の実装に踏み出す
そしていよいよこのK-LMSの通知機能を実装する、10月に入ります。
きっかけは彼の
「K-LMSのログイン毎回いちいちやるの面倒だからアプリから提出できるようにしたいんだよなー」
という一言でした。
それからCanvas APIの話や、SSO、多段入力の話などをするようになり、慶応がいかにしてセキュリティを担保しているのか、そして数万人のユーザーを管理しているのかを徐々に理解していきました。
改めてこの数万人のユーザーを特に重大なトラブルなどなく管理されている慶應義塾大学のIT関係の方には頭が上がりません。
閑話休題。
特に彼の場合は自分でウェブサイトを立ち上げたのもあるため、基礎的なウェブの知識は持っています。
当然「履修登録は自動化できるか」「ログインはそもそも自動化できるのか」という議論はお互いが何らかの前提、根拠をもとに話します。
彼の場合は自分で立ち上げたウェブサイトの知識。
私の場合は、せいぜいmanusで自動ログインを試みた経験と、根気強くGASをいじった経験。これくらいです。彼が根拠に沿った発言をしているのに私は理解もできず、これはできるんだろうか、できないんだろうかとずっと理解ができないままでした。
「なるほど」「ほお」という回答しかできていませんでした。強がって回答したこともありましたが、半分以上は理解していませんでした。
ただ、先ほどお話しした、Manusを使った入力の自動化は本当に生きてきました。ManusにログインIDやパスワードを入力させると勝手にログインして、Salesfroce内の情報を整理したり、引っ張ってくることができるのです。ブラウザ操作の可能性を広げてくれた、この経験が強く印象に残っていました。
たとえるなら、死の間際に走馬灯を見るようなイメージです。極限まで突き詰めたことにより、これまで見えていなかった世界が見える。正直Manusでブラウザ操作を自動化したくらいのことを、走馬灯と同じにするな、と思いますが、良いたとえが浮かばなかったので勘弁してください。
その記憶もあり、ChatGPTとひたすら対話を続けました。ただ、ChatGPTも一部誤解している部分もあり、正確にK-LMSの仕組みを理解しているわけではありません。AIとチャットしても実際にできるかはわかりませんので、理解するよりも先に手を動かすことを意識してプログラムに取り組みました。結果としてこのようなそこそこなプログラムが作れました。
「そこそこ」の自己評価と支え
私は本職でエンジニアをしているわけでもありませんし、周りにたくさんエンジニアがいるわけではありませんので、謙遜などではなく、本当に自分のしたことの成果がすごいとも、たいしたことないとも思っていません。
そこそこ、だと思っています。
使っている技術も、Gemini API以外はどれも2010年-2020年程度の間に完成された技術です。よく言えば、タスクスケジューラやHash、GASのトリガー、APIなどの基礎的な技術をまんべんなく網羅することで、AIエージェントブラウザに近しいことができた、と言えます。
プログラマーの友人は褒めてくれたので多少なりとも自信は持てました。彼がいないと完全に自分自身との戦いになり、もしかしたらあきらめていたかもしれません。
特にGitHubにMacユーザー向けに公開した時は何度も訂正をもらいました。私はWindowsユーザーで正直Macの使用を全くと言っていいほど理解していません。そんなこと聞かれても困る…と思うことが何度もありましたが、それでも彼と一緒に取り組んできたからこそ、わからないこともどんどん吸収していけるようになりました。
彼も彼なりにK-LMSのログインを自動化するアプリケーションを作成していたので、お互いに近い方向性で、楽しめたと思います。
学び:理解よりも先に行動が来る
プログラムの良い、悪いよりも今回学んだこととして大きかったものは「理解よりも先に行動が来る」この感覚です。
これを言い換えると、4点目の「10点でも20点でも良いから自分の中から出す」という言葉につながります。
やはりインプットしている時間というのは楽しいですし、気持ちがよいものです。当然インプットも必要です。基礎・基本を理解していなければいくらAIにコードを書いてもらっても何をすればよいのか、このコードが何を意味しているかは一切わかりません。
ただ、私はほかの人よりも10倍以上アウトプットしている自負があります。別の記事で書くかもしれませんが、慶應に新しいサークルを立ち上げたり、自分でフレグランスブランドを立ち上げたり、過去にもサークルのOBやOGにインタビューしてnoteのインタビュー記事を出したり、ピアノに独学で挑戦したり。
アウトプット量が圧倒的に多いがゆえに前段のインプットが少なくても何とかなってしまうのです。
自転車に1日3時間乗っている人が自転車を乗るときにまずはペダルの位置を見て、などいちいち考えているわけがありません。
それと同じです。
ではなぜこれほどアウトプットにこだわるのか。
人はどうしてもアウトプットしたもの、成果品でみられる部分が強いからです。中身を見てほしい、性格の良さ、誠実さを見てほしい、という気持ちもすごくわかります。
肩書や実績だけで見られないソフトスキルで見てほしい気持ちもすごくわかります。
ただその人の外にあるもので人は判断されることもあります。だから私もアウトプットすることを意識しています。
そして、もしその私の作品やアウトプットに共鳴、共感してくれてくれたら友人の間口も広がります。
内面的な中身の良さだけでなく、外身の実績やアウトプットでも見てくれるから、きっと興味を持ってくれる母数も増えてくれるはずです。
アウトプット中毒になると生きづらい?
矛盾したことをいうようで申し訳ありませんが、私は生きているときに毎度そんなことを考えているわけではありません。そんなことばかり考えていたら窮屈で生きづらくなります。
夜寝る前に歯磨きしよう、というのを変に意識しませんよね。ただ歯は磨く。それに近いです。
毎日唱えたり、アウトプットすることについて考えたりしているわけではありません。ただどこかしらでアウトプットはする、しないとなんだかムズムズが収まらない、そんな感覚です。
定期的に
「自分はインプットしているだけで何か知的な生産活動をできていないのではないか?」
と疑問を問いかけ、もしそれがYes、つまり知的生産活動ができていないのなら、アウトプットすることをお勧めします。
おわりに
今回は自己啓発的な形で、プログラムが完成されるまでの裏話を語りました。
不定期で今後も記事を執筆していこうと思います。
最後までご覧いただきありがとうございました。
