はじめに
先日、Discordサーバのユーザから、自作のDiscord botに犬の画像を送信するコマンドを追加してほしいと要望があり、実装することにしました。
この記事では、API選定、実装について簡単に紹介します。
API選定
Dog API にしました。
候補
- 良いところ:画像を PullRequest で受け付けている。フォーマットが誤ってる場合Closeされるようなので、画像の質が保たれていると思う。
- 気になるところ:Rate limitについて書かれていない。
- 良いところ:リクエストをどれだけ受け付けるか書いてある(Freeで10000リクエスト/月)。
- 気になるところ:画像を気軽にアップロードできるので、Dog APIより質が低そう。
- 系列?のサービス The Cat API を使っているが時々、色を反転しただけの外れ画像があったりする。
Rate limitについては、Discord側でも連投制限があるので多分大丈夫だろう……ということで、Dog APIを使ってみることにしました。
実装
自作Discord botはPython + discord.pyで作られています(discord.pyは 開発を停止 していますが、一旦様子見でdiscord.pyを使っています)。
犬画像を送信するコマンドを作成するため、discord.py に犬画像送信コマンド用の Cog を追加しました。
既存のコマンドに猫画像を送信するコマンドがあり、動物くくりでCogをまとめることも可能でした。
しかし、個別でコマンドをdisableしたい可能性を考え、Cogを別にしています。
犬画像も猫画像も、やることはほぼ同じなため、BaseとなるCogを作成し、それぞれ継承しました。
コード例
BaseとなるCogは以下のような感じです。message.send_image
は指定チャンネルに画像を送信する関数です。
src/utils/message.py
にメッセージ関係の関数をまとめることで、一括でフォーマットを変更しやすくしています。
import aiohttp from typing import Any import discord from discord.ext import commands from src.utils import message class RandomImageBaseCog(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot async def send_image(self, channel: discord.channel, url: str, **kwargs: Any) -> None: """APIから画像URLを取得しchannelに送信する""" json = await self.fetch_url(url, **kwargs) image_url = self.get_image_url_from_json(json) await message.send_image(channel, image_url) async def fetch_url(self, url: str, **kwargs: Any) -> Any: """URLをfetchしjsonを返す""" async with aiohttp.ClientSession(raise_for_status=True) as session: async with session.get(url, **kwargs) as response: json = await response.json() return json def get_image_url_from_json(self, json: Any) -> str: """レスポンスのjsonから画像URLを返す""" raise NotImplementedError
RandomImageBaseCog
を継承する DogCog
はこんな感じです。
from typing import Any import discord from discord.ext import commands from src.cogs.random_image_base import RandomImageBaseCog class DogCog(RandomImageBaseCog): ENDPOINT_URL = "https://dog.ceo/api/breeds/image/random" @commands.command() async def dog(self, ctx: commands.Context) -> None: """犬の画像を表示します [Dog API](https://dog.ceo/dog-api/) から画像のURLを取得し、表示します""" if ctx.author == ctx.bot: return await self.execute_command(ctx.channel) async def execute_command(self, channel: discord.Channel) -> None: await self.send_image(channel, self.ENDPOINT_URL) def get_image_url_from_json(self, json: Any) -> str: return json.get("message")
終わりに
今までは猫画像だけだったのが、犬画像も見られるようになって、より癒されるようになりました。
コマンドを追加して良かったです。