Discord公式APIのラッパー、Discord.py でゲーム内でありがちなランダムなチーム分けを自動で行えるようにしてみます。
もしコードが動かないなどありましたらお知らせください
準備
メッセージ送信者のが参加しているボイスチャンネルの参加者をランダムで割り振りましょう。
ランダム部分は標準モジュールのrandom
を使って実装します。
ボイスチャンネルの取得
わかる人は飛ばしていいよ。
import discord
client = discord.Client()
@client.event
async def on_message(message):
if message.content == "!voicestate":
print(message.author.voice)
if message.content == "!voicechannel":
if message.author.voice is not None:
print(message.author.voice.channel.members)
コードの流れを真面目に解説すると、
message.authorはdiscord.Memberのクラスインスタンスに該当します。
https://discordpy.readthedocs.io/ja/latest/api.html#member
ドキュメントを見ると、voice属性を参照できるようです。これはVoiceStateクラスオブジェクトを返します。
なお、ドキュメントのoptional(オプション)とは、Noneなどの値になる可能性があるということです。

今度はVoiceStateクラスを見てみます。channel属性を参照できるようです。

channel属性、つまりVoiceChannelからメンバーのリストを取得できます。

message.author.voice.channel.members
ランダムな整数リストを生成
ここでは、事前に整数のリストを生成しておき、それをランダムに並べ替え、その各要素にメンバーを当てはめていくように実装してみます。
例えば、人数nに対してmチーム分のリストを生成します。チームごとの人数はn/mで求められるはずです。
import math
n = 8
m = 2
tmp = math.floor(n/m)
l = [team for team in range(m) for n in range(tmp)]
print(l)
始めたばかりの人には見慣れない書き方かもしれません。リスト内包表記と言います。
tmpは各チームの人数、各チームごとに人数をカウントアップし、チームの番号をリストに格納してみました。
しかし、実行してみると分かるのですが、これでは人数がチーム数で割り切れないときでも不足を補えません。
l += [i for i in range(n-len(l))]
nは人数で、len(l)は現在リストに追加されている人数ですから、その差が不足分と考えられます。その不足分をチーム番号としてこのように追加しておきましょう。
実際に8人に対して3チームと設定すれば[0, 0, 1, 1, 2, 2, 0, 1]
というリストが生成されるはずです。
こうしてできた整数のリストをランダムに並べ替えましょう。
ランダムに並べ替えるには、random
モジュールの機能であるshuffleを使用します。
result = random.shuffle(l)
実装
ランダムなリストが生成できました。最後に各メンバーに割り当てます。
import discord
import math
import random
client = discord.Client()
@client.event
async def on_message(message):
if message.content.startswith("!team"):
args = message.content.split(' ')
if len(args)>1:
await message.channel.send("チーム数を入力してください。")
return
if message.author.voice is None:
await message.channel.send("先にボイスチャンネルに接続してください。")
return
member_list = message.author.voice.channel.members
n = len(member_list)
m = int(args[1])
tmp = math.floor(n/m)
l = [team for team in range(m) for n in range(tmp)]
l += [i for i in range(n-len(l))]
result = random.shuffle(l)
reply = ""
c_num = ord("A")
for num,member in enumerate(member_list):
team_chr = chr(c_num + result[num])
reply += f"{member.name} さん / {team_chr}チーム"
await message.channel.send(reply)
client.run(token)
メンバーをボイスチャンネルに移動
このコードではボイスチャンネルから参加メンバーを取得しているわけですから、各メンバーをボイスチャンネルに移動させたくないですか?
ボイスチャンネルでの指定はBot Commands Frameworkで簡単に行えるので、そちらを併用します。
コードのみの掲載となりますが、以下の記事でチャンネル名の変換、メンバーの移動を解説しています。
import discord
from discord.ext import commands
import math
import random
bot = commmands.Bot(command_prefix="!")
@bot.command
async def team(ctx,m:int,*args):
if len(args)==0:
await ctx.send("移動先のチャンネル名を指定してください。")
return
if ctx.author.voice is None:
await ctx.send("先にボイスチャンネルに入ってください。")
return
member_list = ctx.author.voice.channel.members
ch_convert = commands.VoiceChannelConverter()
ch_list = []
for arg in args:
ch = await ch_converter.convert(ctx, arg)
ch_list.append(ch)
member_list += ch.members
n = len(members)
tmp = math.floor(n/m)
l = [team for team in range(m) for n in range(tmp)]
l += [i for i in range(n-len(l))]
result = random.shuffle(l)
reply = ""
for i in result:
member = member_list[i]
await member.move_to(ch_list[i])
c_num = ord("A")
for a in l:
team_chr = chr(c_num + a)
reply += f"{member_list[a].name} さん / {team_chr}チーム"
await ctx.send(reply)
await ctx.send("Succeeded!")