v1.0への移行

v1.0 では完全な書き直しが行われたため,このライブラリにおけるもっとも大きな更新のひとつといえます。

すべての意図と目的を達成するために変更点が長大になった結果,ライブラリは完全に新しいものになりました。

一部の書き換えは,より使いやすく自然に物事を表現するために行われています。あらゆる仕事をするのに Client インスタンスを要求するのではなく,代わりに models を用いることができるようになりました。

Pythonのバージョンの変更

discord.py の開発をより簡単にし,またその依存関係にあるライブラリをアップグレードして Python 3.7 以上を使えるようにするために,discord.py は Python 3.5.3 より古いバージョンに対するサポートを諦めざるを得ませんでした。これはつまり Python 3.4 に対するサポートは打ち切られた ということです。

主要モデルの変更

以下は v1.0 で発生した主要なモデルの変更点です。

Snowflakeのint型への変更

v1.0以前は、全てのsnowflake (id 属性) が文字列として扱われていましたが、これは int 型に変更されました。

簡単な例:

# before
ch = client.get_channel('84319995256905728')
if message.author.id == '80528701850124288':
    ...

# after
ch = client.get_channel(84319995256905728)
if message.author.id == 80528701850124288:
    ...

この変更により,公式クライアントの「 ID をコピー」機能を使用した際に間違いがより起こりにくくなりました。もはや取得した ID をクォーテーションマークで囲う必要はありませんし,内部で JSON の代わりに ETF を用いることで最適化の機会を得ることにもなります。

Server は Guild になりました

公式のAPIドキュメントでは、「Server」は「Guild」と呼ばれています。APIドキュメントとの一貫性を保つため、モデルの名称が Guild へ変更されました。同時にこのモデルを参照するすべてのインスタンスも変更されています。

変更点の一覧

変更前

変更後

Message.server

Message.guild

Channel.server

GuildChannel.guild

Client.servers

Client.guilds

Client.get_server

Client.get_guild()

Emoji.server

Emoji.guild

Role.server

Role.guild

Invite.server

Invite.guild

Member.server

Member.guild

Permissions.manage_server

Permissions.manage_guild

VoiceClient.server

VoiceClient.guild

Client.create_server

Client.create_guild()

モデルのステートフル化

前述したように、多くの機能が Client から各々の model へと移されました。

以下が、これによる変更点の一覧です。

変更前

変更後

Client.add_reaction

Message.add_reaction()

Client.add_roles

Member.add_roles()

Client.ban

Member.ban() または Guild.ban()

Client.change_nickname

Member.edit()

Client.clear_reactions

Message.clear_reactions()

Client.create_channel

Guild.create_text_channel() および Guild.create_voice_channel()

Client.create_custom_emoji

Guild.create_custom_emoji()

Client.create_invite

abc.GuildChannel.create_invite()

Client.create_role

Guild.create_role()

Client.delete_channel

abc.GuildChannel.delete()

Client.delete_channel_permissions

abc.GuildChannel.set_permissions()overwriteNone に設定しました

Client.delete_custom_emoji

Emoji.delete()

Client.delete_invite

Invite.delete() または Client.delete_invite()

Client.delete_message

Message.delete()

Client.delete_messages

TextChannel.delete_messages()

Client.delete_role

Role.delete()

Client.delete_server

Guild.delete()

Client.edit_channel

TextChannel.edit() または VoiceChannel.edit()

Client.edit_channel_permissions

abc.GuildChannel.set_permissions()

Client.edit_custom_emoji

Emoji.edit()

Client.edit_message

Message.edit()

Client.edit_profile

ClientUser.edit() ( Client.user から取得可能)

Client.edit_role

Role.edit()

Client.edit_server

Guild.edit()

Client.estimate_pruned_members

Guild.estimate_pruned_members()

Client.get_all_emojis

Client.emojis

Client.get_bans

Guild.bans()

Client.get_invite

Client.fetch_invite()

Client.get_message

abc.Messageable.fetch_message()

Client.get_reaction_users

Reaction.users()

Client.get_user_info

Client.fetch_user()

Client.invites_from

abc.GuildChannel.invites() または Guild.invites()

Client.join_voice_channel

VoiceChannel.connect() ( ボイスの変更 を参照)

Client.kick

Guild.kick() または Member.kick()

Client.leave_server

Guild.leave()

Client.logs_from

abc.Messageable.history() ( 非同期のイテレータ を参照)

Client.move_channel

TextChannel.edit() または VoiceChannel.edit()

Client.move_member

Member.edit()

Client.move_role

Role.edit()

Client.pin_message

Message.pin()

Client.pins_from

abc.Messageable.pins()

Client.prune_members

Guild.prune_members()

Client.purge_from

TextChannel.purge()

Client.remove_reaction

Message.remove_reaction()

Client.remove_roles

Member.remove_roles()

Client.replace_roles

Member.edit()

Client.send_file

abc.Messageable.send() ( メッセージの送信 を参照)

Client.send_message

abc.Messageable.send() ( メッセージの送信 を参照)

Client.send_typing

abc.Messageable.trigger_typing() (abc.Messageable.typing() を使用してください)

Client.server_voice_state

Member.edit()

Client.start_private_message

User.create_dm()

Client.unban

Guild.unban() または Member.unban()

Client.unpin_message

Message.unpin()

Client.wait_for_message

Client.wait_for() ( イベントの待機 を参照)

Client.wait_for_reaction

Client.wait_for() ( イベントの待機 を参照)

Client.wait_until_login

削除されました

Client.messages

削除されました

Client.wait_until_ready

変更なし

プロパティの変更

一貫性を持たせるために、いくつかのプロパティがメソッドに変更されました。

プロパティの代わりに追加されたメソッドは以下のとおりです。(使用の際にはカッコが必要です)

辞書の値の変更

v1.0以前では、複数のモデルを集約して取得するプロパティは「辞書ビュー」オブジェクトで結果を返していました。

これは、オブジェクトを用いて繰り返し処理を行っている間に、辞書サイズが変更されたとき、RuntimeErrorを発生させてタスクをクラッシュさせていました。これを軽減させるため「辞書ビュー」オブジェクトはリストに変更されました。

以下のビューがリストへ変更されています。

ボイスステートの変更

v0.11.0では、ボイスステートを参照するために VoiceState が追加され、このクラスを参照するために Member.voice が使われていました。

これはユーザーにとって透過的なものでしたが、ライブラリがメモリを節約できるようボイスステートの変化が可視的になりました。

ボイスの属性にアクセスするには Member.voice を用いる方法しか存在しません。メンバーがボイスステートを持たない場合は None が返ることに注意してください。

簡単な例:

# before
member.deaf
member.voice.voice_channel

# after
if member.voice: # can be None
    member.voice.deaf
    member.voice.channel

ユーザーとメンバーの分離

v1.0では、メモリの節約のため、 MemberUser のサブクラスではなくなりました。代わりに、 User に記述されたものと同等のプロパティを実装することで、この二つのクラスは平坦化されています。そのため、使用方法に機能的変更はありません。ただし、 isinstance が使えなくなってしまったことは留意しておくべきです。

メモリの節約は、グローバルな User のキャッシュを持つことで実現しました。これによって Client.get_user() を使ってIDから簡単に User を取得できます。また、あなたのクライアントが見ることができるユーザーを Client.users ですべて取得できるようにもなりました。

チャンネルタイプの分割

v1.0以前のバージョンでは、チャンネルは is_private で判別する ChannelPrivateChannel の二通りしかありませんでした。

メモリ使用量を削減するため、チャンネルを4つのタイプへ分割しました。

  • ギルドのテキストチャンネル用である TextChannel

  • ギルドのボイスチャンネル用である VoiceChannel

  • メンバーのDMチャンネル用である DMChannel

  • メンバーが参加するグループDMチャンネル用である GroupChannel

これらに分割されたことにより、 is_private は削除されました。v1.0では isinstance を使うべきでしょう。

型は二通りの 抽象基底クラス に分けられます。

チャンネルがギルドチャンネルであるかをチェックしたい場合:

isinstance(channel, discord.abc.GuildChannel)

チャンネルがプライベートチャンネルであるかをチェックしたい場合:

isinstance(channel, discord.abc.PrivateChannel)

もちろん、特定のチャンネルタイプを探したい場合、そのチャンネルタイプを渡すことも可能です。

isinstance(channel, discord.TextChannel)

この分割により、イベントにも変更がありました。これについては イベントの変更 に詳細があります。

その他のモデルの変更

一般的なモデルには追加、あるいは削除されたものが多くあります。

以下がその一覧です。

削除

  • Client.login() はEメールとパスワードによるログインを受け付けなくなりました。

    • トークンと bot=False を使用してください。

  • Client.get_all_emojis

  • Client.wait_for_message および Client.wait_for_reaction は削除されました。

  • Channel.voice_members

  • Channel.is_private

    • 代わりに isinstance抽象基底クラス を使用してください。

    • 例を挙げると isinstance(channel, discord.abc.GuildChannel) でプライベートチャンネルであるかを確認できます。

  • Client.accept_invite

    • これに代わるものはありません。これは非推奨のAPIです。

  • Guild.default_channel / Server.default_channel および Channel.is_default

    • デフォルトチャンネルの概念は、Discordから削除されました。 #329 を参照してください。

  • Message.edited_timestamp

  • Message.timestamp

  • Colour.to_tuple()

  • Permissions.view_audit_logs

  • Member.game

  • Guild.role_hierarchy / Server.role_hierarchy

    • 代わりに Guild.roles を使用してください。ソート順が以前の Guild.role_hierarchy とは逆になっていることに注意してください。

変更

  • Member.avatar_urlUser.avatar_url はアバターが設定されていなければ、デフォルトアバターが返るようになりました。

  • Message.embedsdict オブジェクトから Embed のリストに変更されました。

  • Message.attachmentsdict オブジェクトから Attachment のリストに変更されました。

  • Guild.roles はヒエラルキー順にソートされるようになりました。先頭には必ず @everyone が格納されます。

追加

メッセージの送信

変更点の一つは、以前の Client.send_messageClient.send_file の機能を単一のメソッド send() に統合したことです。

基本形

# before
await client.send_message(channel, 'Hello')

# after
await channel.send('Hello')

これは埋め込みメッセージなどといった、従来の send_message が持っていた機能を全てサポートしています。

e = discord.Embed(title='foo')
await channel.send('Hello', embed=e)

これはファイルの送信に対応できるように拡張されましたが、複数のファイルを送信する際には、 File の擬似的な名前付きタプルでファイルを渡す必要があります。

# before
await client.send_file(channel, 'cool.png', filename='testing.png', content='Hello')

# after
await channel.send('Hello', file=discord.File('cool.png', 'testing.png'))

この変更は、複数の添付ファイルの送信を容易にするために行われました。

my_files = [
    discord.File('cool.png', 'testing.png'),
    discord.File(some_fp, 'cool_filename.png'),
]

await channel.send('Your images:', files=my_files)

非同期のイテレータ

v1.0以前のバージョンは、Python 3.4 または 3.5以上の環境において Client.logs_from のような特定の関数で、処理終了時に異なった型を返していました。

これはv1.0で元に戻り、 AsyncIterator という抽象概念を満たす特異な型を返します。

これは通常のイテレータと同様の処理が行なえます。

async for message in channel.history():
    print(message)

またはリストにできます。

messages = await channel.history().flatten()
for message in messages:
    print(message)

AsyncIterator を返すことで便利な点は AsyncIterator.map()AsyncIterator.filter() といった関数をチェーンできることです:

async for m_id in channel.history().filter(lambda m: m.author == client.user).map(lambda m: m.id):
    print(m_id)

AsyncIterator.map() または AsyncIterator.filter() に渡される関数はコルーチンか通常の関数です。

You can also get single elements a la discord.utils.find() or discord.utils.get() via AsyncIterator.get() or AsyncIterator.find():

my_last_message = await channel.history().get(author=client.user)

AsyncIterator を返すのは以下のとおりです:

イベントの変更

多くのイベントに変更がありました。

名前に server が含まれていたイベントのほとんどが、 guild を使った名前に変更されました。

変更前

  • on_server_join

  • on_server_remove

  • on_server_update

  • on_server_role_create

  • on_server_role_delete

  • on_server_role_update

  • on_server_emojis_update

  • on_server_available

  • on_server_unavailable

変更後

on_voice_state_update() イベントの引数が変更されました。

更新前

async def on_voice_state_update(before, after)

更新後

async def on_voice_state_update(member, before, after)

新しくなったイベントは、二つの Member の代わりに、 一つの Member と二つの VoiceState を受け取るようになりました。

on_guild_emojis_update() イベントの引数が変更されました。

更新前

async def on_guild_emojis_update(before, after)

更新後

async def on_guild_emojis_update(guild, before, after)

最初の引数は絵文字の更新が行われた Guild です。

on_member_ban() も引数が変更されました。

更新前

async def on_member_ban(member)

更新後

async def on_member_ban(guild, user)

変更の一つは、イベントが Member または User のどちらかを受け取れるようになったことです。 User で受け取る場合には第一引数として Guild が渡されます。

on_channel_ のようなイベントは、チャンネルタイプにより分割されました ( チャンネルタイプの分割 を参照)。

変更前

  • on_channel_delete

  • on_channel_create

  • on_channel_update

変更後

on_guild_channel_ イベントは更新される abc.GuildChannel (TextChannel および VoiceChannel)に対応しており、 on_private_channel_ イベントは更新される abc.PrivateChannel (DMChannel および GroupChannel)に対応しています。

ボイスの変更

ボイスの送信が完全に再構成されました。

主な変更点は以下のとおりです。

  • ボイスチャンネルへの接続は Client.join_voice_channel に代わって VoiceChannel.connect() になりました。

  • プレイヤーを作成せずに操作が可能になりました。(プレイヤーの保存の必要もありません)

  • 代わりに VoiceClientVoiceClient.play() を介して AudioSource の再生を要求します。

  • There are different built-in AudioSources.

  • create_ffmpeg_player/create_stream_player/create_ytdl_player have all been removed.

  • Using VoiceClient.play() will not return an AudioPlayer.

    • Instead, it's "flattened" like User -> Member is.

  • The after parameter now takes a single parameter (the error).

基本的には以下のとおりです:

更新前

vc = await client.join_voice_channel(channel)
player = vc.create_ffmpeg_player('testing.mp3', after=lambda: print('done'))
player.start()

player.is_playing()
player.pause()
player.resume()
player.stop()
# ...

更新後

vc = await channel.connect()
vc.play(discord.FFmpegPCMAudio('testing.mp3'), after=lambda e: print('done', e))
vc.is_playing()
vc.pause()
vc.resume()
vc.stop()
# ...

AudioSource の再設計により、 VoiceClient.source を介して実行中の VoiceClient のソースを変更できるようになりました。

例えば、 PCMVolumeTransformer を追加すると、ボリュームの変更ができるようになります。

vc.source = discord.PCMVolumeTransformer(vc.source)
vc.source.volume = 0.6

再設計によるさらなる利点は、再接続において遥かに柔軟性を持ったことです。

  • 音声ウェブソケットは、切断された際に自動的に再接続し、ハンドシェイクを再実行します。

  • 初期接続のハンドシェイクは、最大5回までの再試行となったので、大量の asyncio.TimeoutError に悩まされることはなくなりました。

  • VCの切断を検知すると、オーディオは停止し、再開しようとします。

    • これはサーバーリージョンの変更も含まれます。

イベントの待機

v1.0以前のバージョンでは、イベントの発生を待つ方法として Client.wait_for_messageClient.wait_for_reaction の二つの関数が用意されていました。このアプローチの欠点はライブラリが提供するイベント以外の発生を待つことが出来ない点です。

v1.0では別のイベント待機の概念が Client.wait_for() イベントのように一般化されました。

例えば、メッセージを待つ処理は以下のようになります。

# before
msg = await client.wait_for_message(author=message.author, channel=message.channel)

# after
def pred(m):
    return m.author == message.author and m.channel == message.channel

msg = await client.wait_for('message', check=pred)

複数の返り値に対応するため、 Client.wait_for() は単一の引数、引数なし、あるいは引数のタプルを返すようになっています。

例えば、リアクションを待つ処理は以下のようになります。

reaction, user = await client.wait_for('reaction_add', check=lambda r, u: u.id == 176995180300206080)

# use user and reaction

この関数は複数の引数を返すため、 timeout に設定した時間経過すると、 None を返すのではなく、 asyncio.TimeoutError を発生させるようになりました。以下はその例になります。

def pred(m):
    return m.author == message.author and m.channel == message.channel

try:

    msg = await client.wait_for('message', check=pred, timeout=60.0)
except asyncio.TimeoutError:
    await channel.send('You took too long...')
else:
    await channel.send('You said {0.content}, {0.author}.'.format(msg))

依存関係のアップグレード

ライブラリのv1.0への更新に伴い、要件が aiohttp v2.0以上へと変更されました。

これは後方互換性のない変更となるため、 aiohttp の変更点の詳細については changesmigrating ページを参照してください。

ユーザーにとって最も重要な変更点は、以下のヘルパー関数の削除です。

  • aiohttp.get

  • aiohttp.post

  • aiohttp.delete

  • aiohttp.patch

  • aiohttp.head

  • aiohttp.put

  • aiohttp.request

代わりにセッションを作成することをお勧めします。

async with aiohttp.ClientSession() as sess:
    async with sess.get('url') as resp:
        # work with resp

リクエストごとにセッションを作成するのは良いとは言えないため、変数に格納しておき、破棄しなければならない時に session.close を呼び出すのが良いでしょう。

シャーディング

シャーディングの扱いに対して大きな変更があり、現在、シャーディングは第一級オブジェクトとして扱われています。

Botアカウントを使用していて、かつ一つのプロセスでBotをシャーディングしたい場合は、 AutoShardedClient を使用してください。

このクラスは複数のプロセスを起動したり、IPCを処理することなくシャーディングが行えます。

シャーディングしたクライアントはユーザーアカウントをサポートしないことを覚えておきましょう。これは、接続の形態と状態処理の変更によるものです。

使い方は簡単です。

client = discord.AutoShardedClient()

Client の代わりに上記のようにしてください。

これは /gateway/bot エンドポイントを使って、あなたのBotに必要な数のシャードを起動します。このエンドポイントはシャードごとに1000ギルドを割り当てます。

シャードをより詳細に制御したい場合は、 shard_countshard_ids を利用してください。

# launch 10 shards regardless
client = discord.AutoShardedClient(shard_count=10)

# launch specific shard IDs in this process
client = discord.AutoShardedClient(shard_count=10, shard_ids=(1, 2, 5, 6))

コマンド拡張を利用しているユーザーのために、同様に動作する AutoShardedBot が用意されています。

接続の改善

v1.0では、自動再接続機能が大幅に強化されました。

Client.connect() には新しいキーワード引数が追加されました。再接続機能の設定を行う reconnect はデフォルトで True に設定されています。有効にすると、クライアントは全インターネットのインスタンスがオフラインになった際や、Discordが指数関数的後退によってオフラインになった際に自動で再接続を試みます。

Client.run()Client.start() にも同様のキーワード引数が追加されていますが、このさい接続機能をオフにする場合以外は指定する必要はありません。

コマンド拡張の変更

モデルのステートフル化 により、拡張モジュールの設計にもいくつかの変更があります。

コンテキストの変更

v1.0において、 Context は取得と利用の面において、多くの変更があります。

最も大きな変更点は pass_context=True が廃止され、常に Context が渡されるようになったことです。そのため以下のようになります。

# before
@bot.command()
async def foo():
    await bot.say('Hello')

# after
@bot.command()
async def foo(ctx):
    await ctx.send('Hello')

その理由として、 Context が abc.Messageable の要件を満たしていることが挙げられます。 これは TextChannelDMChannel と同等の機能を持っており、 send() を用いることで、従来の bot.say のようにDMまたはテキストチャンネルにメッセージを送信することが出来ます。古いヘルパー関数は新しい abc.Messageable インタフェースの実装に伴い削除されました。詳細は ヘルパー関数の削除 を参照してください。

Context が常に渡されるようになったため、いくつかのショートカットが追加されています。

新しいショートカット

  • authorctx.message.author のショートカットです。

  • guildctx.message.guild のショートカットです。

  • channelctx.message.channel のショートカットです。

  • mectx.message.guild.me あるいは ctx.bot.user のショートカットです。

  • voice_clientctx.message.guild.voice_client のショートカットです。

新しい機能

  • reinvoke() はコマンドを再度呼び出します。

    • クールダウンの回避に利用できます。

コンテキストのサブクラス

v1.0では、 Context を継承したサブクラスを作成し、デフォルトで実装されているものの代わりに使うことが出来ます。

例えば、コンテキストに機能の追加を行いたい場合は以下のように実装が出来ます。

class MyContext(commands.Context):
    @property
    def secret(self):
        return 'my secret here'

また、 on_message() 内で get_context()invoke() を組み合わせることであなたのカスタムコンテキストを使用できます。

class MyBot(commands.Bot):
    async def on_message(self, message):
        ctx = await self.get_context(message, cls=MyContext)
        await self.invoke(ctx)

これにより、コマンドからあなたのカスタムコンテキストにアクセスすることが可能です。

@bot.command()
async def secret(ctx):
    await ctx.send(ctx.secret)

ヘルパー関数の削除

新しい Context の変更によって、たくさんのメッセージ送信用のヘルパー関数が削除されました。

以下が削除された関数のリストです。

変更前

変更後

Bot.say

Context.send()

Bot.upload

Context.send()

Bot.whisper

ctx.author.send

Bot.type

Context.typing() または Context.trigger_typing()

Bot.reply

代替となるものはありません。

コマンドの変更

前述の通り、 pass_context=True は削除されたため、これをパラメータとして渡す必要はありません。

他に no_pm=True も削除されました。代わりに新しい組み込みチェックである guild_only() を使用してください。

BotGroupcommands 属性は辞書からエイリアスを持たないsetに変更されました。以前のような辞書を取得するには all_commands を使用してください。

コマンドインスタンスには新たな属性とプロパティが追加されました。

  1. コマンドのシグネチャを取得する signature

  2. デフォルトのシグネチャをオーバーライドする属性 usage

  3. サブコマンドのルートである親グループを取得する root_parent

GroupBot は次のように変更されました。

  • commands は エイリアスなしの set に変更されました。

    • すべてのコマンドを従来の dict で取得するには all_commands を使用してください。

チェックの変更

v1.0以前のバージョンでは check() は同期関数でしたが、 v1.0のチェックはコルーチンになりました。

この変更に加え、新たなチェックが二つ追加されました。

  • no_pm=True の代わりとなる guild_only()

  • is_owner()Client.application_info() のエンドポイントを使用してオーナーIDを取得します。

    • 実際には is_owner() という別の関数を使用して実行されます。

    • Bot.owner_id に値を指定することで自分でオーナーIDの設定ができます。

  • is_nsfw() はコマンドが実行されたチャンネルがNSFWチャンネルかどうかをチェックします。

    • これは新しく追加された TextChannel.is_nsfw() メソッドにより実行されています。

イベントの変更

すべてのコマンド拡張のイベントが変更されました。

更新前

on_command(command, ctx)
on_command_completion(command, ctx)
on_command_error(error, ctx)

更新後

on_command(ctx)
on_command_completion(ctx)
on_command_error(ctx, error)

on_command()on_command_completion()command パラメータが削除されました。 Command インスタンスは更新されておらず、正しいものではありませんでした。最新の Command インスタンスを取得するには Context.command 属性を使用してください。

Command.error()on_command_error() のようなエラーハンドラは他のイベント及びコマンドとの一貫性を保つため、最初のパラメータとして Context を使用するよう変更されました。

HelpFormatter and Help Command Changes

The HelpFormatter class has been removed. It has been replaced with a HelpCommand class. This class now stores all the command handling and processing of the help command.

The help command is now stored in the Bot.help_command attribute. As an added extension, you can disable the help command completely by assigning the attribute to None or passing it at __init__ as help_command=None.

The new interface allows the help command to be customised through special methods that can be overridden.

Certain subclasses can implement more customisable methods.

The old HelpFormatter was replaced with DefaultHelpCommand, which implements all of the logic of the old help command. The customisable methods can be found in the accompanying documentation.

The library now provides a new more minimalistic HelpCommand implementation that doesn't take as much space, MinimalHelpCommand. The customisable methods can also be found in the accompanying documentation.

A frequent request was if you could associate a help command with a cog. The new design allows for dynamically changing of cog through binding it to the HelpCommand.cog attribute. After this assignment the help command will pretend to be part of the cog and everything should work as expected. When the cog is unloaded then the help command will be "unbound" from the cog.

For example, to implement a HelpCommand in a cog, the following snippet can be used.

class MyHelpCommand(commands.MinimalHelpCommand):
    def get_command_signature(self, command):
        return '{0.clean_prefix}{1.qualified_name} {1.signature}'.format(self, command)

class MyCog(commands.Cog):
    def __init__(self, bot):
        self._original_help_command = bot.help_command
        bot.help_command = MyHelpCommand()
        bot.help_command.cog = self

    def cog_unload(self):
        self.bot.help_command = self._original_help_command

For more information, check out the relevant documentation.

コグの変更

コグは完全に刷新されました。これは コグ としてドキュメント化されています。

コグは将来的な校正のためのクラスである Cog を基底クラスとして持つ必要があります。このクラスには動作のカスタマイズのために、特別なメソッドが用意されています。

コグ内で on_message のようなリスナーを使用していた人は、 commands.Cog.listener() デコレータを用いて、リスナーを明示する必要があります。

Along with that, cogs have gained the ability to have custom names through specifying it in the class definition line. More options can be found in the metaclass that facilitates all this, commands.CogMeta.

すべての特別なメソッドを使用し、そして名前を指定したコグの例が以下のようになります:

class MyCog(commands.Cog, name='Example Cog'):
    def cog_unload(self):
        print('cleanup goes here')

    def bot_check(self, ctx):
        print('bot check')
        return True

    def bot_check_once(self, ctx):
        print('bot check once')
        return True

    async def cog_check(self, ctx):
        print('cog local check')
        return await ctx.bot.is_owner(ctx.author)

    async def cog_command_error(self, ctx, error):
        print('Error in {0.command.qualified_name}: {1}'.format(ctx, error))

    async def cog_before_invoke(self, ctx):
        print('cog local before: {0.command.qualified_name}'.format(ctx))

    async def cog_after_invoke(self, ctx):
        print('cog local after: {0.command.qualified_name}'.format(ctx))

    @commands.Cog.listener()
    async def on_message(self, message):
        pass

前後処理のフック

コマンドに、コマンドの実行前および実行後に処理が行えるようにするフックが新たに追加されました。

これは単一のパラメータとして Context を受け取り、かつコルーチンである必要があります。

また、このフックは全体、コグごと、あるいはコマンドごとに設定することが可能です。

基本形

# global hooks:

@bot.before_invoke
async def before_any_command(ctx):
    # do something before a command is called
    pass

@bot.after_invoke
async def after_any_command(ctx):
    # do something after a command is called
    pass

後処理のフックは コマンドのエラー発生に関わらず 必ず呼び出されます。そのため、データベース接続のようなリソースのクリーンアップやエラー処理に最適です。

コマンドごとに設定する方法は以下のとおりです。

@bot.command()
async def foo(ctx):
    await ctx.send('foo')

@foo.before_invoke
async def before_foo_command(ctx):
    # do something before the foo command is called
    pass

@foo.after_invoke
async def after_foo_command(ctx):
    # do something after the foo command is called
    pass

The special cog method for these is Cog.cog_before_invoke() and Cog.cog_after_invoke(), e.g.:

class MyCog(commands.Cog):
    async def cog_before_invoke(self, ctx):
        ctx.secret_cog_data = 'foo'

    async def cog_after_invoke(self, ctx):
        print('{0.command} is done...'.format(ctx))

    @commands.command()
    async def foo(self, ctx):
        await ctx.send(ctx.secret_cog_data)

Context.command_failed を使うことで、後処理でコマンドがエラーになったかを確認する事ができます。

呼び出される順序は以下のとおりです。

  1. コマンドごとの前処理。

  2. コグごとの前処理。

  3. 全体での前処理。

  4. 実行されたコマンド。

  5. コマンドごとの後処理。

  6. コグごとの後処理。

  7. 全体での後処理。

コンバーターの変更

Prior to v1.0, a converter was a type hint that could be a callable that could be invoked with a singular argument denoting the argument passed by the user as a string.

This system was eventually expanded to support a Converter system to allow plugging in the Context and do more complicated conversions such as the built-in "discord" converters.

In v1.0 this converter system was revamped to allow instances of Converter derived classes to be passed. For consistency, the convert() method was changed to always be a coroutine and will now take the two arguments as parameters.

Essentially, before:

class MyConverter(commands.Converter):
    def convert(self):
        return self.ctx.message.server.me

更新後

class MyConverter(commands.Converter):
    async def convert(self, ctx, argument):
        return ctx.me

コマンドフレームワークにも二つのコンバーターが追加されました。