Update for DRBv3

This commit is contained in:
Logan Cusano
2024-04-03 23:25:31 -04:00
parent 51027d794d
commit e84adaa9c4
3 changed files with 203 additions and 289 deletions

View File

@@ -3,6 +3,7 @@ from NoiseGatev2 import AudioStream
print('Getting a list of devices') print('Getting a list of devices')
list_of_devices = AudioStream().list_devices() list_of_devices = AudioStream().list_devices()
print("----- INPUT DEVICES -----") print("----- INPUT DEVICES -----")
print("----- *You will likely want to pick from one of these devices* -----")
for inputDevice in list_of_devices['Input']: for inputDevice in list_of_devices['Input']:
print(f"{inputDevice}\t-\t{list_of_devices['Input'][inputDevice]}") print(f"{inputDevice}\t-\t{list_of_devices['Input'][inputDevice]}")

480
main.py
View File

@@ -1,301 +1,213 @@
import argparse, platform, os # Python client file (client.py)
from discord import Intents, Client, Member, opus import argparse
import logging
import os
import platform
import socketio
import asyncio
from discord import Intents, opus
from discord.ext import commands from discord.ext import commands
from NoiseGatev2 import NoiseGate from NoiseGatev2 import NoiseGate
# Load the proper OPUS library for the device being used logging.basicConfig(level=logging.INFO)
async def load_opus(): logger = logging.getLogger(__name__)
# Check the system type and load the correct library
# Linux ARM AARCH64 running 32bit OS sio = socketio.AsyncClient()
client = None
device_id = None
ng_threshold = None
### Core functions
def load_opus():
processor = platform.machine() processor = platform.machine()
print("Processor: ", processor) logger.info(f"Processor: {processor}")
if os.name == 'nt': if os.name == 'nt':
if processor == "AMD64": opus_path = './opus/libopus_amd64.dll' if processor == "AMD64" else None
print(f"Loaded OPUS library for AMD64")
opus.load_opus('./opus/libopus_amd64.dll')
return "AMD64"
else: else:
if processor == "aarch64": opus_path = './opus/libopus_aarcch64.so' if processor == "aarch64" else './opus/libopus_armv7l.so'
print(f"Loaded OPUS library for aarch64")
opus.load_opus('./opus/libopus_aarcch64.so') if opus_path:
return "aarch64" opus.load_opus(opus_path)
elif processor == "armv7l": logger.info(f"Loaded OPUS library from {opus_path}")
print(f"Loaded OPUS library for armv7l") return True
opus.load_opus('./opus/libopus_armv7l.so') else:
return "armv7l" logger.error("Unsupported architecture or OS.")
return False
def main(clientId='OTQzNzQyMDQwMjU1MTE1MzA0.Yg3eRA.ZxEbRr55xahjfaUmPY8pmS-RHTY', channelId=367396189529833476, NGThreshold=50, deviceId=1): async def join_voice_channel(channel_id):
intents = Intents.default() global device_id, ng_threshold
channel = client.get_channel(int(channel_id))
logger.info(f"Joining voice channel {channel}")
voice_connection = await channel.connect(timeout=60.0, reconnect=True)
if opus.is_loaded():
logger.info("OPUS library loaded successfully")
stream_handler = NoiseGate(
_input_device_index=device_id,
_voice_connection=voice_connection,
_noise_gate_threshold=ng_threshold
)
stream_handler.run()
logger.info("Audio stream started")
return True
else:
logger.error("Failed to load OPUS library")
return False
async def leave_voice_channel(guild_id):
guild = await client.fetch_guild(guild_id)
logger.info(f"Leaving voice channel in guild {guild}")
voice_client = guild.voice_client
if voice_client:
await voice_client.disconnect()
logger.info("Disconnected from voice channel")
else:
logger.info("Not connected to any voice channel in the guild")
# Check if the client needs to open
if len(check_for_open_vc_connections()) <= 0:
# Tell the server the client is going to close
return False
return True
def check_for_open_vc_connections():
return client.voice_clients
def check_if_discord_vc_connected(guild_id):
if client:
return any(int(vc.guild.id) == int(guild_id) for vc in client.voice_clients)
return False
async def get_discord_username(guild_id):
try:
guild = await client.fetch_guild(guild_id)
member = await guild.fetch_member(get_discord_id())
if member.nick:
print(f"Username: {member.nick}")
return member.nick
print(f"Username: {client.user.name if client.user else None}")
return client.user.name if client.user else None
except Exception as e:
logging.warning(e)
return None
def check_if_client_is_open():
if client:
return client.is_ready()
return False
def get_discord_id():
print(f"ID: {client.user.id if client.user else None}")
return int(client.user.id) if client.user else None
async def on_connect():
logger.info("Connected to WebSocket server")
async def on_disconnect():
logger.info("Disconnected from WebSocket server")
### Socket Events
@sio.event
async def connect_error():
logger.error("Connection to WebSocket server failed")
@sio.event
async def join_server(data):
logger.info(f"Received command to join server: {data['channelId']}")
return await join_voice_channel(data['channelId'])
@sio.event
async def leave_server(data):
logger.info(f"Received command to leave server: {data['guild_id']}")
return await leave_voice_channel(data['guild_id'])
@sio.event
async def check_discord_vc_connected(data):
return check_if_discord_vc_connected(data['guild_id'])
@sio.event
async def request_discord_username(data):
return await get_discord_username(data['guild_id'])
@sio.event
async def check_client_is_open():
return check_if_client_is_open()
@sio.event
async def request_discord_id():
return get_discord_id()
async def on_ready():
logger.info(f"We have logged in as {client.user}")
logger.info("Loading OPUS library")
if not load_opus():
return
# Send update to socket server
try:
logger.info('Emitting to the server')
await sio.emit('discord_ready', {'message': 'Discord bot is ready'})
except Exception as e:
logger.error(f"Error emitting to the server: {e}")
logger.info('Server not ready yet')
async def main(args):
global client, device_id, ng_threshold
# Connect to the WebSocket server
await sio.connect('http://127.0.0.1:{}'.format(args.websocket_port), namespaces=['/'])
logger.info("Connecting to WebSocket server...")
intents = Intents.default()
client = commands.Bot(command_prefix='!', intents=intents) client = commands.Bot(command_prefix='!', intents=intents)
@client.event device_id = args.device_id
async def on_ready(): ng_threshold = args.ng_threshold
print(f'We have logged in as {client.user}')
channelIdToJoin = client.get_channel(channelId) client.add_listener(on_connect)
print("Channel", channelIdToJoin) client.add_listener(on_disconnect)
client.add_listener(on_ready)
print("Loading opus") await client.start(args.client_id)
await load_opus()
if opus.is_loaded():
print("Joining voice")
channelConnection = await channelIdToJoin.connect(timeout=60.0, reconnect=True)
print("Voice Connected")
streamHandler = NoiseGate(
_input_device_index=deviceId,
_voice_connection=channelConnection,
_noise_gate_threshold=NGThreshold)
# Start the audio stream
streamHandler.run()
print("stream running")
client.run(clientId) def parse_arguments():
parser = argparse.ArgumentParser()
parser = argparse.ArgumentParser() parser.add_argument("device_id", type=int, help="The ID of the audio device to use")
parser.add_argument("deviceId", type=int, help="The ID of the audio device to use") parser.add_argument("client_id", type=str, help="The Discord client ID")
parser.add_argument("channelId", type=int, help="The ID of the voice channel to use") parser.add_argument("websocket_port", type=int, help="The port of the WebSocket server")
parser.add_argument("clientId", type=str, help="The discord client ID") parser.add_argument("-n", "--ng_threshold", type=int, default=50,
parser.add_argument("-n", "--NGThreshold", type=int, help="Change the noisegate threshold. This defaults to 50") help="Change the noise gate threshold. Defaults to 50")
args = parser.parse_args() return parser.parse_args()
if (not args.NGThreshold):
args.NGThreshold = 50
print("Arguments:", args)
main(
clientId=args.clientId,
channelId=args.channelId,
NGThreshold=args.NGThreshold,
deviceId=args.deviceId
)
if __name__ == "__main__":
args = parse_arguments()
logger.info("Arguments: %s", args)
asyncio.run(main(args))
#import asyncio
#import functools
#import itertools
#import math
#import random
#
#import discord
#from async_timeout import timeout
#from discord.ext import commands
#
#
#class VoiceError(Exception):
# pass
#
#
#class VoiceState:
# def __init__(self, bot: commands.Bot, ctx: commands.Context):
# self.bot = bot
# self._ctx = ctx
#
# self.current = None
# self.voice = None
# self.next = asyncio.Event()
# self.songs = SongQueue()
#
# self._loop = False
# self._volume = 0.5
# self.skip_votes = set()
#
# self.audio_player = bot.loop.create_task(self.audio_player_task())
#
# def __del__(self):
# self.audio_player.cancel()
#
# @property
# def loop(self):
# return self._loop
#
# @loop.setter
# def loop(self, value: bool):
# self._loop = value
#
# @property
# def volume(self):
# return self._volume
#
# @volume.setter
# def volume(self, value: float):
# self._volume = value
#
# @property
# def is_playing(self):
# return self.voice and self.current
#
# async def audio_player_task(self):
# while True:
# self.next.clear()
#
# if not self.loop:
# # Try to get the next song within 3 minutes.
# # If no song will be added to the queue in time,
# # the player will disconnect due to performance
# # reasons.
# try:
# async with timeout(180): # 3 minutes
# self.current = await self.songs.get()
# except asyncio.TimeoutError:
# self.bot.loop.create_task(self.stop())
# return
#
# self.current.source.volume = self._volume
# self.voice.play(self.current.source, after=self.play_next_song)
# await self.current.source.channel.send(embed=self.current.create_embed())
#
# await self.next.wait()
#
# def play_next_song(self, error=None):
# if error:
# raise VoiceError(str(error))
#
# self.next.set()
#
# def skip(self):
# self.skip_votes.clear()
#
# if self.is_playing:
# self.voice.stop()
#
# async def stop(self):
# self.songs.clear()
#
# if self.voice:
# await self.voice.disconnect()
# self.voice = None
#
#
#class Music(commands.Cog):
# def __init__(self, bot: commands.Bot):
# self.bot = bot
# self.voice_states = {}
#
# def get_voice_state(self, ctx: commands.Context):
# state = self.voice_states.get(ctx.guild.id)
# if not state:
# state = VoiceState(self.bot, ctx)
# self.voice_states[ctx.guild.id] = state
#
# return state
#
# def cog_unload(self):
# for state in self.voice_states.values():
# self.bot.loop.create_task(state.stop())
#
# def cog_check(self, ctx: commands.Context):
# if not ctx.guild:
# raise commands.NoPrivateMessage('This command can\'t be used in DM channels.')
#
# return True
#
# async def cog_before_invoke(self, ctx: commands.Context):
# ctx.voice_state = self.get_voice_state(ctx)
#
# async def cog_command_error(self, ctx: commands.Context, error: commands.CommandError):
# await ctx.send('An error occurred: {}'.format(str(error)))
#
# @commands.command(name='join', invoke_without_subcommand=True)
# async def _join(self, ctx: commands.Context):
# """Joins a voice channel."""
#
# destination = ctx.author.voice.channel
# if ctx.voice_state.voice:
# await ctx.voice_state.voice.move_to(destination)
# return
#
# ctx.voice_state.voice = await destination.connect()
#
# @commands.command(name='summon')
# @commands.has_permissions(manage_guild=True)
# async def _summon(self, ctx: commands.Context, *, channel: discord.VoiceChannel = None):
# """Summons the bot to a voice channel.
#
# If no channel was specified, it joins your channel.
# """
#
# if not channel and not ctx.author.voice:
# raise VoiceError('You are neither connected to a voice channel nor specified a channel to join.')
#
# destination = channel or ctx.author.voice.channel
# if ctx.voice_state.voice:
# await ctx.voice_state.voice.move_to(destination)
# return
#
# ctx.voice_state.voice = await destination.connect()
#
# @commands.command(name='leave', aliases=['disconnect'])
# @commands.has_permissions(manage_guild=True)
# async def _leave(self, ctx: commands.Context):
# """Clears the queue and leaves the voice channel."""
#
# if not ctx.voice_state.voice:
# return await ctx.send('Not connected to any voice channel.')
#
# await ctx.voice_state.stop()
# del self.voice_states[ctx.guild.id]
#
# @commands.command(name='play')
# async def _play(self, ctx: commands.Context, *, search: str):
# """Plays a song.
#
# If there are songs in the queue, this will be queued until the
# other songs finished playing.
#
# This command automatically searches from various sites if no URL is provided.
# A list of these sites can be found here: https://rg3.github.io/youtube-dl/supportedsites.html
# """
#
# if not ctx.voice_state.voice:
# await ctx.invoke(self._join)
#
# async with ctx.typing():
# try:
# source = await YTDLSource.create_source(ctx, search, loop=self.bot.loop)
# except YTDLError as e:
# await ctx.send('An error occurred while processing this request: {}'.format(str(e)))
# else:
# song = Song(source)
#
# await ctx.voice_state.songs.put(song)
# await ctx.send('Enqueued {}'.format(str(source)))
#
# @_join.before_invoke
# @_play.before_invoke
# async def ensure_voice_state(self, ctx: commands.Context):
# if not ctx.author.voice or not ctx.author.voice.channel:
# raise commands.CommandError('You are not connected to any voice channel.')
#
# if ctx.voice_client:
# if ctx.voice_client.channel != ctx.author.voice.channel:
# raise commands.CommandError('Bot is already in a voice channel.')
#
#intents = discord.Intents.default()
#intents.message_content = True
#
#bot = commands.Bot('music.', description="Brent's Revenge", intents=intents)
#bot.add_cog(Music(bot))
#
#
#@bot.event
#async def on_ready():
# print('Logged in as:\n{0.user.name}\n{0.user.id}'.format(bot))
#
#bot.run('OTQzNzQyMDQwMjU1MTE1MzA0.Yg3eRA.ZxEbRr55xahjfaUmPY8pmS-RHTY')

View File

@@ -1,5 +1,6 @@
discord^=2.2.3 discord==2.3.2
PyNaCl^=1.5.0 PyNaCl==1.5.0
pyaudio^=0.2.13 pyaudio==0.2.14
numpy^=1.24.3 numpy==1.26.4
argparse argparse
python-socketio[client]