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]}")

464
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))
client = commands.Bot(command_prefix='!', intents=intents) logger.info(f"Joining voice channel {channel}")
voice_connection = await channel.connect(timeout=60.0, reconnect=True)
@client.event
async def on_ready():
print(f'We have logged in as {client.user}')
channelIdToJoin = client.get_channel(channelId)
print("Channel", channelIdToJoin)
print("Loading opus")
await load_opus()
if opus.is_loaded(): if opus.is_loaded():
print("Joining voice") logger.info("OPUS library loaded successfully")
channelConnection = await channelIdToJoin.connect(timeout=60.0, reconnect=True) stream_handler = NoiseGate(
print("Voice Connected") _input_device_index=device_id,
streamHandler = NoiseGate( _voice_connection=voice_connection,
_input_device_index=deviceId, _noise_gate_threshold=ng_threshold
_voice_connection=channelConnection,
_noise_gate_threshold=NGThreshold)
# Start the audio stream
streamHandler.run()
print("stream running")
client.run(clientId)
parser = argparse.ArgumentParser()
parser.add_argument("deviceId", type=int, help="The ID of the audio device to use")
parser.add_argument("channelId", type=int, help="The ID of the voice channel to use")
parser.add_argument("clientId", type=str, help="The discord client ID")
parser.add_argument("-n", "--NGThreshold", type=int, help="Change the noisegate threshold. This defaults to 50")
args = 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
) )
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
#import asyncio async def on_connect():
#import functools logger.info("Connected to WebSocket server")
#import itertools
#import math
#import random async def on_disconnect():
# logger.info("Disconnected from WebSocket server")
#import discord
#from async_timeout import timeout
#from discord.ext import commands ### Socket Events
# @sio.event
# async def connect_error():
#class VoiceError(Exception): logger.error("Connection to WebSocket server failed")
# pass
#
# @sio.event
#class VoiceState: async def join_server(data):
# def __init__(self, bot: commands.Bot, ctx: commands.Context): logger.info(f"Received command to join server: {data['channelId']}")
# self.bot = bot return await join_voice_channel(data['channelId'])
# self._ctx = ctx
#
# self.current = None @sio.event
# self.voice = None async def leave_server(data):
# self.next = asyncio.Event() logger.info(f"Received command to leave server: {data['guild_id']}")
# self.songs = SongQueue() return await leave_voice_channel(data['guild_id'])
#
# self._loop = False
# self._volume = 0.5 @sio.event
# self.skip_votes = set() async def check_discord_vc_connected(data):
# return check_if_discord_vc_connected(data['guild_id'])
# self.audio_player = bot.loop.create_task(self.audio_player_task())
#
# def __del__(self): @sio.event
# self.audio_player.cancel() async def request_discord_username(data):
# return await get_discord_username(data['guild_id'])
# @property
# def loop(self):
# return self._loop @sio.event
# async def check_client_is_open():
# @loop.setter return check_if_client_is_open()
# def loop(self, value: bool):
# self._loop = value
# @sio.event
# @property async def request_discord_id():
# def volume(self): return get_discord_id()
# return self._volume
#
# @volume.setter async def on_ready():
# def volume(self, value: float): logger.info(f"We have logged in as {client.user}")
# self._volume = value logger.info("Loading OPUS library")
# if not load_opus():
# @property return
# def is_playing(self):
# return self.voice and self.current # Send update to socket server
# try:
# async def audio_player_task(self): logger.info('Emitting to the server')
# while True: await sio.emit('discord_ready', {'message': 'Discord bot is ready'})
# self.next.clear() except Exception as e:
# logger.error(f"Error emitting to the server: {e}")
# if not self.loop: logger.info('Server not ready yet')
# # 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 async def main(args):
# # reasons. global client, device_id, ng_threshold
# try:
# async with timeout(180): # 3 minutes # Connect to the WebSocket server
# self.current = await self.songs.get() await sio.connect('http://127.0.0.1:{}'.format(args.websocket_port), namespaces=['/'])
# except asyncio.TimeoutError: logger.info("Connecting to WebSocket server...")
# self.bot.loop.create_task(self.stop())
# return intents = Intents.default()
# client = commands.Bot(command_prefix='!', intents=intents)
# self.current.source.volume = self._volume
# self.voice.play(self.current.source, after=self.play_next_song) device_id = args.device_id
# await self.current.source.channel.send(embed=self.current.create_embed()) ng_threshold = args.ng_threshold
#
# await self.next.wait() client.add_listener(on_connect)
# client.add_listener(on_disconnect)
# def play_next_song(self, error=None): client.add_listener(on_ready)
# if error:
# raise VoiceError(str(error)) await client.start(args.client_id)
#
# self.next.set()
# def parse_arguments():
# def skip(self): parser = argparse.ArgumentParser()
# self.skip_votes.clear() parser.add_argument("device_id", type=int, help="The ID of the audio device to use")
# parser.add_argument("client_id", type=str, help="The Discord client ID")
# if self.is_playing: parser.add_argument("websocket_port", type=int, help="The port of the WebSocket server")
# self.voice.stop() parser.add_argument("-n", "--ng_threshold", type=int, default=50,
# help="Change the noise gate threshold. Defaults to 50")
# async def stop(self): return parser.parse_args()
# self.songs.clear()
#
# if self.voice: if __name__ == "__main__":
# await self.voice.disconnect() args = parse_arguments()
# self.voice = None logger.info("Arguments: %s", args)
# asyncio.run(main(args))
#
#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]