Прослушивание голосового канала discord.py

В общем такая ситуация, я хочу чтобы бот заходил в голосовой канал (если там кто-то есть), и прослушивал разговор, записывая его в файл для дальнейшей манипуляции с ним. С заходом в голосовой канал всё понятно, но вот как слушать звук? Порылся немного в документации и нашёл voice_client.source, но этот код безуспешно выводит None:

audio_data = ctx.voice_client.source
while True:
    print(audio_data)

Как я понял, надо создать отдельный аудио поток opus, но как это сделать я хз вообще. Кто может помочь разобраться?


Ответы (1 шт):

Автор решения: denisnumb

Вы можете использовать библиотеку PyCord.

pip install py-cord

Там у класса VoiceClient есть метод start_recording, позволяющий записывать звук пользователей в канале.

В свое время, я даже столкнулся с проблемой записи нескольких пользователей, которая была связана с некоторыми особенностями в реализации получения звуковых пакетов от пользователей. После того, как я сообщил о проблеме, реализацию немного поменяли и прямо в репозиторий библиотеки добавили пример, как пользоваться этим методом и получать звуковой файл записи.

Есть пример audio_recording.py — для записи одного пользователя

Есть таже пример для записи нескольких пользователей: audio_recording_merged.py

Код второго:

import io

import pydub  # pip install pydub==0.25.1

import discord
from discord.sinks import MP3Sink

bot = discord.Bot()


@bot.event
async def on_ready():
    print(f"Logged in as {bot.user}")


async def finished_callback(sink: MP3Sink, channel: discord.TextChannel):
    mention_strs = []
    audio_segs: list[pydub.AudioSegment] = []
    files: list[discord.File] = []

    longest = pydub.AudioSegment.empty()

    for user_id, audio in sink.audio_data.items():
        mention_strs.append(f"<@{user_id}>")

        seg = pydub.AudioSegment.from_file(audio.file, format="mp3")

        # Determine the longest audio segment
        if len(seg) > len(longest):
            audio_segs.append(longest)
            longest = seg
        else:
            audio_segs.append(seg)

        audio.file.seek(0)
        files.append(discord.File(audio.file, filename=f"{user_id}.mp3"))

    for seg in audio_segs:
        longest = longest.overlay(seg)

    with io.BytesIO() as f:
        longest.export(f, format="mp3")
        await channel.send(
            f"Finished! Recorded audio for {', '.join(mention_strs)}.",
            files=files + [discord.File(f, filename="recording.mp3")],
        )


@bot.command()
async def join(ctx: discord.ApplicationContext):
    """Join the voice channel!"""
    voice = ctx.author.voice

    if not voice:
        return await ctx.respond("You're not in a vc right now")

    await voice.channel.connect()

    await ctx.respond("Joined!")


@bot.command()
async def start(ctx: discord.ApplicationContext):
    """Record the voice channel!"""
    voice = ctx.author.voice

    if not voice:
        return await ctx.respond("You're not in a vc right now")

    vc: discord.VoiceClient = ctx.voice_client

    if not vc:
        return await ctx.respond(
            "I'm not in a vc right now. Use `/join` to make me join!"
        )

    vc.start_recording(
        MP3Sink(),
        finished_callback,
        ctx.channel,
        sync_start=True,  # WARNING: This feature is very unstable and may break at any time.
    )

    await ctx.respond("The recording has started!")


@bot.command()
async def stop(ctx: discord.ApplicationContext):
    """Stop the recording"""
    vc: discord.VoiceClient = ctx.voice_client

    if not vc:
        return await ctx.respond("There's no recording going on right now")

    vc.stop_recording()

    await ctx.respond("The recording has stopped!")


@bot.command()
async def leave(ctx: discord.ApplicationContext):
    """Leave the voice channel!"""
    vc: discord.VoiceClient = ctx.voice_client

    if not vc:
        return await ctx.respond("I'm not in a vc right now")

    await vc.disconnect()

    await ctx.respond("Left!")


bot.run("TOKEN")

Поизучайте как он работает, почитайте документацию к методу start_recording и, думаю, сможете на основе этого примера сделать какую-то свою реализацию.

→ Ссылка