PycordとRiotAPIを用いたDiscordコマンド(BOT)の作成

## 前提
この記事にはLeague of Legendsというオンラインゲーム内でのみ使用されるような言葉が使用されます、使うときは説明を入れるようにします。また、PycordでBOTを作成するチュートリアルとなることもこの記事は想定しているので、Pycordがわからないという方でも問題ないです。
### 開発環境
Ubuntu 24.04
VScode　ver.1.87
Python 3.12.1
Discord Linux

### リンク
[RiotAPI](URL "https://developer.riotgames.com/")
[Pycord(公式サイト)](URL "https://pycord.dev/")
[Pycord(Github)](URL "https://github.com/Pycord-Development/pycord/")
[Discord](URL "https://Discord.gg/")

:::note info
RiotAPIの利用にはRiotアカウントが、Discordの使用にはDiscordアカウントがそれぞれ必要になります。
:::
## 今回やりたいこと
あるLeague of LegendsのDiscordコミュニティで動かす用のBOTを作成します。Pycordで作成したDiscordBOTがRiotAPIから、スラッシュコマンドでゲーム内IDを入力したひとのサモナーレベル(そのゲームのユーザーレベルのこと)を取得します。

## Pycordの導入
### インストール
#### Pycordのインストール
音声サポートなしのライブラリをインストールするには、次のコマンドを実行します。
```install
# Linux/macOS
python3 -m pip install -U py-cord

# Windows
py -3 -m pip install -U py-cord
```
今回は音声機能を使わないのでこれで問題ないですが、将来的に使用したいということがあればインストールしておきましょう
```
# Linux/macOS
python3 -m pip install -U "py-cord[voice]"

# Windows
py -3 -m pip install -U py-cord[voice]
```
#### error: externally-managed-environment
私がこのようにインストールをしようとしたら
```
error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.

    If you wish to install a non-Debian-packaged Python package,
    create a virtual environment using python3 -m venv path/to/venv.
    Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
    sure you have python3-full installed.

    If you wish to install a non-Debian packaged Python application,
    it may be easiest to use pipx install xyz, which will manage a
    virtual environment for you. Make sure you have pipx installed.

    See /usr/share/doc/python3.11/README.venv for more information.

note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.
```
というエラーが発生しました。`~/.config/pip`ディレクトリに`pip.conf`というファイルを作成して下記の内容を追加すればエラーは解決しますが、venvを使用し仮想環境にインストールするのが安全だと思います。細かいことは以下を参照してください
[externally-managed-environmentエラーへの対処法 - 頑張らないために頑張る](URL "https://ysko909.github.io/posts/get-error-externally-managed-environment/")
```conf:pip.conf
[global]
break-system-packages = true
```
:::note warn
上記の方法は、エラーメッセージを強制的に無視するやり方なので、他プロジェクトとの干渉を防ぐためにも仮想環境を利用したインストールを推奨します。
:::
## DiscordBOTの作成
### BOT作成
Discordのアカウントを作成しログイン後、[Discord Developer Portal](URL "https://discord.com/developers")にアクセスします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716811/638bf8c3-24c5-fb4c-47d8-657d879b860f.png)
右上の New Applicaiton を押して任意のBOT名を入力します。このBOT名はあくまでDiscord Developer Portal内での名前になるので、自分が識別しやすいわかりやすい名前で問題ないです。
左の BOT タブからBOTについての設定をします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716811/a1034af7-7f98-1db6-e7c3-0786ae8507a6.png)![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716811/0a0a1af0-69c1-7e8c-8e94-83e4bc09831a.png)
アイコンを設定したり、BOTの名前を変更できたりできます。チェックが入っている PUBLIC BOTは自分が作成したサーバー以外にもBOTを追加する予定があるならチェックをしてください。
MESSAGE CONTENT INTENTはサーバー内のメッセージテキストを取得するのに必要です。チェック必須です。
### サーバーにBOT導入
事前にテスト用のDiscordサーバーを作成しておいてください。
左のOAuth2タブからBOTの権限設定と導入ができます。OAuth2 URL Generatorセクションで以下のところにチェックを入れてください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716811/6ae1c661-b49c-aa39-9093-ba3caf95ab90.png)
BOT PERMISSIONS(BOTの権限)は、本来必要になるものだけにチェックを入れるのが望ましいですが、自分のサーバーに追加するだけであればAdministorator(管理者)で問題ないと思います。
ページの最下部にBOTをサーバーに導入するための招待リンクが生成されるのでこれにアクセスします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716811/485ae953-57d1-4685-6f00-4e76f07c83d4.png)
右側のメンバーリストよりBOTがサーバーに追加されたのがわかりました。
## コーディング
冒頭にも書いたように今回はRiotAPIを用いてユーザーのサモナーレベルを取得するコマンドを作成します。
### 先に
DiscordBOTを動かすためにはDiscordのアクセストークン、RiotAPIを使用するにはAPIキーが必要になります。
### アクセストークン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716811/77ee50b1-165b-4b3b-4fa9-7ba9c305d411.png)
先ほどのDiscord Developer Portalにアクセスして画像の Reset Token からアクセストークンを使用しましょう。一度しか表示されないので安全な場所にメモしておきましょう。
### APIキー
[Riot Developer Portal](URL "https://developer.riotgames.com/")にアクセスします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716811/82512bfa-3578-46f0-167d-ce31ccfc9f6f.png)
REGENERATE API KEYボタンを押してAPIキーを取得します。そうしたら上のCopyを押してAPIキーをコピーします。APIキーは１日おきに再生成が必要になります。プロジェクトが完成したらRiotの方に申請をして永続のAPIキーを取得することができます。

:::note alert
どちらも厳重に扱ってください。特にDiscordのアクセストークンは漏れるとアカウントの不正ログインの原因なります。間違ってもGithubなどに公開しないようにしましょう。もしGithubに公開したいのであれば、.gitignoreとdotenvを合わせて利用しましょう。
:::
### コード
```python:main.py
import discord
import requests
base_url = "https://jp1.api.riotgames.com"
bot = discord.Bot()

API = 123456789
TOKEN = 12345679

@bot.event #BOTが起動したのを確認するためのコードです、コピペで問題ないです。
async def on_ready():
    print(f"{bot.user} is ready and online!")


@bot.command(description = "あなたのサモナーネームを登録します。 例：'Summoner#123")
async def test(ctx, name):
    endpoint = "/lol/summoner/v4/summoners/by-name/" + name
    url = base_url + endpoint
    headers = {"X-Riot-Token": 'API'}
    try:
        response = requests.get(url, headers=headers)
        if response.status_code == 200:
            await ctx.respond(f"You are {name}")
            print(response.text)
            summoner_data = response.json()
            await ctx.respond(f"あなたのサモナーレベルは{summoner_data['summonerLevel']}です。これがあなたの強さの指標になります。")
        else:
            print(f"Error: {response.status_code}")
            print(response.json())  # エラーレスポンスの詳細を表示

    except Exception as e:
        print(f"エラーが発生しました: {e}")
bot.run(TOKEN) 
```
コードを見ていきましょう。
`import discord`でpycordを、`import requests`APIリクエストを送るのに使うrequestsをインポートします。
`base_url = "https://jp1.api.riotgames.com"`取得したいアカウントは日本サーバーのアカウントなので、エンドポイントのURLはjp1を使用します。これがAPIリクエストを送るときのURLになります。
`bot = discord.Bot()`これで、pycordを呼び出します。
`@bot.command(description = "あなたのサモナーネームを登録します。 例：'Summoner#123")`でDiscordのスラッシュコマンドを作成します。カッコ内に説明を追加することができます。この説明はDiscord内にも反映されます。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716811/3245bd39-f736-d838-e343-d146f61e2124.png)
※「あなたの希望レーンを選択します」がDescriptionに記述した部分です。
`async def test(ctx, name):`テキストボックスを設置します。nameの部分にユーザーネームを入力することになります。追加でテキストボックスを何個も設置することができます。スラッシュコマンドを実行した後、テキストボックス内のテキストがそのまま変数nameに格納されます。
`endpoint = "/lol/summoner/v4/summoners/by-name/" + name`サモナーネームを取得するAPIのURLをつくります。ここで変数nameを使用しています。
`url = base_url + endpoint`先ほどのbas_urlと合わせてURLを完成させます。このURLにAPIリクエストを送信します。
`headers = {"X-Riot-Token": 'API'}`APIリクエストの際のヘッダー部分を記述します。ここでAPIキーが必要になります。APIキーが正しくないとリクエストが送信できません。
`response = requests.get(url, headers=headers)`APIリクエストを送信します。
`await ctx.respond(f"You are {name}")`コマンド使用後、入力内容を確認するためにnameの値をメッセージで返すようにしています。
`summoner_data = response.json()`APIリクエストを送信後、データはjson形式で帰ってくるので.json()を使用して二次元配列変数に格納します。
`await ctx.respond(f"あなたのサモナーレベルは{summoner_data['summonerLevel']}です。これがあなたの強さの指標になります。")`二次元配列のsummoner_date列にサモナーレベルが格納されているのでそれを持ってきています。
`print(f"Error: {response.status_code}"),print(response.json())`もしAPIリクエストが失敗した場合、何のエラーが出たのかをターミナル内にprintします。
`print(f"エラーが発生しました: {e}")`そもそもエンドポイントURLが間違っていたりしてAPIリクエストが送信できなかった場合のエラーをタミーナル内にprintします。
`bot.run(TOKEN) `BOTを実行します。ここでDiscordのアクセストークンが必要になります。  

## 終わりに
これは現在私が作っているBOTの一部のコードになります。PycordとRiotAPIともに日本語の情報はほとんどなく、英語の情報もかなり少なかったです。この記事が他のだれかの一助になれば幸いです。わからないところがあれば気軽にコメントしてください
Pycordは公式のDiscordコミュニティが存在します。簡単な質問であれば返信早いのでぜひご利用を。
[Pycord (Discord)](URL "https://discord.com/invite/pycord")