discord_intro_quiz_bot/bot/app.py

175 lines
6.9 KiB
Python

from flask import Flask, request, jsonify
import logging
import discord
from discord.ext import commands, tasks
from discord import VoiceChannel
import asyncio
app = Flask(__name__)
app.config['DEBUG'] = True
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Discord Botインスタンスを保持するための変数
bot_instance = None
quiz_message_id = None
def create_response(error_message=None, success_message=None):
if error_message:
logger.error(error_message)
return jsonify({"error": error_message}), 500
if success_message:
logger.info(success_message)
return jsonify({"message": success_message}), 200
@app.route('/join', methods=['POST'])
def join_via_api():
if bot_instance is None:
return create_response(error_message="BOTが初期化されていません")
channel_id = request.json.get('channel_id')
logger.info(f"リクエストされたチャンネルID: {channel_id}")
if not channel_id:
return create_response(error_message="チャンネルIDが必要です")
try:
channel_id = int(channel_id)
except ValueError:
return create_response(error_message=f"無効なチャンネルIDです: {channel_id}")
channel = bot_instance.get_channel(channel_id)
if channel:
logger.info(f"チャンネルが見つかりました: {channel.name}")
if isinstance(channel, VoiceChannel):
bot_instance.loop.create_task(channel.connect())
return create_response(success_message=f'{channel.name}に参加します')
else:
return create_response(error_message=f"ID {channel_id} はボイスチャンネルではありません")
else:
return create_response(error_message="指定されたチャンネルが見つかりません")
@app.route('/leave', methods=['POST'])
def leave_via_api():
if bot_instance is None:
return create_response(error_message="BOTが初期化されていません")
if bot_instance.voice_clients:
for vc in bot_instance.voice_clients:
bot_instance.loop.create_task(vc.disconnect())
return create_response(success_message="ボイスチャンネルから切断しました")
return create_response(error_message="ボイスチャンネルに接続されていません")
@app.route('/play', methods=['POST'])
def play_via_api():
if bot_instance is None:
return create_response(error_message="BOTが初期化されていません")
filename = request.json.get('filename')
volume = request.json.get('volume', 100)
try:
volume = float(volume) / 100.0
except ValueError:
return create_response(error_message="無効な音量値です")
if bot_instance.voice_clients:
for vc in bot_instance.voice_clients:
audio_source = discord.FFmpegPCMAudio(f'bucket/{filename}', options=f"-filter:a 'volume={volume}'")
vc.play(audio_source)
return create_response(success_message=f'再生中: {filename}')
return create_response(error_message="ボイスチャンネルに接続されていません")
@app.route('/start_quiz', methods=['POST'])
def start_quiz():
if bot_instance is None:
return create_response(error_message="BOTが初期化されていません")
data = request.json
intro_audio_name = data.get('intro_audio_name')
volume = data.get('volume', 100)
if not intro_audio_name:
return create_response(error_message="イントロ音源名が必要です")
try:
volume = float(volume) / 100.0
except ValueError:
return create_response(error_message="無効な音量値です")
if bot_instance.voice_clients:
for vc in bot_instance.voice_clients:
if not vc.is_connected():
return create_response(error_message="ボイスチャンネルに接続されていません")
# クイズ回答用のメッセージをVCチャットに送信
asyncio.run_coroutine_threadsafe(send_quiz_message(vc.channel, intro_audio_name, volume), bot_instance.loop)
return create_response(success_message=f'イントロ音源再生準備中: {intro_audio_name}')
return create_response(error_message="ボイスチャンネルに接続されていません")
@app.route('/stop_audio', methods=['POST'])
def stop_audio():
if bot_instance is None:
return create_response(error_message="BOTが初期化されていません")
if bot_instance.voice_clients:
for vc in bot_instance.voice_clients:
if vc.is_playing():
vc.stop()
return create_response(success_message="再生を停止しました")
return create_response(error_message="再生中の音源がありません")
async def send_quiz_message(channel, intro_audio_name, volume):
global quiz_message_id
button = discord.ui.Button(label="回答", custom_id="quiz_answer")
view = discord.ui.View()
view.add_item(button)
message = await channel.send("クイズが開始されました!", view=view)
quiz_message_id = message.id
# インターバルを設ける
await asyncio.sleep(5) # 5秒間待機
# イントロ音源を再生
for vc in bot_instance.voice_clients:
if vc.channel == channel:
audio_source = discord.FFmpegPCMAudio(f'bucket/{intro_audio_name}', options=f"-filter:a 'volume={volume}'")
vc.play(audio_source)
await disable_button_after_timeout(message)
async def disable_button_after_timeout(message):
await asyncio.sleep(30)
view = discord.ui.View()
for action_row in message.components:
for item in action_row.children:
item.disabled = True
view.add_item(discord.ui.Button.from_component(item))
await message.edit(view=view)
await message.delete()
def run_app():
app.run(host='0.0.0.0', port=80, use_reloader=False)
def set_bot_instance(bot):
global bot_instance
bot_instance = bot
@bot.event
async def on_interaction(interaction):
global quiz_message_id
global bot_instance
if interaction.data.get('custom_id') == "quiz_answer":
if interaction.message.id == quiz_message_id:
quiz_message_id = None
await interaction.response.send_message(f"{interaction.user.mention}が回答ボタンを押しました!", ephemeral=False)
if bot_instance.voice_clients:
for vc in bot_instance.voice_clients:
if vc.is_playing():
vc.stop()
view = discord.ui.View()
for action_row in interaction.message.components:
for item in action_row.children:
item.disabled = True
view.add_item(discord.ui.Button.from_component(item))
await interaction.message.edit(view=view)