Discord公式API のラッパー、Discord.py を使い始めた時に躓きそうな点をまとめました。
その多くはドキュメントを精読すればわかりますが、初めはその読み方もわからないものです・・・
基本的な操作
メッセージに反応させたい(基本)
import discord
token = ""
client = discord.Client()
@ client.event
async def on_message (message):
await message.channel.send( "メッセージが送信されました、こんにちは" )
client.run(token)
メッセージが送信されたらasync def on_message(message):
以下の処理が働きます。
そのとき、bot はメッセージの情報を受け取る事ができます 。
そのメッセージから送信元のチャンネルを取得して、そこへ新たなメッセージを送信します。
github などにソースコード を公開する場合、tokenをそのまま書くのは乗っ取りの危険があります。
環境変数 を設定可能なら、import discord
の次の行にimport os
と書き込み、
token = ""
はtoken = os.environ[ '環境変数名' ]
と書き換えてください。
if文を使えば、ある内容のメッセージのみに反応させることもできます。
メッセージの内容は、messageに含まれる「content」から取得できます。
import discord
token = ""
client = discord.Client()
@ client.event
async def on_message (message):
if message.content.startswith("こんにちは" ):
await message.channel.send("こんにちは" )
if message.content.endswith("おやすみ" ):
await message.channel.send("おやすみ" )
if message.content == "おはよう" :
await message.channel.send("おはよう" )
if "こんばんは" in message.content:
await message.channel.send("こんばんは" )
client.run(token)
メッセージ主の情報を取得したい・メンションしたい
messageにはさまざまな情報が含まれています。
先ほどの例に出てきた、channel
やcontent
もその一つですね。
送信者の情報はmessage.author
で取得できます。また、member.mention
でメンションを作成することができます。
import discord
token = ""
client = discord.Client()
@ client.event
async def on_message (message):
reply = message.author.mention +" \n "
reply += "送信者名は" + message.author.name + " \n "
reply += "送信されたチャンネル名は" + message.channel.name + " \n "
reply += "送信されたサーバー名は" + message.guild.name + " \n "
await message.channel.send(reply)
余談ですが、DMはサーバーに属していないので、もしこのコードのbot にDMを送信したら、nameが分からないのでエラーが返ってきます。
また、メンションは<@ID>
と書くだけでも実現できます。特定のアカウントのみにメンションを送る場合は試してみてください。
bot のプロフィールを編集したい
import discord
token = ""
client = discord.Client()
@ client.event
async def on_message (message):
if message.content == "!edit" :
payload = { "username" : "my_bot" }
await client.user.edit( payload )
client.run(token)
このコードを実行すると、!edit
と送信したときに、bot の名前が「my_bot 」に変更されます。
アイコンを変更する場合は、
import discord
token = ""
client = discord.Client()
@ client.event
async def on_message (message):
if message.content == "!edit" :
with open ( "ファイルのパス" ,"rb" ) as icon:
payload = { "username" : "my_bot" , "avatar" : icon.read() }
await client.user.edit( payload )
client.run(token)
とします。
これがうまく行かない場合は、ファイルのパスが間違っていないか確認してください。
自分自身のプロフィールを編集させたい場合はこれで問題ありませんが、他人のプロフィールを編集させる場合は、
payload = { "username" : "new_username" , "password" : "編集したいアカウントのパスワード"}
とします。しかし、パスワードで編集させるのはセキュリティを考えると現実的ではありませんね・・・
サーバーの内容を編集したい
サーバーの場合は、上記の方法を応用できます。
サーバーに対して行える編集の種類は非常に多いのでリストにしました、使いたいものをpayloadに加えてください。
(認証などがなされていない一般のサーバーで行える操作のみを書いています)
name → サーバー名。""で囲います。
icon → サーバーのアイコン。書き方は上の見出しでbot のアイコンを設定するコードを参考にしてください。
region → サーバーの音声チャンネルの地域設定。discord.VoiceRegion.地域名
と書きます。地域名の一覧はここ を参照してください。
afk_channel → サーバーのafkチャンネル設定。client.get_channel( 新しいAFKチャンネルのID )
と書きます。
afk_timeout → afkチャンネルに隔離されるまでの時間。""で囲わずに直接秒数を書いてください。
owner → サーバーの所有権を変更する。guild.get_member( 新しい所有者のID )
と書きます。
default_notifications → サーバーの通知設定。discord.NotificationLevel.レベル
と書きます。レベルの一覧はここ
explicit_content_filter → サーバーのフィルター設定。discord.ContentFilter.フィルター対象
と書きます。対象の一覧はここ
system_channel → サーバーの通知チャンネル。client.get_channel( 新しく設定したいチャンネルのID )
と書きます。
reason → サーバーを編集する理由。""で囲います。
ファイルを送信したい
基本的な送信方法はこの記事で解説しています。
coolwind0202.hatenadiary.jp
「プレイ中」を変えたい・オンライン表示を変えたい
await client.change_presence()
を使います。
引数にはactivity
(プレイ中)、status
(オンライン)、afk
(AFKチャンネルに入るか)を指定できます。
import discord
token = ""
client = discord.Client()
@ client.event
async def on_ready (ready):
await client.change_presence( activity = discord.Game( name = "プレイ中欄の文字列" ) , status =discord.Status.ステータス名 )
client.run(token)
と書くと、○○をプレイ中、オンライン表示も指定したステータス名になっているはずです。
使用できるステータス名の一覧はここ を参照してください。
処理を一時停止したい・定時に処理したい
この記事で解説しています。
coolwind0202.hatenadiary.jp
チャンネルの履歴を取得したい
この記事で解説しています。
coolwind0202.hatenadiary.jp
サーバー関連
チャンネルを作りたい(+カテゴリ指定)
カテゴリを取得しその下にチャンネルを作成することで実現します。
import discord
client = discord.Client()
token = ""
@ client.event
async def on_message (message):
if message.content == "!create" :
message.guild.create_text_channel("New Channel" )
if message.content == "!create_say" :
ch = message.guild.create_text_channel("New Channel" )
await ch.send("おはようございます。" )
client.run(token)
このコードは、!createでNew Channelという名のチャンネルを作成します。また、!create_sayで、作成したチャンネルに発言します。
カテゴリを指定して作成する場合は、以下のように書き換えます。
@ client.event
async def on_message (message):
if message.content == "!create" :
category = client.get_channel(message.channel.category_id)
ch = await category.create_text_channel("Category - New Channel" )
await ch.send("こんばんは" )
監査ログを取得したい
監査ログを取得するにはguild.audit_logs()
を使います。
当然、bot には監査ログの閲覧権限が与えられている必要があります。
guild.audit_logs()
には、
limit → 何個のログを取得するか。""で囲わずに数値を書きます。limit=None
なら取得可能な全てのログを取得します。
oldest_frist → 古い順に取得するか。真偽値を書きます。
user → メンバーでフィルターをかけます、guild.get_member( メンバーのID )
と書きます。
before 、after → 指定した時刻の範囲のログを取得します。datetimeオブジェクトを指定します。
import discord
token = ""
client = discord.Client()
@ client.event
async def on_message (message):
if message.content == "!audit" :
if message.guild is None :
return
if not message.guild.me.guild_permissions.view_audit_log:
return
audit_list = [ i.action for i in await message.guild.audit_logs( limit = None ).flatten() ]
print (audit_list)
client.run(token)
※before 、after を指定するには(補足)
直接コードには書きませんが、以下のように指定します(この場合2010年の1/1、1時1分より後のログを取得します)
tmp = datetime.datetime(2015 , 1 , 1 , 1 , 1 )
audit_list = [ i.action for i in await message.guild.audit_logs( limit = None , after = tmp ).flatten() ]
guild.audit_logs()
は非同期イテレータ という形でログを返します。
これだと少し扱いにくいのですが、上記のようにawait guild.audit_logs.flatten()
とすると簡単に扱えるようになります。
async for i in message.guild.audit_logs( limit = None )
print ( i.action )
このように非同期のループで処理しても同様です(一つ一つのログに処理をするときに使うと分かりやすいかもしれないです)
メンバーをキック・BANする
bot は荒らしに使う物じゃないよ。
import discord
client = discord.Client()
@ client.event
async def on_message (message):
if message.content == "!kick" :
await message.author.kick()
if message.content == "!ban" :
await message.author.ban()
client.run(token)
!kick
と送信すると送信者がキックされ、!ban
と送信すると送信者がBANされる。理不尽!
特定のワードを送信したらキックするようなNGワード bot を作るなら、
client.ng_list = ["from" ,"the" ,"far" ,"east" ]
@ client.event
async def on_message (message):
if [word for word in client.ng_list if word in message.content]:
await message.author.send("メッセージに不適切な内容が含まれているのでキックします。" )
await message.author.kick()
メッセージ関連
メッセージを埋め込みたい
この記事で解説しています。
coolwind0202.hatenadiary.jp
メッセージを編集・削除したい
この記事で解説しています。
coolwind0202.hatenadiary.jp
メッセージをピン留めしたい
import discord
token = ""
client = discord.Client()
@ client.event
async def on_message (message):
if message.content == "!pin" :
if not message.guild.me.guild_permissions.manage_messages:
return
await message.pin()
client.run(token)
特定のメッセージをピン留め・ピン留めを解除するコードも載せておきます。(Bot Commands Frameworkを使用)
import discord
from discord.ext import commands
token = ""
bot = commands.Bot(command_prefix="!" )
@ bot.command ()
async def pin ( ctx , message : discord.Message ):
await message.pin()
@ bot.command ()
async def unpin ( ctx , message : discord.Message ):
await message.unpin()
bot.run(token)
メッセージにリアクションを付けたい
カスタムでない標準の絵文字を探すには、このサイトがおすすめです
emojipedia.org
カスタム絵文字でリアクションを付けるには、絵文字のIDが必要です。
絵文字の前にバックスラッシュ('\')を付けて送信するとIDが表示されると思います。
import discord
token = ""
client = discord.Client()
@ client.event
async def on_message (message):
if message.content == "!thinking" :
await message.add_reaction("🤔" )
if message.content == "!custom_emoji" :
await message.add_reaction("<:絵文字名:絵文字ID>" )
client.run(token)
メッセージがピン留めされているか・メッセージに付けられたリアクションを取得したい
message.pinned
、message.reactions
を使用します。
on_message(message)
で取得した所でピン留めはされていないので、コード例ではBot Commands Frameworkを使用します。
import discord
from discord.ext import commands
token = ""
bot = commands.Bot( command_prefix="!" )
@ bot.command ()
async def message_pinned ( ctx , message : discord.Message ):
await ctx.send( message.pinned )
@ bot.command ()
async def message_reaction ( ctx , message : discord.Message):
await ctx.send( "**reactions** \n " + " \n " .join( [ i.emoji for i in message.reactions ] ) )
bot.run(token)
Discordモデル取得、リスト生成系のTips
Discordモデル
共通
discord.utils.get( 検索したいイテラブルオブジェクト , name = "名前" )
discord.utils.get( 検索したいイテラブルオブジェクト , id = ID )
特定のチャンネルを取得したい
client.get_channel( チャンネルID )
特定のメッセージを取得したい
client.fetch_message( メッセージID )
特定のサーバーを取得したい
client.get_guild( サーバーID )
サーバー内の特定のメンバーを取得したい
guild.get_member( メンバーID )
特定のユーザーを取得したい
client.get_user( ユーザーID )
特定の役職を取得したい
guild.get_role( 役職ID )
特定のカスタム絵文字を取得したい
client.get_emoji( 絵文字ID )
絵文字IDは、バックスラッシュの後に絵文字(\絵文字
)と送信することで表示される。
リスト
(便利っぽくて変に長く書かなくてもいいリストをたまーに追加していきたい)
サーバー内のメンバー名の一覧を取得したい
[member.name for member in guild.members]
[member.name for member in guild.members if not member.bot]
bot のいる全サーバーから特定の名前のWebhookのみを取得したい
[discord.utils.get(await guild.webhooks(),name="webhook名") for guild in client.guilds ]
なお、各サーバーから1つずつ取得されます。
同名のWebhookが複数存在するとき、それを全て取得するコードには二重ループを使ってみました。
[webhook for guild in client.guilds for webhook in await guild.webhooks() if webhook.name=="webhook名"]
メンバーの持っている役職名のリスト
[role.name for role in member.roles]
サーバーに存在するけど、メンバーが持っていない役職
リストといいましたがこういうのは集合型でやった方が簡潔なので変換してしまいましょう。
guild_roles = set(member.guild.roles)
my_roles = set(member.roles)
list(guild_roles ^ my_roles) #結果
コマンド
以下の見出しはBot commands frameworkを使用していることが前提です。
コマンド作成やループ管理を楽にしてくれるフレームワーク で、試してみる場合は以下のように書くと楽です。
client = discord.Client()
client.run(token)
の二行を削除し、
import discord
from discord.ext import commands
token = ""
bot = commands.Bot( command_prefix="!" )
bot.run(token)
このように書けばOKです。インスタンス 名は自由です。
また、commands.Bot クラスを継承した新たなbot 用のクラスを作成することも可能ですが、ここでは省略します。
継承や、機能をまとめるコグという機能の利用については以下が詳しいです。
qiita.com
コマンドの作り方
import discord
from discord.ext import commands
token = ""
bot = command.Bot( command_prefix = "!" )
@ bot.command ()
async def new_command (ctx):
await ctx.send( "コマンドが送信されました。" )
bot.run(token)
ctxはコマンドが送信された際に受け取る事ができます、message
と違い直接send
できます(内部的には省略してるだけですが)
message
と同様に、サーバーやチャンネル、送信者の情報を取得できます。
await ctx.send( "サーバー:" + ctx.guild + " \n " + "チャンネル" + ctx.channel + " \n " + "送信者" + ctx.author)
コマンドの引数
Bot Commands Frameworkを使えばコマンドに簡単に引数を設定できます。
import discord
from discord.ext import commands
token = ""
bot = commands.Bot( command_prefix = "!" )
@ bot.command ()
async def arg_example_1 (ctx,string):
await ctx.send(string)
@ bot.command ()
async def arg_example_2 (ctx,*strings):
print ( len (strings) )
if len (strings) > 0 :
await ctx.send( " \n " .join(strings) )
@ bot.command ()
async def arg_example_3 (ctx,*,sentence):
await ctx.send("入力された文章:" + sentence )
@ bot.command ()
async def arg_example_4 (ctx,num:int ,member:discord.Member):
await ctx.send(member.name*num)
bot.run(token)
コマンドの詳細は以下の記事で解説しているので参照してください。
coolwind0202.hatenadiary.jp
コードの実行例
画像では都合上prefixが!
から&
になっていますが、前述したとおり!
限定ではなく、コード内で設定したprefixを使ってください。
コマンドを役職・権限で制限
import discord
from discord.ext import commands
token = ""
bot = commands.Bot( command_prefix = "!" )
@ bot.command ()
@ commands.is_owner ()
async def owner_only (ctx):
await ctx.send( "botの所有者のみが実行できるコマンド。" )
@ bot.command ()
@ commands.has_role ( "役職Aの名前" )
@ commands.has_role ( 役職BのID )
async def role_only (ctx):
await ctx.send( "指定した役職を持っているメンバーのみが実行できるコマンド。" )
@ bot.command ()
@ commands.has_permissions ( 権限名 = True )
async def permission_only (ctx):
await ctx.send( "指定した権限を持っているメンバーのみが実行できるコマンド。" )
bot.run(token)
permission_only
で設定した権限の一覧はここ を参照してください。
そのほか、コマンド実行時に簡単にチェックできるものです。
@commands.has_any_role( 複数の役職をカンマ区切り ) #送信者が指定された役職を一つでも持っているときに実行されます。
@commands.is_nsfw() #nsfwチャンネルだった場合に実行されます。
@commands.guild_only() #サーバー内のテキストチャンネルでのみ実行されます。
@commands.dm_only() #DMチャンネルでのみ実行されます。
@commands.bot_has_role( 役職名 or 役職ID ) #指定された役職をbotが持っている場合にのみ実行されます。
@commands.bot_has_permissions( 権限名 = True ) #指定された権限をbotが持っている場合にのみ実行されます。
@commands.bot_has_any_role( 複数の役職をカンマ区切り) #指定された役職をbotが一つでも持っている場合に実行されます。
補足ですが、チェック用の関数を作ってチェックを自作することもできます。
import discord
from discord.ext import commands
token = ""
bot = commands.Bot( command_prefix = "!" )
def example_check (ctx):
return ctx.message.author.id == チェックしたいID
@ bot.command ()
@ commands.check ( example_check )
async def checked_command (ctx):
await ctx.send( "IDのチェックを通ったコマンドのみ実行されます。" )
bot.run(token)
チェックの自由度が広がると思います。
コマンドのクールダウン
クールダウンというのは、コマンドが過剰に働かないようにおやすみさせるものです( ˘ω˘ ) スヤァ…
@commands.cooldown( クールダウンが発動するまでのコマンド実行回数 , クールダウンの時間 ,クールダウンの対象)
import discord
from discord.ext import commands
token = ""
bot = commands.Bot( command_prefix = "!" )
@ bot.command ()
@ commands.cooldown ( 5 , 30.0 , BucketType.guild )
async def cooldown_example (ctx):
await ctx.send( "5回使用するとそのサーバーでは30秒間実行できません。" )
bot.run(token)
クールダウンまでのコマンド使用回数をカウントする対象は、BucketType.対象名
と指定します。
default Discord全体でカウント
guild サーバーごとにカウント
category サーバーのチャンネルカテゴリごとにカウント
channel サーバーのチャンネルごとにカウント
role サーバーの役職ごとにカウント
member サーバーのメンバーごとにカウント
user Discord全体のユーザーごとにカウント
コマンドにエイリアス を付けたい・使用時は別の名前のコマンドにしたい
import discord
from discord.ext import commands
@ bot.command ( name = "test" )
async def name_example (ctx):
await ctx.send( "!testなら実行できるが、!name_exampleでは実行できない。" )
@ bot.command ( aliases = [ "test1" , "test2" ])
async def aliases_example (ctx):
await ctx.send( "!test1、!test2、!aliases_exampleのどれでも実行できる。" )