discord_peak_bot/app/classes/MessageReader.py

102 lines
4.7 KiB
Python
Raw Normal View History

2024-08-03 11:18:28 +00:00
import uuid
import discord
import os
import re
import asyncio
import logging
class MessageReader:
# 半角カタカナと数字と全角へのマッピング
half_width = "アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲンァィゥェォッャュョー"
full_width = "アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲンアイウエオッヤユヨー"
translation_table = str.maketrans(half_width, full_width)
# 濁点・半濁点を含む半角カタカナの変換マッピング
dakuten_map = {
'ガ': '', 'ギ': '', 'グ': '', 'ゲ': '', 'ゴ': '',
'ザ': '', 'ジ': '', 'ズ': '', 'ゼ': '', 'ゾ': '',
'ダ': '', 'ヂ': '', 'ヅ': '', 'デ': '', 'ド': '',
'バ': '', 'ビ': '', 'ブ': '', 'ベ': '', 'ボ': '',
'パ': '', 'ピ': '', 'プ': '', 'ペ': '', 'ポ': ''
}
def __init__(self, voice_synthesizer, bot):
self.voice_synthesizer = voice_synthesizer
self.queue = asyncio.Queue()
self.is_reading = False
self.bot = bot
async def read_message(self, message, word_dictionary, ffmpeg_executable_path, volume=1.0):
"""メッセージを読み上げるための処理を開始する"""
await self.queue.put((message, word_dictionary, ffmpeg_executable_path, volume))
if not self.is_reading:
await self._process_queue()
async def _process_queue(self):
"""メッセージキューを処理する"""
self.is_reading = True
while not self.queue.empty():
message, word_dictionary, ffmpeg_executable_path, volume = await self.queue.get()
try:
await self._synthesize_and_play(message, word_dictionary, ffmpeg_executable_path, volume)
except Exception as e:
logging.error(f"Failed to read message: {e}")
self.is_reading = False
def _convert_to_full_width(self, text):
"""テキスト内の半角カタカナを全角カタカナに変換する"""
for half, full in self.dakuten_map.items():
text = text.replace(half, full)
text = text.translate(self.translation_table)
return text
async def _synthesize_and_play(self, message, word_dictionary, ffmpeg_executable_path, volume):
"""メッセージの音声合成を行い、再生する"""
if not isinstance(message, str):
if self.bot.is_name_reading:
message.content = f'{message.author.display_name}さん、{message.content}'
for user in message.mentions:
message.content = message.content.replace(f'<@{user.id}>', user.display_name)
message.content = message.content.replace(f'<@!{user.id}>', user.display_name)
for role in message.role_mentions:
message.content = message.content.replace(f'<@&{role.id}>', role.name)
for channel in message.channel_mentions:
message.content = message.content.replace(f'<#{channel.id}>', f'#{channel.name}')
message.content = re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\\-]+', 'URL省略', message.content)
message.content = re.sub(r'<:[a-zA-Z0-9_]+:[0-9]+>', '', message.content)
message.content = self._convert_to_full_width(message.content)
message.content = message.content.replace('\n', ' ')
voice_client = message.guild.voice_client
play_message = message.content
id = message.id
else:
voice_client = self.bot.independence_chat.voice_client
play_message = message
id = str(uuid.uuid4())
if voice_client and voice_client.is_connected():
script = word_dictionary.convert_text(play_message)
if len(script) > 140:
script = script[:140]
output_path = f"{id}.wav"
output_path = await self.voice_synthesizer.synthesize(script, output_path=output_path)
if not os.path.exists(output_path):
logging.error(f"Expected audio file not found: {output_path}")
return
audio_source = discord.FFmpegPCMAudio(executable=ffmpeg_executable_path, source=output_path)
volume_adjusted_source = discord.PCMVolumeTransformer(audio_source, volume=volume)
voice_client.play(volume_adjusted_source, after=lambda e: os.remove(output_path))
while voice_client.is_playing():
await asyncio.sleep(1)