🔹 関連記事
本記事はdiscordの読み上げbot作成 (Mac Intel上で作成)の続きです。
🔹 実装方法
- ボットがVCに参加したら、定期的にVCの状態をチェック
- VCに他のメンバーがいなくなったら自動で退出
✅ 実装
discord.ext.tasks
を使って、定期的にVCのメンバー数を確認し、誰もいなければ切断します。
コード全体
import discord
import requests
from discord import app_commands
from discord.ext import commands, tasks
from pydub import AudioSegment
TOKEN = "YOUR_BOT_TOKEN" # 🔴 自分のBotトークンを入れてください
VOICEVOX_URL = "http://localhost:50021"
intents = discord.Intents.default()
intents.message_content = True
intents.voice_states = True # VCの状態を取得するために必要
bot = commands.Bot(command_prefix="/", intents=intents)
# ボットが接続しているVCのテキストチャンネルを記録
vc_text_channels = {}
@bot.event
async def on_ready():
print(f"✅ {bot.user} が起動しました!")
try:
synced = await bot.tree.sync()
print(f"✅ スラッシュコマンド {len(synced)} 個を同期しました!")
except Exception as e:
print(f"❌ スラッシュコマンドの同期に失敗: {e}")
@bot.tree.command(name="join", description="ボイスチャットに参加します")
async def join(interaction: discord.Interaction):
"""ボットをVCに参加させる"""
if interaction.user.voice:
channel = interaction.user.voice.channel
vc = await channel.connect()
vc_text_channels[interaction.guild.id] = interaction.channel.id # ボットが接続したVCのテキストチャンネルを記録
await interaction.response.send_message("✅ ボイスチャンネルに参加しました!")
# 🔴 チェックループが動作していない場合は開始
if not check_empty_vc.is_running():
check_empty_vc.start(interaction.guild)
else:
await interaction.response.send_message("❌ 先にボイスチャンネルに参加してください!", ephemeral=True)
@bot.tree.command(name="disconnect", description="ボイスチャットから退出")
async def disconnect(interaction: discord.Interaction):
"""ボットをVCから切断"""
if interaction.guild.voice_client:
await interaction.guild.voice_client.disconnect()
vc_text_channels.pop(interaction.guild.id, None) # 記録していたテキストチャンネルIDを削除
check_empty_vc.cancel() # 自動切断のチェックを停止
await interaction.response.send_message("✅ ボイスチャンネルから切断しました!")
else:
await interaction.response.send_message("❌ ボットはVCにいません!", ephemeral=True)
@tasks.loop(seconds=2)
async def check_empty_vc(guild: discord.Guild):
"""VCのメンバーが0人になったら自動で切断"""
if guild.voice_client:
vc = guild.voice_client.channel
members = [member for member in vc.members if not member.bot] # 人間のみをカウント
if len(members) == 0:
print(f"⚠️ {vc.name} に誰もいないため、自動切断します")
await guild.voice_client.disconnect()
check_empty_vc.cancel() # ループを停止
@bot.event
async def on_message(message):
"""ボットが接続しているVCのテキストチャンネルのメッセージのみを読み上げ"""
if message.author == bot.user or message.author.bot:
return # ボット自身や他のBotのメッセージは無視
# ボットがVCに接続していなければ無視
if not message.guild or not message.guild.voice_client:
return
# 読み上げるメッセージ内容
text = message.content
# ボットが接続しているVCのテキストチャンネルか確認
if message.guild.id not in vc_text_channels or message.channel.id != vc_text_channels[message.guild.id]:
print(f"対象外のメッセージを無視: {text}")
return # VCのテキストチャンネル以外は無視
# VOICEVOXで音声合成
query_payload = {"text": text, "speaker": 1}
query_response = requests.post(f"{VOICEVOX_URL}/audio_query", params=query_payload)
if query_response.status_code != 200:
return # エラーなら無視
query_data = query_response.json()
synthesis_response = requests.post(f"{VOICEVOX_URL}/synthesis", json=query_data, params={"speaker": 1})
if synthesis_response.status_code != 200:
return # エラーなら無視
# 音声を保存(WAV形式のまま)
wav_path = "voice.wav"
with open(wav_path, "wb") as f:
f.write(synthesis_response.content)
# VCで再生(WAVをそのまま)
source = discord.FFmpegPCMAudio(wav_path)
message.guild.voice_client.play(source)
await bot.process_commands(message) # 他のコマンドも処理
bot.run(TOKEN)
🔹 実装のポイント
-
tasksモジュールをインポート
from discord.ext import commands, tasks
の箇所 -
VCに接続 (
/join
) すると、自動切断の監視を開始
check_empty_vc.start(interaction.guild)
の箇所 -
@tasks.loop(seconds=2)
を使って2秒ごとにVCの人数をチェック - ボット以外の人間がVCにいない場合、自動で切断
-
VCから退出 (
/disconnect
) すると、監視を停止
check_empty_vc.cancel()
の箇所
✨ 使い方
/join
でボットをVCに招待- VCに誰もいなくなると、2秒後にボットが自動で退出
- 手動で退出したい場合は
/disconnect