Discordで惣菜を発表するBOTを作成してみた

# Discordで惣菜を発表するBOTを作成してみた

## まえがき
こんにちは。あーくと申します。
所属しているN.B.SQUADの [**アドベントカレンダー**](https://adventar.org/calendars/11151) でこの記事を執筆しています。
最近、CHUNITHMにも収録されましたよね。中々面白い譜面です。~~すんげえ視認性悪いのはナイショ~~
noteで記事書いてたんですけど、Markdown使えねーので諦めてこっちで執筆しています。

## TL;DR
タイトルのとおりです。
Discordで惣菜発表系BOTを作成してみました。

## なんでこれを作ろうと思った？
どうせ書くなら理系っぽいことをやろうかな～と思ったから。。。
というかこれ以外ネタが思いつかない上に、時間がない！！！
~~時間くださいなんでもしますから~~

## 作成手順

### 1. DiscordのBOTユーザーを作成する
まずは [**こ↑こ↓**](https://discord.com/developers/applications) にアクセスしてBOTユーザーを作成しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3964032/55b131a7-7371-97af-14e0-dececd5eeeb6.png)

右上にある **New Application** を押しましょう。

そしたらアバターなり色々設定していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3964032/d55462dc-398f-baa4-db35-f807ad239215.png)

次に、インテントを有効化します。これがないとBOTがメッセージなどを送ることが出来ません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3964032/be84053a-99d5-01b3-d55c-d6245868c2ab.png)

次にBOT Tokenを生成します。
<font color="#851b0d">**※1回生成したら次回から表示されないので安全なところに保管しましょう！**</font>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3964032/7d2b4e06-d1cd-10a6-d454-42d5c5ec101f.png)
↑大事に保管しましょう。

そしたら、自分のサーバーに今作ったBOTを召喚しましょう。

左の **[OAuth2]** タブから

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3964032/84d57198-7b67-3b51-721e-864a6985e3ef.png)

`bot` のチェックを入れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3964032/5135a877-8640-1069-7b8f-c8bcc0b2dacb.png)

BOTに与える権限を設定します。必要最低限にとどめておきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3964032/1fc9ece6-0b2d-1d8a-09f4-e40f92582cb2.png)

招待URLに飛びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3964032/531d40b2-8f11-76cb-3248-1c4b16b06557.png)

:robot: 「私はロボットではありません」
~~？？？「私は人間じゃないので」~~

### 2. 画像を作成
機構としては、入力された惣菜の名前を画像形式でDiscordに投下させることです。
今回は、PILライブラリを使って、惣菜の名前を合成しましょう。
今回の開発では、IntelliJ IDEAを使います。JetBrains社のIDEはまじでいいぞ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3964032/9c64f9e4-19c0-9e8d-0349-a9cbcddb03a9.png)

`SouzaiDragon` プロジェクトを作成しました。

次に、PILとDiscord.pyもインストールしてしまいましょう。

```shell:必要なライブラリのインストール
pip install pillow
pip install discord.py
```

次に、入力した文字を画像に変換するプログラムがこちらです。

```python:SouzaiTest.py
from PIL import Image, ImageDraw, ImageFont
import datetime

# 画像を開く
image = Image.open("./好きな惣菜発表ドラゴン.png")
draw = ImageDraw.Draw(image)

# 使用するフォント
font_path = "YuGothB.ttc"
font_size = 130


# 挿入するテキストと位置
text = input("発表したい惣菜を入力してね: ")
text_replaced = text.replace("\\n", "\n")
text_position = (530, 380)
text_color = (0, 0, 0)

# 最大サイズ
max_width = 560
max_height = 400

while True:
    font = ImageFont.truetype(font_path, font_size)
    box = draw.textbbox(text_position, text, anchor="mm", font=font, spacing=(font_size * 0.2))

    width = box[2] - box[0]
    height = box[3] - box[1]

    if width > max_width or height > max_height:
        font_size -= 1
    else:
        break


# テキストを画像に描画
draw.text(text_position, text, fill=text_color, anchor="mm", font=font, spacing=(font_size * 0.2))

# 画像を保存
now = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
output_path = f"./{now}.png"
image.save(output_path)

# 画像をプレビュー
image.show()

print("発表したよ")
```

```shell:実行結果
発表したい惣菜を入力してね: からあげ
発表したよ
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3964032/aac25464-3b08-4d70-5994-38689b9dda7e.png)

こんな感じで生成されました！

### 3. 生成した画像をDiscordに投下
さて、ここまで来ればラストスパートです。
PILのままでは、Discordに送信することが出来ないので、`BytesIO()` を使ってあげます。

```python:bot.py
import discord
from discord import app_commands
from discord.ext import commands
from PIL import Image, ImageDraw, ImageFont
import io

def dragon_image(text):
    # 画像を開く
    image = Image.open("./好きな惣菜発表ドラゴン.png")
    draw = ImageDraw.Draw(image)

    # 使用するフォント
    font_path = "YuGothB.ttc"
    font_size = 130

    # 挿入するテキストと位置
    text_replaced = text.replace("\\n", "\n")
    text_position = (530, 380)
    text_color = (0, 0, 0)

    # 最大サイズ
    max_width = 560
    max_height = 400

    while True:
        font = ImageFont.truetype(font_path, font_size)
        box = draw.textbbox(text_position, text_replaced, anchor="mm", font=font, spacing=(font_size * 0.2))

        width = box[2] - box[0]
        height = box[3] - box[1]

        if width > max_width or height > max_height:
            font_size -= 1
        else:
            break

    # テキストを画像に描画
    draw.text(text_position, text_replaced, fill=text_color, anchor="mm", font=font, spacing=(font_size * 0.2))

    # 画像を保存
    image_bytes = io.BytesIO()
    image.save(image_bytes, format="PNG")  # PNG形式で保存
    image_bytes.seek(0)  # ファイルポインタを先頭に戻す

    return image_bytes


# Intentsを設定
intents = discord.Intents.default()

# Botクラスの作成
bot = commands.Bot(command_prefix="!", intents=intents)

# Botが準備完了したときに実行されるイベント
@bot.event
async def on_ready():
    print(f"Botがログインしました: {bot.user}")
    try:
        # スラッシュコマンドを同期（必要に応じてギルドIDを指定）
        synced = await bot.tree.sync()
        print(f"スラッシュコマンドが同期されました: {len(synced)} 個のコマンド")
    except Exception as e:
        print(f"コマンドの同期に失敗しました: {e}")

# /dragon コマンドの実装
@bot.tree.command(name="dragon", description="惣菜を発表するよ")
@app_commands.describe(souzai="発表する惣菜の名前")
async def dragon(interaction: discord.Interaction, souzai: str):
    dragon = dragon_image(souzai)
    file = discord.File(fp=dragon, filename="image.png")
    await interaction.response.send_message(file=file)

# Botを起動
token = input("BOT Token を入力してください: ")
bot.run(token)
```

実行結果です！うまくいきました！

![souzai.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3964032/cffdf2d2-2fd8-405d-baf9-e9ed2decf105.gif)


### 4. まとめ
PythonでPILとDiscord.pyを使って非常にお手軽に実装出来ました。
今度はもっと複雑なBOTを作ってみたいですね～