n8nとChrome拡張で地味に面倒くさいGoogleカレンダー登録を楽チンにした話

## はじめに
世の中って地味に面倒臭いことが多いですよね〜〜

Googleカレンダーのイベント登録はその一つだと思います。
予定が決まったら、Googleカレンダーを開いて、タイトルと日時をぽちぽち入力して登録しなければなりません。

私は大のめんどくさがり屋なので、その作業を怠って予定をブッキングさせてしまうことも多々あります...。

この記事では、そんな面倒臭い作業を「**n8n**」というワークフロー自動化ツールと**Chrome拡張**を使って楽チンに行えるツールを開発した話を書いていきます！

n8nを使ってみたい方もぜひ読んでみてください！


## 作ったもの

脱出ゲームの予約をカレンダーに登録する例を紹介します！

Webサイトに表示されている予約日時を選択して、ショートカットキー（Cmd + Shift + G）を押すと、、、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950277/7d637ad8-46e5-dba3-a788-6f57e4c08b8e.png)


以下のようにGoogleカレンダーに登録されます！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950277/8840784e-c8ab-7a43-d63b-402771399bb0.png)



また、**柔軟な入力に対応できる**ため、以下のようなGoogleフォームや、


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950277/509fc475-1b73-b35d-0568-fb632c45f98c.png)

Gmailの予約完了画面などでも使えます！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950277/f605ddea-9a48-c9ab-02a6-8aa27ad4000e.png)

## どうなってんの？
### Chrome拡張部分

割とシンプルです。以下の処理を行なっています。

1. ショートカットを検知
1. 文字選択がされている部分を含むhtml要素を検出
1. n8nで設定しているエンドポイントに送信

リポジトリはこちらになります。

https://github.com/madomaru/QuickCal

拡張機能として公開はしていないので、もし使いたい場合は開発者モードで試してみてください。
（方法はREADME参照）


### n8n部分
ワークフローは以下のようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950277/44da30f0-d804-7769-6933-b2d599ec133f.png)

それぞれのノードについて簡単に解説すると、

**①Webhookノード**：Chrome拡張から送信されたデータをPOSTメソッドで受け取ります

**②OpenAI Message Modelノード**：html要素からカレンダーイベントをJSON形式で抽出します

ここでLLMを使っているため、柔軟な入力に対応できます！

<details><summary>OpenAIで用いたプロンプト詳細</summary>

```text
以下のHTMLから、フォームで選択されているイベント情報を抽出してください。未選択の情報は結果に含めないでください。

### 出力フォーマット
出力はJSON形式で、各イベントを以下のフィールドに基づいて記載してください。
- `start`: イベントの開始日時（YYYY-MM-DD HH:MM:SS形式）。
- `end`: イベントの終了日時（YYYY-MM-DD HH:MM:SS形式）。
- `title`: イベントのタイトル（可能な場合）。
- `additional_info`: フォーム内で特定のイベントに関連する補足情報（該当する場合）。

### 特記事項
1. フォームで「選択済み」の情報のみを出力してください。未選択の情報やデフォルト表示は結果に含めないでください。
2. 情報が不足している場合は、`null`を指定してください。

### 出力例
以下は、選択された情報に基づく例です：

```json
[
  {
    "start": "2025-01-19 13:00:00",
    "end": "2025-01-19 16:00:00",
    "title": "イベント1",
    "additional_info": null
  },
  {
    "start": "2025-01-28 13:00:00",
    "end": "2025-01-28 16:00:00",
    "title": "イベント2",
    "additional_info": "オンライン開催"
  }
]
## 入力HTML
{{html_body}}

## 注意事項
- フォームの選択状態を考慮し、選択されていない情報は除外してください。
- JSON形式以外の出力を含めないでください。
- 情報が不足している場合やフォーマットが異なる場合でも柔軟に処理してください。
- 終了時間がわからない場合は、開始時間から1時間後にしてください
- 可能な限り、予定をこなすのに必要だと思われる追加情報や、選択されたテキストの情報をadditinal_infoに入れるようにお願いします。予約番号や必要なURLや持ち物や場所などです。記述内容を省略しないでください。記述がなければ大丈夫です。
```
</details>


**③Codeノード**：OpenAIが出力するのは以下のようなJSONであり、イベントの情報部分が構造化されていないため、JavaScriptで整形する必要があります。

コーディング初心者の方は難易度が高いと感じるかもしれませんが、「Ask AI」ボタンを押すと、どのようなコードを書いて欲しいかAIに命令するだけで、コードを生成してくれます！（もちろん別の生成AIを用いてもいい！）

<details><summary>OpenAI Message Modelから出力されるJSON</summary>

```json
[
    {
        "index": 0,
        "message": {
            "role": "assistant",
            "content": "```json\n[\n {\n "start": "2025-01-25 11:40:00",\n "end": "2025-01-25 12:40:00",\n "title": "海賊王が遺した部屋からの脱出",\n "additional_info": "null"\n }\n]\n```",
            "refusal": null
        },
        "logprobs": null,
        "finish_reason": "stop"
    }
]
```

</details>

**④Split Outノード**：以下のように複数のイベントが抽出された際に、1つ1つの要素を分けて次のノードに送ってくれます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950277/e7699884-9c8d-7b12-7fd8-9c1eef570dfb.png)

（こんな感じで1itemから4itemに分けてくれている）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950277/42dd7333-ed49-6a0d-a428-4d6b5cf7f696.png)

**⑤Google Calendar create:eventノード**：Googleカレンダーに登録します

自分のGoogleアカウントとの連携もとても簡単です！以下のボタンからGoogleにログインするだけで完了！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950277/1c37c87b-40d1-086b-9393-eacfc63e4107.png)

また、登録に必要なパラメータも前のノードの内容が表示されている左の部分からドラッグ&ドロップするだけで設定できます！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950277/39343c68-9e0c-d785-179c-a1743af09ce4.png)

**⑥Gmail send messageノード**：最後にちゃんと登録できたことを通知するために、自分宛にメールを送るようにしています


## 追加機能

**Webページにない情報を補足としてLLMに入れられるようにしました！**

具体的な流れとしては、ショートカットキーを押したら以下のようなウィンドウを表示させ、テキストを入力後送信できるようにしました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950277/c2b230a3-287a-b0a0-2d1f-eccc34d60619.png)

## おわりに

今回作った、HTML要素を読み取りエンドポイントに送信する拡張機能 & n8nの組み合わせは、さまざまな応用が効くと思います。

例えば、Webページの内容を要約してNotionに登録することも可能です！

この記事を読んで、「試したい！」「地味に面倒くさいを解消したい！」と思った方はぜひやってみてください！