GoogleAppsScriptでメンションする

# やりたいこと
GoogleAppsScriptを使って自分宛へのメンション
![スクリーンショット 2022-08-31 19.44.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565925/698b1d8e-1b65-c462-6b51-461f85d46bf9.png)

応用すれば別のユーザーへのメンションにも使えるので、
特定のメールが来たら、特定のユーザーにメンションを飛ばすとかが簡単に実装できます。

# コード

`@google/clasp`を使ってローカル環境でTypeScriptで記載します.
型定義は`@type/google-apps-script`を使います
(GASコードも後ろの方で記載します)

## メンション用のユーザーIDを取得する

公式リファレンスからGUIでも取得することができます
[Method: people\.get  \|  People API  \|  Google Developers](https://developers.google.com/people/api/rest/v1/people/get?apix_params=%7B%22resourceName%22%3A%22people%2Fme%22%2C%22personFields%22%3A%22metadata%22%7D)

↑のリンクを使えば各項目は記載されていますが、一応スクショも記載
![スクリーンショット 2022-08-31 20.12.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565925/ec126abc-82ed-992b-8b8b-bee4d6e1f22f.png)


GASで取得するとこんな感じ
**※マニフェストのスコープを手動で変更する必要があります**

`oauthScopes`の一つ目はPeopleAPIでmetadataを取得するため、
二つ目はURLFetchを使うためです。

```json:appsscript.json
{
  "timeZone": "Asia/Tokyo",
  "dependencies": {
    "enabledAdvancedServices": [
      {
        "userSymbol": "People",
        "version": "v1",
        "serviceId": "peopleapi"
      }
    ]
  },
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "oauthScopes": [
    "https://www.googleapis.com/auth/userinfo.profile",
    "https://www.googleapis.com/auth/script.external_request"
  ]
}

```

metadata以外のフィールドの取得や、resourceName以外の値については省きます。
↑で記載しているGUIで見た方が早いです

```typescript:main.ts
/**
 * メンション用のUserIDを取得する
 * @returns {string|undefined} userのIDを取得する
 */
const getUserId = (): string | undefined => {
  if (People.People) {
    const me = People.People?.get("people/me", { personFields: "metadata" });
    const { resourceName } = me;
    if (resourceName) {
      const matchItem = resourceName.match(/^people\/(?<id>[^/]+)$/);
      if (matchItem) {
        const id = matchItem.groups?.id;
        return id;
      } else console.warn(`resourceNameが/^people\/[^/]+$/に一致しませんでした\nresourceName: ${resourceName}`);
    } else console.warn(`resourceNameが取得できませんでした`);
  } else console.warn(`People.Peopleが存在しませんでした`);
  return undefined;
};
```

## メンション用の関数

```typescript:main.ts
/**
 * UserID宛にメンションする
 * @param {string} userId - メンションするUserID
 * @param {string} botURL - 送信するBOTのWebhookリンク
 */
const notify = (userId: string, botURL: string): void => {
  const message = {
    text: `俺だよ、俺、<users/${userId}>だよ`,
  };
  const param: GoogleAppsScript.URL_Fetch.URLFetchRequestOptions & { method: GoogleAppsScript.URL_Fetch.HttpMethod } = {
    method: `post`,
    headers: {
      "Content-Type": "application/json; charset=UTF-8",
    },
    payload: JSON.stringify(message),
  };
  UrlFetchApp.fetch(botURL, param);
};
```


## 実行用のメイン関数

`hookURL`のところはスペースで作ったWebhookのリンクを使ってくださいね
![スクリーンショット 2022-08-31 20.01.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565925/4b7b0352-9ad5-58b2-4f4e-e4fa673f2adc.png)


```typescript:main.ts
/**
 * 実行用の関数
 */
const main = (): void => {
  const hookURL = `https://chat.googleapis.com/v1/spaces/SpaceID/messages?key=BotKeys&token=BotToken`;
  const userId = getUserId();
  if (userId) notify(userId, hookURL);
};
```

## コピペ用

### TypeScript

<details>
<summary>コード</summary>
<div>

```json:appsscript.json
{
  "timeZone": "Asia/Tokyo",
  "dependencies": {
    "enabledAdvancedServices": [
      {
        "userSymbol": "People",
        "version": "v1",
        "serviceId": "peopleapi"
      }
    ]
  },
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "oauthScopes": [
    "https://www.googleapis.com/auth/userinfo.profile",
    "https://www.googleapis.com/auth/script.external_request"
  ]
}

```

```typescript:code.ts
/**
 * 実行用の関数
 */
const main = (): void => {
  const hookURL = `https://chat.googleapis.com/v1/spaces/SpaceID/messages?key=BotKeys&token=BotToken`;
  const userId = getUserId();
  if (userId) notify(userId, hookURL);
};

/**
 * メンション用のUserIDを取得する
 * @returns {string|undefined} userのIDを取得する
 */
const getUserId = (): string | undefined => {
  if (People.People) {
    const me = People.People?.get("people/me", { personFields: "metadata" });
    const { resourceName } = me;
    if (resourceName) {
      const matchItem = resourceName.match(/^people\/(?<id>[^/]+)$/);
      if (matchItem) {
        const id = matchItem.groups?.id;
        return id;
      } else console.warn(`resourceNameが/^people\/[^/]+$/に一致しませんでした\nresourceName: ${resourceName}`);
    } else console.warn(`resourceNameが取得できませんでした`);
  } else console.warn(`People.Peopleが存在しませんでした`);
  return undefined;
};

/**
 * UserID宛にメンションする
 * @param {string} userId - メンションするUserID
 * @param {string} botURL - 送信するBOTのWebhookリンク
 */
const notify = (userId: string, botURL: string): void => {
  const message = {
    text: `俺だよ、俺、<users/${userId}>だよ`,
  };
  const param: GoogleAppsScript.URL_Fetch.URLFetchRequestOptions & { method: GoogleAppsScript.URL_Fetch.HttpMethod } = {
    method: `post`,
    headers: {
      "Content-Type": "application/json; charset=UTF-8",
    },
    payload: JSON.stringify(message),
  };
  UrlFetchApp.fetch(botURL, param);
};

```
</div>
</details>

### GAS

<details>
<summary>コード</summary>
<div>

```json:appsscript.json
{
  "timeZone": "Asia/Tokyo",
  "dependencies": {
    "enabledAdvancedServices": [
      {
        "userSymbol": "People",
        "version": "v1",
        "serviceId": "peopleapi"
      }
    ]
  },
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "oauthScopes": [
    "https://www.googleapis.com/auth/userinfo.profile",
    "https://www.googleapis.com/auth/script.external_request"
  ]
}

```

```javascript:code.gs
/**
 * 実行用の関数
 */
const main = () => {
  const hookURL = `https://chat.googleapis.com/v1/spaces/SpaceID/messages?key=BotKeys&token=BotToken`;
  const userId = getUserId();
  if (userId) notify(userId, hookURL);
};

/**
 * メンション用のUserIDを取得する
 * @returns {string|undefined} userのIDを取得する
 */
const getUserId = () => {
  if (People.People) {
    const me = People.People?.get("people/me", { personFields: "metadata" });
    const { resourceName } = me;
    if (resourceName) {
      const matchItem = resourceName.match(/^people\/(?<id>[^/]+)$/);
      if (matchItem) {
        const id = matchItem.groups?.id;
        return id;
      } else console.warn(`resourceNameが/^people\/[^/]+$/に一致しませんでした\nresourceName: ${resourceName}`);
    } else console.warn(`resourceNameが取得できませんでした`);
  } else console.warn(`People.Peopleが存在しませんでした`);
  return undefined;
};

/**
 * UserID宛にメンションする
 * @param {string} userId - メンションするUserID
 * @param {string} botURL - 送信するBOTのWebhookリンク
 */
const notify = (userId, botURL) => {
  const message = {
    text: `俺だよ、俺、<users/${userId}>だよ`,
  };
  const param = {
    method: `post`,
    headers: {
      "Content-Type": "application/json; charset=UTF-8",
    },
    payload: JSON.stringify(message),
  };
  UrlFetchApp.fetch(botURL, param);
};
```
</div>
</details>

# 結果

`main`を実行して、スコープを認証すれば通知が行われます。
![スクリーンショット 2022-08-31 19.44.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565925/698b1d8e-1b65-c462-6b51-461f85d46bf9.png)

# 他ユーザーへのメンション

自分以外のUserIDについては管理者ロールを持っていれば取得できそうですが、
私は持っていないため、取得方法は不明。
なので、一般権限のユーザーが、他のユーザーへのメンションを作成する場合は、
前もってメンションするユーザーにIDを取得しておいてもらう必要があるので注意が必要です。
（私の場合は、各自にWebAppにアクセスしてもらい、裏でIDを取得してスプレッドシートに記載されるような仕組みにしました）

::: note info

試していませんがAdmin SDK APIでも取得できるらしいです
こっちだとメールアドレスで取得できるからこっちの方でしょう、絶対！
[Google Chat botがメンションするのに必要なUser IDを開発者コンソールに出力するスクリプト \- Qiita](https://qiita.com/takatama/items/9029ceef857516363c32#admin-sdk-api--apps-script%E3%82%92%E4%BD%BF%E3%81%86)

:::

# まとめ

ということでお手軽にメンションすることができましたね。
metadataを取得するために、`oauthScopes`を自分で記載する必要があることがミソでしょうか。
普段は自動でスコープを与えてくれている分、
いざFetchを使ったり、SpreadSheetを使うと「権限が足りん！」って怒られちゃうので、
その辺は割と面倒だったり。。。
とはいえ、PeopleAPIなんかは特に適当にロール付与しまくるのは怖いので、
ロール管理は学んでおいた方がいいのかもしれない。。。