discord_peak_bot/app/bot.py

176 lines
7.4 KiB
Python
Raw Permalink Normal View History

2024-08-03 11:18:28 +00:00
import discord
import signal
import asyncio
import logging
import os
from utils.file_utils import read_text
from config import Config
from classes.VoiceSynthesizer import VoiceSynthesizer
from classes.WordDictionary import WordDictionary
from classes.CommandHandler import CommandHandler
from classes.VoiceChannelManager import VoiceChannelManager
from classes.MessageReader import MessageReader
from classes.IndependenceChat import IndependenceChat
from threading import Thread
from flask import Flask, request, jsonify
logging.basicConfig(level=logging.INFO)
class MyDiscordBot(discord.Client):
def __init__(self, config, intents, system_setting_file_path="config/system_setting.txt"):
super().__init__(intents=intents)
self.config = config
self.system_setting_file_path = system_setting_file_path
self.voice_synthesizer = VoiceSynthesizer(
config["voicepeak_executable_path"],
config["voicevox_url"],
config["default_navigator"]
)
self.word_dictionary = WordDictionary()
self.prefix = config["prefix"]
self.target_channel_id = None
self.volume = 1.0
self.emotion_happy = 50
self.emotion_sad = 50
self.emotion_angry = 0
self.emotion_fun = 0
self.is_name_reading = False
self.is_independence_chat = False
self.independence_chat = IndependenceChat(self)
self.command_handler = CommandHandler(self)
self.voice_channel_manager = VoiceChannelManager()
self.message_reader = MessageReader(self.voice_synthesizer, self)
self.independence_channel_ids = config["independence_channel_ids"]
self.independence_timers = []
# デバッグモードのチェック
self.debug_mode_file = "debug_mode.txt"
self.debug_mode_enabled = os.path.exists(self.debug_mode_file)
# シグナルハンドラを設定
signal.signal(signal.SIGINT, self.shutdown)
signal.signal(signal.SIGTERM, self.shutdown)
# Flaskサーバーを別スレッドで起動
self.start_flask_server()
def start_flask_server(self):
app = Flask(__name__)
@app.route('/debug', methods=['POST'])
def toggle_debug():
data = request.json
if 'enable' not in data:
return jsonify({'error': 'Invalid request'}), 400
if data['enable']:
open(self.debug_mode_file, 'w').close()
self.debug_mode_enabled = True
logging.info("デバッグモードが有効になりました。")
asyncio.run_coroutine_threadsafe(self.independence_chat.start_debug_mode_check(), self.loop)
else:
if os.path.exists(self.debug_mode_file):
os.remove(self.debug_mode_file)
self.debug_mode_enabled = False
logging.info("デバッグモードが無効になりました。")
return jsonify({'success': True})
def run_flask():
app.run(host='0.0.0.0', port=5000)
flask_thread = Thread(target=run_flask)
flask_thread.daemon = True
flask_thread.start()
def reset_emotion(self):
"""感情の設定をリセットし、音声合成器に反映させる"""
self.emotion_happy = 50
self.emotion_sad = 50
self.emotion_angry = 0
self.emotion_fun = 0
self.voice_synthesizer.set_emotion(self.emotion_happy, self.emotion_sad, self.emotion_angry, self.emotion_fun)
self.voice_synthesizer.set_pitch(50)
async def on_ready(self):
"""ボットが準備完了時に実行されるイベントハンドラ"""
self.voice_synthesizer.set_emotion(self.emotion_happy, self.emotion_sad, self.emotion_angry, self.emotion_fun)
self.voice_synthesizer.set_pitch(50)
await self.set_bot_status(f"タイキチュウ… | {self.prefix}help")
logging.info(f'{self.user} がログインしました')
# 独立チャットモードの自動参加を開始
await self.independence_chat.start_independence_mode_check()
async def on_message(self, message):
"""メッセージ受信時に実行されるイベントハンドラ"""
await self.command_handler.handle_message(message)
async def on_voice_state_update(self, member, before, after):
"""ボイスチャンネルの状態が更新された時に実行されるイベントハンドラ"""
try:
if before.channel is None and after.channel is not None:
# ユーザーがVCに参加した場合
if self.is_independence_chat and after.channel == self.voice_channel_manager.get_connected_channel(member.guild):
greeting = await self.independence_chat.generate_greeting(member.display_name)
await self.message_reader.read_message(greeting, self.word_dictionary, self.config["ffmpeg_executable_path"], self.volume)
elif before.channel and not after.channel and before.channel == self.voice_channel_manager.get_connected_channel(member.guild):
# ユーザーがVCから退出した場合
members = [m for m in before.channel.members if m != self.user]
if not members:
self.target_channel_id = None
await self.voice_channel_manager.leave_voice_channel(before.channel.guild, self)
except Exception as e:
logging.error(f"Failed to update voice state: {e}")
raise
def is_valid_channel(self, message):
"""メッセージが有効なチャンネルであるかどうかを確認する"""
if self.target_channel_id and message.channel.id != self.target_channel_id:
return False
if not message.guild.voice_client or not message.guild.voice_client.is_connected():
return False
return True
def enable_independence_chat(self, voice_client):
"""独立チャット機能を有効にする"""
system_setting = read_text(self.system_setting_file_path)
self.is_independence_chat = True
self.independence_chat.enable(system_setting, voice_client, self.config["independence_character_name"])
def disable_independence_chat(self):
"""独立チャット機能を無効にする"""
self.is_independence_chat = False
self.independence_chat.disable()
def shutdown(self, signum, frame):
"""シグナルを受け取ってシャットダウンする"""
logging.info("シャットダウン中...")
asyncio.create_task(self.cleanup())
async def cleanup(self):
"""クリーンアップ処理"""
for guild in self.guilds:
if guild.voice_client and guild.voice_client.is_connected():
await self.voice_channel_manager.leave_voice_channel(guild, self)
await self.close()
async def set_bot_status(self, status):
"""ステータスを変更する"""
activity = None
if status:
activity = discord.Activity(type=discord.ActivityType.listening, name=status)
await self.change_presence(activity=activity)
if __name__ == "__main__":
config = Config.load_config()
intents = discord.Intents.default()
intents.guilds = True
intents.messages = True
intents.message_content = True
intents.voice_states = True
bot = MyDiscordBot(config, intents)
bot.run(config["discord_token"])