Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Udp Connection Reciving Voice Data but bot wont speak it #6740

Open
Z3Trix8 opened this issue Mar 20, 2024 · 0 comments
Open

Udp Connection Reciving Voice Data but bot wont speak it #6740

Z3Trix8 opened this issue Mar 20, 2024 · 0 comments
Labels
bug synced Synced to internal tracker

Comments

@Z3Trix8
Copy link

Z3Trix8 commented Mar 20, 2024

Description

I am Reciving everyting until the opcode 4 but when i try to send the data the udp receives it but nothing happens tried with a discord bot library and the intents are correct and the bot can speak he has the permission

Steps to Reproduce

import socket
import struct
import nacl.utils
import json
import asyncio
from pyogg import OpusEncoder
from nacl.secret import SecretBox
import websockets
from Crypto.Cipher import Salsa20
import struct
import subprocess
import os
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
import pyogg
from pydub import AudioSegment
import time

class Voice:
def init(self, guild, token, endpoint, session_id, user_id):
self.guild_id = guild
self.token = token
self.endpoint = endpoint
self.session_id = session_id
self.user_id = user_id
self.websocket = None
self.RTP_HEADER_SIZE = 12
self.RTP_HEADER = bytearray([0x80, 0x78])
self.sequence = 0
self.timestemp = 0
self.ip = None
self.port = None
self.ssrc = None
self.secret_key = None

async def Connect(self):
    try:
        self.websocket = await websockets.connect('wss://' + str(self.endpoint)+ '/?v=4')
        try:
            await self.__identify(self.websocket)
        except Exception as e:
            print(str(e))
        await self.__listen(self.websocket)
    except websockets.exceptions.ConnectionClosed as e:
        print(f"Connection closed: {e.code} - {e.reason}")

async def __send_heartbeat(self, intr):
    while True:
        packet = {
            "op": 3,
            "d": 1501184119561  # Consider updating this value as needed.
        }
        try:
            if self.websocket and not self.websocket.closed:
                await self.websocket.send(json.dumps(packet))
                print('heartbeat sent')
            else:
                print('WebSocket is closed, stopping heartbeat.')
                break  # Exit the loop if the websocket is closed
        except Exception as e:
            print(f"Error sending heartbeat: {str(e)}")

        await asyncio.sleep(intr / 1000)

async def __identify(self, socket):
    identify_payload = {
        "op": 0,
        "d": {
            "server_id": str(self.guild_id),
            "user_id": str(self.user_id),
            "session_id": str(self.session_id),
            "token": str(self.token)
        }
    }
    try:
        await socket.send(json.dumps(identify_payload))
    except Exception as e:
        print(str(e))

async def perform_ip_discovery(self, udp_ip, udp_port, ssrc):
    packet = struct.pack('>HHI', 0x1, 70, ssrc) + bytearray(66)
    self.sock.sendto(packet, (udp_ip, udp_port))
    data, _ = self.sock.recvfrom(1024)
    ip_end = data.find(b'\x00', 8)
    external_ip = data[8:ip_end].decode('utf-8')
    external_port = struct.unpack('>H', data[-2:])[0]

    return external_ip, external_port

async def __SetProtocol(self, ip, port, mode):
    packet = {
        "op": 1,
        "d": {
            "protocol": "udp",
            "data": {
                "address": str(ip),
                "port": port,
                "mode": mode
            }
        }
    }
    await self.websocket.send(json.dumps(packet))

async def send_speaking_notification(self):
    speaking_payload = {
        "op": 5,
        "d": {
            "speaking": 1,
            "delay": 0,
            "ssrc": self.ssrc
        }
    }
    try:
        await self.websocket.send(json.dumps(speaking_payload))
    except Exception as e:
        print(str(e))

def __udpconnect(self):
    self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # Use a separate socket

def __udplisten(self):
    while True:
        data, address = self.sock.recvfrom(1024)
        print("Received data:", data)
        print("From address:", address)

async def send_audio(self, audio_path):
    # Load the audio file and convert it to an AudioSegment
    audio = AudioSegment.from_mp3(audio_path)

    # Encode the audio to Opus frames using the provided function
    opus_frames = self.encode_audio_to_opus(audio)

    frame_duration = 20 / 1000.0  # 20 ms duration for each Opus frame

    # Send each Opus frame in an RTP packet
    for opus_frame in opus_frames:
        # Create an RTP packet with the Opus frame as the payload
        rtp_packet = self.create_rtp_packet(opus_frame)

        await self.send_speaking_notification()
        self.sock.sendto(rtp_packet, (self.xip, self.xport))
        print(opus_frame)
        # Wait for the frame duration to simulate real-time audio
        await asyncio.sleep(frame_duration)

def encode_audio_to_opus(self, audio_segment):
    # Convert AudioSegment to raw PCM data
    pcm_data = audio_segment.raw_data

    # Initialize the encoder
    encoder = pyogg.OpusBufferedEncoder()
    encoder.set_application("audio")
    encoder.set_sampling_frequency(audio_segment.frame_rate)
    encoder.set_channels(audio_segment.channels)

    # Calculate the frame size in bytes
    frame_size_ms = 20  # Frame duration in milliseconds
    frame_size = int(audio_segment.frame_rate / 1000 * frame_size_ms * audio_segment.channels * 2)  # 2 bytes per sample for 16-bit audio

    opus_frames = []
    for i in range(0, len(pcm_data), frame_size):
        frame = pcm_data[i:i + frame_size]
        if len(frame) < frame_size:
            frame += b'\x00' * (frame_size - len(frame))  # Padding with silence if necessary
        opus_frame = encoder.encode(frame)
        opus_frames.append(opus_frame)

    return opus_frames
def create_rtp_packet(self, payload):
    # Construct RTP header
    timestamp_increment = 960  # Specific to Opus, for 20ms frames at 48kHz
    self.timestemp += timestamp_increment
    self.sequence += 1
    header = self.RTP_HEADER + struct.pack('>H', self.sequence) + struct.pack('>I', self.timestemp) + struct.pack('>I', self.ssrc)

    # Encrypt the payload if necessary (depending on the chosen mode)
    if self.modes == 'xsalsa20_poly1305_suffix':
        nonce = nacl.utils.random(nacl.secret.SecretBox.NONCE_SIZE)
        box = SecretBox(self.secret_key)
        payload = box.encrypt(payload, nonce).ciphertext + nonce

    # Concatenate the header and the encrypted Opus payload
    rtp_packet = header + payload

    return rtp_packet

async def __listen(self, socket1):
    while True:
        message = await socket1.recv()
        data = json.loads(message)

        try:
            op = data['op']
        except Exception as e:
            print(str(e) + 'error')

        print(data)

        if op == 2:
            self.ip = data['d'].get('ip')
            self.port = data['d'].get('port')
            self.ssrc = data['d'].get('ssrc')
            self.modes = 'xsalsa20_poly1305_suffix'
            self.__udpconnect()
            self.xip, self.xport = await self.perform_ip_discovery(self.ip, self.port, self.ssrc)
            await self.__SetProtocol(self.xip, self.xport, self.modes)

        if op == 4:
            self.secret_key = data['d'].get('secret_key')
            self.secret_key = bytes(self.secret_key)
            # self.__udplisten()
            await self.send_speaking_notification()
            try:
                await self.send_audio(r'D:\DiscordGo\Now is the Best Time to Learn Tailwind.mp3')
            except Exception as e:
                print(str(e))
        if op == 8:
            intr = data['d']['heartbeat_interval']
            self.heartbeat_task = asyncio.create_task(self.__send_heartbeat(intr))

Expected Behavior

It should speak the .mp3 file

Current Behavior

UDP connection Reciving the data but nothing happens

Screenshots/Videos

No response

Client and System Information

Windows 11

@Z3Trix8 Z3Trix8 added the bug label Mar 20, 2024
@Rodentman87 Rodentman87 added the synced Synced to internal tracker label Mar 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug synced Synced to internal tracker
Projects
None yet
Development

No branches or pull requests

2 participants