Lambdaを使ってDiscordBot(Python)の実行

## はじめに
今回作成したDiscordBot（Python）は、Discordのチャンネルに対してメッセージを書き込むものを作成しました。
また、毎日0:00に書き込んでほしいので、EventBridgeからLambdaを起動して書き込む仕組みにしました。

用途としては、FiveMのガソリンスタンドを経営しており、ガソリンの価格を日々変更したく、一定の範囲のランダム値を投稿するのと、ゲーム内でのX（ツイッター）でポストするためのテンプレート文章をDiscordのチャンネルに投稿するものを作成しました。

## 構成イメージ

EventBridgeからLambdaを起動して、Discordのチャンネルにメッセージを書き込みます。
【全体の概要構成】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3868301/d03f1b86-7eea-e78d-2707-51147cf2ef6b.png)


【EventBridgeとLambdaの構成】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3868301/1fd434ab-2e41-2cef-a91c-84c10c547a09.png)


## Discordの動作イメージ
特定のチャンネルに対して、毎日0:00に価格がランダム（280-420の間）でDiscordBotがメッセージを記載します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3868301/06efe733-a14d-bf50-2c2a-1fc7282b82d3.png)

↑この文章をコピペして、ゲーム内でつぶやいています。

## Pythonのコード

``` python:lambda_function.py
import discord
import random
import os
import asyncio

TOKEN = os.environ['DISCORD_BOT_TOKEN']
CHANNEL_ID = int(os.environ['DISCORD_CHANNEL_ID'])
CHANNEL_ID_TW = int(os.environ['DISCORD_CHANNEL_ID_TW'])

intents = discord.Intents.default()
client = discord.Client(intents=intents)

async def send_gas_price():
    await client.wait_until_ready()
    channel = client.get_channel(CHANNEL_ID)
    channel_tw = client.get_channel(CHANNEL_ID_TW)
    
    if channel is not None:
        price = round(random.uniform(280, 420), 2)
        message = f"本日のガソリン販売価格は {price} 円です"
        await channel.send(message)
        
    else:
        print("チャンネルが見つかりません")

    if channel_tw is not None:
        message = f"本日の8140番地GSのガソリン販売価格は {price} 円/Lです!!\n（他のGSは、700円台/Lです）"
        await channel_tw.send(message)
        
    else:
        print("チャンネルが見つかりません")

@client.event
async def on_ready():
    await send_gas_price()
    await client.close()

def lambda_handler(event, context):
    loop = asyncio.get_event_loop()
    loop.run_until_complete(client.start(TOKEN))

```

:::note info
DiscordBotのトークンやチャンネルIDはLambdaの環境変数に登録して呼び出します。
```python
TOKEN = os.environ['DISCORD_BOT_TOKEN']
CHANNEL_ID = int(os.environ['DISCORD_CHANNEL_ID'])
CHANNEL_ID_TW = int(os.environ['DISCORD_CHANNEL_ID_TW'])
```
:::
なお、チャンネルは複数にメッセージを書き込むため、２つ用意しています。

DiscordBotのトークンやチャンネルIDの確認については、過去の記事を参照してください。

https://qiita.com/KumaRider/items/5b8c50af953e61cf09be

https://qiita.com/KumaRider/items/c50016c71c96588a8db2

## Lambdaでのデプロイ
外部ライブラリ（dicord.py）を使用するため、Zipにまとめてのデプロイを実施しています。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-package.html

### 事前準備
１）アップロードするディレクトリを作成します。
```zsh
mkdir 35-GS-price-Discord-Bot
cd 35-GS-price-Discord-Bot 
```

２）外部ライブラリをフォルダに格納（install）します。
```python
pip install discord.py -t .
pip install requests -t .
```

３）同じディレクトリ内にlambda_function.pyを作成します。
　（上記で記載しているpythonコード）


４）35-GS-price-Discord-Botフォルダをzipで圧縮します。

### Lmabdaの作成
AWSマネージメントコンソールからLabmda関数を作成します。

![スクリーンショット 2024-09-28 13.33.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3868301/6b767079-cf04-c41b-a32b-6225a2b416df.png)
[選択する内容]
関数名：任意の名前
ランタイム：Python 3.12
アーキテクチャ：任意

### 【Zipのアップロード】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3868301/76e6c47a-3573-7761-d5a1-4525eb439eee.png)


アップロード元で.zipファイルを選択しアップロードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3868301/45a2abb0-93ed-88ac-ea76-fa75330fb17c.png)

### タイムアウト値の変更
デフォルトの3秒だと、タイムアウトしてしまうので、タイムアウト値を30秒に変更します。
![スクリーンショット 2024-09-28 15.01.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3868301/e57c9fb8-071c-3c50-78d6-86038e22f91c.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3868301/45e81e68-c7d5-03db-bb73-bace0f592a99.png)


### Lambdaの環境変数の設定
Lambdaの画面から「設定」タブをクリックし、左メニューの「環境変数」をクリックすします。
キーにpythonで記載したキー名（DISCORD_BOT_TOKENなど）を記載し、値にトークンやチャンネルIDを入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3868301/cb91a447-866f-8360-73d4-61f25cf64a81.png)


![スクリーンショット 2024-09-27 12.18.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3868301/c0726d00-ace0-1b73-f0e7-5cdc84a7f672.png)

## EventBridgeの設定
Lambdaを毎日0:00に実行するEventBridgeを作成します。
今回はLabmdaの画面から[+トリガーを追加]をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3868301/8a27e2b0-c7d8-5de9-1c78-18a13df4d58c.png)

トリガーの設定で[EventBridge（CloudWach Envets）]を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3868301/b57f7a6c-a63a-a912-a30d-ebeb2452e06d.png)

新規のルールを作成を選択し、ルール名とスケジュール式を入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3868301/7690e21c-4cec-c1c7-36bd-f78be669cccb.png)

:::note info
Linuxのcronとは若干書式が異なります。また、UTCのため０時に起動するためには15時にする必要があります。
:::

## 動作テスト
Lmabdaの動作テストをするには、テストタブから[テスト]をクリックします。
※今回のスクリプトはそのままの実行できます。

![スクリーンショット 2024-09-28 15.38.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3868301/1ea16f35-8468-1477-7ea8-69b682bc4c9c.png)
[結果画面]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3868301/7707bfc4-a29f-0bc9-3c85-b4ff4441dcaa.png)

これでLambdaのデプロイとEventBridgeの設定は完了です。

## 最後に
今回は定期的に呟くDiscordBotであり、常時起動させておく必要がなかったため、Lambdaで実装しました。
また、今回はLambdaでのDiscordBot実装が初めてだったのと、今後の他のDiscordBotをLambdaで作成する予定もないため、Lambdaレイヤーは使わずに、外部モジュールも全部合わせてZipにしています。
次回は、Lambdaレイヤーを使った実装に挑戦してみようと思います。