PythonでDiscordのbotを作った備忘録

マーダーミステリーのサーバーを立てるのに役に立ちそうなbotが作りたかったので作った。その備忘録。 自分用メモなので常体。

最終更新日:2022/05/04

これの続きみたいなもん(前の記事は見る必要は特にない)。

使用環境

python 3.9.7

discord.py 1.7.3

VisualStudio2019

初期設定

discord.pyの導入は下記参考サイトに書いてあるから調べてほしい

import discord

TOKEN = "トークンを記入"
client = discord.Client()

#メッセージ受信時に動作する処理
@client.event
async def on_message(message):
    #メッセージ送信者がbotの場合は無視
    if message.author.bot:
        return

    #メッセージ送信者が管理者権限を持っていなかったら無視することもできる
    #if not message.author.guild_permissions.administrator:
    #    return

    #出力
    await message.channel.send(message.content)

#起動時に動作する処理(不要ならなくてもいい)
@client.event
async def on_ready():
    # 起動したらターミナルにログイン通知が表示される
    print("ログインしました")

# Botの起動とDiscordサーバーへの接続
client.run(TOKEN)

関数名が「on+イベント名」の場合は「@client.event」を書く必要がある。

discordとやり取り(メッセージを受け取ったり出力したり)をする関数には「async」を忘れずにつけること。

また、実際にやり取りをする箇所には「await」を付けること。

細かい理由などは参考サイトを見てほしい。

また、TOKENは他人には教えないこと。

以下はbotを生成する時にそれに適した権限を付与する必要がある。(チャンネル管理・ロール管理等。ただし、無闇に管理者権限は与えない方がいい)

メッセージの送信

await message.channel.send("ここにメッセージを入力")

これで「ここにメッセージを入力」というメッセージが出力される。

メッセージのピン留め

await message.pin()

2022/05/04追記 これで取得したメッセージをピン留めすることができる。条件文を適当に書いて必要なメッセージをピン留めするようにしような。

カテゴリの作成

Category = await message.guild.create_category("カテゴリ名")

これで「カテゴリ名」というカテゴリが作成できる。変数を指定することも可能。

discord.pyではサーバーのことをguildと表現することに注意。

カテゴリの取得

Category = discord.utils.get(message.guild.categories, name = "カテゴリ名")

これで「カテゴリ名」というカテゴリがあったらCategoryに格納される。なければNoneが返ってくる。

チャンネルの作成

カテゴリ内のテキストチャンネル・ボイスチャンネルを作成する。

await message.guild.create_voice_channel(name="チャンネル名", category=Category)

#channel = await message.guild.create_voice_channel(name="チャンネル名")
#await channel.edit(category=Category)

create_voice_channelをcreate_text_channelにすればテキストチャンネルが作成できる。

categoryは設定しなくても作成はできる。カテゴリ取得をして上記のように設定すれば、カテゴリの設定もできる。

コメントアウトしてある方でも可能だが、わざわざ複数行にする意味もそんなにないと思う。

チャンネル取得

channel = discord.utils.get(message.guild.channels, name = "チャンネル名")

指定した名前に一致するチャンネルをサーバー内から取得する。

カテゴリからチャンネル取得

Category = await message.guild.create_category("カテゴリ名")
for channel in Category.channels:
    await message.channels.send(channel.name)

for文でカテゴリ内にあるチャンネルだけ回す。そのカテゴリ内にあるチャンネル名がmessageを受け取ったチャットに出力される。

カテゴリ・チャンネルの削除

channel.delete()
Category.delete()

以上。上記のfor文と組み合わせたりすると複数個消せたりできる。

ロールの作成

Role = await message.guild.create_role("ロール名")

任意のロールが作れる。

ロールの取得

Role = discord.utils.get(message.guild.roles, name="ロール名")

ロール名に一致するものがあればRoleに格納される。なければNoneが入る。

テキスト・ボイスチャンネルの制限

ロールの制限
Category = await message.guild.create_category("神")
Role = discord.utils.get(message.guild.roles, name="神")
overwrites={
    message.guild.default_role:discord.PermissionOverwrite(read_message=False),
    Role:discord.PermissionOverwrite(read_message=True)
}
await message.guild.create_text_channel("神", category = Category, overwrites=overwrites)

overwrites内で閲覧できるロールの制限をする。default_roleは「@everyone」のこと。read_messageはテキストチャンネルを閲覧することができるかどうかを表す。この場合は「@everyone」は見ることはできないが、「神」は見ることができる。

チャンネルを作るときに引数に追加すればそのチャンネルにロールの制限がかけられる。

ロールの付与
await member.add_roles(role)

2021/11/11追記 ロールの付与ができる。以上。

ロールの剥奪
await member.remove_roles(role)

2021/11/12追記 ロールが剥奪できる。ただし、管理者がGM等のロールを持っている場合は権限がないため外せない。

人数の制限
message.guild.create_voice_channel(name"密談", category=category, user_limit=2)

ボイスチャンネルを作るときに使える機能。上記の書き方で2人しか入れないチャンネル名「密談」のボイスチャンネルができる。

タイマー(指定した時間を計測する)

from discord.ext import tasks

@tasks.loop(seconds = 60, count = 10)
async def Loop():
    cl = client.get_channel(ChannelID)
    if Loop.current_loop == 0:
        await cl.send("タイマースタート")
    elif Loop.current_loop == 10:
        await cl.send("終了")
        Loop.stop()
    else:
        await cl.send(Loop.current_loop+"分経過")

async def on_message(message):
    if message.content == "/timer" and not Loop.is_running():
        Loop.start()
        return
    if message.content == "/stop" and Loop.is_running():
        Loop.stop()
        return

「from discord.ext import tasks」をimportする必要がある。また、関数名の上に「@tasks.loop()」を書いて何秒でループするのか指定する必要がある。

今回は60秒ごとに1回カウントを進め、10回ループする。countは省略してもよい。

今回は回数を指定したが、任意の数を指定する方法がわからなかったので、泣く泣くグローバル変数を使ったこともある。

secondsはhoursやminutesに書き変えることもできる。secondsでよさそう。知らんけど。

Loop.start()で起動、Loop.stop()で停止する。終わらせたいタイミングでストップを書き忘れないようにすること(1敗)。

Loop.current_loopが現在のループ数を示す。戻り値はint。

Loopが動いているかどうかはLoop.is_running()で判定。戻り値はbool。

ロール削除

for role in info.guild.roles:
    if role.name != "@everyone" and role.name != "GM":
        await role.delete()
        await info.msg.channel.send("ロールを削除しました")

これでロールが削除できるが、注意点がある。

「ロールの制限」の項で用いた「overwrites」を設定しているロールを削除しようとすると「403 Forbidden (error code: 50013): Missing Permissions(権限がない)」とエラーが出て削除することができない。これを解消するには、「overwrites」を使わない、もしくは以下のように閲覧権限を設定する必要がある。

client = discord.Client()

async def on_message(message):
    roleGod = await message.guild.create_role(name="神")
    bot = discord.utils.get(message.guild.roles, name="botのロール名")

    overwrites={
        message.guild.default_role:discord.PermissionOverwrite(read_message=False),
        roleGod:discord.PermissionOverwrite(read_message=True),
        bot:discord.PermissionOverwrite(read_message=True)
    }

    # 以下上記のような削除文

メンバー取得

2021/10/09追記 サーバー内のメンバーの取得ができなかったが、解決できたので以下に記しておく

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

@client.event
async def on_message(message):
    async for member in message.guild.fetch_members():
        await message.channel.send(member.name)

これでサーバー内のメンバーの名前が全て取得・出力できる。 intentsをする必要があるらしい(わかっていない顔)。

テキストの削除

2022/02/20追記

await message.channel.purge(limit = n)

テキストチャンネルのテキストを削除できる。limitの最大値は多分100(面倒なので調べるのをやめた)。引数を省略すると100件消すことができる。limit以外にも引数を取ることができるが、よくわからんので必要なら調べてほしい。

とりあえず以上。

何かあればTwitter@ezakiprnまで

参考サイト

Pythonで実用Discord Bot(discordpy解説) - Qiita

【Discord.py】チャンネルを持ったカテゴリを作成する - Qiita

discord.py入門(1) - Qiita

discord.py入門(2) - Qiita

役職制限のついたテキストチャンネルを作成する

[Python]Discordで指定時間に発言させるBOT - Qiita

API Reference

【Discord.py】Discord.py 1.5で追加されたIntentsを攻略する - Qiita

Heroku スターターガイド (Python) | Heroku Dev Center