[Gather.town]Denoでチャット内にオウム返しBOTを作成

- リモートコミュニケーションツールとして[Gather](https://gather.town/)を利用しています。
- Gatherは基本機能以外の仕掛けが沢山あり、その中の1つに[Socket API](http://gather-game-client-docs.s3-website-us-west-2.amazonaws.com/classes/Game.html)があり、プログラムでGather上の操作が可能です。
- 今回はそちらと[Deno](https://deno.com/)を利用してGatherのチャット内に簡単なBOTを実装する方法を記録いたします。

## 結果
- 成果物として、以下のようにチャット内でオウム返しするBOTを実装します。

![gather-chat.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/332c01d8-c71c-97a5-b002-41c7bb6ed2e1.png)

## 環境
- macOS 13.0
- deno 1.36.0
    - **※未インストールの場合は[こちら](https://deno.com/manual@v1.36.0/getting_started/installation)**

## 準備
- 操作する対象の[Gather](https://www.gather.town/)のスペースを用意してください。
- 用意後、そのスペースの**スペースID**及び**スペース名**をメモしておいてください。
    - **※スペースIDはスペースURL(https://app.gather.town/app/xxxx/yyyy)の「xxxx」です。**
    - **※スペース名はスペースURL(https://app.gather.town/app/xxxx/yyyy)の「yyyy」です。**

## 手順

### BOT用アカウント作成
- 対象のGatherスペースに別のメールアドレスでBOT用アカウントとして参加してもらいます。
- 参加後、対象のスペースに管理者アカウントで参加して設定画面でメンバーを確認します。
    - **※アカウント権限は「Mapmaker」以下で問題ありません。**

![gather-member.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/d9a6de6e-9866-5c35-2a43-65c5e2015a16.png)

### APIキーの取得
- APIキーを取得するために https://app.gather.town/apikeys に**BOTアカウント**にてアクセスします。
- 以下の「**generate new key**」ボタンをクリックするとAPIキーが生成されます。

![gather-generate-apiKey.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/9995b6b9-9dfd-edfd-85b5-974c469ade43.png)

- 生成後に以下の画面が表示されるため、**赤枠で囲んでいる部分のキーをメモしておきます。**

![gather-apiKey.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/d56f2634-49b9-206a-6c49-ebd0e6c2bd42.png)

### プログラム記述
- スペース情報及びAPIキーが揃ったのでプログラムを書いていきます。
- ローカルの任意のディレクトリに**jsファイル及び環境変数ファイル**を作成します。

```shell
# 任意のjsとenvファイルを作成
touch app.js .env
```

- 作成後、`app.js`の中身を以下のように記述します。

```javascript
import { Game } from "npm:@gathertown/gather-game-client@42.0.0";
import { load } from "https://deno.land/std@0.197.0/dotenv/mod.ts";
const env = await load();

// 設定情報
const BOT_NAME = "TOD_BOT";
const API_KEY = env["API_KEY"];
const SPACE_ID = env["SPACE_ID"];
const SPACE_NAME = env["SPACE_NAME"];
const SPACE_INFO = `${SPACE_ID}\\${SPACE_NAME}`;

// 初期化
const game = new Game(SPACE_INFO, () => Promise.resolve({ apiKey: API_KEY }));

// 接続
game.connect();

// ボット参加
game.enter({
  name: BOT_NAME,
  isNpc: true,
});

// 接続時の処理
game.subscribeToConnection((connected) => {
  console.log(`接続可否: ${connected}`);
});

// チャット動作時の処理
// イベント: playerChats
// URL: http://gather-game-client-docs.s3-website-us-west-2.amazonaws.com/classes/Game.html#subscribeToEvent
game.subscribeToEvent("playerChats", (data) => {
  // 「/todbot」で発火
  const contents = data.playerChats.contents;
  if (!contents.startsWith("/todbot ")) {
    return;
  }

  // 「/todbot」より右を抽出。
  const text = contents.replace(/\S+\s+/, "");
  console.log(text);

  // 返信
  // URL: http://gather-game-client-docs.s3-website-us-west-2.amazonaws.com/classes/Game.html#chat
  game.chat(
    data.playerChats.recipient,
    [],
    data.playerChats.roomId || "",
    {
      contents: text,
    },
  );
});
```

- 記述後、上記でメモした**スペースID・スペース名・APIキー**を`.env`に記述します。

```conf
SPACE_ID=スペースID
SPACE_NAME=スペース名
API_KEY=APIキー
```

### 実行
- 記述後、以下のコマンドを実行します。

```shell
deno run -A app.js
```

- 実行後、**BOTアカウント以外のユーザー**でアクセスして、以下のように**オウム返しBOTが動作している**ことを確認します。

![gather-player.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/8cbfecaa-3719-5327-cef9-08c2a1ddfe31.png)

![gather-chat.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/332c01d8-c71c-97a5-b002-41c7bb6ed2e1.png)

- 以上です。

## 補足
- 他の操作の情報は[こちらのサイト](http://gather-game-client-docs.s3-website-us-west-2.amazonaws.com/classes/Game.html)から確認できます。

## 参考
- [Gather Town](https://gather.town/)
- [Gather Socket API](http://gather-game-client-docs.s3-website-us-west-2.amazonaws.com/classes/Game.html)
- [Denoでのnpmモジュール利用](https://deno.com/manual@v1.33.4/node)