2 Commits
master ... GUI

Author SHA1 Message Date
Logan Cusano
fa259c9f93 Update gitignore 2021-12-12 20:35:58 -05:00
Logan Cusano
9334f51e22 Create GUI
Update README
2021-12-12 20:13:08 -05:00
26 changed files with 499 additions and 2398 deletions

13
.gitignore vendored
View File

@@ -1,12 +1,7 @@
/.idea/
/Releases/
/Old/
**/__pycache__/
**/venv/
/__pycache__/
/venv/
/DSDPlus/
config.ini
*.7z
*.bat
/DSDPlus/
**/._.DS_Store
*.log
*.ini
**/logs/*

View File

@@ -1,47 +1,12 @@
import sound
import configparser
import logging
import os
from datetime import date
from os.path import exists
from NoiseGatev2 import AudioStream
# Handler configs
PDB_ACCEPTABLE_HANDLERS = {'gqrx': {
'Modes': ['wfm', 'fm']
},
'op25': {
'Modes': ['d', 'p25']
}}
# Known bot IDs
PDB_KNOWN_BOT_IDS = {756327271597473863: "Greada", 915064996994633729: "Jorn", 943742040255115304: "Brent"}
# Default value to set noisegate on new or unknown profiles
DEFAULT_NOISEGATE_THRESHOLD = 50
# Initialize the logger for this file
LOGGER = logging.getLogger('Discord_Radio_Bot.Bot_Resources')
# Location of the gqrx binary
GQRX_BIN_LOCATION = "/usr/bin/"
GQRX_BIN = "/usr/bin/gqrx"
# Default radio settings
DEFAULT_RADIO_SETTINGS = {
'profile_name': None,
'freq': "104700000",
'mode': "wfm",
'squelch': 0,
'noisegate_sensitivity': DEFAULT_NOISEGATE_THRESHOLD,
}
def check_if_config_exists():
if exists('./config.ini'):
config = configparser.SafeConfigParser()
config.read('./config.ini')
if config.has_section('Bot_Info') and config.has_section('Device') and config.has_section('Config'):
if config.has_section('Bot_Info') and config.has_section('Device'):
return True
else:
return False
@@ -53,121 +18,106 @@ def read_config_file():
config = configparser.ConfigParser()
config.read('./config.ini')
try:
config_return = {
'Bot Token': config['Bot_Info']['Token'],
'Device ID': int(config['Device']['ID']),
'Device Name': str(config['Device']['Name']),
'Mention Group': str(config['Bot_Info']['Mention_Group']),
'Channel ID': int(config['Bot_Info']['Channel_ID']),
'Handler': str(config['Config']['Handler'])
}
config_return = {
'Token': config['Bot_Info']['Token'],
'Device ID': int(config['Device']['ID']),
'Device Name': str(config['Device']['Name']),
}
if config.has_option('Bot_Info', 'Mention_Group'): #'Mention Group' in config['Bot_Info'].items():
config_return['Mention Group'] = str(config['Bot_Info']['Mention_Group'])
LOGGER.debug("Found config options:")
for key in config_return.keys():
LOGGER.debug(f"\t{key} : {config_return[key]}")
if 'Channel_ID' in config['Bot_Info'].keys():
config_return['Channel ID'] = int(config['Bot_Info']['Channel_ID'])
return config_return
except Exception as err:
LOGGER.warning(err)
return None
print("Found config options:")
for key in config_return.keys():
print(f"\t{key} : {config_return[key]}")
return config_return
def write_config_file(**kwargs):
config = configparser.SafeConfigParser()
if not kwargs['init'] and exists('./config.ini'):
if 'init' not in kwargs.keys() and exists('./config.ini'):
config.read('./config.ini')
if not config.has_section('Bot_Info'):
config.add_section('Bot_Info')
if not config.has_section('Config'):
config.add_section('Config')
if not config.has_section('Device'):
config.add_section('Device')
if 'handler' in kwargs.keys():
config['Config']['Handler'] = str(kwargs['handler'])
elif kwargs['init']:
config['Config']['Handler'] = str(get_handler())
if 'token' in kwargs.keys():
config['Bot_Info']['Token'] = str(kwargs['token'])
elif kwargs['init']:
elif 'init' in kwargs.keys():
config['Bot_Info']['Token'] = str(get_user_token())
if 'device_id' in kwargs.keys() or 'device_name' in kwargs.keys():
config['Device']['ID'] = str(kwargs['device_id'])
config['Device']['Name'] = str(kwargs['device_name'])
elif kwargs['init']:
elif 'init' in kwargs.keys():
device_id, device_name = get_user_device_selection()
config['Device']['ID'] = str(device_id)
config['Device']['Name'] = str(device_name)
if 'mention_group' in kwargs.keys():
config['Bot_Info']['Mention_Group'] = str(kwargs['mention_group'])
elif kwargs['init']:
elif 'init' in kwargs.keys():
config['Bot_Info']['Mention_Group'] = str(get_user_mention_group())
if 'channel_id' in kwargs.keys():
config['Bot_Info']['Channel_ID'] = str(kwargs['channel_id'])
elif kwargs['init']:
elif 'init' in kwargs.keys():
config['Bot_Info']['Channel_ID'] = str(get_user_mention_channel_id())
with open('./config.ini', 'w') as config_file:
config.write(config_file)
try:
with open('./config.ini', 'w') as config_file:
config.write(config_file)
except Exception as e:
print(e)
LOGGER.info("Config Saved")
print("Config Saved")
return True
def get_device_list():
list_of_devices = AudioStream().list_devices()
LOGGER.debug(list_of_devices)
return list_of_devices
return sound.query_devices().items()
def get_user_device_selection():
device_list = get_device_list()
org_device_list = []
for device, dev_id in device_list:
LOGGER.debug(f"{dev_id + 1}\t-\t{device}")
print(f"{dev_id + 1}\t-\t{device}")
org_device_list.append((dev_id, device))
selected_list_id = None
selected_device = None
while not selected_list_id:
LOGGER.debug(f"selected device: {selected_list_id}")
LOGGER.debug(device_list)
selected_id = None
while not selected_id:
try:
selected_list_id = int(input(f"Please select the input device from above:\t"))
selected_id = int(input(f"Please select the input device from above:\t")) - 1
except Exception as e:
LOGGER.warning(e)
continue
if int(selected_list_id) < int(len(device_list)):
LOGGER.debug("Selected ID within range")
print(e)
continue
elif selected_list_id > int(len(device_list)):
LOGGER.debug("Out of range, try again...")
selected_list_id = None
if selected_id and not selected_id + 1 > int(len(device_list)):
continue
elif selected_id > int(len(device_list)):
print("Out of range, try again...")
selected_id = None
continue
else:
selected_list_id = None
LOGGER.error("Internal error, try again")
selected_id = None
print("Internal error, try again")
continue
for dev_dict in org_device_list:
LOGGER.debug(list(device_list)[selected_list_id-1][0])
if dev_dict[1] == list(device_list)[selected_list_id-1][0]:
selected_device = dev_dict
LOGGER.debug(selected_device)
if dev_dict[0] == selected_id:
selected_id = dev_dict
return selected_device
return selected_id
def get_user_token():
@@ -177,7 +127,7 @@ def get_user_token():
if len(token) == 59:
return token
else:
LOGGER.error('Length error in token, please try again...')
print('Length error in token, please try again...')
token = None
continue
@@ -196,81 +146,6 @@ def get_user_mention_channel_id():
if len(str(channel_id)) == len('757379843792044102'):
return channel_id
else:
LOGGER.error("Length error in ID, please try again")
print("Length error in ID, please try again")
channel_id = None
continue
def get_handler():
handler = None
while not handler:
handler = str(input(f"Please enter the name of the handler you would like to use:\t"))
if handler in PDB_ACCEPTABLE_HANDLERS:
return handler
elif handler == ['', "none"]:
handler = "None"
return handler
def check_negative(s):
try:
f = float(s)
if (f < 0):
return True
# Otherwise return false
return False
except ValueError:
return False
# Check if message is a ping request and respond even if it is a bot
async def check_and_reply_to_ping(bot, message):
if "check_modules" in message.content:
ctx = await bot.get_context(message)
await bot.invoke(ctx)
return True
else:
await bot.process_commands(message)
return False
# Create the logger
def init_global_logger(_verbose_level: str = "WARNING"):
numeric_log_level = getattr(logging, _verbose_level.upper(), None)
if not isinstance(numeric_log_level, int):
raise ValueError('Invalid log level: %s' % _verbose_level)
else:
# create logger
init_logger = logging.getLogger('Discord_Radio_Bot')
init_logger.setLevel(logging.DEBUG)
# create file handler which logs even debug messages
if not exists("./logs/"):
os.mkdir("./logs/")
fh = logging.FileHandler(f'./logs/DRB-{date.today()}.log')
fh.setLevel(logging.INFO)
fh_debug = logging.FileHandler(f'./logs/DRB-{date.today()}.DEBUG.log')
fh_debug.setLevel(logging.DEBUG)
# create terminal handler with a higher log level
th = logging.StreamHandler()
th.setLevel(numeric_log_level)
# create formatter and add it to the handlers
fh_debug_formatter = logging.Formatter('[%(asctime)s %(name)s->%(funcName)s():%(lineno)s] - %(levelname)s - %(message)s')
fh_formatter = logging.Formatter('[%(asctime)s %(name)s->%(funcName)s()] - %(levelname)s - %(message)s')
th_formatter = logging.Formatter('[%(asctime)s %(name)s->%(funcName)s()] - %(levelname)s - %(message)s')
fh_debug.setFormatter(fh_debug_formatter)
fh.setFormatter(fh_formatter)
th.setFormatter(th_formatter)
# add the handlers to the logger
init_logger.addHandler(fh)
init_logger.addHandler(th)
init_logger.addHandler(fh_debug)
#if __name__ is not 'main':
# init_global_logger()
# LOGGER = logging.getLogger('Discord_Radio_Bot')
continue

View File

@@ -1,214 +0,0 @@
import audioop
import logging
import math
import time
import pyaudio
import discord
import numpy
voice_connection = None
LOGGER = logging.getLogger("Discord_Radio_Bot.NoiseGateV2")
# noinspection PyUnresolvedReferences
class AudioStream:
def __init__(self, _channels: int = 2, _sample_rate: int = 48000, _frames_per_buffer: int = 1024,
_input_device_index: int = None, _output_device_index: int = None, _input: bool = True,
_output: bool = True, _init_on_startup: bool = True):
self.paInstance_kwargs = {
'format': pyaudio.paInt16,
'channels': _channels,
'rate': _sample_rate,
'input': _input,
'output': _output,
'frames_per_buffer': _frames_per_buffer
}
if _input_device_index:
if _input:
self.paInstance_kwargs['input_device_index'] = _input_device_index
else:
LOGGER.warning(f"[AudioStream.__init__]:\tInput was not enabled."
f" Reinitialize with '_input=True'")
if _output_device_index:
if _output:
self.paInstance_kwargs['output_device_index'] = _output_device_index
else:
LOGGER.warning(f"[AudioStream.__init__]:\tOutput was not enabled."
f" Reinitialize with '_output=True'")
if _init_on_startup:
# Init PyAudio instance
LOGGER.info("Creating PyAudio instance")
self.paInstance = pyaudio.PyAudio()
# Define and initialize stream object if we have been passed a device ID (pyaudio.open)
self.stream = None
if _output_device_index or _input_device_index:
if _init_on_startup:
LOGGER.info("Init stream")
self.init_stream()
def init_stream(self, _new_output_device_index: int = None, _new_input_device_index: int = None):
# Check what device was asked to be changed (or set)
if _new_input_device_index:
if self.paInstance_kwargs['input']:
self.paInstance_kwargs['input_device_index'] = _new_input_device_index
else:
LOGGER.warning(f"[AudioStream.init_stream]:\tInput was not enabled when initialized."
f" Reinitialize with '_input=True'")
if _new_output_device_index:
if self.paInstance_kwargs['output']:
self.paInstance_kwargs['output_device_index'] = _new_output_device_index
else:
LOGGER.warning(f"[AudioStream.init_stream]:\tOutput was not enabled when initialized."
f" Reinitialize with '_output=True'")
self.close_if_open()
# Open the stream
self.stream = self.paInstance.open(**self.paInstance_kwargs)
def close_if_open(self):
# Stop the stream if it is started
if self.stream:
if self.stream.is_active():
self.stream.stop_stream()
self.stream.close()
LOGGER.debug(f"[ReopenStream.close_if_open]:\t Stream was open; It was closed.")
def list_devices(self, _display_input_devices: bool = True, _display_output_devices: bool = True):
info = self.paInstance.get_host_api_info_by_index(0)
numdevices = info.get('deviceCount')
devices = {
'Input': {},
'Output': {}
}
for i in range(0, numdevices):
if (self.paInstance.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels')) > 0:
input_device = self.paInstance.get_device_info_by_host_api_device_index(0, i).get('name')
devices['Input'][i] = input_device
if _display_input_devices:
LOGGER.debug("Input Device id ", i, " - ", input_device)
if (self.paInstance.get_device_info_by_host_api_device_index(0, i).get('maxOutputChannels')) > 0:
output_device = self.paInstance.get_device_info_by_host_api_device_index(0, i).get('name')
devices['Output'][i] = output_device
if _display_output_devices:
LOGGER.debug("Output Device id ", i, " - ", output_device)
return devices
async def stop(self):
await voice_connection.disconnect()
self.close_if_open()
self.stream.close()
self.paInstance.terminate()
# noinspection PyUnresolvedReferences
class NoiseGate(AudioStream):
def __init__(self, _voice_connection, _noise_gate_threshold: int, **kwargs):
super(NoiseGate, self).__init__(_init_on_startup=True, **kwargs)
global voice_connection
voice_connection = _voice_connection
self.THRESHOLD = _noise_gate_threshold
self.NGStream = NoiseGateStream(self)
self.Voice_Connection_Thread = None
def run(self) -> None:
global voice_connection
# Start the audio stream
LOGGER.debug(f"Starting stream")
self.stream.start_stream()
# Start the stream to discord
self.core()
def core(self, error=None):
if error:
LOGGER.warning(error)
while not voice_connection.is_connected():
time.sleep(.2)
if not voice_connection.is_playing():
LOGGER.debug(f"Playing stream to discord")
voice_connection.play(self.NGStream, after=self.core)
async def close(self):
LOGGER.debug(f"Closing")
await voice_connection.disconnect()
if self.stream.is_active:
self.stream.stop_stream()
LOGGER.debug(f"Stopping stream")
# noinspection PyUnresolvedReferences
class NoiseGateStream(discord.AudioSource):
def __init__(self, _stream):
super(NoiseGateStream, self).__init__()
self.stream = _stream # The actual audio stream object
self.NG_fadeout = 240/20 # Fadeout value used to hold the noisegate after de-triggering
self.NG_fadeout_count = 0 # A count set when the noisegate is triggered and was de-triggered
self.process_set_count = 0 # Counts how many processes have been made
def read(self):
try:
while voice_connection.is_connected():
curr_buffer = bytearray(self.stream.stream.read(960))
buffer_rms = audioop.rms(curr_buffer, 2)
if buffer_rms > 0:
buffer_decibel = 20 * math.log10(buffer_rms)
if self.process_set_count % 10 == 0:
if buffer_decibel >= self.stream.THRESHOLD:
LOGGER.debug(f"[Noisegate Open] {buffer_decibel} db")
else:
LOGGER.debug(f"[Noisegate Closed] {buffer_decibel} db")
if buffer_decibel >= self.stream.THRESHOLD:
self.NG_fadeout_count = self.NG_fadeout
self.process_set_count += 1
if curr_buffer:
return bytes(curr_buffer)
else:
if self.NG_fadeout_count > 0:
self.NG_fadeout_count -= 1
LOGGER.debug(f"Frames in fadeout remaining: {self.NG_fadeout_count}")
self.process_set_count += 1
if curr_buffer:
return bytes(curr_buffer)
except OSError as e:
LOGGER.warning(e)
pass
def audio_datalist_set_volume(self, datalist, volume):
""" Change value of list of audio chunks """
sound_level = (volume / 100.)
for i in range(len(datalist)):
chunk = numpy.fromstring(datalist[i], numpy.int16)
chunk = chunk * sound_level
datalist[i] = chunk.astype(numpy.int16)
if __name__ == '__main__':
input_index = int(input("Input:\t"))
output_index = int(input("Output:\t"))
ng = NoiseGate(_input_device_index=input_index, _output_device_index=output_index)
ng.list_devices()
ng.start()

View File

@@ -1,19 +1,13 @@
# Discord-Radio-Bot
This project is intended to allow users in discord to be able to listen to their favorite radio stations, *music or other ;)*, while talking to their friends.
## Requirements
- Python 3.X
- *See `requirements.txt`*
- GQRX (if you want the bot to be able to control the sdr)
- Any SDR app you want, as long as it plays audio
## Setup
1. Install the pip packages found in the ```Requirements.txt``` file
- *Optional if you are using OP25* Run `echo 0 > /sys/module/usbcore/parameters/usbfs_memory_mb`
3. Run ```main.py``` with Python
## Usage
1. Install Python 3.X
2. Install the pip packages found in the ```Requirements.txt``` file
3. Run ```main.py``` with Python
4. Follow the prompts in the terminal
5. Ensure your audio is playing on the selected device
5You're all set! Ask the bot to join!
5. Redirect your audio to the selected device
6. You're all set!
### Understanding Audio Input
This title can be a bit confusing. The bot will display both 'input' and 'output' devices but not always *all* devices connected.
@@ -23,9 +17,16 @@ Voicemeeter is **highly** recommended for this bot. See a detailed guide on how
To change the audio source, simply delete the ```config.ini``` that was generated and restart the bot.
It will re-do the setup and allow you to select a new device.
### [To-Do](https://git.vpn.cusano.net/Discord_Bot_Gang/Discord-Radio-Bot/src/branch/master/TODO.md)
**Notes for readme leter on**
- Users need to save profile after any change in discord
-
### To-Do
- [ ] <*Bug*> Update GUI to allow stopping of bot while GUI stays open
- [ ] Add method for user to change audio device without redoing entire config file
- [ ] Interact with soapysdr directly from the bot
- [ ] Allow chat interaction with soapysdr
- [ ] Transcode radio transmissions to text
- [x] Move cogs to their own files
- [X] Add a disable function for cogs
- [X] Send a message details of digital comms
- [X] Send only one message at join and update this message with details of digital comms
- [X] Update WillieTimer with replies to all msgs
- [X] Add saving of changes to mention and channel
- [X] Add a pool of responses to 4:20

56
TODO.md
View File

@@ -1,56 +0,0 @@
# To-Do
## Main Bot
### Main Development
#### Core
- [ ] Add new handlers for GQRX: https://github.com/gqrx-sdr/gqrx/blob/master/resources/remote-control.txt
- [ ] Add logging for Elasticstack https://www.elastic.co/guide/en/ecs-logging/python/master/installation.html
- [ ] Add a process handler to start/stop gqrx
- [ ] Fix the bug where they *disconnect* after a period of time and must be manually moved out and back in to hear them
- *May* have been fixed with the noise gate?
- [ ] Fix bug that shows different index number for audio device selection on linux
- [ ] New bug on linux shows a bunch of ALSA output
- [x] Add more proper help text with real examples for discord commands
- [x] Add command line args with argparse for main bot
- [x] Add method for user to change audio device without redoing entire config file
- [ ] Need to create a method for the bot to toggle debug mode for OP25
#### Features
- [ ] Add to a linux service
- [ ] Add a function to the bot to allow *authorized* users to; update, restart, git pull, etc.
### Polishing
- [ ] Clean up code
- [ ] Revise discord help text
- [ ] Revise logging
### Final
- [ ] Create setup script
- [ ] Test setup
- [ ] Revise readme
--------------------------------------------
## Modules
### Willie Timer
- [ ] Revise the current loop, ensure it re-enters itself after triggering
- [ ] Can we use a cron somehow?
- [ ] Get more training data for WillieTimer
- [ ] Use the ```Phrases.txt``` file as the random seed?
- [ ] Figure out a way to give the model a suggestion
-------------------------------
## Dreams
- [ ] Transcode radio transmissions to text
---------------------
## Done Previously
- [x] Interact with soapysdr directly from the bot
- [x] Allow chat interaction with soapysdr
- [x] Move cogs to their own files
- [X] Add a disable function for cogs
- [X] Send a message details of digital comms
- [X] Send only one message at join and update this message with details of digital comms
- [X] Update WillieTimer with replies to all msgs
- [X] Add saving of changes to mention and channel
- [X] Add a pool of responses to 4:20
- [x] Add a profile system ('YPD' = preset Squelch, Freq, Sample Rate, etc.)
- [x] Add proper comments

591
bot.py
View File

@@ -1,593 +1,98 @@
import asyncio
import logging
import os
import platform
import discord
import BotResources
import configparser
import NoiseGatev2
import sound
from discord.ext import commands
from main import write_config_file
# Init class for bot
class Bot(commands.Bot):
def __init__(self, **kwargs):
# If there is no custom command prefix (!help, ?help, etc.), use '>!' but also accept @ mentions
def __init__(self, **kwargs): # bot_token, device_id, device_name, command_prefix='>!'):
if 'command_prefix' not in kwargs.keys():
bot_intents = set_server_intents()
kwargs['command_prefix'] = '>!'
commands.Bot.__init__(self, command_prefix=commands.when_mentioned_or(kwargs['command_prefix']),
activity=discord.Game(name=f"@ me"), status=discord.Status.idle,
intents=bot_intents)
# Create the logger for the bot
self.logger = logging.getLogger("Discord_Radio_Bot.Bot")
# Init the core bot variables
commands.Bot.__init__(self, command_prefix=commands.when_mentioned_or(kwargs['command_prefix']))
self.DEVICE_ID = kwargs['Device_ID']
self.DEVICE_NAME = kwargs['Device_Name']
self.BOT_TOKEN = kwargs['Token']
self.Default_Channel_ID = kwargs['Channel_ID']
self.Default_Mention_Group = kwargs['Mention_Group']
self.Handler = kwargs['Handler']
self.Command_Prefix = kwargs['command_prefix']
self.Devices_List = sound.query_devices().items()
# Init Variable for sound
self.streamHandler = None
# Init 'lock' variable for when the bot is joined
self.Bot_Connected = False
# Init the audio devices list
#self.Devices_List = sound.query_devices().items()
self.Devices_List = NoiseGatev2.AudioStream().list_devices(_display_input_devices=False,
_display_output_devices=False)
# Init radio parameters
self.profile_name = BotResources.DEFAULT_RADIO_SETTINGS['profile_name']
self.freq = BotResources.DEFAULT_RADIO_SETTINGS['freq']
self.mode = BotResources.DEFAULT_RADIO_SETTINGS['mode']
self.squelch = BotResources.DEFAULT_RADIO_SETTINGS['squelch']
self.noisegate_sensitivity = BotResources.DEFAULT_RADIO_SETTINGS['noisegate_sensitivity']
# Init SDR Variables
self.system_os_type = None
self.sdr_started = False
# Check the handler being used
self.check_handler()
# Set linux or windows
self.check_os_type()
# Add discord commands to the bot
self.add_commands()
# Add discord events to the bot
self.add_events()
self.check_for_modules()
# Start the bot
def start_bot(self):
self.run(self.BOT_TOKEN)
# Add discord commands to the bot
async def stop_bot(self):
await self.close()
return
def add_commands(self):
# Command to display what channel the bot is in
@self.command(help="Use this command to display what channel the bot is in",
brief="Where ya at?")
async def wya(ctx, member: discord.Member = None):
member = member or ctx.author.display_name
if self.Bot_Connected:
await ctx.send(f"Hey {str(member).capitalize()}, I'm in {ctx.voice_client.channel}")
@self.command(help="Use this to test if the bot is alive", brief="Sends a 'pong' in response")
async def ping(ctx):
await ctx.send('pong')
else:
await ctx.send(f"{str(member).capitalize()}, I am not in any channel.")
# Command to join the bot the voice channel the user who called the command is in
@self.command(help="Use this command to join the bot to your channel",
brief="Joins the voice channel that the caller is in")
@self.command(help="Use this command to join the bot to your channel", brief="Joins the voice channel that the caller is in")
async def join(ctx, *, member: discord.Member = None):
member = member or ctx.author.display_name
self.logger.info(f"Join requested by {member}")
await self.wait_until_ready()
discord.opus.load_opus('./opus/libopus.dll')
if not self.Bot_Connected:
# Wait for the bot to be ready to connect
await self.wait_until_ready()
if discord.opus.is_loaded():
stream = sound.PCMStream()
channel = ctx.author.voice.channel
await ctx.send(f"Ok {member}, I'm joining {channel}")
# Load respective opus library
self.load_opus()
stream.change_device(self.DEVICE_ID)
if discord.opus.is_loaded():
channel = ctx.author.voice.channel
self.logger.debug("Sending hello")
await ctx.send(f"Ok {str(member).capitalize()}, I'm joining {channel}")
# Join the voice channel with the audio stream
self.logger.debug('Joining')
voice_connection = await channel.connect()
# Create an audio stream from selected device
self.logger.debug("Starting noisegate/stream handler")
self.streamHandler = NoiseGatev2.NoiseGate(_input_device_index=self.DEVICE_ID,
_voice_connection=voice_connection,
_noise_gate_threshold=self.noisegate_sensitivity)
# Start the audio stream
self.streamHandler.run()
# Start the SDR and begin playing to the audio stream
self.logger.debug("Starting SDR")
self.start_sdr()
# Change the activity to the channel and band-type being used
self.logger.debug("Changing presence")
await self.set_activity()
# 'Lock' the bot from connecting
self.logger.debug("Locking the bot")
self.Bot_Connected = True
else:
# Return that the opus library would not load
await ctx.send("Opus won't load")
self.logger.critical("OPUS didn't load properly")
voice_connection = await channel.connect()
voice_connection.play(discord.PCMAudio(stream))
else:
await ctx.send(f"{str(member).capitalize()}, I'm already connected")
self.logger.info("Bot is already in a channel")
await ctx.send("Opus won't load")
@self.command(help="Use this command to have the bot leave your channel",
brief="Leaves the current voice channel")
async def leave(ctx, member: discord.Member = None):
member = member or ctx.author.display_name
self.logger.info(f"Leave requested by {member}")
@self.command(help="Use this command to have the bot leave your channel", brief="Leaves the current voice channel")
async def leave(ctx):
await ctx.voice_client.disconnect()
if self.Bot_Connected:
# Stop the sound handlers
# Disconnect the client from the voice channel
self.logger.debug("Disconnecting")
await self.streamHandler.close()
self.logger.debug("Changing presence")
# Change the presence to away and '@ me'
await self.set_activity(False)
# Stop the SDR so it can cool off
self.logger.debug("Stopping SDR")
self.stop_sdr()
self.logger.debug("Unlocking the bot")
# 'Unlock' the bot
self.Bot_Connected = False
self.logger.debug("Sending Goodbye")
await ctx.send(f"Goodbye {str(member).capitalize()}.")
else:
await ctx.send(f"{str(member).capitalize()}, I'm not in a channel")
self.logger.info("Bot is not in a channel")
# Add command to change the NoiseGate threshold
@self.command(help="Use this command to change the threshold of the noisegate",
brief="Change noisegate sensitivity")
async def chngth(ctx, _threshold: int, member: discord.Member = None):
member = member or ctx.author.display_name
self.logger.info(f"Change of NoiseGate threshold requested by {member}")
await ctx.send(f"Ok {str(member).capitalize()}, I'm changing the threshold from "
f"{self.streamHandler.THRESHOLD} to {_threshold}")
self.streamHandler.THRESHOLD = _threshold
self.noisegate_sensitivity = _threshold
# Reset the profile name since we have made a change
self.profile_name = None
# If the SDR is started, restart it with the updates
await self.use_current_radio_config()
# Add commands for GQRX and OP25
if self.Handler in BotResources.PDB_ACCEPTABLE_HANDLERS.keys():
# Command to display the current config
@self.command(name='displaycurprofile',
help="Use this command to display the current configuration of the bot.\n"
"Example command:\n"
"\t@ displaycurprofile",
breif="Display current bot config")
async def _displaycurprofile(ctx, member: discord.Member = None):
member = member or ctx.author.display_name
message = self.display_current_radio_config()
await ctx.send(f"Ok {str(member).capitalize()},\n{message}")
self.logger.info(f"Displaying current profile; requested by {member}")
# Command to display the current config
@self.command(name='displayprofiles',
help="Use this command to display the saved profiles.\n"
"Example command:\n"
"\t@ displayprofiles",
breif="Display current bot config")
async def _displayprofiles(ctx, member: discord.Member = None):
member = member or ctx.author.display_name
message = self.display_saved_radio_configs()
await ctx.send(f"Ok {str(member).capitalize()},\n{message}")
self.logger.info(f"Displaying all profiles; requested by {member}")
# Command to change the current frequency and mode
@self.command(name='chfreq', help="Use this command to change the frequency the bot is listening to.\n"
"Example GQRX command:\n"
"\tTune to 104.7Mhz Wideband FM (Radio) - '@ chfreq wfm 104700000\n"
"\tTune to 155.505Mhz Narrowband FM (Radio) - '@ chfreq fm 155505000\n"
"Example OP25 command:\n"
"\tTune to 155.310Mhz, decode using P25 - '@ chfreq p25 155.310",
brief="Changes radio frequency")
async def chfreq(ctx, mode: str, freq: str, member: discord.Member = None):
# Possible band-types that can be used
member = member or ctx.author.display_name
self.logger.info(f"{member} requested change of frequency to Mode: {mode}, Freq: {freq}")
# Check to make sure the frequency input matches the syntax needed
if len(freq) >= 6:
self.freq = freq
# Check to make sure the selected mode is valid
if mode in self.possible_modes:
self.mode = mode
await ctx.send(f"Ok {str(member).capitalize()}, I'm changing the mode to "
f"{str(self.mode).upper()} and frequency to {self.freq}")
# Reset the profile name since we have made a change
self.profile_name = None
# If the SDR is started, restart it with the updates
await self.use_current_radio_config()
else:
await ctx.send(f"{str(member).capitalize()}, {mode} is not valid."
f" You may only enter {self.possible_modes}")
else:
await ctx.send(f"{str(member).capitalize()}, {freq} is not valid. "
f"Please refer to the help page '@ help chfreq'")
# GQRX Specific commands
if self.Handler == 'gqrx':
@self.command(name='chsquelch', help="Use this command to change the squelch for the frequency "
"the bot is listening to\n"
"Example Commands:\n"
"\tNo Squelch\t'@ chsquelch 150'\n"
"\tFully Squelched\t'@ chsquelch 0'",
brief="Changes radio squelch")
async def chsquelch(ctx, squelch: float, member: discord.Member = None):
member = member or ctx.author.display_name
self.logger.info(f"{member} requested change of squelch to: {squelch}")
self.squelch = squelch
await ctx.send(f"Ok {str(member).capitalize()}, I'm changing the squelch to {self.squelch}")
# Reset the profile name since we have made a change
self.profile_name = None
# If the SDR is started, restart it with the updates
await self.use_current_radio_config()
# Hidden admin commands
@self.command(name='saveprofile', hidden=True)
async def _saveprofile(ctx, profile_name: str, member: discord.Member = None):
member = member or ctx.author.display_name
await self.save_radio_config(profile_name)
await ctx.send(f"Ok {str(member).capitalize()}, I saved the current settings as {profile_name}")
@self.command(name='loadprofile', hidden=True)
async def _loadprofile(ctx, profile_name: str, member: discord.Member = None):
member = member or ctx.author.display_name
config_loaded = await self.load_radio_config(profile_name)
if config_loaded:
await ctx.send(f"Ok {str(member).capitalize()}, I loaded the settings saved as {profile_name}")
else:
await ctx.send(f"{str(member).capitalize()}, there is no profile with the name '{profile_name}'")
# Hidden admin commands
@self.command(name='reload', hidden=True)
async def _reload(ctx, module: str, member: discord.Member = None):
"""Reloads a module."""
member = member or ctx.author.display_name
if self.reload_modules(module):
await ctx.send(f"Ok {str(member).capitalize()}, I reloaded {module}")
await ctx.send(f"Ok {member}, I reloaded {module}")
else:
await ctx.send(f"{str(member).capitalize()}, something went wrong. Please check the console")
await ctx.send(f"{member}, something went wrong. Please check the console")
@self.command(name='startsdr', hidden=True)
async def _startsdr(*args):
self.start_sdr()
def check_device(self):
for device, index in self.Devices_List:
if int(index) == self.DEVICE_ID and str(device) == self.DEVICE_NAME:
return True
@self.command(name='stopsdr', hidden=True)
async def _stopsdr(*args):
self.stop_sdr()
for device, index in self.Devices_List:
if str(device) == self.DEVICE_NAME:
self.DEVICE_ID = int(index)
return True
# Add discord events to the bot
def add_events(self):
# Run any functions that need to have the bot running to complete
@self.event
async def on_ready():
# Check the ./modules folder for any modules (cog.py)
await self.check_for_modules()
self.logger.info("Bot started!")
# Check to see if other bots are online
async def check_other_bots_online(self):
self.logger.info('Checking if other bots are online')
channel = self.get_channel(self.Default_Channel_ID)
self.logger.debug(f"Testing in: {channel}")
bots_online = []
def verify_bot_msg(msg):
if msg.author.id in BotResources.PDB_KNOWN_BOT_IDS.keys():
bots_online.append(BotResources.PDB_KNOWN_BOT_IDS[msg.author.id])
await self.wait_until_ready()
# Send the ping command with the prefix the current bot is using
await channel.send(f"{self.Command_Prefix}check_modules")
seconds_waited = 0
while seconds_waited < 3:
try:
await self.wait_for("message", check=verify_bot_msg, timeout=1)
except asyncio.exceptions.TimeoutError:
seconds_waited += 1
self.logger.debug(f"Bots Online: {bots_online}")
if len(bots_online) == 0:
return False
elif len(bots_online) > 0:
return True
# Check the handler being used during init
def check_handler(self):
if self.Handler == "gqrx":
self.logger.info("Starting GQRX handler")
from gqrxHandler import GQRXHandler
self.GQRXHandler = GQRXHandler()
self.GQRXHandler.start()
self.possible_modes = BotResources.PDB_ACCEPTABLE_HANDLERS['gqrx']['Modes']
elif self.Handler == 'op25':
self.logger.info("Starting OP25 handler")
from op25Handler import OP25Handler
self.OP25Handler = OP25Handler()
self.OP25Handler.start()
self.possible_modes = BotResources.PDB_ACCEPTABLE_HANDLERS['op25']['Modes']
# Load the proper OPUS library for the device being used
def load_opus(self):
# Check the system type and load the correct library
# Linux ARM AARCH64 running 32bit OS
if self.system_os_type == 'Linux_ARMv7l':
self.logger.debug(f"Loaded OPUS library for {self.system_os_type}")
discord.opus.load_opus('./opus/libopus_armv7l.so')
# Linux ARM AARCH64 running 64bit OS
if self.system_os_type == 'Linux_AARCH64':
self.logger.debug(f"Loaded OPUS library for {self.system_os_type}")
discord.opus.load_opus('./opus/libopus_aarcch64.so')
# Windows 64bit
if self.system_os_type == 'Windows_x64':
self.logger.debug(f"Loaded OPUS library for {self.system_os_type}")
discord.opus.load_opus('./opus/libopus_amd64.dll')
# Check to make sure the selected device is still available and has not changed its index
def check_device(self, _override):
# Check to see if an override has been passed
if not _override:
self.logger.debug(f"Device list {self.Devices_List}")
for device, index in self.Devices_List['Input']:
if int(index) == self.DEVICE_ID and str(device) == self.DEVICE_NAME:
return True
for device, index in self.Devices_List['Input']:
if str(device) == self.DEVICE_NAME:
self.DEVICE_ID = int(index)
return True
else:
return False
else:
# If an override has been passed just reply true
return True
return False
# Search the ./modules folder for any modules to load
async def check_for_modules(self):
# Check to see if other bots are online and don't load the modules if they are
if not await self.check_other_bots_online():
# A valid module must be built as a 'cog', refer to the docs for more information
for folder_name in os.listdir("modules"):
if str(folder_name)[0] == '.':
continue
elif os.path.exists(os.path.join("modules", folder_name, "cog.py")):
self.logger.debug(f"Loaded extension: {folder_name}")
self.load_extension(f"modules.{folder_name}.cog")
def check_for_modules(self):
for folder_name in os.listdir("modules"):
if str(folder_name)[0] == '.':
continue
elif os.path.exists(os.path.join("modules", folder_name, "cog.py")):
print(f"Loaded extension: {folder_name}")
self.load_extension(f"modules.{folder_name}.cog")
# Reload a selected module for changes
def reload_modules(self, module):
try:
self.unload_extension(f"modules.{module}.cog")
self.logger.debug(f"Unloaded {module}")
print(f"Unloaded {module}")
self.load_extension(f"modules.{module}.cog")
self.logger.debug(f"Loaded {module}")
print(f"Loaded {module}")
return True
except Exception as e:
self.logger.error(e)
print(e)
return False
# Check and store the OS type of the system for later use
def check_os_type(self):
processor = platform.machine()
if os.name == 'nt':
if processor == "AMD64":
self.system_os_type = 'Windows_x64'
self.logger.debug(f"OS/Arch is {self.system_os_type}")
else:
if processor == "aarch64":
self.system_os_type = 'Linux_AARCH64'
self.logger.debug(f"OS/Arch is {self.system_os_type}")
elif processor == "armv7l":
self.system_os_type = 'Linux_ARMv7l'
self.logger.debug(f"OS/Arch is {self.system_os_type}")
# Check to see if there is only one frequency
def start_sdr(self):
if self.Handler in BotResources.PDB_ACCEPTABLE_HANDLERS.keys():
if type(self.freq) == str:
# Single freq sent
# Stop the SDR if it is running
self.stop_sdr()
# Start the radio
self.logger.debug(f"Starting freq: {self.freq}")
if self.Handler == 'gqrx':
# Set the settings in GQRX
self.GQRXHandler.set_gqrx_parameters(_frequency=self.freq, _squelch=self.squelch,
_fm_mode=self.mode, _output_device_name=self.DEVICE_NAME,
_start=True)
elif self.Handler == 'op25':
self.OP25Handler.set_op25_parameters(self.freq, _start=True, _output_device_name=self.DEVICE_NAME)
# Set the started variable for later checks
self.sdr_started = True
# Check to see if the SDR is running
def stop_sdr(self):
if self.sdr_started:
# Wait for the running processes to close
# Close the GQRX handler
if self.Handler == 'gqrx':
self.GQRXHandler.set_gqrx_parameters(_stop=True)
# Close the OP25 handler
if self.Handler == 'op25':
self.OP25Handler.set_op25_parameters(_stop=True)
# self.OP25Handler.join()
self.sdr_started = False
# Set the activity of the bot
async def set_activity(self, connected=True):
if connected:
if self.Handler in BotResources.PDB_ACCEPTABLE_HANDLERS.keys():
if self.profile_name is None:
await self.change_presence(activity=discord.Activity(type=discord.ActivityType.listening,
name=f"{self.freq[:-1]}"
f" {str(self.mode).upper()}"),
status=discord.Status.online)
elif type(self.profile_name) == str:
await self.change_presence(activity=discord.Activity(type=discord.ActivityType.listening,
name=f"{str(self.profile_name).upper()}"),
status=discord.Status.online)
else:
await self.change_presence(activity=discord.Activity(type=discord.ActivityType.listening,
name=f"the airwaves"),
status=discord.Status.online)
elif not connected:
await self.change_presence(activity=discord.Game(name=f"@ me"), status=discord.Status.idle)
# Save the current radio settings as a profile
async def save_radio_config(self, _profile_name: str):
self.logger.debug(f"Saving profile {_profile_name}")
config = configparser.SafeConfigParser()
if os.path.exists('./profiles.ini'):
config.read('./profiles.ini')
profile_name = str(_profile_name).upper()
if not config.has_section(str(profile_name)):
config.add_section(str(profile_name))
config[str(profile_name)]['Frequency'] = self.freq
config[str(profile_name)]['Mode'] = self.mode
config[str(profile_name)]['Squelch'] = str(self.squelch)
config[str(profile_name)]['Noisegate_Sensitivity'] = str(self.noisegate_sensitivity)
with open('./profiles.ini', 'w+') as config_file:
config.write(config_file)
self.profile_name = profile_name
await self.use_current_radio_config()
async def use_current_radio_config(self):
if self.sdr_started:
# Set the loaded profile settings into GQRX
if self.Handler == "gqrx":
self.GQRXHandler.set_gqrx_parameters(_frequency=self.freq, _squelch=self.squelch,
_fm_mode=self.mode)
# Restart OP25 to use the loaded profile
if self.Handler == "op25":
self.start_sdr()
# Set the activity to reflect the loaded profile
await self.set_activity()
# Load a saved profile into the current settings
async def load_radio_config(self, profile_name):
self.logger.debug(f"Loading profile {profile_name}")
config = configparser.ConfigParser()
if os.path.exists('./profiles.ini'):
config.read('./profiles.ini')
if config.has_section(str(profile_name).upper()):
self.profile_name = str(profile_name).upper()
self.freq = config[self.profile_name]['Frequency']
self.mode = config[self.profile_name]['Mode']
self.squelch = float(config[self.profile_name]['Squelch'])
try:
self.noisegate_sensitivity = int(config[self.profile_name]['Noisegate_Sensitivity'])
except KeyError:
self.logger.warning(f"Config does not contain a 'noisegate sensitivity' value, "
f"creating one now with the default value: "
f"{BotResources.DEFAULT_NOISEGATE_THRESHOLD}")
self.noisegate_sensitivity = BotResources.DEFAULT_NOISEGATE_THRESHOLD
await self.save_radio_config(self.profile_name)
await self.use_current_radio_config()
return True
else:
return False
else:
return False
def display_current_radio_config(self):
message_body = ""
if self.profile_name:
message_body += f"Profile Name: {str(self.profile_name).upper()}\n"
message_body += f"\tMode:\t\t\t\t\t{self.mode}\n" \
f"\tFrequency:\t\t\t{self.freq}\n" \
f"\tNoisegate Sensitivity:\t{self.noisegate_sensitivity}"
if self.squelch:
message_body += f"\tSquelch:\t\t\t\t{self.squelch}"
return message_body
def display_saved_radio_configs(self):
message_body = f"Saved configs\n"
config = configparser.ConfigParser()
if os.path.exists('./profiles.ini'):
config.read('./profiles.ini')
for section in config.sections():
message_body += f"\nProfile Name: {section}:\n" \
f"\tMode:\t\t\t\t\t{config[section]['Mode']}\n" \
f"\tFrequency:\t\t\t{config[section]['Frequency']}\n"
try:
message_body += f"\tNoisegate Sensitivity:\t{config[section]['Noisegate_Sensitivity']}\n"
except KeyError:
self.logger.warning(f"Config does not contain a 'noisegate sensitivity' value. Please load the profile")
message_body += f"\tSquelch:\t\t\t\t{config[section]['Squelch']}\n"
return message_body
# Set discord intents and return the intent object
def set_server_intents():
bot_intents = discord.Intents.default()
#bot_intents.messages = True
#bot_intents.message_content = True
#bot_intents.members = True
return bot_intents

View File

@@ -1,239 +0,0 @@
import configparser
import shutil
import logging
import threading
import subprocess
import time
from pathlib import Path
from telnetlib import Telnet
from BotResources import *
from time import sleep
def reset_crashed(_config_path):
config = configparser.SafeConfigParser()
config.read(_config_path)
if config.has_section('General'):
if config.getboolean('General', 'crashed'):
config['General']['crashed'] = 'false'
with open(_config_path, 'w') as config_file:
config.write(config_file)
return True
else:
return False
def enable_agc(_config_path):
config = configparser.SafeConfigParser()
config.read(_config_path)
if config.has_option('receiver', 'agc_off'):
config.remove_option('receiver', 'agc_off')
with open(_config_path, 'w') as config_file:
config.write(config_file)
return True
class GQRXHandler(threading.Thread):
def __init__(self):
super().__init__()
self.GQRX_Config_Path = Path(f"{Path.home()}/.config/gqrx/drb_defaults.conf")
self.GQRXDir: str = GQRX_BIN_LOCATION
self.GQRXEXE: str = shutil.which(GQRX_BIN)
self.GQRXProc = None
self.GQRX_Started = False
self.logger = logging.getLogger("Discord_Radio_Bot.GQRXHandler")
self.Frequency = None
self.Mode = DEFAULT_RADIO_SETTINGS['mode']
self.Frequency = DEFAULT_RADIO_SETTINGS['freq']
self.Squelch = DEFAULT_RADIO_SETTINGS['squelch']
self.Start_GQRX = False
self.Stop_GQRX = False
self.Output_Device_Name = None
self.hostname = "localhost"
self.port = 7356
self.tel_conn = None
def run(self) -> None:
while True:
if self.Start_GQRX:
self.open_gqrx()
self.Start_GQRX = False
self.Stop_GQRX = False
self.logger.debug("GQRX is open, waiting for it to close")
while not self.Stop_GQRX:
sleep(1)
self.logger.debug('Request to close GQRX')
self.close_gqrx()
sleep(.5)
def set_gqrx_parameters(self, _frequency: str = False, _start: bool = False, _stop: bool = False,
_output_device_name: str = None, _fm_mode: str = None, _squelch: float = None,
_hostname: str = None, _port: int = None, _start_dsp: bool = None):
if _frequency:
self.Frequency = _frequency
if self.GQRX_Started:
self.change_freq(_frequency)
if _output_device_name:
self.Output_Device_Name = _output_device_name
if _fm_mode:
self.Mode = _fm_mode
if self.GQRX_Started:
self.change_mode(_fm_mode)
if _squelch:
self.Squelch = _squelch
if self.GQRX_Started:
self.change_squelch(_squelch)
if _hostname:
self.hostname = _hostname
self.Start_GQRX = True
if _port:
self.port = _port
self.Start_GQRX = True
if _start_dsp:
self.start_dsp()
if _start:
self.Start_GQRX = _start
if _stop:
self.Stop_GQRX = _stop
def open_gqrx(self):
if self.GQRX_Started:
self.close_gqrx()
gqrx_kwargs = [f"gqrx", "-c", "drb_defaults.conf"]
self.logger.info(f"Resetting 'crashed' option in the GQRX config")
self.reset_or_create_config()
self.logger.info(f"Starting GQRX")
self.logger.debug(f"GQRX Keyword Args: {gqrx_kwargs}")
self.GQRXProc = subprocess.Popen(gqrx_kwargs, executable=self.GQRXEXE, shell=False)
while not self.tel_conn:
self.create_telnet_connection()
sleep(2)
self.logger.debug(f"Waiting for GQRX to start")
self.GQRX_Started = True
self.start_dsp()
self.set_all_settings(_squelch=self.Squelch, _mode=self.Mode, _freq=self.Frequency)
self.logger.debug('Finished opening GQRX')
def close_gqrx(self):
self.logger.info(f"Closing GQRX")
try:
self.GQRXProc.kill()
seconds_waited = 0
while self.GQRXProc.poll() is None:
# Terminate the process every 5 seconds
if seconds_waited % 5 == 0:
self.logger.info("Terminating GQRX")
self.GQRXProc.terminate()
sleep(1)
self.logger.debug(f"Waited {seconds_waited} seconds")
seconds_waited += 1
self.logger.debug("GQRX Closed")
self.GQRX_Started = False
self.tel_conn = None
except Exception as e:
self.logger.error(e)
def create_telnet_connection(self):
self.logger.debug("Creating connection")
try:
self.tel_conn = Telnet(self.hostname, self.port)
self.tel_conn.open(self.hostname, self.port)
self.logger.debug(f"GQRX is open")
return True
except Exception as err:
self.logger.warning(err)
self.tel_conn = None
return False
def check_dsp(self):
self.logger.debug(f"Checking if DSP is running on GQRX")
self.tel_conn.write(bytes(f"u DSP", 'utf-8'))
if self.tel_conn.read_some() == b"1":
return True
else:
return False
def start_dsp(self):
if not self.check_dsp():
self.logger.debug(f"Starting DSP on GQRX")
self.tel_conn.write(bytes(f"U DSP 1", 'utf-8'))
self.tel_conn.read_until(b'RPRT 0')
def change_freq(self, freq):
self.logger.debug(f"Changing freq to {freq}")
self.tel_conn.write(bytes(f"F {int(freq)}", 'utf-8'))
self.tel_conn.read_until(b'RPRT 0')
def change_squelch(self, squelch):
if not check_negative(squelch):
squelch = float(-abs(squelch))
self.logger.debug(f"Changing squelch to {squelch}")
self.tel_conn.write(bytes(f"L SQL {float(squelch)}", 'utf-8'))
self.tel_conn.read_until(b'RPRT 0')
def change_mode(self, mode):
self.logger.debug(f"Changing mode to {mode}")
self.tel_conn.write(bytes(f"M {str(mode)}", 'utf-8'))
self.tel_conn.read_until(b'RPRT 0')
def set_all_settings(self, _mode, _squelch, _freq):
self.change_squelch(0)
self.change_mode(_mode)
self.change_freq(_freq)
self.change_squelch(_squelch)
def creat_config(self):
from templates.gqrx_config_template import drb_defaults
config = drb_defaults
try:
with open(self.GQRX_Config_Path, 'w+') as config_file:
config_file.write(config)
except OSError as err:
self.logger.error(err)
def reset_or_create_config(self):
if self.GQRX_Config_Path.is_file():
try:
self.logger.debug(f"Enabling AGC in the GQRX config")
enable_agc(_config_path=self.GQRX_Config_Path)
self.logger.debug(f"GQRX Config exists, resetting 'crashed' setting")
reset_crashed(_config_path=self.GQRX_Config_Path)
except configparser.DuplicateOptionError as err:
self.logger.warning(err)
self.creat_config()
else:
self.logger.debug(f"GQRX config does not exist, creating it from template")
self.creat_config()

417
main.py
View File

@@ -1,9 +1,14 @@
import logging
import sys
import os
import threading
import time
import bot
import argparse
import BotResources
import asyncio
from os.path import exists
from BotResources import check_if_config_exists, write_config_file, read_config_file, get_device_list
from PyQt6 import QtWidgets, QtGui
from PyQt6.QtCore import QObject, pyqtSignal, QThread
from PyQt6.QtWidgets import QMainWindow, QApplication
# Jorn
#token = 'OTE1MDY0OTk2OTk0NjMzNzI5.YaWKsA.Y9yaCGg_VXRL_qQVbs05vo7gSAc'
@@ -11,130 +16,320 @@ import BotResources
# Greada
#token = 'NzU2MzI3MjcxNTk3NDczODYz.X2QOqQ.LVLj2b-RXQzPmhNuBC1eGFMcYls'
# Brent
#token = OTQzNzQyMDQwMjU1MTE1MzA0.Yg3eRA.ZxEbRr55xahjfaUmPY8pmS-RHTY
#name = "VoiceMeeter Output"
LOGGER = logging.getLogger("Discord_Radio_Bot.Main")
class CreateConfigFile():
def __init__(self, **kwargs):
write_config_file(**kwargs)
class BotWorker(threading.Thread):
def __init__(self, config):
super(BotWorker, self).__init__()
self.config = config
self.discord_bot_client = bot.Bot(Token=self.config['Token'], Device_ID=self.config['Device ID'],
Device_Name=self.config['Device Name'],
Mention_Group=self.config['Mention Group'], Channel_ID=self.config['Channel ID'])
print(f"Verifying audio device:\t{self.config['Device Name']}")
if not self.discord_bot_client.check_device():
raise BotDeviceNotFound(self.config['Device Name'])
def run(self):
print("Bot started!")
self.discord_bot_client.start_bot()
async def stop_bot(self):
print("Stopping Bot")
await self.discord_bot_client.stop_bot()
print("ASD")
class mainWindow(QMainWindow):
def __init__(self):
super(mainWindow, self).__init__()
print('Checking config file...')
if check_if_config_exists():
self.config = read_config_file()
else:
self.config = None
windowDimensions, uiInformation = self.createInitVars()
self.windowDimensions = windowDimensions
self.uiInformation = uiInformation
self.device_list = get_device_list()
self.bot_thread = None
### UI Init
self.labelDevice = QtWidgets.QLabel(self)
self.contextEditMenu = QtWidgets.QMenu("&Edit", self)
self.contextFileMenu = QtWidgets.QMenu("&File", self)
self.btn1 = QtWidgets.QPushButton(self)
self.btn2 = QtWidgets.QPushButton(self)
self.btn3 = QtWidgets.QPushButton(self)
self.labelTitle = QtWidgets.QLabel(self)
self.labelToken = QtWidgets.QLabel(self)
self.labelMention_Group = QtWidgets.QLabel(self)
self.labelChannel_ID = QtWidgets.QLabel(self)
self.lineToken = QtWidgets.QLineEdit(self)
self.lineMention_Group = QtWidgets.QLineEdit(self)
self.lineChannel_ID = QtWidgets.QLineEdit(self)
self.comboDevice = QtWidgets.QComboBox(self)
self.contextMenuBar = self.menuBar()
self.createUI()
def createUI(self):
# Load Icon if one exists
if exists('./icon.png'):
self.setWindowIcon(QtGui.QIcon('./icon.png'))
# Loading text and UI variables
buttons = self.uiInformation['buttons']
labels = self.uiInformation['labels']
# Button 1 - Connect Bot
self.btn1.setText(buttons['btn1']['Text'])
self.btn1.clicked.connect(self.btn1Clicked)
self.btn1.move(self.windowDimensions['Width'] - 220, self.windowDimensions['Height'] - 40)
# Button 2 - Disconnect Bot
self.btn2.setText(buttons['btn2']['Text'])
self.btn2.setEnabled(False)
self.btn2.clicked.connect(self.btn2Clicked)
self.btn2.move(self.windowDimensions['Width'] - 110, self.windowDimensions['Height'] - 40)
# Button 3 - Save Config
self.btn3.setText(buttons['btn3']['Text'])
self.btn3.clicked.connect(self.btn3Clicked)
self.btn3.move(15, self.windowDimensions['Height'] - 40)
### Label Init
# Title
self.labelTitle.setText(labels['lblTitle']['Text'])
self.labelTitle.move(15, 30)
self.labelTitle.adjustSize()
# Token
self.labelToken.setText(labels['lblToken']['Text'])
self.labelToken.move(15, 70)
# Device
self.labelDevice.setText(labels['lblDevice']['Text'])
self.labelDevice.move(15, 190)
# Mention Group
self.labelMention_Group.setText(str(labels['lblMention_Group']['Text']))
self.labelMention_Group.move(15, 150)
self.labelMention_Group.adjustSize()
# Channel ID
self.labelChannel_ID.setText(str(labels['lblChannel_ID']['Text']))
self.labelChannel_ID.move(15, 110)
self.labelChannel_ID.adjustSize()
### Line Edit Init
# Token
self.lineToken.move(150, 75)
self.lineToken.resize(420, 28)
if self.config:
if 'Token' in self.config.keys():
self.lineToken.setText(self.config['Token'])
# Channel ID
self.lineChannel_ID.move(150, 115)
self.lineChannel_ID.resize(420, 28)
if self.config:
if 'Channel ID' in self.config.keys():
self.lineChannel_ID.setText(str(self.config['Channel ID']))
# Mention Group
self.lineMention_Group.move(150, 155)
self.lineMention_Group.resize(420, 28)
if self.config:
if 'Mention Group' in self.config.keys():
self.lineMention_Group.setText(self.config['Mention Group'])
# Device
for device, dev_id in self.device_list:
self.comboDevice.addItem(device)
self.comboDevice.move(150, 195)
self.comboDevice.resize(420, 28)
if self.config:
if 'Device Name' in self.config.keys():
self.comboDevice.setCurrentText(self.config['Device Name'])
### Window Setup
self.setGeometry(self.windowDimensions['Xpos'], self.windowDimensions['Ypos'], self.windowDimensions['Width'],
self.windowDimensions['Height'])
self.setFixedSize(self.windowDimensions['Width'], self.windowDimensions['Height'])
self.setWindowTitle(self.uiInformation['Title'])
# Creating file menu
self.contextMenuBar.addMenu(self.contextFileMenu)
# Creating edit menu
self.contextMenuBar.addMenu(self.contextEditMenu)
self.show()
def btn1Clicked(self):
if not self.config:
if len(self.lineToken.text()) == 59 and self.comboDevice.currentText() and len(
str(self.lineChannel_ID.text())) == len(
str("918029426397184000")) and self.lineMention_Group.text():
self.update_config()
CreateConfigFile(token=str(self.lineToken.text()), device_name=self.config['Device Name'],
device_id=self.config['Device ID'], channel_id=self.lineChannel_ID.text(),
mention_group=self.lineMention_Group.text())
self.config = read_config_file()
try:
clearance = 0
if self.update_config():
clearance += 1
if len(self.lineToken.text()) == 59:
clearance += 2
if clearance == 3:
self.bot_thread = BotWorker(self.config)
self.bot_thread.daemon = True
self.bot_thread.start()
elif clearance < 3:
raise Exception("Error in ID or Device")
except Exception as e:
print(e)
def btn2Clicked(self):
try:
asyncio.run(self.bot_thread.stop_bot())
self.bot_thread = BotWorker(self.config)
except Exception as e:
print(e)
return
def btn3Clicked(self):
print(len(self.lineToken.text()))
print(len(str(self.lineChannel_ID.text())))
print(self.comboDevice.currentText())
if len(self.lineToken.text()) == 59 and self.comboDevice.currentText() and len(
str(self.lineChannel_ID.text())) == len(
str("918029426397184000")) and self.lineMention_Group.text():
print('saving.....')
self.update_config()
CreateConfigFile(token=str(self.lineToken.text()), device_name=self.config['Device Name'],
device_id=self.config['Device ID'], channel_id=self.lineChannel_ID.text(), mention_group=self.lineMention_Group.text())
self.config = read_config_file()
print("Saved config")
return
def update_config(self):
if not self.config:
self.config = {}
for device, device_id in get_device_list():
if device == self.comboDevice.currentText():
self.config['Device ID'] = device_id
self.config['Device Name'] = device
return True
return False
def createInitVars(self):
windowDimensions = \
{
'Xpos': 400,
'Ypos': 300,
'Width': 600,
'Height': 325
}
uiInformation = \
{
'Title': "Simple Step Time Tracking",
'buttons':
{
'btn1':
{
'Text': "Connect Bot"
},
'btn2':
{
'Text': "Disconnect Bot"
},
'btn3':
{
'Text': "Save Config"
}
},
'labels':
{
'lblTitle':
{
'Text': "Please enter the fields below and click Connect!"
},
'lblToken':
{
'Text': "Bot Token:"
},
'lblChannel_ID':
{
'Text': "Default Text Channel ID:"
},
'lblMention_Group':
{
'Text': "Default Mention Group:"
},
'lblDevice':
{
'Text': "Input Device:"
},
'lblActivity':
{
'Text': "Activitiy"
},
'lblDescription':
{
'Text': "Description"
}
}
}
return windowDimensions, uiInformation
class BotDeviceNotFound(Exception):
def __init__(self, device):
LOGGER.debug(f"Unable to find the device: {device}")
print(f"Unable to find the device: {device}")
try:
os.remove('./config.ini')
except OSError:
LOGGER.warning("Config file not found, restarting.")
print("Config file not found, restarting.")
#os.execv(__file__, sys.argv)
def main(**passed_config):
# Don't create a config if the user passed parameters
if len(passed_config.keys()) == 0:
LOGGER.info('Checking config file...')
if not BotResources.check_if_config_exists():
LOGGER.warning("No config file exists, please enter this information now")
BotResources.write_config_file(init=True)
config = BotResources.read_config_file()
if not config:
LOGGER.warning("No config file exists, please enter this information now")
BotResources.write_config_file(init=True)
config = BotResources.read_config_file()
# Overwrite config options if they were passed
if len(passed_config.keys()) == 0:
for sub in config:
# checking if key present in other dictionary
if sub in passed_config and passed_config[sub]:
config[sub] = passed_config[sub]
LOGGER.info('Starting Bot...')
discord_bot_client = bot.Bot(Token=config['Bot Token'], Device_ID=config['Device ID'],
Device_Name=config['Device Name'], Mention_Group=config['Mention Group'],
Channel_ID=config['Channel ID'], Handler=config['Handler'])
LOGGER.debug(f"Verifying audio device:\t{config['Device Name']}")
if not discord_bot_client.check_device(_override=bool("Device ID" in passed_config.keys())):
raise BotDeviceNotFound(config['Device Name'])
discord_bot_client.start_bot()
def cmd_arguments():
parser = argparse.ArgumentParser(description='Discord Bot Gang - Premium Discord Bot\n'
'The more you listen, the less you can see')
# Add the arguments
# Arg to override bot token
parser.add_argument('-t', '--token',
metavar='Bot Token',
dest='Bot Token',
type=str,
help='Override saved bot token')
# Arg to override the input device in the config
parser.add_argument('-i', '--input_device_id',
metavar='Device ID',
dest='Device ID',
type=int,
help='Override saved input device')
# Arg to override mention group
parser.add_argument('-m', '--mention',
metavar='Mention Group',
dest='Mention Group',
type=str,
help='Override saved mention group')
# Arg to override default channel to send messages
parser.add_argument('-c', '--channel',
metavar='Channel ID',
dest='Channel ID',
type=int,
help='Override saved sending channel')
# Arg to override handler
parser.add_argument('-u', '--handler',
metavar='Handler',
dest='Handler',
type=str,
help='Override saved SDR handler')
# Arg to save the overridden arguments
#parser.add_argument('-s', '--save',
# metavar='save_overrides',
# dest='save_overrides',
# type=str,
# help='Save the overridden arguments passed')
# Retrieve the args
args = vars(parser.parse_args())
return args
def main():
print('Starting Bot...')
try:
app = QApplication(sys.argv)
window = mainWindow()
sys.exit(app.exec())
except Exception as e:
print(e)
if __name__ == '__main__':
cmd_args = cmd_arguments()
BotResources.init_global_logger()
if not all(cmd_args.values()):
LOGGER.debug("Passed arguments:")
for arg in cmd_args:
if cmd_args[arg]:
LOGGER.debug(f"\t{arg}:\t{cmd_args[arg]}")
try:
LOGGER.info('Starting...')
print('Starting...')
while True:
try:
main(**cmd_args)
except BotDeviceNotFound:
LOGGER.info("Restarting...")
main()
except (BotDeviceNotFound, Exception) as e:
print(e)
print("Restarting...")
time.sleep(2)
except KeyboardInterrupt:
LOGGER.error("Exiting...")
except (KeyboardInterrupt, Exception) as e:
print(e)
print("Exiting...")

View File

@@ -1,52 +0,0 @@
import logging
from discord.ext import commands
LOGGER = logging.getLogger("Discord_Radio_Bot.LinkCop")
class ClearMessages(commands.Cog):
def __init__(self, bot):
self.Bot = bot
@commands.command(name='clear',
help="Use this command to clear a given number of messages from the channel it is called in.\n"
"There is a limit of 100 messages. Please be patient, it may take a while to process a large request."
"Example command:\n"
"\t@ clear\n"
"\t@ clear 10",
breif="Clear x messages in the channel it's called in")
async def clear(self, ctx, amount=2):
member = ctx.author.display_name
member_id = ctx.author.id
mtn_member = f"<@{member_id}>"
if isinstance(amount, int):
if not amount > 0:
ctx.channel.send(f"{member}, the number needs to be positive...")
else:
LOGGER.info(f"Clear {amount} messages requested by {member}")
authors = {}
async for message in ctx.channel.history(limit=amount):
if message.author not in authors:
authors[message.author] = 1
else:
authors[message.author] += 1
await message.delete()
msg = f"{mtn_member}, I deleted {sum(authors.values())} messages from {len(authors.keys())} users:\n"
LOGGER.debug(f"Deleted {sum(authors.values())} messages from {ctx.message.channel}")
for author in authors.keys():
msg += f"\t{str(author).split('#', 1)[0]}: {authors[author]}\n"
LOGGER.debug(f"Deleted {authors[author]} messages from {author}")
await ctx.channel.send(msg)
else:
ctx.channel.send(f"{member}, you should check out the 'help' section...")
def setup(bot: commands.Bot):
bot.add_cog(ClearMessages(bot))

View File

@@ -1,136 +0,0 @@
import logging
import re
import discord
from discord.ext import commands
import random
from BotResources import PDB_KNOWN_BOT_IDS, check_and_reply_to_ping
regex_link = re.compile('(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)')
### Langvars for the response strings
### {%<langvar>%}
### Possible langvars
### 'mtn_user' - Mentions the user that sent the original message - {%mtn_user%}
### 'ref_og_channel' - Reference the channel that the user sent the original message - {%ref_og_channel%}
### 'ref_first_link' - Reference the first link that the user originally sent - {%ref_first_link%}
### 'ref_og_msg' - Reference the original message that the user sent - {%ref_og_msg%}
random_message = ["{%mtn_user%}, tsk tsk. Links belong here:\n{%ref_og_msg%}", "{%mtn_user%}, the channel is quite literally called 'links':\n{%ref_og_msg%}",
"{%mtn_user%}. Well, well, well, if it isn't the man who's been posting links in the wrong channel.\n'{%ref_og_msg%}'",
"{%mtn_user%}, isn't this convenient. A whole channel for links and you put links in, and you put {%ref_first_link_%} in {%ref_og_channel%}.\n\n'{%ref_og_msg%}'",
]
### Channel IDs
# testing 918029426397184000
# links 767303243285790721
# welcome 757379843792044102
# the-round-table 367396189529833474
LOGGER = logging.getLogger("Discord_Radio_Bot.LinkCop")
class LinkCop(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.reply_channel_id = 767303243285790721
self.blocked_channels = [
757379843792044102,
367396189529833474
]
self.whitelisted_groups = [
757375638926655709, # Superadmins
758792783020163084 # Bots
]
# Bring in the known bot IDs from PDB bots
self.whitelisted_ID = PDB_KNOWN_BOT_IDS
self.whitelisted_ID['Carl Bot'] = 235148962103951360
self.add_events()
def add_events(self):
@self.bot.event
async def on_message(ctx):
if self.bot.user.id not in self.whitelisted_ID:
self.whitelisted_ID.append(self.bot.user.id)
if ctx.channel.id in self.blocked_channels:
if ctx.author.id not in self.whitelisted_ID:
try:
if not any(item.id in self.whitelisted_groups for item in ctx.author.roles):
if check_message(ctx.content):
LOGGER.info('Msg with links detected in a blocked channel')
response_text = self.generate_response(ctx)
# Send the response in the links channel
await self.send_message(response_text)
# Delete the original message
await ctx.delete()
except AttributeError as err:
LOGGER.error(f"Link Cop Error: '{err}'\nBot or other non-user that has not "
f"been whitelisted sent a message")
await check_and_reply_to_ping(self.bot, ctx)
async def send_message(self, message):
send_channel = self.bot.get_channel(id=self.reply_channel_id)
await send_channel.send(message)
def generate_response(self, ctx):
# Get message
og_message_text = ctx.content
# Get the random string to edit
response_text = random_message[random.randint(0, len(random_message)-1)]
# Get the sender of the message
member = ctx.author.id
mtn_member = f"<@{member}>"
# Get the name of the channel the message was sent
ref_og_channel = ctx.channel.name
# Get the first link the user sent
ref_first_link = get_links(message_text=og_message_text)
if ref_first_link:
ref_first_link = ref_first_link[0]
# Mention the user
response_text = str(response_text).replace("{%mtn_user%}", mtn_member)
# Reference the original channel
response_text = str(response_text).replace("{%ref_og_channel%}", ref_og_channel)
# Reference the original message
response_text = str(response_text).replace("{%ref_og_msg%}", og_message_text)
# Reference the first link
response_text = str(response_text).replace("{%ref_first_link%}", ref_first_link)
return response_text
def check_message(message_text):
matches = regex_link.findall(message_text)
if bool(matches):
return True
else:
return False
def get_links(message_text):
links = regex_link.findall(message_text)
LOGGER.error(links)
if len(links) > 0:
return links
def setup(bot: commands.Bot):
bot.add_cog(LinkCop(bot))

View File

@@ -1,24 +0,0 @@
from discord.ext import commands
from BotResources import check_and_reply_to_ping
class ModuleProbe(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.channel_id = 767303243285790721
self.add_events()
def add_events(self):
@self.bot.event
async def on_message(message):
await check_and_reply_to_ping(self.bot, message)
@commands.command(help="This command is used by other bots to test if there are any bots online with modules.")
async def check_modules(self, ctx):
if ctx.author.id != self.bot.user.id:
await ctx.send('pong')
def setup(bot: commands.Bot):
bot.add_cog(ModuleProbe(bot))

View File

@@ -1,3 +0,0 @@
/lyrics.txt
*.hdf5
*.json

View File

@@ -1,10 +0,0 @@
Hey, are you busy? I just wanted to let you know IT'S 4:20!
You know, the funny thing about time is we often forget to pay attention to it and we miss out on things. ALMOST LIKE 4:20! LIGHT UP!
Oh say can you see. By the blunts early light. IT'S 4:20!
Can you smell what the rock is cooking? No? PROBABLY BECAUSE IT'S 4:20!
Willie wanted to let you know that IT'S 4:20!
It's 4:20! It's time to light up!
4:20. Can you stand it? Wait, that's not right. IT'S 4:20!!
smoke on 4/20 inhale with friends at Lowlife, Red Tiger, Taraval, Buddha Lounge, and Sticky Rice
4:20! Well, I'll take a hit. Why not! Isn't this the right way to think about time. I mean, hey, we're at 4:20 in the afternoon today, so it must make sense to think of 4:20 as THE RIGHT TIME TO FLIP A CIGARETTE!
(I'll now take a moment to explain to you what 4:20 actually means.)

View File

@@ -6,45 +6,32 @@ from discord.ext import commands
from discord.ext.tasks import loop
from BotResources import write_config_file
#from phraseGenerator import PhraseGenerator
class WillieTimer(commands.Cog):
def __init__(self, bot): #, mention_group='Superadmins', channel_id=757379843792044102):
self.bot = bot
self.mention_group = str(self.bot.Default_Mention_Group)
self.channel_id = int(self.bot.Default_Channel_ID)
self.channel = None
self.guild = None
self.lock = False
self.current_420_phrase = None
self.current_warm_up_phrase = None
self.caller_name = None
#self.PG = PhraseGenerator()
self.message_pool = [
"It's 4:20! It's time to light up!",
"Willie wanted to let you know that IT'S 4:20!",
"Can you smell what the rock is cooking? No? PROBABLY BECAUSE IT'S 4:20!",
"Oh say can you see. By the blunts early light. IT'S 4:20!",
"Hey, are you busy? I just wanted to let you know IT'S 4:20!",
"You know, the funny thing about time is we often forget to pay attention to it and we miss out on things. ALMOST LIKE 4:20! LIGHT UP!"
]
@loop(minutes=1)
async def bg_timer(self):
# Get variables ready before waiting for the next minute, might take a few seconds
await self.bot.wait_until_ready()
output_string = self.get_output_string()
seconds_until_next_minute = int(60 - int(datetime.datetime.now().strftime('%S')))
if not seconds_until_next_minute <= 2:
await asyncio.sleep(seconds_until_next_minute)
# If it is 4:20pm
if datetime.datetime.now().strftime('%H:%M') in "16:20":
if datetime.datetime.now().strftime('%H:%M') in ("04:20", "16:20"):
print(f"It's {datetime.datetime.now().strftime('%H:%M:%S')}!")
await self.channel.send(output_string['420'])
# A warm up to 4:20pm
if datetime.datetime.now().strftime('%H:%M') in "16:17":
print(f"It's {datetime.datetime.now().strftime('%H:%M:%S')}!")
await self.channel.send(output_string['warm up'])
del output_string
channel = self.bot.get_channel(id=self.channel_id)
await channel.send(f"<@&{self.bot_get_role().id}> {choice(self.message_pool)}")
@commands.command(help="Use this command to start the background task to wait for 4:20",
brief="Starts the 4:20 clock")
@@ -52,10 +39,7 @@ class WillieTimer(commands.Cog):
member = member or ctx.author.display_name
if not self.lock:
await ctx.send(f"Thanks {member}, Willie will be in touch soon...")
self.channel = self.bot.get_channel(id=self.channel_id)
self.guild = self.channel.guild
self.lock = True
self.caller_name = str(member)
self.bg_timer.start()
else:
await ctx.send(f"I already told Willie {member}, if you want me to tell him to stop, @ me and say 'stop420'")
@@ -67,11 +51,10 @@ class WillieTimer(commands.Cog):
await ctx.send(f"Ok {member}, Willie will go back to writing music...")
self.bg_timer.stop()
self.lock = False
self.caller_name = None
else:
await ctx.send(f"{member} I haven't told Willie yet. If you want me to, @ me and say 'start420'")
@commands.command(name='420chchn', help="Example '@Greada chchn 757379843792044102'",
@commands.command(help="Example '@Greada chchn 757379843792044102'",
brief="Change the channel the bot chats in at 4:20")
async def chchn(self, ctx, message, *, member: discord.Member = None):
member = member or ctx.author.display_name
@@ -88,7 +71,7 @@ class WillieTimer(commands.Cog):
await ctx.send(f"{member}, {message} is not a valid channel ID, please try again")
pass
@commands.command(name='420chmtn', help="Example '@Greada chmtn 'Willies Friends'",
@commands.command(help="Example '@Greada chmtn 'Willies Friends'",
brief="Use this command to change who the bot mentions at 4:20")
async def chmtn(self, ctx, message, *, member: discord.Member = None):
member = member or ctx.author.display_name
@@ -104,81 +87,12 @@ class WillieTimer(commands.Cog):
await ctx.send(f"{member}, {message} is not a valid role, please try again")
pass
@commands.command(help='Test random choice')
async def test_rchoice(self, ctx, *, member: discord.Member = None):
member = member or ctx.author.display_name
self.channel = self.bot.get_channel(id=self.channel_id)
self.guild = self.channel.guild
strings_dict = self.get_output_string()
await ctx.send(f"420:\t{strings_dict['420']}\nWarning:\t{strings_dict['warm up']}", tts=True)
def bot_get_role(self):
role = discord.utils.get(self.guild.roles, name=self.mention_group)
channel = self.bot.get_channel(id=self.channel_id)
guild = channel.guild
role = discord.utils.get(guild.roles, name=self.mention_group)
return role
def bot_get_user(self, input_user=None):
if self.caller_name is not None:
user = discord.utils.get(self.guild.members, name=self.caller_name)
return user
elif input_user is not None:
user = discord.utils.get(self.guild.members, name=input_user)
return user
def get_random_420_phrase(self):
selected_phrase = None
while not selected_phrase:
with open('./modules/WillieTimer/Phrases.txt', 'r') as phrase_file:
phrases = phrase_file.readlines()
selected_phrase = choice(phrases)
if selected_phrase is not None:
if selected_phrase != self.current_420_phrase:
selected_phrase = selected_phrase.replace('\n', '')
self.current_420_phrase = selected_phrase
return selected_phrase
else:
selected_phrase = None
continue
def get_random_warm_up_phrase(self):
selected_phrase = None
while not selected_phrase:
#selected_phrase = self.PG.pg_generate()
with open('./modules/WillieTimer/lyrics.min.txt', 'r') as phrase_file:
phrases = phrase_file.readlines()
selected_phrase = choice(phrases)
if selected_phrase is not None:
if selected_phrase != self.current_warm_up_phrase:
selected_phrase = selected_phrase.replace('\n', '')
self.current_warm_up_phrase = selected_phrase
return selected_phrase
else:
selected_phrase = None
continue
def get_output_string(self):
willie_time_string = ""
warm_up_string = ""
if self.caller_name is not None:
willie_time_string += f"<@{self.bot_get_user().id}> & "
warm_up_string += f"<@{self.bot_get_user().id}> & "
willie_time_string += f"<@&{self.bot_get_role().id}>! "\
f"Willie wanted me to tell you:\n" \
f"\"{self.get_random_420_phrase()}\""
warm_up_string += f"<@&{self.bot_get_role().id}>! Heads up! " \
f"Willie's passing an early message along:\n" \
f"\"{self.get_random_warm_up_phrase()}\""
output_dict = {
'420': str(willie_time_string),
'warm up': str(warm_up_string)
}
return output_dict
def setup(bot: commands.Bot):
bot.add_cog(WillieTimer(bot))

View File

@@ -1,433 +0,0 @@
Now I was at a club on a late late Sunday
Peeping at the bitches 'till the next day Monday
Had a couple of drinks, so I was feeling good
And suddenly I saw this bitch that lives in my neighborhood
She went to church every week so now I lucked up
She was at the end of the bar gettin' fucked up
Back at the house she was bitch n' be ignoring
And when she start to talk the ho' was kinda boring
Yo, but now I got to dawn, see
Now she's dancing on the floor with a skirt and no panties on
Shaking that ass like a salt shaker
I already got my plans -
Just while I'm a take her to a room
But yo I mean a rest room
And stick my dick in her mouth like a wet broom
So I grabbed her hand and she's wit(h) it
So when she turns sober, she'll never admit it
So while she's dropped I'd better get it quick
And see for myself if she sucks a good dick!
It's the world's biggest dick
Don't matter just don't bite it
It's the world's biggest dick
Don't matter just don't bite it
"What do you want me to do with it?"
Don't matter just don't bite it
She swallowed it (yeah)
They hardly happy for you, keep doing what you do
You can't please everybody, and not everybody is you
Don't try to force a square peg in a round circle, that shit'll hurt you
Don't try to fit in either, you're better off with neither
Few veteran speakers get medicine when you need it
Especially when we all need knowledge instead of sneakers
Stop hangin' on to childhood trauma, it defeats us
Our challenge is holding ourselves back, I hope you felt that
Queen, gotta learn to let it go and move forward
King, you should learn to say no, keep all your dough in
King, Michael Jordan gives back and you didn't know it
Like LeBron does, but it's just seldom they show it
King, get ten points from one bird doin' your thing
King, 'til one of your homies decide to sing
King, I started saying "Peace King" on my song "The Flyest"
And after that, it took off like fire, peace, King4
What up?
Escobar season begins
Repent your sins
Nine-Six
Trackmasters
Check it
Spark the lye with the eight-forty I fly
Pretty thug show no love its like when doves cry
Maybe I'm just too demanding, parked the Land
Mark Buchanan, fuck a job I'mma die scramblin
Digger gold, dig a bigger hole in ya soul
Que Pasa, Pablo's throne cabron
At the airport the Mobb picked me up in a truck
Jewelry chunky like fuck never scared to get stuck
So whats the deal Papi?
Heard the feds could of knocked me
Had the cuban posse all up in my room and lobby
Negotiating, my name holds weight, we motivatin'
Hustling, as if God was dancing with Satan
The Firm waitin', to get ajourned cause we facing
Double life, thuggin for life, you taste it
The white numbin ya toungue
I bought it from Dominicans
A suitcase of benjamins swung tight in his fingers
Had the Firm gun slingers, hit the lights, grab the white
Murder everything in site, and jet in the Bimmers
Now im home with the phone screaner
Resting the stones in a jewelry cleaner
Checkin my mail reading my supoena
To the witness standing, I got remanded
Livin on the 4th building and can't stand it
Gotta fight my case from the inside
Niggas telling, I felt it coming when my pockets started swelling
But thats the game for ya
Hid a stash to ya lawyers
Supreme court, Queens New York will bore ya'
Suited up, booted up, with the jail cut
Million dollar bail what?
Z put up the house and I was out
Word the Fuck Up
Time to get the shit on son
Tryna trap a nigga
Incarcerate me yo'
Check it tho, yo, yo
Im gonna make it if I try, cross my heart hope to die
(cross my heart hope to die)
Im gonna make it if I try, cross my heart hope to die
Praise the Lord, ive been scarred with hot water
Days are shorter, next court dates around the corner
Money ain't the same, shit is outta order
Since the days of Rich Porter
Something gotta give, I ain't trying to bid
Now I run up in cribs, body shit, but no women no kids
In the Bridge where the murderers live
I used to make a quarter million monthly
I was ill until they sunk me
Killed the snitch with the pump 3 shots
Bought a passport off a junkie
The Firm helped me flee out the country
Word and I'm out, Pakastinian land
Chilling with my man Jungle
The commissioner Stout
Killa B, what it is nigga firm biz4
Verse 1:
You only get 1 shot to make a first impression. There ain't no such thing as second chances in this game. It only takes 1 man to knock you out the box. It only takes 1 woman to drive you insane. It only took 1 sniff now look Im addicted. It took 1 bad decision. Now my whole life is shifted. It only took 1 shot to kill my best friend. It only took 1 Glock for me to get revenge. It only takes 1 pill just to ease the pain. It only takes 1 bullet to end everything. Right here and right now. cause of sick of all of it. The 1 times. The jakes and the fakes. Sick of all of them. To many haters hating 1 man. Wish that I could burry all of them. Under 1 grave. Without even 1 chance of being found. Yo it takes 1 strike. 2 strikes. 3. And youre out of here. Getting 25 to life. Without a chance to get out of there. 12 jurors. 1 judge. 1 cell. No love. 1
You get 1 shot. And 1 life. This is real life. On 1 Mic. You get 1 shot. And 1 life. This is real life. A ha...A ha
You get 1 shot. And 1 life. This is real life. On 1 Mic. You get 1 shot. And 1 life. Better get it right.A ha...A ha
It only takes 1 beat for me to fxxx it up. It only takes 1 verse for you to give it up. It only takes 1 mic for me to reck shit. It only takes 1 time for you to know the time. It only takes 1 listen for you to feel the man. It only takes 1 DJ to make a band. It only takes 1 convo to understand. It only takes 1 snitch to go and take the stand. It only takes 1 blunt to make the pain go. It only takes 1 lie to know she gotta go. It only took 1 look to know that shes a hoe. It only took 1 no from you for her to go. She asked you to marry her. And you passed it up. You had your opportunity
You get 1 shot. And 1 life. This is real life. On 1 Mic. You get 1 shot. And 1 life. This is real life. A ha...A ha
You get 1 shot. And 1 life. This is real life. On 1 Mic. You get 1 shot. And 1 life. A ha...A ha
Hit-Boy
Bitch, I'm in a good mood, good groove, pour me up (Ice)
The plane leave in thirty, fix your life, hurry up (Fix your life)
Ackee, rice, peas, puttin' curry over duck (Mm)
Courtside Rockets, Warriors, Curry up (Swish)
Smokin' weed in a tux, sippin' Ricard
Sitting on Governors Isle with all the killers
Premier movies with my man De Niro
And Johnny Nunez got all the pictures
Black-grown, black-owned
Black women is the backbone (Love)
Latin food in the back room
Big business, I'ma drop a new 'gnac soon
Followin' the cash rules
Rich, matte-black Rolls, yeah, I got 'em seeing ghosts (Ghosts)
Twenty-seven summers, that wasn't even the goal (Goal)
Blowing kush clouds and we all for the smoke (Smoke)
Black card, black Rolls, more black CEOs
Bitch, I'm in a good mood, good group pulled me up (Dino)
Mass Appeal, movies and music, sign with us (Al Pacino)
All my niggas millionaires, G-Code know what's up (What up, G-Code?)
What up, Jung'? What up, Stoute? What up, Ant? What up, East?
What they say about us? (What they say about us?)
Rich, matte-black Rolls, yeah, I got 'em seeing ghosts (Ghosts)
Twenty-seven summers, that wasn't even the goal (Goal)
Blowing kush clouds and we all for the smoke (Smoke)
Bitch, black card, black Rolls, more black CEOs9
Yeah, hahaha
When I flow for the street, who else could it be?
Nas
Yo, explode, my thoughts were drunken from quarts of beers
Was years back, before Nasir would explore a career in rap
As a music dude, I mastered this Rubik's Cube
Godzilla, fought Gargantua, eyes glued to the tube
Was a long time ago, John Boy Ice
Geronimo po-lice jumpin' out Chryslers, e-z wider paper
Pops puffin' his cess, punchin' his chest like a gorilla
Outside was psychos, killers
Saw Divine, Goon, and Chongo, Lil' Turkey
R.I.P. Tyrone, 'member no cursin' front of Ms. Vercey
Big Percy, Crazy Paul, the Sledge Sisters
My building was 40-16, once in the blue, hallways was clean
I knew all that I'd seen had meant somethin'
Learned early to fear none, little Nas was huntin'
Livin' carefree laughin', got jokes on the daily
Y'all actin' like some old folks, y'all don't hear me
Yo, I'm in my second childhood
When I flow for the street, who else could it be?
N-A-S, Nas
Resurrect through the birth of my seed, Queensbridge
Make everything right, get yours, nigga
When I flow for the street, who else could it be?
N-A-S, Nas
Resurrect through the birth of my seed, Queensbridge
Make everything right, get yours
Yo, dude is 31, livin' in his mom's crib
Ex-convict, was paroled there after his long bid
Cornrows in his hair, still slingin', got a crew
They break his mom's furniture, watchin' Comicview
Got babies by different ladies, high, smokin' L's
In the same spot he stood since '85, well
When his stash slow, he be crazy
Say he by his moms, hit her on her payday
Junior high school dropout, teachers never cared
They was paid just to show up and leave, no one succeeds
So he moves with his peers, different blocks, different years
Sittin' on different benches like it's musical chairs
All his peoples moved on in life, he's on the corners at night
With young dudes, it's them he wanna be like
It's sad but it's fun to him, right? He never grew up
31 and can't give his youth up, he's in his second childhood
When I flow for the street, who else could it be?
N-A-S, Nas
Resurrect through the birth of my seed, Queensbridge
Make everything right, get yours, nigga
When I flow for the street, who else could it be?
N-A-S, Nas
Resurrect through the birth of my seed, Queensbridge
Make everything right, get yours
Baby girl, she's always talkin', name droppin', hangin' late
Drinkin', smokin', hates her baby daddy, craves shoppin'
E poppin', ecstasy takin', won't finish her education
Best friend she keeps changin', stuck with limitations
Lustin' men, many hotels, Fendi, Chanel
With nothin' in her bank account frontin' she do well
Her kid suffers, he don't get that love he deserve
He the Sun, she the Earth, single mom, even worse
No job, never stay workin', mad purty
Shorty, they call her the brain surgeon
Time flyin', she the same person
Never matures, all her friends married, doin' well
She's in the streets yakkety yakkin' like she was twelve
Honey is twenty-seven, argues fights
Selfish in her own right for life
Guess she's in her second childhood
When I flow for the street, who else could it be?
N-A-S, Nas
Resurrect through the birth of my seed, Queensbridge
Make everything right, get yours, nigga
When I flow for the street, who else could it be?
N-A-S, Nas
Resurrect through the birth of my seed, Queensbridge
Make everything right, get yours
Who else could it be?
N-A-S, Nas18
Mmm (Hit-Boy)
OG talk, project halls
40 Side where a nigga seen it all (For real, for real)
I can send niggas a slide, I'd rather show them the ropes
Integrity matters the most, I gave the hood hope
Damn, I gave the hood classics
Something to open your mind, instead of going out crashin'
How was you real when you hate the real?
My nigga, I'm simply askin'
Can't just talk about the plan, you gotta put it in action
Ain't no pity party for yourself
You gotta get up, rise for your wealth
Can't spend all that time in your feelings
Tryna sympathize with yourself (For real, for real)
Don't nobody owe you, ain't nobody holding you back (Woo)
You stuck in illusions, attached to something that aint even that
South Beach, Nassau, Amalfi, Maldives, same beach
Unless you are after somethin' most men just cant see
Shorty off the block, shorty off the runway, same freak (For real, for real)
We all got the access to open doors like we share the same key
OG talk
A lot of things go down like incarceration
You know, crime, drugs, violence
Outta alla' this you gotta look for the positive
OG talk!
OG Talk (OG talk) project halls (Project halls)
40 Side where a nigga seen it all (Seen it all, for real, for real)
I can send niggas a slide (Slide) I'd rather show them the ropes (Ropes)
Integrity matters the most (Most) I gave the hood hope (Hope)
Damn, niggas really got it jumpin'
They finally gave Nas a Grammy, just front me the gold
That wasnt the goal
First clip was a warning shot, nigga, we spinnin' back up the road
Through the boroughs, pull up our Rolls
Empty this bitch and reload (For real, for real)
Fake thug, no love
You get the slug, CB4 Gusto
Your luck low
I didn't know til I was drunk though
You freak cats get played out
Get robbed and laid out
Prostitute turned snitch
I got the gauge out
96 ways I made out, Montana way
The Good-F-E-L-L-A, verbal AK spray
Dipped attache, jumped out the Range
Empty out the ashtray
A glass of 'ze make a man Cassius Clay
Red dot plots, thirty-two shots
Ya'll niggas don't want none of this
Say a prayer for "Do we have to?"
You ain't right, Jeremiah Wrong pastor
In love with a slave master
Sincerely yours, USA's most brave rapper
Jesse carjacker, Uncle Tom kidnapper
Ask around: Bentley coupe off the Richter
Bitch called "Life": I pimped her, what
Politics, politricks, Klan-shooter
Deacon for defense, progress-producer
Nothing on the stove, a survival-booster
Gotta do what we gotta do
We ain't got no governors coming through to help
Anything we need done, gotta do for self
New, improved JFK on the way
It ain't the 60s again, niggas ain't hippies again
We ain't falling for the same traps
Standing on the balconies where they shot the King at
McCain got apologies; ain't nobody hearing that
People need honesty
("Although it seems heaven-sent
We ain't ready, to have a black president")
("Although it seems heaven-sent
We ain't ready, to have a black president")
Yes we can, change the world
{CHANGE THE WORLD...} they say
("Although it seems heaven-sent
We ain't ready, to have a black president")
("Although it seems heaven-sent
We ain't ready, to have a black president")
Yes we can, change the world
The world, the world, the WORLD...
It is my distinct honor, and privilege to introduce
The next President of the United States - Barack Obama
{*crowd cheers LOUDLY*}10
I know you can feel the magic, baby
Turn the motherfuckin' lights down
Esco, whattup? (Whattup, homie?)
I mean, it's what you expected, ain't it? (Hahaha)
Let's go... uhh, uh, uhh, uh, uhh, uh
Turn the music up in the headphones
Uh, yeah, that's perfect (yeah, right, right)
Uhh, uh, you gotta take ya time, make a nigga wait on this muh'fucka (hahaha!)
Make niggas mad and shit like
Niggas usually just start rappin' after four bars, nigga, go in!
Just start dancin' in this muh'fucka
Yeah, (yeah) we just come outta nowhere
I feel like a Black Republican, money I got comin' in
Can't turn my back on the hood, I got love for them
Can't clean my act up for good, too much thug in him (nah)
Probably end up back in the hood, like, "Fuck it then"
Huddlin' over the oven, we was like brothers then (what?)
Though you was nothin' other than a son of my mother's friend
We had covenant, who would've thought the love would end?
Like Ice Cold's album (uhh), all good things
Never thought we sing the same song that all hoods sang
Thought it was all wood-grain, all good brain
We wouldn't bicker like the other fools, talk good game
Never imagine all the disaster that one good reign, could bring
Should blame, the game, and I could
It's kill or be killed, how could I refrain?
And forever be in debt, and that's never a good thing
So the pressure for success can put a good strain
On a friend you call best, and yes it could bring
Out the worst in every person, even the good and sane
Although we rehearsed it, it just ain't the same
When you put in the game at age sixteen
Then you mix things: like cars, jewelry, and Miss Thing
Jealousy, ego, and pride, and this brings
It all to a head like a coin, cha-ching
The root of evil strikes again, this could sting
Now the team got beef between the Post and the Point
This puts the ring in jeopardy indefinitely
I feel like a black republican, money I got comin' in
Can't turn my back on the hood, I got love for them (uhh, uhh)
Can't clean my act up for good, too much thug in him (nah)
Probably end up back in the hood, I'm like, "Fuck it then"
I feel like a black militant takin' over the government
Can't turn my back on the hood, too much love for them (nah)
Can't clean my act up for good, too much thug in him
Probably end up back in the hood, I'm like, "Fuck it then"
I'm back in the hood, they like, "Hey Nas" (uh)
Blowin' on purp, reflectin' on they lives (uh)
Couple of fat cats, couple of A.I.'s
Dreamin' of fly shit instead of them gray skies
Gray 5s, hatah's wishin' our reign dies
Pitch, sling pies, and niggas they sing, "Why"? (uhh)
Guess they ain't strong enough to handle their jail time
Weak minds keep tryin', follow the street signs
I'm standing on the roof of my building
I'm feelin' the whirlwind of beef, I inhale it
Just like an acrobat ready to hurl myself, through the hoops of fire
Sippin' 80 proof, bulletproof under my attire
Could it be the forces of darkness
Against hood angels of good, that form street politics?
Makes a sweet honest kid, turn illegal for commerce (uhh)
To get his feet out of them Converse that's my word
I feel like a black republican, money keep comin' in
Can't turn my back on the hood, I got love for them (uhh, uhh)
Can't clean my act up for good, too much thug in him (nah)
Probably end up back in the hood, ah, "Fuck it then"
I feel like a black militant takin' over the government
Can't turn my back on the hood, too much love for them
Can't clean my act up for good, too much thug in him
Probably end up back in the hood, I'm like, "Fuck it then"16
Uhh, yo, you believe when they say we ain't shit, we can't grow?
All we are is dope dealers, and gangstas and hoes?
And you believe when they be telling you lie, all on the media?
They make the world look crazy to keep you inside?
Why you listen when the teachers at school
Know you a young single parent out struggling, they think you a fool
Give your kids bad grades and put 'em in dumber classes
Killing shorty future, I wonder how do we last it?
Underground in they casket, ancestors turning
I'm learning something every day, there is no Lazarus
Words like God is Greek or Latin
So if you study Egypt, you'll see the truth written by the masters
My niggas is chilling, getting high, relaxing
Envisioning, owning shit, yo it can happen
What do we own? Not enough land, not enough homes
Not enough banks, to give my brother a loan
What do we own? The skin on our backs, we rent and we ask
For reparations, then they hit us with tax
And insurance if we live to be old, what about now?
So stop being controlled, we black zombies
Walking talking dead, though we think we're living (black zombies)
We just copy-cat, following the system (black zombies)
Walking talking dead, though we think we're living (black zombies)
We just copy-cat, following the system (black zombies)
Ayo, we trapped in our own brain, fucked behind bars
We've already gone insane
We've already gave up, cut our own heads offs
Stab our own backs and dream too much
Without fulfilling reality; too greedy and
Can't have one or two chains, we need three of them
Can't have one or two guns without squeezing 'em
On our own people and, fuck black leaders
'Cause whites ain't got none leading them, the rhythm is cosmic
Nas is divinity, the deity's prophet
Let's all get down and get up
Victims walking 'round with Down's Syndrome, all stuck
Fainting, shouting, catching Holy Ghost in church
Scared to do it for ourselves 'less we see somebody doing it first
We begged, we prayed, petitioned and demonstrated
Just to make another generation - black zombies
Walking talking dead, though we think we're living (black zombies)
We just copy-cat, following the system (black zombies)
Walking talking dead, though we think we're living (black zombies)
We just copy-cat, following the system (black zombies)
You scared to be yourself, 'cause you in a trance
Feel free, hear the music and dance
If you cared what they think, why wear what they wear, just for you
Dumb niggas with long beards like they Arabs or Jews
Or from Israel, Bismillah al rahman al rahim
Islam's a beautiful thing
And Christian and Rastafari, helps us to bring
Peace against the darkness, which is ungodly
So what's the black man's true religion, who should we follow?
Use your own intuition, you are tomorrow
{*roaring*} .. That's the sound of the beast
I'm a Columbia record slave, so get paid
Control your own destiny, you are a genius
Don't let it happen to you like it did to me, I was a black zombie
Walking talking dead, though we think we're living (black zombies)
We just copy-cat, following the system (black zombies)
Walking talking dead, though we think we're living (black zombies)
We just copy-cat, following the system (black zombies)

View File

@@ -1,83 +0,0 @@
from lyricsgenius import Genius
import json
import re
import os
class GetLyrics():
def __init__(self):
self.GENIUS_TOKEN = "gMnJyj87FvjyP2W093rQ_mjo5ZwwLw1u2r0AmcVqYcJ8kkjjW6ZbObeGnS726SrH"
self.notes_re = re.compile('((?:\[[0-9a-zA-Z :()&+-.]+\])(?: \+ \([a-zA-Z -.]+)?(?:\\n)?)')
self.footer_re = re.compile('((?:EmbedShare)[ ]*(?:URLCopyEmbedCopy))')
self.multiline_re = re.compile(('(\\n){2,}'))
def get_songs(self, artists: dict = None):
session = Genius(self.GENIUS_TOKEN, retries=2, timeout=20, sleep_time=0.3)
if artists is not None:
# get songs
for artist in artists:
try:
songlist = session.search_artist(artist, max_songs=75, sort='title')
except Exception as e:
print(e)
continue
songlist.save_lyrics()
return songlist
else:
print("Please enter an artist")
def sanitize_lyrics(self, input):
sanitized_input = self.notes_re.sub('', input)
sanitized_input = self.footer_re.sub('', sanitized_input)
sanitized_input = sanitized_input.replace("[?]", '')
sanitized_input = self.multiline_re.sub('\n', sanitized_input)
return sanitized_input
def get_lyrics_from_json(self, json_file):
artist_dict = json.load(json_file)
ready_lyrics = []
print(artist_dict.keys())
for song in artist_dict['songs']:
sanitized_lyrics = self.sanitize_lyrics(song['lyrics'])
print(sanitized_lyrics)
ready_lyrics.append(sanitized_lyrics)
return ready_lyrics
def save_sanitized_lyrics(self):
sanitized_lyrics_list = []
for file in os.listdir("./"):
if file.endswith(".json"):
with open(file, 'r', encoding="utf-8") as read_file:
sanitized_lyrics_list.extend(self.get_lyrics_from_json(read_file))
print(sanitized_lyrics_list)
with open('./lyrics.txt', 'a+', encoding="utf-8") as lyrics_file:
for lyrics in sanitized_lyrics_list:
print(lyrics)
lyrics_file.write(f"{lyrics}\n")
def sanitize_file(self):
sanitized_lyrics = ''
with open('./lyrics.txt', 'r', encoding="utf-8") as lyrics_file:
file_read_lines = lyrics_file.read()
sanitized_lyrics = self.sanitize_lyrics(file_read_lines)
del lyrics_file
with open('./lyrics.txt', 'w', encoding="utf-8") as lyrics_file:
lyrics_file.write(sanitized_lyrics)
lyrics = GetLyrics()
#lyrics.get_songs([])
#"noel miller", "tiny meat gang", "young gravy", "parov stelar", "mac miller", "marilyn manson"
#"ozzy osborn", "eminem", "2 chainz", "elvis presley", "jim croce", "nwa", "nas", "biggie", "willie nelson", "snoop dog",
#"lil nas x", "lil wayne", "post malone", "michael jackson", "misterwives", "p!nk", "labrinth", "elton john",
#"the lovin spoonful", "blake shelton", "avenge sevenfold", "wiz khalifa", "we the kings", "leonard cohen",
#"john denver", "yes", "pink floyd", "paul simon", "u2"
#lyrics.save_sanitized_lyrics()
lyrics.sanitize_file()

View File

@@ -1,55 +0,0 @@
import os
import argparse
from textgenrnn import textgenrnn
class PhraseGenerator(textgenrnn):
def __init__(self, input_training_file_path='./lyrics.txt', input_epochs=1, input_temperature=.5,
input_model_file_path='./WillieBotModel_weights.hdf5', logging_level=str(2)):
# Set logging for Tensorflow
os.environ['TF_CPP_MIN_LOG_LEVEL'] = str(logging_level)
self.logging_level = logging_level
# Init vars
self.training_file_path = input_training_file_path
self.model_file_path = input_model_file_path
self.epochs = input_epochs
self.temperature = input_temperature
# Init Textgenrnn
super().__init__(weights_path=self.model_file_path, allow_growth=True, name='WillieBotModel')
def pg_train(self):
self.train_from_file(self.training_file_path, num_epochs=self.epochs,
verbose=0 if self.logging_level == '2' else 1, top_n=5, return_as_list=True)
def pg_generate(self):
generated_text = self.generate(1, temperature=self.temperature, return_as_list=True)
print(generated_text[0])
return str(generated_text[0])
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Description of your program')
parser.add_argument('-t', '--train', action='store_true', help='Train the model', required=False)
parser.add_argument('-g', '--generate', action='store_true', help='Generate text', required=False)
parser.add_argument('-e', '--epochs', action='store', type=int, help='Set amount of epochs (defaults to 5)',
required=False)
parser.add_argument('-p', '--temp', action='store', type=int,
help='Set temperature for generation (defaults to .5)', required=False)
parser.add_argument('-f', '--training_file', action='store', type=str,
help='Set the training file (defaults to \'./lyrics.txt\')', required=False)
args = vars(parser.parse_args())
print(args)
print('Starting')
pg = PhraseGenerator(input_epochs=args['epochs'] if args['epochs'] else 1,
input_training_file_path=args['training_file'] if args['training_file'] else './lyrics.txt',
input_temperature=args['temp'] if args['temp'] else .5,
logging_level=str(2) if args['generate'] else str(0))
if args['train']:
pg.pg_train()
if args['generate']:
pg.pg_generate()

View File

@@ -1,93 +0,0 @@
import logging
import shutil
import subprocess
import threading
import time
from BotResources import DEFAULT_RADIO_SETTINGS
class OP25Handler(threading.Thread):
def __init__(self):
super().__init__()
self.OP25Dir: str = "/home/pi/op25/op25/gr-op25_repeater/apps"
self.OP25EXE: str = shutil.which("/home/pi/op25/op25/gr-op25_repeater/apps/rx.py")
self.OP25Proc = None
self.Frequency = DEFAULT_RADIO_SETTINGS['freq']
self.HTTP_ENABLED = False
self.Start_OP25 = False
self.Stop_OP25 = False
self.Output_Device_Name = None
self.logger = logging.getLogger("Discord_Radio_Bot.OP25Handler")
def run(self) -> None:
while True:
if self.Start_OP25:
self.open_op25()
self.Start_OP25 = False
self.Stop_OP25 = False
while not self.Stop_OP25:
time.sleep(1)
self.close_op25()
time.sleep(.5)
def set_op25_parameters(self, _frequency: str = False, _http_enabled: bool = True, _start: bool = False,
_stop: bool = False, _output_device_name: str = None):
if _frequency:
self.Frequency = _frequency
if _start:
self.Start_OP25 = _start
if _stop:
self.Stop_OP25 = _stop
if _http_enabled:
self.HTTP_ENABLED = _http_enabled
if _output_device_name:
self.Output_Device_Name = _output_device_name
def open_op25(self):
if self.OP25Proc is not None:
self.close_op25()
p25_kwargs = [f"./rx.py", "--args", "rtl", "-N", "LNA:49", "-s", "200000", "-o", "25600", "-w", "-U", "-O",
f"{self.Output_Device_Name}", "-f", f"{self.Frequency}e6", "-X", "-2"]
self.logger.info(f"Starting OP25")
# Change the interpreter's working directory (idr why)
if self.HTTP_ENABLED:
p25_kwargs.extend(["-l", "http:0.0.0.0:8080"])
self.logger.debug(f"OP25 Keyword Args: {p25_kwargs}")
self.OP25Proc = subprocess.Popen(p25_kwargs, executable=self.OP25EXE, shell=False, cwd=self.OP25Dir,
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
def close_op25(self):
self.logger.info(f"Closing OP25")
try:
self.OP25Proc.kill()
seconds_waited = 0
while self.OP25Proc.poll() is None:
# Terminate the process every 5 seconds
if seconds_waited % 5 == 0:
self.logger.info("Terminating OP25")
self.OP25Proc.terminate()
time.sleep(1)
self.logger.debug(f"Waited {seconds_waited} seconds")
seconds_waited += 1
except Exception as e:
self.logger.error(e)

Binary file not shown.

Binary file not shown.

View File

@@ -1,4 +1,2 @@
discord==2.1.0
numpy==1.23.5
PyAudio==0.2.12
sounddevice==0.4.5
discord~=1.7.3
sounddevice~=0.4.3

57
sound.py Normal file
View File

@@ -0,0 +1,57 @@
import sounddevice as sd
from pprint import pformat
DEFAULT = 0
sd.default.channels = 2
sd.default.dtype = "int16"
sd.default.latency = "low"
sd.default.samplerate = 48000
class PCMStream:
def __init__(self):
self.stream = None
def read(self, num_bytes):
# frame is 4 bytes
frames = int(num_bytes / 4)
data = self.stream.read(frames)[0]
# convert to pcm format
return bytes(data)
def change_device(self, num):
if self.stream is not None:
self.stream.stop()
self.stream.close()
self.stream = sd.RawInputStream(device=num)
self.stream.start()
class DeviceNotFoundError(Exception):
def __init__(self):
self.devices = sd.query_devices()
self.host_apis = sd.query_hostapis()
super().__init__("No Devices Found")
def __str__(self):
return (
f"Devices \n"
f"{self.devices} \n "
f"Host APIs \n"
f"{pformat(self.host_apis)}"
)
def query_devices():
options = {
device.get("name"): index
for index, device in enumerate(sd.query_devices())
if (device.get("max_input_channels") > 0 and device.get("hostapi") == DEFAULT)
}
if not options:
raise DeviceNotFoundError()
return options

View File

View File

@@ -1,41 +0,0 @@
drb_defaults = """[General]
configversion=2
crashed=false
[audio]
gain=-20
udp_host=localhost
[dxcluster]
DXCAddress=localhost
DXCFilter=
DXCPort=7300
DXCSpotTimeout=10
DXCUsername=nocall
[fft]
averaging=85
fft_window=5
waterfall_span=1
[gui]
geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\x4t\0\0\0$\0\0\a\x7f\0\0\x3\x12\0\0\x4v\0\0\0\x42\0\0\a\x7f\0\0\x3\x12\0\0\0\0\0\0\0\0\a\x80\0\0\x4v\0\0\0\x42\0\0\a\x7f\0\0\x3\x12)
state=@ByteArray(\0\0\0\xff\0\0\0\0\xfd\0\0\0\x2\0\0\0\x1\0\0\x1G\0\0\x2t\xfc\x2\0\0\0\x2\xfc\0\0\0\x42\0\0\x1\x89\0\0\x1\x89\0\b\0!\xfa\0\0\0\x1\x2\0\0\0\x3\xfb\0\0\0\x18\0\x44\0o\0\x63\0k\0I\0n\0p\0u\0t\0\x43\0t\0l\x1\0\0\0\0\xff\xff\xff\xff\0\0\x1P\0\xff\xff\xff\xfb\0\0\0\x12\0\x44\0o\0\x63\0k\0R\0x\0O\0p\0t\x1\0\0\0\0\xff\xff\xff\xff\0\0\x1g\0\a\xff\xff\xfb\0\0\0\xe\0\x44\0o\0\x63\0k\0\x46\0\x66\0t\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\xc8\0\a\xff\xff\xfc\0\0\x1\xd1\0\0\0\xe5\0\0\0\xc3\0\xff\xff\xff\xfa\0\0\0\0\x2\0\0\0\x2\xfb\0\0\0\x12\0\x44\0o\0\x63\0k\0\x41\0u\0\x64\0i\0o\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\xc3\0\xff\xff\xff\xfb\0\0\0\xe\0\x44\0o\0\x63\0k\0R\0\x44\0S\0\0\0\0\0\xff\xff\xff\xff\0\0\0h\0\xff\xff\xff\0\0\0\x3\0\0\0\0\0\0\0\0\xfc\x1\0\0\0\x1\xfb\0\0\0\x1a\0\x44\0o\0\x63\0k\0\x42\0o\0o\0k\0m\0\x61\0r\0k\0s\0\0\0\0\0\xff\xff\xff\xff\0\0\x1\x42\0\xff\xff\xff\0\0\x1\xbd\0\0\x2t\0\0\0\x1\0\0\0\x2\0\0\0\b\0\0\0\x2\xfc\0\0\0\x1\0\0\0\x2\0\0\0\x1\0\0\0\x16\0m\0\x61\0i\0n\0T\0o\0o\0l\0\x42\0\x61\0r\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0)
[input]
dc_cancel=true
device="rtl=0"
frequency=154785000
gains=@Variant(\0\0\0\b\0\0\0\x1\0\0\0\x6\0L\0N\0\x41\0\0\0\x2\0\0\x1P)
sample_rate=1800000
[receiver]
demod=3
filter_high_cut=2500
filter_low_cut=-2500
offset=-590400
sql_level=-50
[remote_control]
allowed_hosts=localhost, 127.0.0.1, ::1
enabled=true"""