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)