はじめに
ライクル開発部のエンジニアの永井です。
ライクルでは、GoogleBusinessProfileを通した集客支援サービスを提供しています。
突然ですが、皆様はこんなランダムアサインslack botを設定していたりしませんか?
今回は、チーム運営する上でランダムアサインを便利に設定できるslack botを開発したモチベとその構成について話したいと思います。
目的
ライクル開発部では、上記のようなランダムアサインbotを簡単に作るために
GASにて実装していました。
しかしGASで実装すると、どのGASで実装したかわからなくなったり
同じようなGASが各チームで実装されたりします。
汎用的なbotを1つ作ることで、一元管理したいと考えました。
裏の目的
上記の目的以外にも、私自身の技術的興味として下記試したいということもありました。 (こっちが本当の目的)
- slack botフレームワークであるboltを試したい
- 既に事業部内でboltで実装されているbotのメンテナンスができるように
- AWS Lambda Function URLsを使ってみたい
- AWSにおけるEventBridge以外の定期実行方法を考えてみたい
設定UI
実際に設定を行うUIはBlock Kit Builderを使い下記のようにしました。
アイコンダサい。。
設定閲覧UI
また設定の削除はコマンドを実行することで下記のようなメッセージを実行者本人のみに表示し、削除ボタンを押すことで可能にしています。
アーキテクチャ
メッセージの定時送信はStep FunctionsのWaitの機能を使い指定時間までメッセージ送信を遅延させることで実現しています。
Waitしている間は料金は全くかからず、最大1年待つことができるとのこと。
アーキテクチャは下記のようになっています。
また、Step Functionsはこのようになっています。
CDK
参考までにCDKの設定も載せます。(cdk version: 2.23.0)
// dynamodbs // 定期実行設定保存先 const dynamodbSchedulesTable = new aws_dynamodb.Table( this, 'randomizer_schedules', { partitionKey: { name: 'id', type: aws_dynamodb.AttributeType.STRING }, tableName: 'randomizer_schedules', }, ) // 前回アサイン者保存先 const dynamodbLastTimesTable = new aws_dynamodb.Table( this, 'randomizer_last_times', { partitionKey: { name: 'schedule_id', type: aws_dynamodb.AttributeType.STRING, }, tableName: 'randomizer_last_times', }, ) // lambdas // Slack BoltのLambda設定 const bolt = new NodejsFunction(this, 'randomizer-bolt-lambda', { entry: 'src/app.ts', handler: 'handler', environment: { SLACK_BOT_TOKEN: process.env.SLACK_BOT_TOKEN || '', SLACK_SIGNING_SECRET: process.env.SLACK_SIGNING_SECRET || '', }, memorySize: 2 ** 8, }) // [AWS Lambda Function URLsの設定 bolt.addFunctionUrl({ authType: FunctionUrlAuthType.NONE, }) // Step Functionの最初のLambda設定 const kicker = new NodejsFunction(this, 'randomizer-kicker-lambda', { entry: 'src/command/kicker.ts', handler: 'handler', memorySize: 2 ** 8, }) // メッセージ送信Lambda設定 const messenger = new NodejsFunction(this, 'randomizer-messenger-lambda', { entry: 'src/command/messenger.ts', handler: 'handler', environment: { INCOMING_WEBHOOK_URL: process.env.INCOMING_WEBHOOK_URL || '', }, memorySize: 2 ** 8, }) // invokes const kickerInvoke = new LambdaInvoke(this, 'randomizer-kicker', { lambdaFunction: kicker, }) // 設定数分以降のstepを実行 const mapInvoke = new Map(this, 'mapper', { itemsPath: JsonPath.stringAt('$.Payload.mapped'), parameters: { 'Payload.$': '$$.Map.Item.Value', }, }) // 次のlambdaの起動時間を設定 const wait = new Wait(this, 'randomizer-wait', { time: WaitTime.timestampPath('$.Payload.time'), }) // 定期メッセージ送信 const messengerInvoke = new LambdaInvoke(this, 'randomizer-messenger', { lambdaFunction: messenger, payload: TaskInput.fromJsonPathAt('$.Payload'), }) // step function definition const definition = kickerInvoke.next( mapInvoke.iterator(wait.next(messengerInvoke)), ) // Step Functionの設定 const stateMachine = new StateMachine(this, 'randomizer-messenger-stepfunction', { stateMachineName: 'randomizer-messenger-stepfunction', definition, }) // event rule new Rule(this, 'randomizer-stepfunction-rule', { ruleName: 'randomizer-stepfunction-rule', schedule: Schedule.cron({ minute: '00', hour: '15', // 日本時間 00時 weekDay: 'SUN-THU', // 月曜〜土曜 }), targets: [new SfnStateMachine(stateMachine)] }) // state machine grants stateMachine.grantStartExecution(bolt) // dynamodb grants dynamodbSchedulesTable.grantReadWriteData(bolt) dynamodbSchedulesTable.grantReadData(kicker) dynamodbSchedulesTable.grantReadData(messenger) dynamodbLastTimesTable.grantReadWriteData(messenger)
終わりに
まだ運用し始めですがこのようにslack botを作成しました。
ただ終わってみるとslack botのスケジュールされたメッセージの設定はScheduling messages を使って対応できるのかなとも思いました。
使ってみたい技術を欲のままに詰め込みましたが、時間があったらもっと便利にSlack boltの機能を使いAWSインフラの引き算をしたいと思います。