247 lines
6.8 KiB
Python
247 lines
6.8 KiB
Python
# Python client file (client.py)
|
|
import argparse
|
|
import os
|
|
import platform
|
|
import socketio
|
|
import asyncio
|
|
from discord import Intents, opus, Activity, ActivityType
|
|
from discord.ext import commands
|
|
from NoiseGatev2 import NoiseGate
|
|
from debugger import setup_logger, running_dir
|
|
import faulthandler
|
|
faulthandler.enable()
|
|
|
|
logger = setup_logger('main')
|
|
|
|
# Example usage
|
|
logger.info("Logging initialized successfully.")
|
|
|
|
sio = socketio.AsyncClient()
|
|
client = None
|
|
device_id = None
|
|
ng_threshold = None
|
|
|
|
|
|
### Core functions
|
|
def load_opus():
|
|
logger.info(f"Running dir: '{running_dir}'")
|
|
processor = platform.machine()
|
|
logger.info(f"Processor: {processor}")
|
|
logger.info(f'OS: {os.name}')
|
|
|
|
if os.name == 'nt':
|
|
opus_path = f'{running_dir}/opus/libopus_amd64.dll' if processor == "AMD64" else None
|
|
else:
|
|
opus_path = f'{running_dir}/opus/libopus_aarcch64.so' if processor == "aarch64" else f'{running_dir}/opus/libopus_armv7l.so'
|
|
|
|
logger.debug(f"Opus path: '{opus_path}'")
|
|
logger.info(f"Opus path: '{opus_path}'")
|
|
|
|
if opus_path:
|
|
opus.load_opus(opus_path)
|
|
logger.info(f"Loaded OPUS library from {opus_path}")
|
|
return True
|
|
else:
|
|
logger.error("Unsupported architecture or OS.")
|
|
return False
|
|
|
|
|
|
async def set_discord_presense(presense):
|
|
# Set the presence of the bot (what it's listening to)
|
|
await client.change_presence(activity=Activity(type=ActivityType.listening, name=presense))
|
|
|
|
|
|
async def join_voice_channel(channel_id):
|
|
global device_id, ng_threshold
|
|
channel = client.get_channel(int(channel_id))
|
|
logger.info(f"Joining voice channel {channel}")
|
|
try:
|
|
voice_connection = await channel.connect(timeout=60.0, reconnect=True)
|
|
|
|
if opus.is_loaded():
|
|
logger.info("OPUS library loaded successfully")
|
|
logger.info(f"Input index: {device_id}, Voice Connection: {voice_connection}, Channel: {channel}")
|
|
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
|
|
except Exception as e:
|
|
logging.error(e)
|
|
logging.info('error encountered')
|
|
|
|
|
|
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 set_system(data):
|
|
logger.info(f"Received command to set system name (presense): {data['system']}")
|
|
return await set_discord_presense(data['system'])
|
|
|
|
|
|
@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['guildId']}")
|
|
return await leave_voice_channel(data['guildId'])
|
|
|
|
|
|
@sio.event
|
|
async def check_discord_vc_connected(data):
|
|
return check_if_discord_vc_connected(data['guildId'])
|
|
|
|
|
|
@sio.event
|
|
async def request_discord_username(data):
|
|
return await get_discord_username(data['guildId'])
|
|
|
|
|
|
@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()
|
|
|
|
@sio.event
|
|
async def request_client_close():
|
|
exit()
|
|
|
|
|
|
async def on_ready():
|
|
logger.info(f"We have logged in as {client.user}")
|
|
logger.info("Loading OPUS library")
|
|
load_opus()
|
|
|
|
logger.info(opus.is_loaded())
|
|
|
|
if opus.is_loaded():
|
|
logger.info('Emitting to the server')
|
|
await sio.emit('discord_ready', {'message': 'Discord bot is ready'})
|
|
|
|
|
|
async def start_bot(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)
|
|
|
|
device_id = args.device_id
|
|
ng_threshold = args.ng_threshold
|
|
|
|
client.add_listener(on_connect)
|
|
client.add_listener(on_disconnect)
|
|
client.add_listener(on_ready)
|
|
|
|
await client.start(args.client_id)
|
|
|
|
|
|
def parse_arguments():
|
|
parser = argparse.ArgumentParser()
|
|
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")
|
|
parser.add_argument("websocket_port", type=int, help="The port of the WebSocket server")
|
|
parser.add_argument("-n", "--ng_threshold", type=int, default=50,
|
|
help="Change the noise gate threshold. Defaults to 50")
|
|
return parser.parse_args()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
args = parse_arguments()
|
|
logger.info("Arguments: %s", args)
|
|
# Create an event loop
|
|
loop = asyncio.get_event_loop()
|
|
# Run the start_bot function within the event loop
|
|
loop.run_until_complete(start_bot(args))
|
|
except Exception as e:
|
|
logger.error(e)
|
|
logger.warning("Exiting now...")
|