[ TypeScript/JavaScript ] LambdaからSlackへカスタム通知を送る方法

# はじめに
この記事は、[ミロゴス Advent Calendar 2023](https://qiita.com/advent-calendar/2023/milogos) 22日目の投稿です。

業務でLambdaからSlackへAWS Chatbotのカスタム通知を送る実装を行ったので、実際のコードとポイントを紹介していきたいと思います。
既にSlackチャンネルと紐づいたSNSトピックを作成済みの前提で、カスタム通知をLambdaから送信する方法を記述していきます。

# カスタム通知とは
Chatbotのカスタム通知は通常の通知とは異なり、ユーザー側でメッセージをカスタマイズすることが可能です。

今まではSlackで任意のメッセージ通知を行いたい場合、LambdaとSlackのIncomming Webhook機能を組み合わせて使うことが主流だったかと思います。
Chatbotのカスタム通知機能の登場により、Slackと紐づいたSNSトピックを準備するだけで任意のメッセージを送ることが可能になりました。

# 実行環境

- **AWS CDK**
  - パッケージ: `aws-cdk`
  - バージョン: `2.110.1`

- **AWS CDK ライブラリ**
  - パッケージ: `aws-cdk-lib`
  - バージョン: `2.110.0`

- **AWS SDK**
    - パッケージ: `aws-sdk`
    - バージョン: `2.1304.0`

- **TypeScript**
    - パッケージ: `typescript`
    - バージョン: `4.8.4`

- **Lambda Node.js**
    -  バージョン:`18.x`


# 実装の紹介
LambdaからSlackへカスタム通知を送るに当たり必要な実装を紹介します。

## Lambda関数の作成
まずは呼び出すLambdaの作成です。

Lambdaを作成するに当たり、下記の二つが必要になります。
- LambdaからSNSへのアクセス権限を設定する
- SNSのTopic ARNを環境変数として渡す

SNSトピックの作成、SNSとChatbotを紐付けてSlackに通知できるようにするには別途設定が必要になります。
今回は作成済みのSNSからTopic ARNを引用する想定で記載しています。
```ts
import * as cdk from 'aws-cdk-lib';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as nodejs from 'aws-cdk-lib/aws-lambda-nodejs';

export default class LambdaConstruct extends Construct {
  constructor(
    scope: Construct,
    id: string,
    topicArn: string
  ) {
    super(scope, id);
    
    const customRole = new iam.Role(this, 'S3DownloaderRole', {
      assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSNSFullAccess'),
      ],
        }),
      },
    });

    new nodejs.NodejsFunction(
      this,
      'lambda',
      {
           role: customRole,
           environment: {
               TOPIC_ARN: topicArn
           },
           //その他設定は省略
        },
    );
  }
}

```

## Slackクラスの定義
Lambdaで使用するSlackクラスを作成します。
このクラスからカスタム通知を送信します。
`PublishCommand`で送信する`Message`プロパティは、型にあったJSON化できる文字列でなければいけないので注意が必要です。
型の参考：https://docs.aws.amazon.com/ja_jp/chatbot/latest/adminguide/custom-notifs.html#parameter-details
```ts
import { PublishCommand, SNSClient } from '@aws-sdk/client-sns';

export class Slack {
  private topic: SNSClient;
  private topicArn: string;

  constructor(topicArn: string) {
    this.topic = new SNSClient({ region: 'ap-northeast-1' });
    this.topicArn = topicArn;
    log.info('slack', { slack: this });
  }

  public async sendMessage() {
    const description =
      `@channel\n$エラーが発生しました`

    const message = {
      version: '1.0',
      source: 'custom',
      content: {
        title: this.service,
        description: description,
      },
    };

    const result = await this.topic.send(
      new PublishCommand({
        Message: JSON.stringify(message),
        TopicArn: this.topicArn,
      })
    );
    log.info('Success send message');
  }
```
## Lambda本体の実装
エラーをキャッチして、カスタム通知を送信する処理の例です。
```ts
import { Slack } from './slack';
import { ScheduledEvent } from 'aws-lambda';

const TOPIC_ARN = process.env.TOPIC_ARN || '';

export const handler = async (event: ScheduledEvent) => {

  const slack = new Slack(TOPIC_ARN);

  try {
    // 処理を記述
    log.info('Success All Process');
  } catch (error) {
    log.error('Error S3Downloader', { error });
    await slack.sendMessage(error);
    throw error;
  }
};
```

# まとめ
今回は、Chatbotを利用してLambdaからSlackへカスタム通知を送る方法を紹介しました。
Chatbotのカスタム通知設定は新機能で情報も少ないと思われるので、少しでも他の方の助けになれば幸いです。