moodle webhook の実装 : hkob の雑記録 (500)

はじめに

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 を投げる)

全体アーキテクチャ(概要)

  1. Moodle(FW内)
    • Event observer で「課題作成」を捕捉
    • Webhook URL に JSON を POST(署名付き)
  2. Worker(外部で到達可能)
    • Webhook を受信
    • 署名検証
    • Notion Tasks DB に 1 件追加
  3. 冪等性(推奨)
    • site + assign_id をキーに二重作成を防止(Notion 側に External ID 的なプロパティを用意するか、Worker 側に保存)

シーケンス図(課題作成 → Notion タスク作成)

シーケンス図

この下には具体的な手順などが書かれていたのですが、今回のものと被るので省略します。

実装開始

ここから実装を開始していきます。Claude code に投げれば動くところまで全部やってくれるのでしょうが、ブラックボックスで中身を理解しないで使うのは気持ち悪いので、Notion AI とペアプロで作成することにしました。まず、実装ページを作成し、以下のように投げてみました。

手順の記載依頼

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

作業記録に転記依頼

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

ディレクトリ名の修正依頼

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

workers new

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

ntn update

index.ts の記述

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

プロパティの検討

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

冪等性のためのプロパティ

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

External ID プロパティを作成

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

設定するプロパティの選択

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

次の手順

SECRET の作成

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

MOODLE_WEBHOOK_SECRET の提案依頼

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

作成方法と env set

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

正しい順序に変更

src 修正と deploy

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

テンプレートに含まれる example でソースを修正依頼

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

deploy 成功

SECRET の保存

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

MOODLE_WEBHOOK_SECRET の保存

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

database_id から data_source_id への変更

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

query の修正依頼 (失敗)

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

NOTION_ で始まる環境変数は設定不可

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

NOTION_ を外したら成功

user_id も登録しています。

user_id 環境変数を設定

テスト実施

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

テスト用 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 が設定できないことをちゃんと覚えていました。

NOTION_API_TOKEN の設定

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

.env 記述 → env push

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

data_sources.query に変更

成功を確認

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

成功した run 一覧

おわりに

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

https://hkob.notion.site/hkob-16dd8e4e98ab807cbe3cf3cc94cdfe0f?pvs=4hkob.notion.site