はじめに
hkob の雑記録の第500回目(連続73日目)からは、moodle で課題 (Assignment) が作成された瞬間に外部 (Notion Worker) へ Webhook を投げて Notion にタスクを作成する仕組みについて解説します。
事前設計
すでに実装に入る前に以下のような設計書を Notion AI に作成してもらっていました。
目的
Moodle(FW内)で「課題(Assignment)が作成された瞬間」に、外部(Notion Worker)へ Webhook を投げて Notion にタスクを作成する。
二部構成
- 第1部: Notion Webhook Worker 作成編(外部で受信して Notion Tasks を作る)
- 第2部: Moodle local_ プラグイン作成編(課題作成イベントを捕捉して Webhook を投げる)
全体アーキテクチャ(概要)
- Moodle(FW内)
- Event observer で「課題作成」を捕捉
- Webhook URL に JSON を POST(署名付き)
- Worker(外部で到達可能)
- Webhook を受信
- 署名検証
- Notion Tasks DB に 1 件追加
- 冪等性(推奨)
site + assign_idをキーに二重作成を防止(Notion 側に External ID 的なプロパティを用意するか、Worker 側に保存)シーケンス図(課題作成 → Notion タスク作成)
シーケンス図
この下には具体的な手順などが書かれていたのですが、今回のものと被るので省略します。
実装開始
ここから実装を開始していきます。Claude code に投げれば動くところまで全部やってくれるのでしょうが、ブラックボックスで中身を理解しないで使うのは気持ち悪いので、Notion AI とペアプロで作成することにしました。まず、実装ページを作成し、以下のように投げてみました。

普通にチャットに結果を書かれてしまったので、作業記録にしたい旨を説明しました。

書いてもらって実行しようとしたらディレクトリ名が間違っているのに気づいて、すぐに作業記録の修正をお願いしました。

workers new をした結果、作業記録に残して欲しかったので、以下のようにお願いしました。

ntn のバージョンが上がっていますね。update しておきました。

index.ts の記述
次に index.ts を書いてもらいます。まだ未決だったプロパティをどうするか相談してきました。

逆に冪等性のためのプロパティが足りないといっています。これは INSTRUCTION.md にも解説がありましたが、プロパティを作成するべきですね。

ということで、当然 A でお願いしました。「プロパティを作成してください」だけで、Notion のデータソースにプロパティが生成されるのは楽でいいですね。

設定するプロパティは現在の Alfred のものと同じで、Title, Link, Assignee だけでした。しかも Assignee は私固定なので、受け取るのは Title と Link だけです。

この後、TypeScript の設定周りでゴタゴタしましたが、ここでは関係ない話なので省略します。次は secrets の設定です。以下のように指示されましたが、実際は間違っています。

SECRET の作成
実際に MOODLE_WEBHOOK_SECRET の値をどう作ればいいか質問してみました。生成例のところにはいくつかのパターンが紹介されていました。

今回は、ここにあるように base64 で作成しました。.env に入れて push することもできますが、ファイルの扱いにミスしないように env set の方が適切のようですね。値自体は Notion に記録してあります。

実際に env set したら、deploy を先にやるように指摘されました。作業記録も勝手に直してくれるのは嬉しいですね。

src 修正と deploy
deploy したらエラーが出まくったので、webhook-example.ts を渡して修正してもらいました。やはり新しい仕組みなので、ちゃんと例題を渡してあげないとダメでしたね。

流石に見本を見せたらちゃんと deploy できました。

SECRET の保存
早速、MOODLE_WEBHOOK_SECRET を保存しました。これは問題なく成功しています。

次の環境変数を設定しようとした時に DATABASE_ID と書かれていることに気づきました。直してもらうように指示しました。

ソースを確認しましたが、まだ query の部分に databases が残っていました。間違っていると指摘したのですが、これは正しいと主張しています。面倒なので後で証拠を見せて間違いを理解してもらうことにしましょう。

早速、data_source_id を設定しようとしたら NOTION_TASKS_DATA_SOURCE_ID の設定に失敗しました。NOTION_ で始まる環境変数は env set では設定できないそうです。

早速修正した環境変数にしたら成功しました。

user_id も登録しています。

テスト実施
まず、curl でテストをしてみます。スクリプトを作成してもらいました。

このスクリプトの実行結果を見ると 202 は返ってきましたが、ページは作成されませんでした。ここでログを確認します。

デバッグ
log を確認したところ、NOTION_API_TOKEN がセットされていないと書かれていました。そういえば、493 回目のブログで以下のように webhook の場合には事前認証されないと書いてあったのを思い出しました。
すべての execute ハンドラは context.notion オブジェクト(@notionhq/client の SDK インスタンス)を受け取る。これを使って Notion API を呼び出せる。ただし、context.notion が 事前認証済み になるのは、Custom Agent から呼び出された tool capability の場合のみ。その場合、プラットフォームが Custom Agent の権限で NOTION_API_TOKEN を自動設定するため、追加設定は不要。それ以外(sync / automation / webhook)の capability では context.notion は 事前認証されない。ユーザーが自分で NOTION_API_TOKEN 環境変数を設定する必要がある
ということで、NOTION_API_TOKEN を設定します。Notion AI は NOTION_ で始まる env が設定できないことをちゃんと覚えていました。

ということで、.env に記述して env push するように指示してきました。その通りにします。

これをやってもエラーになりました。やはり指摘したように databases.query で失敗しています。この databases.query が失敗した証拠を掴み、間違いを理解させました。ちゃんとソースを修正してくれたので、deploy しなおしてテストします。

成功を確認
これによって、無事に動作していることを確認しました。同じ curl を再度送っても二重化せず冪等性を保っていることも確認できました。

おわりに
Claude Code で中身もわからずに全部作ってもらうのと違い、自分でも中身が理解できるので、Notion AI とのペアプログラミングも面白いなと感じました。こうやって作っておけば、後で拡張したくなった時にも、この文書を見ながらまたペアプログラミングを続けていけますね。ドキュメントが綺麗に残るのが本当に助かります。明日は、moodle のプラグインを同じように作成していこうと思います。今度は php ですね。
https://hkob.notion.site/hkob-16dd8e4e98ab807cbe3cf3cc94cdfe0f?pvs=4hkob.notion.site
