[Python] Bot translator in discord

Discussão em 'Python' iniciado por Stack, Julho 10, 2025.

  1. Stack

    Stack Membro Participativo

    I was trying to create a bot in discord that would perform the function of double translation in voice channels, this code is written through the deepseek neural network. I ran into such a problem that when downloading discord.py [voice] discord is not installed.sinks, without which, as I understand it, work is not possible, I cannot find this library separately. And if possible, write down what can be added, corrected, and how best to do it.

    import os
    import asyncio
    import discord
    from discord.ext import commands
    from discord.sinks import MP3Sink # Импорт из стандартной библиотеки
    from transformers import (
    WhisperForConditionalGeneration,
    WhisperProcessor,
    AutoTokenizer,
    AutoModelForSeq2SeqLM
    )
    from gtts import gTTS
    from io import BytesIO
    import logging

    # Настройка логирования
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger(__name__)

    # Конфигурация (замените на свои значения)
    DISCORD_TOKEN = "---------------------"
    VOICE_CHANNEL_ID = 937370496989806607 # ID голосового канала
    TEXT_CHANNEL_ID = 937370496989806605 # ID текстового канала

    # Модели
    WHISPER_MODEL_NAME = "openai/whisper-large-v2"
    NLLB_MODEL_NAME = "facebook/nllb-200-distilled-600M"

    # Языки
    SOURCE_LANG = "eng_Latn"
    TARGET_LANG = "rus_Cyrl"

    class VoiceTranslateBot(commands.Bot):
    def __init__(self, **kwargs):
    intents = discord.Intents.all()
    super().__init__(command_prefix='!', intents=intents)
    self.voice_client = None
    self.whisper_processor = None
    self.whisper_model = None
    self.nllb_tokenizer = None
    self.nllb_model = None
    self.audio_queue = asyncio.Queue()

    async def setup_hook(self):
    await self.load_models()
    await self.connect_to_voice()
    self.loop.create_task(self.audio_processing_loop())

    async def load_models(self):
    logger.info("Загружаем модели...")
    self.whisper_processor = WhisperProcessor.from_pretrained(WHISPER_MODEL_NAME)
    self.whisper_model = WhisperForConditionalGeneration.from_pretrained(WHISPER_MODEL_NAME).to('cuda')
    self.nllb_tokenizer = AutoTokenizer.from_pretrained(NLLB_MODEL_NAME)
    self.nllb_model = AutoModelForSeq2SeqLM.from_pretrained(NLLB_MODEL_NAME).to('cuda')
    logger.info("Модели загружены.")

    async def connect_to_voice(self):
    channel = self.get_channel(VOICE_CHANNEL_ID)
    if channel:
    self.voice_client = await channel.connect()

    # Начинаем запись с использованием MP3Sink
    self.voice_client.start_recording(
    MP3Sink(),
    self.on_voice_data,
    self.loop
    )
    logger.info("Подключено и начата запись.")
    else:
    logger.warning("Голосовой канал не найден!")

    async def on_voice_data(self, sink, audio_data, *args):
    """Callback при получении аудиоданных"""
    for user_id, data in audio_data.items():
    if user_id != self.user.id: # Игнорируем собственный голос
    await self.audio_queue.put(data.file.read())

    async def audio_processing_loop(self):
    """Цикл обработки аудио"""
    while True:
    audio_bytes = await self.audio_queue.get()
    try:
    text = await self.transcribe_audio(audio_bytes)
    if text.strip():
    await self.process_translation(text)
    except Exception as e:
    logger.error(f"Ошибка обработки аудио: {e}")

    async def transcribe_audio(self, audio_bytes):
    """Распознавание речи через Whisper"""
    with open("temp_input.wav", "wb") as f:
    f.write(audio_bytes)

    from transformers import pipeline
    transcriber = pipeline(
    "automatic-speech-recognition",
    model=self.whisper_model,
    tokenizer=self.whisper_processor,
    device=0
    )
    result = transcriber("temp_input.wav")
    os.remove("temp_input.wav")
    return result['text']

    def translate_text(self, text):
    """Перевод текста через NLLB"""
    self.nllb_tokenizer.src_lang = SOURCE_LANG
    encoded = self.nllb_tokenizer(text, return_tensors="pt").to('cuda')
    forced_bos_token_id = self.nllb_tokenizer.lang_code_to_id[TARGET_LANG]
    generated_ids = self.nllb_model.generate(**encoded, forced_bos_token_id=forced_bos_token_id)
    return self.nllb_tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]

    def synthesize_speech(self, text, lang='ru'):
    """Синтез речи через gTTS"""
    tts = gTTS(text=text, lang=lang)
    fp = BytesIO()
    tts.write_to_fp(fp)
    fp.seek(0)
    return fp

    async def process_translation(self, text):
    """Обработка перевода"""
    translated = self.translate_text(text)
    logger.info(f"Перевод: {translated}")

    audio_fp = self.synthesize_speech(translated)
    await self.play_audio(audio_fp)

    async def play_audio(self, audio_fp):
    """Воспроизведение аудио"""
    source = discord.FFmpegPCMAudio(audio_fp, pipe=True)
    if self.voice_client.is_playing():
    self.voice_client.stop()
    self.voice_client.play(source)

    async def on_ready(self):
    logger.info(f"Бот запущен как {self.user}")

    # Запуск бота
    bot = VoiceTranslateBot()
    bot.run("-------")


    Log -

    E:\Translator\Python>python voice_translate_discord.py
    Traceback (most recent call last):
    File "E:\Translator\Python\voice_translate_discord.py", line 5, in <module>
    from discord.sinks import MP3Sink # Импорт из стандартной библиотеки
    ModuleNotFoundError: No module named 'discord.sinks'


    New code -

    import os
    import asyncio
    import logging
    from io import BytesIO

    import discord
    from discord.ext import commands
    from discord.sinks import MP3Sink
    from discord.sinks.core import Filters
    from transformers import (
    WhisperForConditionalGeneration,
    WhisperProcessor,
    AutoTokenizer,
    AutoModelForSeq2SeqLM
    )
    from gtts import gTTS
    from transformers import pipeline

    # Настройка базового логирования
    logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
    logging.StreamHandler(),
    logging.FileHandler('bot.log', encoding='utf-8')
    ]
    )
    logger = logging.getLogger(__name__)

    # Конфигурация
    DISCORD_TOKEN = "--------------"
    VOICE_CHANNEL_ID = 937370496989806607
    TEXT_CHANNEL_ID = 937370496989806605

    # Модели
    WHISPER_MODEL_NAME = "openai/whisper-large-v2"
    NLLB_MODEL_NAME = "facebook/nllb-200-distilled-600M"

    # Языки
    SOURCE_LANG = "eng_Latn"
    TARGET_LANG = "rus_Cyrl"

    class VoiceTranslateBot(commands.Bot):
    def __init__(self, **kwargs):
    intents = discord.Intents.all()
    super().__init__(command_prefix='!', intents=intents)
    self.voice_client = None
    self.whisper_processor = None
    self.whisper_model = None
    self.nllb_tokenizer = None
    self.nllb_model = None
    self.audio_queue = asyncio.Queue()
    self.recording_sink = None

    async def setup_hook(self):
    await self.load_models()
    self.loop.create_task(self.audio_processing_loop())

    async def load_models(self):
    """Загрузка моделей обработки языка"""
    logger.info("Загружаем модели...")
    try:
    self.whisper_processor = WhisperProcessor.from_pretrained(WHISPER_MODEL_NAME)
    self.whisper_model = WhisperForConditionalGeneration.from_pretrained(WHISPER_MODEL_NAME).to('cuda')
    self.nllb_tokenizer = AutoTokenizer.from_pretrained(NLLB_MODEL_NAME)
    self.nllb_model = AutoModelForSeq2SeqLM.from_pretrained(NLLB_MODEL_NAME).to('cuda')
    logger.info("Модели успешно загружены.")
    except Exception as e:
    logger.error(f"Ошибка загрузки моделей: {e}")
    raise

    @commands.command()
    async def join(self, ctx):
    """Подключиться к голосовому каналу пользователя"""
    try:
    if ctx.author.voice is None:
    await ctx.send("Вы не в голосовом канале!")
    return

    if self.voice_client and self.voice_client.is_connected():
    await ctx.send("Бот уже подключен к голосовому каналу!")
    return

    self.voice_client = await ctx.author.voice.channel.connect(timeout=30.0, reconnect=True)

    # Настройка аудио захвата
    self.recording_sink = MP3Sink(
    filters=Filters(
    filter_users=[self.user.id],
    filter_audio_data=lambda data: True
    ),
    output_path="recordings"
    )

    self.voice_client.start_recording(
    self.recording_sink,
    self.on_voice_data,
    self.loop
    )

    await ctx.send(f"Подключился к каналу {ctx.author.voice.channel.name} и начал запись!")
    logger.info(f"Подключение к голосовому каналу {ctx.author.voice.channel.id}")

    except discord.Forbidden:
    await ctx.send("Ошибка: Нет прав на подключение к голосовому каналу!")
    logger.error("Нет прав на подключение к голосовому каналу")
    except discord.HTTPException as e:
    await ctx.send(f"Ошибка сети: {e}")
    logger.error(f"Ошибка сети при подключении: {e}")
    except Exception as e:
    await ctx.send(f"Неизвестная ошибка: {e}")
    logger.error(f"Ошибка подключения: {e}")

    @commands.command()
    async def leave(self, ctx):
    """Отключиться от голосового канала"""
    if not self.voice_client or not self.voice_client.is_connected():
    await ctx.send("Бот не подключен к голосовому каналу!")
    return

    try:
    if self.voice_client.is_recording():
    self.voice_client.stop_recording()

    await self.voice_client.disconnect()
    self.voice_client = None
    await ctx.send("Отключился от голосового канала!")
    logger.info("Отключение от голосового канала")
    except Exception as e:
    await ctx.send(f"Ошибка отключения: {e}")
    logger.error(f"Ошибка отключения: {e}")

    async def on_voice_data(self, sink, audio_data):
    """Обработка входящих аудиоданных"""
    try:
    for user_id, data in audio_data.items():
    user = self.get_user(user_id)
    logger.info(f"Получены аудиоданные от {user.name if user else user_id}")
    await self.audio_queue.put((user_id, data.file.read()))
    except Exception as e:
    logger.error(f"Ошибка обработки аудиоданных: {e}")

    async def audio_processing_loop(self):
    """Основной цикл обработки аудио"""
    while True:
    user_id, audio_bytes = await self.audio_queue.get()
    try:
    text = await self.transcribe_audio(audio_bytes)
    if text.strip():
    await self.process_translation(user_id, text)
    except Exception as e:
    logger.error(f"Ошибка обработки аудио: {e}")

    async def transcribe_audio(self, audio_bytes):
    """Распознавание речи через Whisper"""
    try:
    temp_file = "temp_input.wav"
    with open(temp_file, "wb") as f:
    f.write(audio_bytes)

    transcriber = pipeline(
    "automatic-speech-recognition",
    model=self.whisper_model,
    tokenizer=self.whisper_processor,
    device=0
    )
    result = transcriber(temp_file)
    os.remove(temp_file)
    return result['text']
    except Exception as e:
    logger.error(f"Ошибка транскрибации: {e}")
    return ""

    def translate_text(self, text):
    """Перевод текста через NLLB"""
    try:
    self.nllb_tokenizer.src_lang = SOURCE_LANG
    encoded = self.nllb_tokenizer(text, return_tensors="pt").to('cuda')
    forced_bos_token_id = self.nllb_tokenizer.lang_code_to_id[TARGET_LANG]
    generated_ids = self.nllb_model.generate(**encoded, forced_bos_token_id=forced_bos_token_id)
    return self.nllb_tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
    except Exception as e:
    logger.error(f"Ошибка перевода: {e}")
    return text

    def synthesize_speech(self, text, lang='ru'):
    """Синтез речи через gTTS"""
    try:
    tts = gTTS(text=text, lang=lang)
    fp = BytesIO()
    tts.write_to_fp(fp)
    fp.seek(0)
    return fp
    except Exception as e:
    logger.error(f"Ошибка синтеза речи: {e}")
    return None

    async def process_translation(self, user_id, text):
    """Обработка перевода"""
    try:
    translated = self.translate_text(text)
    logger.info(f"Перевод от пользователя {user_id}: {translated}")

    if audio_fp := self.synthesize_speech(translated):
    await self.play_audio(audio_fp)
    except Exception as e:
    logger.error(f"Ошибка обработки перевода: {e}")

    async def play_audio(self, audio_fp):
    """Воспроизведение аудио"""
    try:
    if not self.voice_client or not self.voice_client.is_connected():
    logger.warning("Невозможно воспроизвести: нет подключения к голосовому каналу")
    return

    source = discord.FFmpegPCMAudio(audio_fp, pipe=True)
    if self.voice_client.is_playing():
    self.voice_client.stop()

    self.voice_client.play(source)
    except Exception as e:
    logger.error(f"Ошибка воспроизведения аудио: {e}")

    async def on_ready(self):
    """Обработчик события готовности бота"""
    logger.info(f"Бот запущен как {self.user} (ID: {self.user.id})")
    logger.info(f"Доступные серверы: {len(self.guilds)}")
    for guild in self.guilds:
    logger.info(f"- {guild.name} (ID: {guild.id})")

    if __name__ == "__main__":
    # Проверка зависимостей
    try:
    from discord.sinks import MP3Sink
    from discord.sinks.core import Filters
    except ImportError as e:
    logger.error(
    "Требуется установить зависимости:\n"
    "pip install py-cord[voice] transformers gtts\n"
    f"Ошибка: {e}"
    )
    raise

    # Создание необходимых директорий
    os.makedirs("recordings", exist_ok=True)

    # Запуск бота
    bot = VoiceTranslateBot()
    bot.run("---------------")


    Log -

    E:\Translator\Python>python voice_translate_discord.py
    2025-07-10 16:07:07,467 - discord.client - INFO - logging in using static token
    2025-07-10 16:07:08,229 - discord.gateway - INFO - Shard ID None has sent the IDENTIFY payload.
    2025-07-10 16:07:08,497 - discord.gateway - INFO - Shard ID None has connected to Gateway: ["gateway-prd-us-east1-b-zn7s",{"micros":95814,"calls":["id_created",{"micros":387,"calls":[]},"session_lookup_time",{"micros":535,"calls":[]},"session_lookup_finished",{"micros":11,"calls":[]},"discord-sessions-prd-2-46",{"micros":94607,"calls":["start_session",{"micros":61890,"calls":["discord-api-rpc-5758b8d864-csz2n",{"micros":44247,"calls":["get_user",{"micros":14425},"get_guilds",{"micros":3725},"send_scheduled_deletion_message",{"micros":13},"guild_join_requests",{"micros":2},"authorized_ip_coro",{"micros":12},"pending_payments",{"micros":4408},"apex_experiments",{"micros":6542},"user_activities",{"micros":3},"played_application_ids",{"micros":2}]}]},"starting_guild_connect",{"micros":29,"calls":[]},"presence_started",{"micros":8920,"calls":[]},"guilds_started",{"micros":71,"calls":[]},"lobbies_started",{"micros":1,"calls":[]},"guilds_connect",{"micros":0,"calls":[]},"presence_connect",{"micros":23675,"calls":[]},"connect_finished",{"micros":23679,"calls":[]},"build_ready",{"micros":15,"calls":[]},"clean_ready",{"micros":0,"calls":[]},"optimize_ready",{"micros":1,"calls":[]},"split_ready",{"micros":0,"calls":[]}]}]}] (Session ID: dd0d615ff919f6b371a568dce7816632).
    2025-07-10 16:07:10,508 - __main__ - INFO - Бот запущен как Translator bot#8792 (ID: 1392200178802360560)
    2025-07-10 16:07:10,508 - __main__ - INFO - Доступные серверы: 1
    2025-07-10 16:07:10,509 - __main__ - INFO - - Сервер cRATE (ID: 937370496989806602)
    Ignoring exception in command None:
    discord.ext.commands.errors.CommandNotFound: Command "join" is not found

    Continue reading...

Compartilhe esta Página