【Raycast】ランチャーアプリからGoogleカレンダーを操作する【React】

# はじめに
早いもので､もう3年生になりました｡作業効率化が好きな@7wataaaです｡
[N/S高等学校 Advent Calendar 2022](https://qiita.com/advent-calendar/2022/n-s-highschool)の19日目記事です｡
この記事では､Raycastというアプリの拡張機能を作成する方法について解説してみようと思います｡
拙い記事ですがご容赦ください🙇

# ランチャーアプリRaycast

[Raycast](https://www.raycast.com/)は無料のランチャーアプリです｡

使い心地が良くて､最近とても気に入っています｡

https://zenn.dev/yum3/articles/i_raycast_or_alfred

https://zenn.dev/toono_f/articles/45d2a49650ebf9

# 拡張機能の開発について

Raycastでは､コマンドを新たに自作したり､[Store](https://www.raycast.com/store)からインストールしたりして機能を拡張することができます｡

拡張機能の開発は、こちらの公式ドキュメントを参考に進めました｡

https://developers.raycast.com/

ReactとTypeScriptを使って開発することできるようですね｡
スタイルを自分で作成する必要はなく､提供されるコンポーネントを組み合わせていく形で開発できました｡

# Google Calendar APIでイベントを追加する

今回はRaycastから自分用のGoogle Calendarに予定を追加する機能を開発してみたいと思います｡

(Raycastの開発だけご覧になりたい方は[ここ](#インストールから起動まで)まで飛ばしてください)  

イベントの追加には､Calendar APIのquickAddなるものを使ってみました｡

https://developers.google.com/calendar/api/v3/reference/events/quickAdd

```http
POST /calendar/v3/calendars/calendarId/events/quickAdd HTTP/1.1
Content-Type: application/json
Host: www.googleapis.com
Content-Length: 23

{
  "text": "Meeting"
}
```

bodyに予定の文字列を付与することでカレンダーに追加できるようです。

日時も､
```pl
"12/19 1400-1500 Meeting"
```
とすることで指定できるようです｡
(細かい仕様がわからないのでご教授いただけたら嬉しいです｡)

あとはアクセストークンを用意し､Bearerの形式で一緒に送ります。

# GCPでOAuthの設定をする

クライアントの作成と､スコープの設定を行います｡

[PKCEフローのために､iOSのクライアントを使用する必要があるよう](https://github.com/raycast/extensions/blob/main/examples/api-examples/src/oauth/google.tsx#:~:text=//%20Create%20an%20OAuth,enter%3A%20com.raycast)です｡
![oauth-client.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643955/62c45b90-581a-617a-810d-79d93eec805a.png)

今回必要なスコープはこちら↓
![oauth-scope.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643955/afbabf60-ccaf-35cc-bf03-8a32b773bf83.png)

これでGCPの設定は完了です｡

アクセストークンは､[公式ドキュメント](https://developers.raycast.com/api-reference/oauth?q=oauth)を参考にするか､[API ExampleのOAuth機能](https://github.com/raycast/extensions/blob/main/examples/api-examples/src/oauth/google.tsx)を少し改変するだけで取得できそうです｡

それでは､Raycast側の開発に移っていきます｡

# インストールから起動まで

公式ドキュメントを参考に進めました｡

https://developers.raycast.com/basics/getting-started

デフォルトで用意されている拡張機能作成用のコマンドでプロジェクトを作成します｡
![create-extension.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643955/5d3bb812-5106-84bf-e4d9-d7d43e0a344d.png)

いろんなテンプレートが用意されているようです｡
"Detail"を選択してみます｡
![create-extension-templates.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643955/0c29d188-ba29-506d-6b64-2a6e95a4e1d3.png)

作成完了です｡
![create-extension-success.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643955/cfb91ea1-b821-577f-7a4e-742a164cbc69.png)

コマンドを起動すると､こんな画面になりました｡
![create-extension-detail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643955/4d5c129d-1844-a46d-9180-7bf2db170474.png)

生成された`index.tsx`はこんな感じ｡
```tsx
import { Detail } from '@raycast/api';

export default function Command() {
  return <Detail markdown="# Hello World" />;
}
```

ここではDetailコンポーネントが使用されていますが､
他にも様々なコンポーネントが用意されており､API Examplesを起動することで確認できます｡
いろんなコンポーネントがあるので眺めるだけでも楽しいです｡

https://github.com/raycast/extensions/tree/main/examples/api-examples

# 必要なUIを作る

[Preferences](https://developers.raycast.com/api-reference/preferences)という機能を使って､カレンダーIDを保存し､
![extension-setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643955/e4db6291-2224-d324-dc58-3a295d0024c9.png)

サインインします､､

![extension-oauth.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643955/7339600d-ef2b-9442-179d-fa18101888a9.png)

アカウントを選択し､､

![extension-oauth-browser.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643955/cea8fc84-06c6-59ae-db37-b90733d25f0f.png)

認証できました🎉

![extension-auth-success_Moment.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643955/ca641385-a53e-1fe6-302d-a45a7017e74b.jpeg)

続いて､quickAddのtextを入力するフォームを作る必要があります｡

![extension-quickadd.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643955/54271a23-b83c-222e-c8f3-a1c527993c6e.png)

この部分のコードはこんな感じになりました。

```tsx
export const QuickAddPage = () => {
  useEffect(() => {
    authorize();
  }, []);

  return (
    <Form
      actions={
        <ActionPanel>
          <Action.SubmitForm
            title="Submit"
            onSubmit={async (values: QuickFieldValues) => {
              // textを受け取れるので、ここからリクエストを送信する
            }}
          />
        </ActionPanel>
      }
    >
      <Form.TextField
        id="quickAddField"
        title="QuickAdd"
        placeholder="e.g., Meeting with X next tuesday at 3pm"
        autoFocus
      />
      <Form.Separator />
    </Form>
  );
};
```
スタイルを考えなくて良いため簡単です。

残りのリクエスト部分は、
```tsx
export const insertEventToGoogleCalendar = async (calendarId: string, params: QuickAddParams) => {
  await authorize();

  const quickAddURL = `https://www.googleapis.com/calendar/v3/calendars/${calendarId}/events/quickAdd`;

  const res = await axios.post<calendar_v3.Schema$Event>(quickAddURL, params, {
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${(await client.getTokens())?.accessToken}`,
    },
  });

  return res;
};
```
このように実装できました。
使い慣れたTypeScriptを使えるのはやっぱりいいですね。

# 完成

![extension-complete.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643955/32ab96e5-2b22-2185-2cfd-3be2129cb938.gif)

無事完成させることができました。

# おわりに

本記事では、実際にRaycastの拡張機能としてGoogleカレンダーに予定を追加する機能を実装してみました。
Raycastはデベロッパーフレンドリーであり、アプリ自体のUXもかなりいいのでおすすめです。

そして今回はReact & TypeScriptで拡張機能を実装しましたが、簡単なコマンドであれば手軽にShellScript等でも書くことができます。[例: 自作コマンド](https://github.com/7wataaa/raycast-commands)

みなさんもぜひ拡張機能を使いこなして面倒な作業から開放されましょう。

ここまで読んでくださりありがとうございました！

[今回のリポジトリ](https://github.com/7wataaa/post2googlecalendar)