From 1997d5ca5d14842fd0f4ab97f57d7baa9d62f40f Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Thu, 17 Feb 2022 00:50:12 -0500 Subject: [PATCH 01/62] Remove Noise Gate from master (moved to its own branch) --- bot.py | 5 ++++- sound.py | 15 +-------------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/bot.py b/bot.py index bf1f148..e92f657 100644 --- a/bot.py +++ b/bot.py @@ -88,10 +88,13 @@ class Bot(commands.Bot): voice_connection = await channel.connect() # Create an audio stream from selected device - self.streamHandler = sound.PCMStream(voice_connection) + self.streamHandler = sound.PCMStream() # Ensure the selected device is available and start the audio stream self.streamHandler.change_device(self.DEVICE_ID) + # Play the stream + voice_connection.play(discord.PCMAudio(self.streamHandler)) + # Start the SDR and begin playing to the audio stream self.start_sdr() diff --git a/sound.py b/sound.py index bf0297f..c27ca36 100644 --- a/sound.py +++ b/sound.py @@ -1,6 +1,5 @@ import sounddevice as sd from pprint import pformat -from NoiseGate import NoiseGate DEFAULT = 0 sd.default.channels = 2 @@ -8,14 +7,11 @@ sd.default.dtype = "int16" sd.default.latency = "low" sd.default.samplerate = 48000 -noisegate_obj = NoiseGate() - class PCMStream: globals() - def __init__(self, voice_connection): + def __init__(self): self.stream = None - self.voice_connection = voice_connection def read(self, num_bytes): # frame is 4 bytes @@ -29,10 +25,8 @@ class PCMStream: self.clean_up() self.stream = sd.RawInputStream(device=device_ID) - noisegate_obj.init_stream(device_ID, self.voice_connection, self) self.stream.start() - noisegate_obj.start() def clean_up(self): global noisegate_obj @@ -40,13 +34,6 @@ class PCMStream: self.stream.stop() self.stream.close() - if noisegate_obj.NG_Started.is_set(): - print("Closing the noisegate") - noisegate_obj.stop_NG.set() - noisegate_obj.join() - noisegate_obj = NoiseGate() - print("Started the noisegate") - class DeviceNotFoundError(Exception): def __init__(self): From ae92057986dfa3e6111f3d95fbfbfb21cdb76e46 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Thu, 17 Feb 2022 00:50:39 -0500 Subject: [PATCH 02/62] Added third key for Brent --- main.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/main.py b/main.py index e1a67de..a49a723 100644 --- a/main.py +++ b/main.py @@ -10,6 +10,9 @@ from BotResources import check_if_config_exists, write_config_file, read_config_ # Greada #token = 'NzU2MzI3MjcxNTk3NDczODYz.X2QOqQ.LVLj2b-RXQzPmhNuBC1eGFMcYls' +# Brent +#token = OTQzNzQyMDQwMjU1MTE1MzA0.Yg3eRA.ZxEbRr55xahjfaUmPY8pmS-RHTY + #name = "VoiceMeeter Output" From 6fcf0dcc644258d9ba08e8cabe2bfe1190067b1f Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Thu, 17 Feb 2022 00:51:59 -0500 Subject: [PATCH 03/62] update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8684325..a9c4648 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /.idea/ /Releases/ +/Old/ /__pycache__/ */__pycache__/ /venv/ From 882fc5940071eba158b049d0353b153f20150822 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Thu, 17 Feb 2022 01:29:53 -0500 Subject: [PATCH 04/62] Added Support for OP25 --- bot.py | 73 +++++++++++++++++++++++++++++++++++--------------- op25Handler.py | 35 ++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 21 deletions(-) create mode 100644 op25Handler.py diff --git a/bot.py b/bot.py index e92f657..c71541b 100644 --- a/bot.py +++ b/bot.py @@ -5,6 +5,7 @@ import sound import configparser from discord.ext import commands from gqrxHandler import GQRXHandler +from op25Handler import OP25Handler # Init class for bot @@ -43,9 +44,8 @@ class Bot(commands.Bot): self.system_os_type = None self.sdr_started = False - if self.Handler == "gqrx": - print("Starting gqrx handler") - self.GQRXHandler = GQRXHandler() + # Check the handler being used + self.check_handler() # Set linux or windows self.check_os_type() @@ -56,6 +56,16 @@ class Bot(commands.Bot): # Check the ./modules folder for any modules (cog.py) self.check_for_modules() + # Check the handler being used during init + def check_handler(self): + if self.Handler == "gqrx": + print("Starting GQRX handler") + self.GQRXHandler = GQRXHandler() + + elif self.Handler == 'op25': + print("Starting OP25 handler") + self.OP25Handler = OP25Handler() + # Start the bot def start_bot(self): self.run(self.BOT_TOKEN) @@ -126,14 +136,22 @@ class Bot(commands.Bot): # 'Unlock' the bot self.Bot_Connected = False - if self.Handler == 'gqrx': + # Add commands for GQRX and OP25 + if self.Handler == 'gqrx' or self.Handler == 'op25': @self.command(name='chfreq', help="Use this command to change the frequency the bot is listening to. " "\nExample command: '@ chfreq wfm 104700000\n" - "Example command: '@ chfreq fm 154785000", + "Example command: '@ chfreq p25 154.785", brief="Changes radio frequency") async def chfreq(ctx, mode: str, freq: str, member: discord.Member = None): # Possible band-types that can be used - possible_modes = ['wfm', 'fm'] + possible_modes = [] + + if self.Handler == 'gqrx': + possible_modes = ['wfm', 'fm'] + + elif self.Handler == 'op25': + possible_modes = ['d', 'p25'] + member = member or ctx.author.display_name # Check to make sure the frequency input matches the syntax needed @@ -154,22 +172,26 @@ class Bot(commands.Bot): self.start_sdr() await self.set_activity() else: - await ctx.send(f"{str(member).capitalize()}, {mode} is not valid. You may only enter 'fm' or 'wbfm'") + await ctx.send(f"{str(member).capitalize()}, {mode} is not valid." + f" You may only enter {possible_modes}") else: - await ctx.send(f"{str(member).capitalize()}, {freq} is not valid. please refer to the help page '@ help chfreq'") + await ctx.send(f"{str(member).capitalize()}, {freq} is not valid. " + f"Please refer to the help page '@ help chfreq'") - @self.command(name='chsquelch', help="Use this command to change the squelch for the frequency" - "the bot is listening to", - brief="Changes radio squelch") - async def chsquelch(ctx, squelch: float, member: discord.Member = None): - member = member or ctx.author.display_name + # 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", + brief="Changes radio squelch") + async def chsquelch(ctx, squelch: float, member: discord.Member = None): + member = member or ctx.author.display_name - self.squelch = squelch - await ctx.send(f"Ok {str(member).capitalize()}, I'm changing the squelch to {self.squelch}") + self.squelch = squelch + await ctx.send(f"Ok {str(member).capitalize()}, I'm changing the squelch to {self.squelch}") - # If the SDR is started, restart it with the updates - if self.sdr_started: - self.start_sdr() + # If the SDR is started, restart it with the updates + if self.sdr_started: + self.start_sdr() # Hidden admin commands @self.command(name='saveprofile', hidden=True) @@ -205,6 +227,7 @@ class Bot(commands.Bot): async def _stopsdr(ctx, member: discord.Member = None): self.stop_sdr() + # Load the proper OPUS library for the device being used def load_opus(self): # Check the system type and load the correct library if self.system_os_type == 'Linux_32': @@ -263,7 +286,7 @@ class Bot(commands.Bot): # Check to see if there is only one frequency def start_sdr(self): - if self.Handler == 'gqrx': + if self.Handler in ['gqrx', 'op25']: if type(self.freq) == str: # Single freq sent # Stop the SDR if it is running @@ -272,8 +295,12 @@ class Bot(commands.Bot): # Start the radio print(f"Starting freq: {self.freq}") - # Set the settings in GQRX - self.GQRXHandler.set_all_settings(self.mode, self.squelch, self.freq) + if self.Handler == 'gqrx': + # Set the settings in GQRX + self.GQRXHandler.set_all_settings(self.mode, self.squelch, self.freq) + + elif self.Handler == 'op25': + self.OP25Handler.set_op25_parameters(self.freq) # Set the started variable for later checks self.sdr_started = True @@ -282,6 +309,8 @@ class Bot(commands.Bot): def stop_sdr(self): if self.sdr_started: # Wait for the running processes to close + if self.Handler == 'op25': + self.OP25Handler.close_op25() # Need a way to 'close' GQRX self.sdr_started = False @@ -348,3 +377,5 @@ class Bot(commands.Bot): return False else: return False + + diff --git a/op25Handler.py b/op25Handler.py new file mode 100644 index 0000000..f01aad7 --- /dev/null +++ b/op25Handler.py @@ -0,0 +1,35 @@ +import threading +import subprocess +import os + + +class OP25Handler(threading.Thread): + def __init__(self): + super().__init__() + self.OP25Dir: str = "/home/pi/op25/op25/gr-op25_repeater/apps" + self.OP25Proc = None + + self.Frequency = None + + def run(self) -> None: + self.open_op25() + + def set_op25_parameters(self, _frequency): + self.Frequency = _frequency + + def open_op25(self): + if self.OP25Proc is not None: + self.close_op25() + + print(f"Starting OBS") + os.chdir(self.OP25Dir) + + self.OP25Proc = subprocess.Popen([f"./rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", "200000", "-o", + "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2"]) + + def close_op25(self): + print(f"Closing OP25") + try: + self.OP25Proc.kill() + except Exception as e: + print(e) From cf756e32e68c7784a1006d1def0bcb941f79bf00 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Thu, 17 Feb 2022 01:37:07 -0500 Subject: [PATCH 05/62] BUGFIX op25Handler.py - Possible shell issue --- op25Handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op25Handler.py b/op25Handler.py index f01aad7..8e2eb7f 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -25,7 +25,7 @@ class OP25Handler(threading.Thread): os.chdir(self.OP25Dir) self.OP25Proc = subprocess.Popen([f"./rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", "200000", "-o", - "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2"]) + "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2"], shell=True) def close_op25(self): print(f"Closing OP25") From c4edfae8eefb7ba2b1bd5808b8304b04b1030855 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 00:42:43 -0500 Subject: [PATCH 06/62] BUGFIX op25Handler.py - Padded error and out pipes --- op25Handler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/op25Handler.py b/op25Handler.py index 8e2eb7f..312bbc9 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -25,7 +25,8 @@ class OP25Handler(threading.Thread): os.chdir(self.OP25Dir) self.OP25Proc = subprocess.Popen([f"./rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", "200000", "-o", - "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2"], shell=True) + "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2"], shell=True, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) def close_op25(self): print(f"Closing OP25") From 65363e8238ca39a762dca821ae7d9b81f4e78a88 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 00:46:44 -0500 Subject: [PATCH 07/62] Added debug http server to ensure the app is running --- op25Handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op25Handler.py b/op25Handler.py index 312bbc9..934ff47 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -25,7 +25,7 @@ class OP25Handler(threading.Thread): os.chdir(self.OP25Dir) self.OP25Proc = subprocess.Popen([f"./rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", "200000", "-o", - "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2"], shell=True, + "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2", "-l" "http:0.0.0.0:8080"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) def close_op25(self): From 4dd7539c5b5316accbb4cbef8639f15011cd98e1 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 00:50:21 -0500 Subject: [PATCH 08/62] BUGFIG never started OP25 --- bot.py | 1 + op25Handler.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/bot.py b/bot.py index c71541b..78261bb 100644 --- a/bot.py +++ b/bot.py @@ -301,6 +301,7 @@ class Bot(commands.Bot): elif self.Handler == 'op25': self.OP25Handler.set_op25_parameters(self.freq) + self.OP25Handler.start() # Set the started variable for later checks self.sdr_started = True diff --git a/op25Handler.py b/op25Handler.py index 934ff47..225cbaf 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -21,7 +21,7 @@ class OP25Handler(threading.Thread): if self.OP25Proc is not None: self.close_op25() - print(f"Starting OBS") + print(f"Starting OP25") os.chdir(self.OP25Dir) self.OP25Proc = subprocess.Popen([f"./rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", "200000", "-o", From 15ce6a7aeab29cfdfb4e53d82d08758b8e104fdf Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 00:58:59 -0500 Subject: [PATCH 09/62] BUGFIG op25 --- op25Handler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/op25Handler.py b/op25Handler.py index 225cbaf..c5f1b7f 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -28,6 +28,8 @@ class OP25Handler(threading.Thread): "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2", "-l" "http:0.0.0.0:8080"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + print(self.OP25Proc.poll()) + def close_op25(self): print(f"Closing OP25") try: From 8a765ad58e7045f6be91964b1906f0e829bdc8ce Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:00:33 -0500 Subject: [PATCH 10/62] BUGFIG op25 --- bot.py | 2 +- op25Handler.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bot.py b/bot.py index 78261bb..e7d082b 100644 --- a/bot.py +++ b/bot.py @@ -301,7 +301,7 @@ class Bot(commands.Bot): elif self.Handler == 'op25': self.OP25Handler.set_op25_parameters(self.freq) - self.OP25Handler.start() + self.OP25Handler.open_op25() # Set the started variable for later checks self.sdr_started = True diff --git a/op25Handler.py b/op25Handler.py index c5f1b7f..4af2da6 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -3,7 +3,7 @@ import subprocess import os -class OP25Handler(threading.Thread): +class OP25Handler: def __init__(self): super().__init__() self.OP25Dir: str = "/home/pi/op25/op25/gr-op25_repeater/apps" From 1e060bcdbacc29c87cc6b1a1b642048b859f8c0e Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:08:50 -0500 Subject: [PATCH 11/62] BUGFIG op25 --- op25Handler.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/op25Handler.py b/op25Handler.py index 4af2da6..d66bca3 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -3,7 +3,7 @@ import subprocess import os -class OP25Handler: +class OP25Handler(threading.Thread): def __init__(self): super().__init__() self.OP25Dir: str = "/home/pi/op25/op25/gr-op25_repeater/apps" @@ -24,12 +24,10 @@ class OP25Handler: print(f"Starting OP25") os.chdir(self.OP25Dir) - self.OP25Proc = subprocess.Popen([f"./rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", "200000", "-o", + self.OP25Proc = subprocess.call([f"./rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", "200000", "-o", "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2", "-l" "http:0.0.0.0:8080"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - print(self.OP25Proc.poll()) - def close_op25(self): print(f"Closing OP25") try: From 3c844e71fa1592ad847eddf85ac122f61189be6f Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:10:52 -0500 Subject: [PATCH 12/62] BUGFIG op25 --- bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot.py b/bot.py index e7d082b..78261bb 100644 --- a/bot.py +++ b/bot.py @@ -301,7 +301,7 @@ class Bot(commands.Bot): elif self.Handler == 'op25': self.OP25Handler.set_op25_parameters(self.freq) - self.OP25Handler.open_op25() + self.OP25Handler.start() # Set the started variable for later checks self.sdr_started = True From 71fd0133a15100e92310a58753653ca1bab8ef3b Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:14:03 -0500 Subject: [PATCH 13/62] BUGFIG op25 --- op25Handler.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/op25Handler.py b/op25Handler.py index d66bca3..aed587b 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -22,9 +22,10 @@ class OP25Handler(threading.Thread): self.close_op25() print(f"Starting OP25") - os.chdir(self.OP25Dir) - self.OP25Proc = subprocess.call([f"./rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", "200000", "-o", + print(os.getcwd()) + + self.OP25Proc = subprocess.call(["python3", f"{self.OP25Dir}/rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", "200000", "-o", "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2", "-l" "http:0.0.0.0:8080"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) From 300524d4af7c6db66ec76182158d01456313b902 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:15:30 -0500 Subject: [PATCH 14/62] BUGFIG op25 --- op25Handler.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/op25Handler.py b/op25Handler.py index aed587b..67cdd9b 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -25,9 +25,10 @@ class OP25Handler(threading.Thread): print(os.getcwd()) - self.OP25Proc = subprocess.call(["python3", f"{self.OP25Dir}/rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", "200000", "-o", - "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2", "-l" "http:0.0.0.0:8080"], shell=True, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + self.OP25Proc = subprocess.Popen(["python3", f"{self.OP25Dir}/rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", + "200000", "-o","25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2", + "-l" "http:0.0.0.0:8080"], shell=True, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) def close_op25(self): print(f"Closing OP25") From 364748c3d4696951718692de546e0f017714dcf0 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:16:44 -0500 Subject: [PATCH 15/62] BUGFIG op25 --- op25Handler.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/op25Handler.py b/op25Handler.py index 67cdd9b..ebd9a89 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -27,8 +27,7 @@ class OP25Handler(threading.Thread): self.OP25Proc = subprocess.Popen(["python3", f"{self.OP25Dir}/rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", "200000", "-o","25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2", - "-l" "http:0.0.0.0:8080"], shell=True, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + "-l" "http:0.0.0.0:8080"], shell=True) def close_op25(self): print(f"Closing OP25") From 4a996f468f53df4e47d58eae151358b36b6e9474 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:18:54 -0500 Subject: [PATCH 16/62] BUGFIG op25 --- op25Handler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/op25Handler.py b/op25Handler.py index ebd9a89..47f8ef8 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -26,8 +26,8 @@ class OP25Handler(threading.Thread): print(os.getcwd()) self.OP25Proc = subprocess.Popen(["python3", f"{self.OP25Dir}/rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", - "200000", "-o","25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2", - "-l" "http:0.0.0.0:8080"], shell=True) + "200000", "-o", "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2", + "-l" "http:0.0.0.0:8080"]) def close_op25(self): print(f"Closing OP25") From 0ca2552567b0bbe07dec22f99fe59a26b7eec2e6 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:20:18 -0500 Subject: [PATCH 17/62] BUGFIG op25 --- op25Handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op25Handler.py b/op25Handler.py index 47f8ef8..46c6a55 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -25,7 +25,7 @@ class OP25Handler(threading.Thread): print(os.getcwd()) - self.OP25Proc = subprocess.Popen(["python3", f"{self.OP25Dir}/rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", + self.OP25Proc = subprocess.Popen([f"python3 {self.OP25Dir}/rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", "200000", "-o", "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2", "-l" "http:0.0.0.0:8080"]) From ff048ac8f49b4a11f0fbb4856ecfbd4d908e444b Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:21:52 -0500 Subject: [PATCH 18/62] BUGFIG op25 --- op25Handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op25Handler.py b/op25Handler.py index 46c6a55..5d582de 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -25,7 +25,7 @@ class OP25Handler(threading.Thread): print(os.getcwd()) - self.OP25Proc = subprocess.Popen([f"python3 {self.OP25Dir}/rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", + self.OP25Proc = subprocess.Popen([f".{self.OP25Dir}/rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", "200000", "-o", "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2", "-l" "http:0.0.0.0:8080"]) From 00d8204fdd505a789066492240c4c0bb4a722e0f Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:23:05 -0500 Subject: [PATCH 19/62] BUGFIG op25 --- op25Handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op25Handler.py b/op25Handler.py index 5d582de..418096e 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -25,7 +25,7 @@ class OP25Handler(threading.Thread): print(os.getcwd()) - self.OP25Proc = subprocess.Popen([f".{self.OP25Dir}/rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", + self.OP25Proc = subprocess.Popen([f"{self.OP25Dir}/rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", "200000", "-o", "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2", "-l" "http:0.0.0.0:8080"]) From b8ad42f66ecc1ccbac12416c2b1245c23a0f6280 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:25:17 -0500 Subject: [PATCH 20/62] BUGFIG op25 --- op25Handler.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/op25Handler.py b/op25Handler.py index 418096e..b0884b4 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -25,7 +25,11 @@ class OP25Handler(threading.Thread): print(os.getcwd()) - self.OP25Proc = subprocess.Popen([f"{self.OP25Dir}/rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", + os.chdir(self.OP25Dir) + + print(os.getcwd()) + + self.OP25Proc = subprocess.Popen([f"./rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", "200000", "-o", "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2", "-l" "http:0.0.0.0:8080"]) From 9ebdb49d37b2211b5e3bf0e95e3e5118a988576e Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:26:17 -0500 Subject: [PATCH 21/62] BUGFIG op25 --- op25Handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op25Handler.py b/op25Handler.py index b0884b4..1444232 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -29,7 +29,7 @@ class OP25Handler(threading.Thread): print(os.getcwd()) - self.OP25Proc = subprocess.Popen([f"./rx.py", "--args", "'rtl'", "-N", "'LNA:49'", "-s", + self.OP25Proc = subprocess.Popen([f"./rx.py", "--args", "rtl", "-N", "LNA:49", "-s", "200000", "-o", "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2", "-l" "http:0.0.0.0:8080"]) From dcfa2301b8f43b5899b6f9f381479ad07545bfed Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:28:53 -0500 Subject: [PATCH 22/62] BUGFIG bot - Missing activity --- bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot.py b/bot.py index 78261bb..3b20dc3 100644 --- a/bot.py +++ b/bot.py @@ -318,7 +318,7 @@ class Bot(commands.Bot): # Set the activity of the bot async def set_activity(self, connected=True): if connected: - if self.Handler == 'gqrx': + if self.Handler == ['gqrx', 'op25']: if self.profile_name is None: await self.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name=f"{self.freq[:-1]}" From 15a592ad35e2c1ca7d60e68386e8f239c1e625fd Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:29:41 -0500 Subject: [PATCH 23/62] BUGFIG bot - Missing activity --- bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot.py b/bot.py index 3b20dc3..65325d8 100644 --- a/bot.py +++ b/bot.py @@ -318,7 +318,7 @@ class Bot(commands.Bot): # Set the activity of the bot async def set_activity(self, connected=True): if connected: - if self.Handler == ['gqrx', 'op25']: + if self.Handler in ['gqrx', 'op25']: if self.profile_name is None: await self.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name=f"{self.freq[:-1]}" From 570d09fe450df77d3d3dbe335f44fb0ce74da0f1 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:31:51 -0500 Subject: [PATCH 24/62] BUGFIG bot - Doesn't close properly --- bot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot.py b/bot.py index 65325d8..e1d686d 100644 --- a/bot.py +++ b/bot.py @@ -312,6 +312,7 @@ class Bot(commands.Bot): # Wait for the running processes to close if self.Handler == 'op25': self.OP25Handler.close_op25() + self.OP25Handler.join() # Need a way to 'close' GQRX self.sdr_started = False From d288d067fe22efdd9bc30e734a890b59c669c1ee Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:33:56 -0500 Subject: [PATCH 25/62] BUGFIG bot - Doesn't close properly --- op25Handler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/op25Handler.py b/op25Handler.py index 1444232..eff1cfb 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -37,5 +37,6 @@ class OP25Handler(threading.Thread): print(f"Closing OP25") try: self.OP25Proc.kill() + self.OP25Proc.terminate() except Exception as e: print(e) From 4a9cdefa4a3db26c784e9f84d59d01e0dc46b781 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:48:19 -0500 Subject: [PATCH 26/62] BUGFIG bot - Doesn't close properly --- op25Handler.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/op25Handler.py b/op25Handler.py index eff1cfb..29e0dc4 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -1,5 +1,6 @@ import threading import subprocess +import time import os @@ -37,6 +38,14 @@ class OP25Handler(threading.Thread): print(f"Closing OP25") try: self.OP25Proc.kill() - self.OP25Proc.terminate() + + seconds_waited = 0 + while self.OP25Proc.poll() is None: + # Terminate the process every 5 seconds + if seconds_waited % 5 == 0: + self.OP25Proc.terminate() + time.sleep(1) + seconds_waited += 1 + except Exception as e: print(e) From 2d6deeb2122ee5cc2da979f6ee1ec8213338c5a7 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:51:53 -0500 Subject: [PATCH 27/62] BUGFIG bot - Doesn't close properly --- bot.py | 2 +- op25Handler.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bot.py b/bot.py index e1d686d..8be2685 100644 --- a/bot.py +++ b/bot.py @@ -312,7 +312,7 @@ class Bot(commands.Bot): # Wait for the running processes to close if self.Handler == 'op25': self.OP25Handler.close_op25() - self.OP25Handler.join() + #self.OP25Handler.join() # Need a way to 'close' GQRX self.sdr_started = False diff --git a/op25Handler.py b/op25Handler.py index 29e0dc4..f88f6b9 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -4,7 +4,7 @@ import time import os -class OP25Handler(threading.Thread): +class OP25Handler: #(threading.Thread): def __init__(self): super().__init__() self.OP25Dir: str = "/home/pi/op25/op25/gr-op25_repeater/apps" @@ -12,7 +12,7 @@ class OP25Handler(threading.Thread): self.Frequency = None - def run(self) -> None: + def start(self) -> None: self.open_op25() def set_op25_parameters(self, _frequency): From 34b8fe2c0273750330285eb288ccda74f05de825 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:56:11 -0500 Subject: [PATCH 28/62] BUGFIG bot - Doesn't leave properly --- op25Handler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/op25Handler.py b/op25Handler.py index f88f6b9..19610cc 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -43,6 +43,7 @@ class OP25Handler: #(threading.Thread): while self.OP25Proc.poll() is None: # Terminate the process every 5 seconds if seconds_waited % 5 == 0: + print("Terminating OP25") self.OP25Proc.terminate() time.sleep(1) seconds_waited += 1 From 28149abaff1a98f2fb05c521c6f2ed25925163e9 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 01:59:47 -0500 Subject: [PATCH 29/62] Removed noisegate remnants --- sound.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sound.py b/sound.py index c27ca36..be7cf28 100644 --- a/sound.py +++ b/sound.py @@ -29,7 +29,6 @@ class PCMStream: self.stream.start() def clean_up(self): - global noisegate_obj if self.stream is not None: self.stream.stop() self.stream.close() From 44e6deef3de01b11284296cac26030a0ecc85b29 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 20:03:29 -0500 Subject: [PATCH 30/62] Removed noisegate remnants --- op25Handler.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/op25Handler.py b/op25Handler.py index 19610cc..f2a7c58 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -1,6 +1,6 @@ import threading import subprocess -import time +import asyncio import os @@ -45,7 +45,8 @@ class OP25Handler: #(threading.Thread): if seconds_waited % 5 == 0: print("Terminating OP25") self.OP25Proc.terminate() - time.sleep(1) + asyncio.sleep(5) + print(f"Waited {seconds_waited} seconds") seconds_waited += 1 except Exception as e: From 0ff02899974461cf685aa01703a817177a0b7fb4 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 20:07:03 -0500 Subject: [PATCH 31/62] BUGFIG bot - async sleep is in milliseconds --- op25Handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op25Handler.py b/op25Handler.py index f2a7c58..3926ec0 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -45,7 +45,7 @@ class OP25Handler: #(threading.Thread): if seconds_waited % 5 == 0: print("Terminating OP25") self.OP25Proc.terminate() - asyncio.sleep(5) + asyncio.sleep(1000) print(f"Waited {seconds_waited} seconds") seconds_waited += 1 From fc30c6a183e22c10a290dfc92e4b33e4c86696d4 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 20:32:57 -0500 Subject: [PATCH 32/62] Implemented a global variable for acceptable handlers --- BotResources.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/BotResources.py b/BotResources.py index 0d05b7e..4052be6 100644 --- a/BotResources.py +++ b/BotResources.py @@ -2,6 +2,8 @@ import sound import configparser from os.path import exists +PDB_ACCEPTABLE_HANDLERS = ['gqrx', 'op25'] + def check_if_config_exists(): if exists('./config.ini'): @@ -167,7 +169,7 @@ 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 == "gqrx": + if handler in PDB_ACCEPTABLE_HANDLERS: return handler elif handler == '': return handler From 0fa0018b6656c18c9ce9c958a565f9273ac27099 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 20:35:21 -0500 Subject: [PATCH 33/62] Improved the loading of handlers --- bot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot.py b/bot.py index 8be2685..efc1776 100644 --- a/bot.py +++ b/bot.py @@ -4,8 +4,6 @@ import discord import sound import configparser from discord.ext import commands -from gqrxHandler import GQRXHandler -from op25Handler import OP25Handler # Init class for bot @@ -60,10 +58,12 @@ class Bot(commands.Bot): def check_handler(self): if self.Handler == "gqrx": print("Starting GQRX handler") + from gqrxHandler import GQRXHandler self.GQRXHandler = GQRXHandler() elif self.Handler == 'op25': print("Starting OP25 handler") + from op25Handler import OP25Handler self.OP25Handler = OP25Handler() # Start the bot From eb6d09bed9c145a7d34bc3797d39d121c94e9542 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 20:44:36 -0500 Subject: [PATCH 34/62] Improved help messages --- bot.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/bot.py b/bot.py index efc1776..c20698a 100644 --- a/bot.py +++ b/bot.py @@ -138,9 +138,12 @@ class Bot(commands.Bot): # Add commands for GQRX and OP25 if self.Handler == 'gqrx' or self.Handler == 'op25': - @self.command(name='chfreq', help="Use this command to change the frequency the bot is listening to. " - "\nExample command: '@ chfreq wfm 104700000\n" - "Example command: '@ chfreq p25 154.785", + @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 @@ -161,8 +164,8 @@ class Bot(commands.Bot): if mode in possible_modes: self.mode = mode - await ctx.send(f"Ok {str(member).capitalize()}, I'm changing the mode to {str(self.mode).upper()} and frequency to" - f" {self.freq}") + 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 to the freq self.profile_name = None @@ -180,8 +183,11 @@ class Bot(commands.Bot): # 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", + @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 From 15cfa5bf9473413cb23325e2a65d5f0222a15875 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 20:54:17 -0500 Subject: [PATCH 35/62] Added check to see if the user sending ping is itself - This will be important to figure out who is a bot --- bot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot.py b/bot.py index c20698a..05add51 100644 --- a/bot.py +++ b/bot.py @@ -75,7 +75,8 @@ class Bot(commands.Bot): # Test command to see if the bot is on (help command can also be used) @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') + if ctx.author.id != self.user.id: + await ctx.send('pong') # 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", From 90ad6f89cd4d3cb971473175414dd00ebf64d963 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 22:18:13 -0500 Subject: [PATCH 36/62] Check the main variable for bot IDs --- modules/LinkCop/cog.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/LinkCop/cog.py b/modules/LinkCop/cog.py index b2ccdf9..4942376 100644 --- a/modules/LinkCop/cog.py +++ b/modules/LinkCop/cog.py @@ -2,6 +2,7 @@ import re import discord from discord.ext import commands import random +from BotResources import PDB_KNOWN_BOT_IDS regex_link = re.compile('(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)') @@ -40,11 +41,12 @@ class LinkCop(commands.Cog): 758792783020163084 # Bots ] - self.whitelisted_ID = [ - 756327271597473863, # Greada ID - 915064996994633729, # Jorn ID + # Bring in the known bot IDs from PDB bots + self.whitelisted_ID = PDB_KNOWN_BOT_IDS + + self.whitelisted_ID.append([ 235148962103951360 # Carl Bot - ] + ]) self.add_events() From 04f3d25fd93872f4891d95132a267bc8ff0f40a0 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 22:19:36 -0500 Subject: [PATCH 37/62] Add the initial revision of 'check for other bots' --- BotResources.py | 1 + bot.py | 74 +++++++++++++++++++++++++++++++++++++------------ 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/BotResources.py b/BotResources.py index 4052be6..b205b11 100644 --- a/BotResources.py +++ b/BotResources.py @@ -3,6 +3,7 @@ import configparser from os.path import exists PDB_ACCEPTABLE_HANDLERS = ['gqrx', 'op25'] +PDB_KNOWN_BOT_IDS = [756327271597473863, 915064996994633729, 943742040255115304] def check_if_config_exists(): diff --git a/bot.py b/bot.py index 05add51..80977cf 100644 --- a/bot.py +++ b/bot.py @@ -1,6 +1,9 @@ +import asyncio import os import platform import discord + +import BotResources import sound import configparser from discord.ext import commands @@ -22,6 +25,7 @@ class Bot(commands.Bot): self.Default_Channel_ID = kwargs['Channel_ID'] self.Default_Mention_Group = kwargs['Mention_Group'] self.Handler = kwargs['Handler'] + self.Command_Prefix = kwargs['command_prefix'] # Init Variable for sound self.streamHandler = None @@ -51,20 +55,8 @@ class Bot(commands.Bot): # Add discord commands to the bot self.add_commands() - # Check the ./modules folder for any modules (cog.py) - self.check_for_modules() - - # Check the handler being used during init - def check_handler(self): - if self.Handler == "gqrx": - print("Starting GQRX handler") - from gqrxHandler import GQRXHandler - self.GQRXHandler = GQRXHandler() - - elif self.Handler == 'op25': - print("Starting OP25 handler") - from op25Handler import OP25Handler - self.OP25Handler = OP25Handler() + # Add discord events to the bot + self.add_events() # Start the bot def start_bot(self): @@ -77,6 +69,7 @@ class Bot(commands.Bot): async def ping(ctx): if ctx.author.id != self.user.id: await ctx.send('pong') + await self.process_commands(ctx) # 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", @@ -234,6 +227,52 @@ class Bot(commands.Bot): async def _stopsdr(ctx, member: discord.Member = None): self.stop_sdr() + # Add discord events to the bot + def add_events(self): + @self.event + async def on_ready(): + # Check the ./modules folder for any modules (cog.py) + await self.check_for_modules() + + # Check to see if other bots are online + async def check_other_bots_online(self): + print('Checking if other bots are online') + channel = self.get_channel(self.Default_Channel_ID) + print(f"Channel to be tested in: {channel}") + + bots_online = [] + + def verify_bot_msg(msg): + print(f"Response ID: {msg.author.id}") + if msg.author.id in BotResources.PDB_KNOWN_BOT_IDS: + bots_online.append(msg.author.id) + + await self.wait_until_ready() + + await channel.send(f"{self.Command_Prefix}ping") + + seconds_waited = 0 + while seconds_waited < 5: + try: + await self.wait_for("message", check=verify_bot_msg, timeout=1) + except asyncio.exceptions.TimeoutError: + seconds_waited += 1 + print(seconds_waited) + + print(f"Bots Online: {bots_online}") + + # Check the handler being used during init + def check_handler(self): + if self.Handler == "gqrx": + print("Starting GQRX handler") + from gqrxHandler import GQRXHandler + self.GQRXHandler = GQRXHandler() + + elif self.Handler == 'op25': + print("Starting OP25 handler") + from op25Handler import OP25Handler + self.OP25Handler = OP25Handler() + # Load the proper OPUS library for the device being used def load_opus(self): # Check the system type and load the correct library @@ -259,7 +298,10 @@ class Bot(commands.Bot): return False # Search the ./modules folder for any modules to load - def check_for_modules(self): + async def check_for_modules(self): + print('Checking modules') + 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] == '.': @@ -386,5 +428,3 @@ class Bot(commands.Bot): return False else: return False - - From 54c2eeedbff4d04b8adbfefcc52b6190673c5af2 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 22:34:47 -0500 Subject: [PATCH 38/62] Trying an on_message function that gets disabled after the bot is fully loaded --- bot.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/bot.py b/bot.py index 80977cf..4259566 100644 --- a/bot.py +++ b/bot.py @@ -27,6 +27,9 @@ class Bot(commands.Bot): self.Handler = kwargs['Handler'] self.Command_Prefix = kwargs['command_prefix'] + # Init variable to check if startup has completed + self.Startup_Complete = False + # Init Variable for sound self.streamHandler = None @@ -69,7 +72,6 @@ class Bot(commands.Bot): async def ping(ctx): if ctx.author.id != self.user.id: await ctx.send('pong') - await self.process_commands(ctx) # 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", @@ -229,11 +231,20 @@ class Bot(commands.Bot): # 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() + # Set the startup completed variable as true + self.Startup_Complete = True + + @self.event + async def on_message(message): + if not self.Startup_Complete: + await self.process_commands(message) + # Check to see if other bots are online async def check_other_bots_online(self): print('Checking if other bots are online') From 57225413fc2fcb6d468bc6f381fe8345a84f73f1 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 22:43:15 -0500 Subject: [PATCH 39/62] Trying an on_message function that gets disabled after the bot is fully loaded --- bot.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/bot.py b/bot.py index 4259566..0821d5c 100644 --- a/bot.py +++ b/bot.py @@ -27,9 +27,6 @@ class Bot(commands.Bot): self.Handler = kwargs['Handler'] self.Command_Prefix = kwargs['command_prefix'] - # Init variable to check if startup has completed - self.Startup_Complete = False - # Init Variable for sound self.streamHandler = None @@ -220,7 +217,7 @@ class Bot(commands.Bot): await ctx.send(f"Ok {str(member).capitalize()}, I reloaded {module}") else: await ctx.send(f"{str(member).capitalize()}, something went wrong. Please check the console") - + @self.command(name='startsdr', hidden=True) async def _startsdr(ctx, member: discord.Member = None): self.start_sdr() @@ -237,12 +234,9 @@ class Bot(commands.Bot): # Check the ./modules folder for any modules (cog.py) await self.check_for_modules() - # Set the startup completed variable as true - self.Startup_Complete = True - @self.event async def on_message(message): - if not self.Startup_Complete: + if "ping" in message.content: await self.process_commands(message) # Check to see if other bots are online @@ -263,12 +257,11 @@ class Bot(commands.Bot): await channel.send(f"{self.Command_Prefix}ping") seconds_waited = 0 - while seconds_waited < 5: + while seconds_waited < 2: try: await self.wait_for("message", check=verify_bot_msg, timeout=1) except asyncio.exceptions.TimeoutError: seconds_waited += 1 - print(seconds_waited) print(f"Bots Online: {bots_online}") @@ -372,7 +365,7 @@ class Bot(commands.Bot): # Wait for the running processes to close if self.Handler == 'op25': self.OP25Handler.close_op25() - #self.OP25Handler.join() + # self.OP25Handler.join() # Need a way to 'close' GQRX self.sdr_started = False From eaaeee710946d9415b77b91604b77be7ed756a75 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 22:51:51 -0500 Subject: [PATCH 40/62] Trying an on_message function that gets disabled after the bot is fully loaded --- bot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot.py b/bot.py index 0821d5c..4d56726 100644 --- a/bot.py +++ b/bot.py @@ -236,6 +236,7 @@ class Bot(commands.Bot): @self.event async def on_message(message): + print(message.content) if "ping" in message.content: await self.process_commands(message) From e36c77a7a544a7d9d271fc511a0be39690da9f72 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 23:08:01 -0500 Subject: [PATCH 41/62] BUGFIX - Working on the startup check --- bot.py | 37 ++++++++++++++++++++++++------------- modules/LinkCop/cog.py | 3 ++- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/bot.py b/bot.py index 4d56726..850d6ae 100644 --- a/bot.py +++ b/bot.py @@ -236,9 +236,7 @@ class Bot(commands.Bot): @self.event async def on_message(message): - print(message.content) - if "ping" in message.content: - await self.process_commands(message) + await self.check_and_reply_to_ping(message) # Check to see if other bots are online async def check_other_bots_online(self): @@ -266,6 +264,11 @@ class Bot(commands.Bot): print(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": @@ -304,16 +307,15 @@ class Bot(commands.Bot): # Search the ./modules folder for any modules to load async def check_for_modules(self): - print('Checking modules') - 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")): - print(f"Loaded extension: {folder_name}") - self.load_extension(f"modules.{folder_name}.cog") + # 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")): + 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): @@ -433,3 +435,12 @@ class Bot(commands.Bot): return False else: return False + + # Check if message is a ping request and respond even if it is a bot + async def check_and_reply_to_ping(self, message): + if "ping" in message.content: + ctx = await self.get_context(message) + await self.invoke(ctx) + return True + else: + return False diff --git a/modules/LinkCop/cog.py b/modules/LinkCop/cog.py index 4942376..ab8fcbd 100644 --- a/modules/LinkCop/cog.py +++ b/modules/LinkCop/cog.py @@ -73,7 +73,8 @@ class LinkCop(commands.Cog): print(f"Link Cop Error: '{err}'") print(f"Bot or other non-user that has not been whitelisted sent a message") - await self.bot.process_commands(ctx) + if not await self.bot.check_and_reply_to_ping(ctx): + await self.bot.process_commands(ctx) async def send_message(self, message): send_channel = self.bot.get_channel(id=self.reply_channel_id) From 174d3bcb57199216d5b33b01d2fb995e3d417bd5 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 23:12:22 -0500 Subject: [PATCH 42/62] BUGFIX - Working on the startup check --- bot.py | 2 +- modules/LinkCop/cog.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/bot.py b/bot.py index 850d6ae..bd8e063 100644 --- a/bot.py +++ b/bot.py @@ -247,7 +247,6 @@ class Bot(commands.Bot): bots_online = [] def verify_bot_msg(msg): - print(f"Response ID: {msg.author.id}") if msg.author.id in BotResources.PDB_KNOWN_BOT_IDS: bots_online.append(msg.author.id) @@ -443,4 +442,5 @@ class Bot(commands.Bot): await self.invoke(ctx) return True else: + await self.process_commands(message) return False diff --git a/modules/LinkCop/cog.py b/modules/LinkCop/cog.py index ab8fcbd..9d70c13 100644 --- a/modules/LinkCop/cog.py +++ b/modules/LinkCop/cog.py @@ -73,8 +73,7 @@ class LinkCop(commands.Cog): print(f"Link Cop Error: '{err}'") print(f"Bot or other non-user that has not been whitelisted sent a message") - if not await self.bot.check_and_reply_to_ping(ctx): - await self.bot.process_commands(ctx) + await self.bot.check_and_reply_to_ping(ctx) async def send_message(self, message): send_channel = self.bot.get_channel(id=self.reply_channel_id) From d16ccc49fb267419487bbc37831a752f896df26e Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 23:24:33 -0500 Subject: [PATCH 43/62] Added a command to display the current settings --- bot.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/bot.py b/bot.py index bd8e063..487eaca 100644 --- a/bot.py +++ b/bot.py @@ -208,6 +208,12 @@ class Bot(commands.Bot): else: await ctx.send(f"{str(member).capitalize()}, there is no profile with the name '{profile_name}'") + @self.command(name='displayprofile', hidden=True) + async def _displayprofile(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}") + # Hidden admin commands @self.command(name='reload', hidden=True) async def _reload(ctx, module: str, member: discord.Member = None): @@ -435,6 +441,17 @@ class Bot(commands.Bot): else: return False + def display_current_radio_config(self): + message_body = "" + if self.profile_name: + message_body += f"Profile Name: {self.profile_name}\n" + message_body += f"Frequency: {self.freq}\n" \ + f"Mode: {self.mode}\n" + if self.squelch: + message_body += f"Squelch: {self.squelch}" + + return message_body + # Check if message is a ping request and respond even if it is a bot async def check_and_reply_to_ping(self, message): if "ping" in message.content: From a67b5f845ecc204d09ac8e939e313f52f501392e Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 23:34:51 -0500 Subject: [PATCH 44/62] BUGFIX OP25 - Handler would stay in the OP25 directory, causing profiles to get saved there --- op25Handler.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/op25Handler.py b/op25Handler.py index 3926ec0..b35f8dd 100644 --- a/op25Handler.py +++ b/op25Handler.py @@ -24,7 +24,8 @@ class OP25Handler: #(threading.Thread): print(f"Starting OP25") - print(os.getcwd()) + cwd = os.getcwd() + print(cwd) os.chdir(self.OP25Dir) @@ -34,6 +35,8 @@ class OP25Handler: #(threading.Thread): "200000", "-o", "25600", "-U", "-f", f"{self.Frequency}e6", "-X", "-2", "-l" "http:0.0.0.0:8080"]) + os.chdir(cwd) + def close_op25(self): print(f"Closing OP25") try: From f00afa307a3c7d519ef1f5197e2f8b6bb8f75587 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 23:36:41 -0500 Subject: [PATCH 45/62] Moved the 'Bot Started!' to where the bot is finished loading --- bot.py | 1 + main.py | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/bot.py b/bot.py index 487eaca..bf204bf 100644 --- a/bot.py +++ b/bot.py @@ -239,6 +239,7 @@ class Bot(commands.Bot): async def on_ready(): # Check the ./modules folder for any modules (cog.py) await self.check_for_modules() + print("Bot started!") @self.event async def on_message(message): diff --git a/main.py b/main.py index a49a723..76ea1e8 100644 --- a/main.py +++ b/main.py @@ -44,8 +44,6 @@ def main(): if not discord_bot_client.check_device(): raise BotDeviceNotFound(config['Device Name']) - print("Bot started!") - discord_bot_client.start_bot() From 4d742886b5f4313a9db773e8ab62d2815617cde9 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 23:45:03 -0500 Subject: [PATCH 46/62] Updated TODO --- TODO.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 69bda45..679fd44 100644 --- a/TODO.md +++ b/TODO.md @@ -3,7 +3,7 @@ - [ ] 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 -- [ ] Add more proper help text with real examples for discord commands +- [x] Add more proper help text with real examples for discord commands - [ ] Add command line args with argparse for main bot - [ ] Add method for user to change audio device without redoing entire config file - [ ] Clean up code From c68cdf9d7b335e3a391f732d07b55505ff47d194 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 18 Feb 2022 23:45:18 -0500 Subject: [PATCH 47/62] Started work on argparse --- main.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index 76ea1e8..3c322b2 100644 --- a/main.py +++ b/main.py @@ -26,14 +26,20 @@ class BotDeviceNotFound(Exception): #os.execv(__file__, sys.argv) -def main(): +def main(**passed_config): print('Checking config file...') if not check_if_config_exists(): print("No config file exists, please enter this information now") - write_config_file(init=True) + write_config_file(init=True, **passed_config) config = read_config_file() + # Overwrite config options if they were passed + for sub in config: + # checking if key present in other dictionary + if sub in passed_config: + config[sub] = passed_config[sub] + print('Starting Bot...') discord_bot_client = bot.Bot(Token=config['Bot Token'], Device_ID=config['Device ID'], Device_Name=config['Device Name'], From 62968654d2544d4e924991566e2e50e87184f92c Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Feb 2022 13:40:21 -0500 Subject: [PATCH 48/62] v2.0.1 - Args - Better configs - Other shit too, idr --- BotResources.py | 12 ++- bot.py | 123 ++++++++++++++---------- main.py | 73 ++++++++++++-- opus/{libopus.dll => libopus_amd64.dll} | Bin opus/{libopus.so => libopus_armv7l.so} | Bin 5 files changed, 146 insertions(+), 62 deletions(-) rename opus/{libopus.dll => libopus_amd64.dll} (100%) rename opus/{libopus.so => libopus_armv7l.so} (100%) diff --git a/BotResources.py b/BotResources.py index b205b11..b0d9ec6 100644 --- a/BotResources.py +++ b/BotResources.py @@ -2,8 +2,13 @@ import sound import configparser from os.path import exists -PDB_ACCEPTABLE_HANDLERS = ['gqrx', 'op25'] -PDB_KNOWN_BOT_IDS = [756327271597473863, 915064996994633729, 943742040255115304] +PDB_ACCEPTABLE_HANDLERS = {'gqrx': { + 'Modes': ['wfm', 'fm'] + }, + 'op25': { + 'Modes': ['d', 'p25'] + }} +PDB_KNOWN_BOT_IDS = {756327271597473863: "Greada", 915064996994633729: "Jorn", 943742040255115304: "Brent"} def check_if_config_exists(): @@ -172,7 +177,8 @@ def get_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 == '': + elif handler == ['', "none"]: + handler = "None" return handler diff --git a/bot.py b/bot.py index bf204bf..d08269a 100644 --- a/bot.py +++ b/bot.py @@ -116,7 +116,8 @@ class Bot(commands.Bot): @self.command(help="Use this command to have the bot leave your channel", brief="Leaves the current voice channel") - async def leave(ctx): + async def leave(ctx, member: discord.Member = None): + member = member or ctx.author.display_name if self.Bot_Connected: # Stop the sound handlers self.streamHandler.clean_up() @@ -128,9 +129,23 @@ class Bot(commands.Bot): self.stop_sdr() # 'Unlock' the bot self.Bot_Connected = False + await ctx.send(f"Goodbye {str(member).capitalize()}.") + else: + await ctx.send(f"{str(member).capitalize()}, I'm not in a channel") # Add commands for GQRX and OP25 - if self.Handler == 'gqrx' or self.Handler == 'op25': + if self.Handler in BotResources.PDB_ACCEPTABLE_HANDLERS.keys(): + # Command to display the current config + @self.command(name='displayprofile', help="Use this command to display the current configuration of the bot.\n" + "Example command:\n" + "\t@ displayprofile", + breif="Display current bot config") + async def _displayprofile(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}") + + # 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" @@ -140,21 +155,13 @@ class Bot(commands.Bot): brief="Changes radio frequency") async def chfreq(ctx, mode: str, freq: str, member: discord.Member = None): # Possible band-types that can be used - possible_modes = [] - - if self.Handler == 'gqrx': - possible_modes = ['wfm', 'fm'] - - elif self.Handler == 'op25': - possible_modes = ['d', 'p25'] - member = member or ctx.author.display_name # 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 possible_modes: + if mode in self.possible_modes: self.mode = mode await ctx.send(f"Ok {str(member).capitalize()}, I'm changing the mode to " @@ -169,7 +176,7 @@ class Bot(commands.Bot): await self.set_activity() else: await ctx.send(f"{str(member).capitalize()}, {mode} is not valid." - f" You may only enter {possible_modes}") + 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'") @@ -208,12 +215,6 @@ class Bot(commands.Bot): else: await ctx.send(f"{str(member).capitalize()}, there is no profile with the name '{profile_name}'") - @self.command(name='displayprofile', hidden=True) - async def _displayprofile(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}") - # Hidden admin commands @self.command(name='reload', hidden=True) async def _reload(ctx, module: str, member: discord.Member = None): @@ -249,20 +250,21 @@ class Bot(commands.Bot): async def check_other_bots_online(self): print('Checking if other bots are online') channel = self.get_channel(self.Default_Channel_ID) - print(f"Channel to be tested in: {channel}") + print(f"Testing in: {channel}") bots_online = [] def verify_bot_msg(msg): - if msg.author.id in BotResources.PDB_KNOWN_BOT_IDS: - bots_online.append(msg.author.id) + 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}ping") seconds_waited = 0 - while seconds_waited < 2: + while seconds_waited < 3: try: await self.wait_for("message", check=verify_bot_msg, timeout=1) except asyncio.exceptions.TimeoutError: @@ -281,35 +283,46 @@ class Bot(commands.Bot): print("Starting GQRX handler") from gqrxHandler import GQRXHandler self.GQRXHandler = GQRXHandler() + self.possible_modes = BotResources.PDB_ACCEPTABLE_HANDLERS['gqrx']['Modes'] elif self.Handler == 'op25': print("Starting OP25 handler") from op25Handler import OP25Handler self.OP25Handler = OP25Handler() + 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 - if self.system_os_type == 'Linux_32': - discord.opus.load_opus('./opus/libopus.so') - elif self.system_os_type == 'Linux_64': + + # Linux ARM AARCH64 running 32bit OS + if self.system_os_type == 'Linux_ARMv7l': + discord.opus.load_opus('./opus/libopus_armv7l.so') + # Linux ARM AARCH64 running 64bit OS + if self.system_os_type == 'Linux_AARCH64': discord.opus.load_opus('./opus/libopus_aarcch64.so') - elif self.system_os_type == 'Windows': - discord.opus.load_opus('./opus/libopus.dll') + # Windows 64bit + if self.system_os_type == 'Windows_x64': + discord.opus.load_opus('./opus/libopus_amd64.dll') - # Check to make sure the selected device is still available and has not changed it's index - 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 + # 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: + for device, index in self.Devices_List: + if int(index) == self.DEVICE_ID and str(device) == self.DEVICE_NAME: + return True - for device, index in self.Devices_List: - if str(device) == self.DEVICE_NAME: - self.DEVICE_ID = int(index) - return True + for device, index in self.Devices_List: + if str(device) == self.DEVICE_NAME: + self.DEVICE_ID = int(index) + return True + else: + return False else: - return False + # If an override has been passed just reply true + return True # Search the ./modules folder for any modules to load async def check_for_modules(self): @@ -337,18 +350,19 @@ class Bot(commands.Bot): # Check and store the OS type of the system for later use def check_os_type(self): + processor = platform.machine() if os.name == 'nt': - self.system_os_type = 'Windows' + if processor == "AMD64": + self.system_os_type = 'Windows_x64' else: - processor = platform.architecture()[0] - if processor == "64bit": - self.system_os_type = 'Linux_64' - elif processor == "32bit": - self.system_os_type = 'Linux_32' + if processor == "aarch64": + self.system_os_type = 'Linux_AARCH64' + elif processor == "armv7l": + self.system_os_type = 'Linux_ARMv7l' # Check to see if there is only one frequency def start_sdr(self): - if self.Handler in ['gqrx', 'op25']: + if self.Handler in BotResources.PDB_ACCEPTABLE_HANDLERS.keys(): if type(self.freq) == str: # Single freq sent # Stop the SDR if it is running @@ -372,6 +386,7 @@ class Bot(commands.Bot): def stop_sdr(self): if self.sdr_started: # Wait for the running processes to close + # Close the OP25 handler if self.Handler == 'op25': self.OP25Handler.close_op25() # self.OP25Handler.join() @@ -381,7 +396,7 @@ class Bot(commands.Bot): # Set the activity of the bot async def set_activity(self, connected=True): if connected: - if self.Handler in ['gqrx', 'op25']: + 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]}" @@ -399,12 +414,14 @@ class Bot(commands.Bot): 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): + async def save_radio_config(self, profile_name: str): 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)) @@ -426,11 +443,11 @@ class Bot(commands.Bot): config = configparser.ConfigParser() if os.path.exists('./profiles.ini'): config.read('./profiles.ini') - if config.has_section(str(profile_name)): - self.profile_name = profile_name - self.freq = config[str(profile_name)]['Frequency'] - self.mode = config[str(profile_name)]['Mode'] - self.squelch = float(config[str(profile_name)]['Squelch']) + 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']) if self.sdr_started: self.start_sdr() @@ -445,9 +462,9 @@ class Bot(commands.Bot): def display_current_radio_config(self): message_body = "" if self.profile_name: - message_body += f"Profile Name: {self.profile_name}\n" + message_body += f"Profile Name: {str(self.profile_name).upper()}\n" message_body += f"Frequency: {self.freq}\n" \ - f"Mode: {self.mode}\n" + f"Mode: {str(self.mode).upper()}\n" if self.squelch: message_body += f"Squelch: {self.squelch}" diff --git a/main.py b/main.py index 3c322b2..f401c0e 100644 --- a/main.py +++ b/main.py @@ -30,35 +30,96 @@ def main(**passed_config): print('Checking config file...') if not check_if_config_exists(): print("No config file exists, please enter this information now") - write_config_file(init=True, **passed_config) + write_config_file(init=True) config = read_config_file() # Overwrite config options if they were passed for sub in config: # checking if key present in other dictionary - if sub in passed_config: + if sub in passed_config and passed_config[sub]: config[sub] = passed_config[sub] print('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']) + 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']) print(f"Verifying audio device:\t{config['Device Name']}") - if not discord_bot_client.check_device(): + 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 + if __name__ == '__main__': + args = cmd_arguments() + + if not all(args.values()): + print("Passed arguments:") + for arg in args: + if args[arg]: + print(f"\t{arg}:\t{args[arg]}") + try: print('Starting...') while True: try: - main() + main(**args) except BotDeviceNotFound: print("Restarting...") time.sleep(2) diff --git a/opus/libopus.dll b/opus/libopus_amd64.dll similarity index 100% rename from opus/libopus.dll rename to opus/libopus_amd64.dll diff --git a/opus/libopus.so b/opus/libopus_armv7l.so similarity index 100% rename from opus/libopus.so rename to opus/libopus_armv7l.so From a5f0a4b19349bbfeeecec0a068aa25ad0b626844 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Feb 2022 16:30:38 -0500 Subject: [PATCH 49/62] BUGFIX appending to dict, fixed --- modules/LinkCop/cog.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/LinkCop/cog.py b/modules/LinkCop/cog.py index 9d70c13..7556d3c 100644 --- a/modules/LinkCop/cog.py +++ b/modules/LinkCop/cog.py @@ -44,9 +44,7 @@ class LinkCop(commands.Cog): # Bring in the known bot IDs from PDB bots self.whitelisted_ID = PDB_KNOWN_BOT_IDS - self.whitelisted_ID.append([ - 235148962103951360 # Carl Bot - ]) + self.whitelisted_ID['Carl Bot'] = 235148962103951360 self.add_events() From fdb9bb519cb77ea9cbdf489ec85a688b31be1504 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Feb 2022 17:09:06 -0500 Subject: [PATCH 50/62] Grammar fix Comma in `Ok *,*\n` --- bot.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bot.py b/bot.py index d08269a..2f2509e 100644 --- a/bot.py +++ b/bot.py @@ -136,14 +136,15 @@ class Bot(commands.Bot): # Add commands for GQRX and OP25 if self.Handler in BotResources.PDB_ACCEPTABLE_HANDLERS.keys(): # Command to display the current config - @self.command(name='displayprofile', help="Use this command to display the current configuration of the bot.\n" + @self.command(name='displayprofile', + help="Use this command to display the current configuration of the bot.\n" "Example command:\n" "\t@ displayprofile", breif="Display current bot config") async def _displayprofile(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}") + await ctx.send(f"Ok {str(member).capitalize()},\n{message}") # 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" @@ -226,11 +227,11 @@ class Bot(commands.Bot): await ctx.send(f"{str(member).capitalize()}, something went wrong. Please check the console") @self.command(name='startsdr', hidden=True) - async def _startsdr(ctx, member: discord.Member = None): + async def _startsdr(*args): self.start_sdr() @self.command(name='stopsdr', hidden=True) - async def _stopsdr(ctx, member: discord.Member = None): + async def _stopsdr(*args): self.stop_sdr() # Add discord events to the bot From 59a3ca22ea02abab6b41ffe5d6d0e406fc77f88e Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Feb 2022 17:11:59 -0500 Subject: [PATCH 51/62] Debug output for leaving --- bot.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bot.py b/bot.py index 2f2509e..9cd68d1 100644 --- a/bot.py +++ b/bot.py @@ -119,16 +119,22 @@ class Bot(commands.Bot): async def leave(ctx, member: discord.Member = None): member = member or ctx.author.display_name if self.Bot_Connected: + print("Cleaning up") # Stop the sound handlers self.streamHandler.clean_up() + print("Disconnecting") # Disconnect the client from the voice channel await ctx.voice_client.disconnect() + print("Changing presence") # Change the presence to away and '@ me' await self.set_activity(False) # Stop the SDR so it can cool off + print("Stopping SDR") self.stop_sdr() + print("Unlocking the bot") # 'Unlock' the bot self.Bot_Connected = False + print("Sending Goodbye") await ctx.send(f"Goodbye {str(member).capitalize()}.") else: await ctx.send(f"{str(member).capitalize()}, I'm not in a channel") From 002288d632813d567db8d3a26dd76d991687cb9c Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Feb 2022 17:14:31 -0500 Subject: [PATCH 52/62] Error catching for cleaning up --- sound.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sound.py b/sound.py index be7cf28..e0f0eb1 100644 --- a/sound.py +++ b/sound.py @@ -30,8 +30,15 @@ class PCMStream: def clean_up(self): if self.stream is not None: - self.stream.stop() - self.stream.close() + try: + self.stream.stop() + except Exception as e: + print(f"Error on Stop: '{e}'") + + try: + self.stream.close() + except Exception as e: + print(f"Error on Close: '{e}'") class DeviceNotFoundError(Exception): From 137d0ae097ec617ba361d9498a8b1f6498964bfc Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Feb 2022 17:17:23 -0500 Subject: [PATCH 53/62] Error catching for cleaning up --- sound.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/sound.py b/sound.py index e0f0eb1..0b38cfc 100644 --- a/sound.py +++ b/sound.py @@ -29,16 +29,15 @@ class PCMStream: self.stream.start() def clean_up(self): - if self.stream is not None: - try: + try: + if self.stream is not None: self.stream.stop() - except Exception as e: - print(f"Error on Stop: '{e}'") - try: self.stream.close() - except Exception as e: - print(f"Error on Close: '{e}'") + + except Exception as e: + print(f"Error in clean_up: '{e}'") + class DeviceNotFoundError(Exception): From 167c06f33101216c24b55de67df7d5a020cb1e94 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Feb 2022 17:21:53 -0500 Subject: [PATCH 54/62] Error catching - PCMStream.clean_up - PCMStream.read --- sound.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/sound.py b/sound.py index 0b38cfc..5d428a5 100644 --- a/sound.py +++ b/sound.py @@ -1,3 +1,4 @@ +import sounddevice import sounddevice as sd from pprint import pformat @@ -14,12 +15,15 @@ class PCMStream: self.stream = None def read(self, num_bytes): - # frame is 4 bytes - frames = int(num_bytes / 4) - data = self.stream.read(frames)[0] + try: + # frame is 4 bytes + frames = int(num_bytes / 4) + data = self.stream.read(frames)[0] - # convert to pcm format - return bytes(data) + # convert to pcm format + return bytes(data) + except sounddevice.PortAudioError as err: + print(f"Error in PCMStream.read: '{err}'") def change_device(self, device_ID): self.clean_up() @@ -35,8 +39,8 @@ class PCMStream: self.stream.close() - except Exception as e: - print(f"Error in clean_up: '{e}'") + except sounddevice.PortAudioError as err: + print(f"Error in PCMStream.clean_up: '{err}'") From f9594e2afe21b8cde6e7053244bcd1aa6c760fc4 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Feb 2022 17:25:10 -0500 Subject: [PATCH 55/62] Error catching - PCMStream.clean_up - PCMStream.read --- sound.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound.py b/sound.py index 5d428a5..21edbbb 100644 --- a/sound.py +++ b/sound.py @@ -22,7 +22,7 @@ class PCMStream: # convert to pcm format return bytes(data) - except sounddevice.PortAudioError as err: + except Exception as err: print(f"Error in PCMStream.read: '{err}'") def change_device(self, device_ID): @@ -39,7 +39,7 @@ class PCMStream: self.stream.close() - except sounddevice.PortAudioError as err: + except Exception as err: print(f"Error in PCMStream.clean_up: '{err}'") From 17c528c153edd5580ee07d3123dce6b2fe7c9640 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Feb 2022 17:29:08 -0500 Subject: [PATCH 56/62] Working on bug in leaving --- sound.py | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/sound.py b/sound.py index 21edbbb..1060dd9 100644 --- a/sound.py +++ b/sound.py @@ -1,3 +1,5 @@ +import time + import sounddevice import sounddevice as sd from pprint import pformat @@ -15,15 +17,12 @@ class PCMStream: self.stream = None def read(self, num_bytes): - try: - # frame is 4 bytes - frames = int(num_bytes / 4) - data = self.stream.read(frames)[0] + # frame is 4 bytes + frames = int(num_bytes / 4) + data = self.stream.read(frames)[0] - # convert to pcm format - return bytes(data) - except Exception as err: - print(f"Error in PCMStream.read: '{err}'") + # convert to pcm format + return bytes(data) def change_device(self, device_ID): self.clean_up() @@ -33,14 +32,11 @@ class PCMStream: self.stream.start() def clean_up(self): - try: - if self.stream is not None: - self.stream.stop() + if self.stream is not None: + self.stream.stop() + time.sleep(.1) + self.stream.close() - self.stream.close() - - except Exception as err: - print(f"Error in PCMStream.clean_up: '{err}'") From 7fa11a4abe1e1dc3fe0f1c6e14a9a1c5f09afdab Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Feb 2022 18:36:18 -0500 Subject: [PATCH 57/62] Working on bug in leaving --- bot.py | 6 +++--- sound.py | 32 ++++++++++++++------------------ 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/bot.py b/bot.py index 9cd68d1..0b0f114 100644 --- a/bot.py +++ b/bot.py @@ -91,9 +91,9 @@ class Bot(commands.Bot): voice_connection = await channel.connect() # Create an audio stream from selected device - self.streamHandler = sound.PCMStream() - # Ensure the selected device is available and start the audio stream - self.streamHandler.change_device(self.DEVICE_ID) + self.streamHandler = sound.PCMStream(self.DEVICE_ID) + # Start the audio stream + self.streamHandler.stream.start() # Play the stream voice_connection.play(discord.PCMAudio(self.streamHandler)) diff --git a/sound.py b/sound.py index 1060dd9..2d02fc4 100644 --- a/sound.py +++ b/sound.py @@ -13,31 +13,27 @@ sd.default.samplerate = 48000 class PCMStream: globals() - def __init__(self): - self.stream = None + def __init__(self, _device_id): + self.stream = sd.RawInputStream(device=_device_id) - 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, device_ID): + def change_device(self, _device_id): self.clean_up() - self.stream = sd.RawInputStream(device=device_ID) - - self.stream.start() + self.stream = sd.RawInputStream(device=_device_id) def clean_up(self): - if self.stream is not None: - self.stream.stop() - time.sleep(.1) - self.stream.close() + if not self.stream.closed: + self.stream.stop(ignore_errors=True) + self.stream.close(ignore_errors=True) + def read(self, num_bytes): + if self.stream.active: + # frame is 4 bytes + frames = int(num_bytes / 4) + data = self.stream.read(frames)[0] + # convert to pcm format + return bytes(data) class DeviceNotFoundError(Exception): From 706f4a31dff0eeab67220aaa3719773a96b54342 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Feb 2022 18:40:36 -0500 Subject: [PATCH 58/62] Working on bug in leaving --- bot.py | 8 ++++---- sound.py | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/bot.py b/bot.py index 0b0f114..136b02e 100644 --- a/bot.py +++ b/bot.py @@ -93,7 +93,7 @@ class Bot(commands.Bot): # Create an audio stream from selected device self.streamHandler = sound.PCMStream(self.DEVICE_ID) # Start the audio stream - self.streamHandler.stream.start() + self.streamHandler.play() # Play the stream voice_connection.play(discord.PCMAudio(self.streamHandler)) @@ -119,12 +119,12 @@ class Bot(commands.Bot): async def leave(ctx, member: discord.Member = None): member = member or ctx.author.display_name if self.Bot_Connected: - print("Cleaning up") - # Stop the sound handlers - self.streamHandler.clean_up() print("Disconnecting") # Disconnect the client from the voice channel await ctx.voice_client.disconnect() + print("Cleaning up") + # Stop the sound handlers + self.streamHandler.pause() print("Changing presence") # Change the presence to away and '@ me' await self.set_activity(False) diff --git a/sound.py b/sound.py index 2d02fc4..8d48f0b 100644 --- a/sound.py +++ b/sound.py @@ -26,6 +26,12 @@ class PCMStream: self.stream.stop(ignore_errors=True) self.stream.close(ignore_errors=True) + def pause(self): + self.stream.stop(ignore_errors=True) + + def play(self): + self.stream.start() + def read(self, num_bytes): if self.stream.active: # frame is 4 bytes From 7682d8c72c93f80e5f64ed0339d25f2b67c88975 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Feb 2022 18:44:02 -0500 Subject: [PATCH 59/62] Working on bug in leaving --- bot.py | 6 +++--- sound.py | 10 ++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/bot.py b/bot.py index 136b02e..728e099 100644 --- a/bot.py +++ b/bot.py @@ -119,12 +119,12 @@ class Bot(commands.Bot): async def leave(ctx, member: discord.Member = None): member = member or ctx.author.display_name if self.Bot_Connected: - print("Disconnecting") - # Disconnect the client from the voice channel - await ctx.voice_client.disconnect() print("Cleaning up") # Stop the sound handlers self.streamHandler.pause() + print("Disconnecting") + # Disconnect the client from the voice channel + await ctx.voice_client.disconnect() print("Changing presence") # Change the presence to away and '@ me' await self.set_activity(False) diff --git a/sound.py b/sound.py index 8d48f0b..8be70b2 100644 --- a/sound.py +++ b/sound.py @@ -21,17 +21,23 @@ class PCMStream: self.stream = sd.RawInputStream(device=_device_id) + # Stops and destroys the current stream def clean_up(self): if not self.stream.closed: self.stream.stop(ignore_errors=True) self.stream.close(ignore_errors=True) + # Stops the current running stream but does not destroy it def pause(self): - self.stream.stop(ignore_errors=True) + if self.stream.active: + self.stream.stop(ignore_errors=True) + # Plays the stream def play(self): - self.stream.start() + if not self.stream.active: + self.stream.start() + # call back read function for the stream def read(self, num_bytes): if self.stream.active: # frame is 4 bytes From b721fa3df459ac2d6c9730a2b3fbf7da2592fa2a Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Feb 2022 19:00:55 -0500 Subject: [PATCH 60/62] Working on bug in leaving --- bot.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bot.py b/bot.py index 728e099..61b81d4 100644 --- a/bot.py +++ b/bot.py @@ -88,7 +88,7 @@ class Bot(commands.Bot): await ctx.send(f"Ok {str(member).capitalize()}, I'm joining {channel}") # Join the voice channel with the audio stream - voice_connection = await channel.connect() + self.voice_connection = await channel.connect() # Create an audio stream from selected device self.streamHandler = sound.PCMStream(self.DEVICE_ID) @@ -96,7 +96,7 @@ class Bot(commands.Bot): self.streamHandler.play() # Play the stream - voice_connection.play(discord.PCMAudio(self.streamHandler)) + self.voice_connection.play(discord.PCMAudio(self.streamHandler)) # Start the SDR and begin playing to the audio stream self.start_sdr() @@ -124,7 +124,8 @@ class Bot(commands.Bot): self.streamHandler.pause() print("Disconnecting") # Disconnect the client from the voice channel - await ctx.voice_client.disconnect() + #await ctx.voice_client.disconnect() + self.voice_connection.disconnect() print("Changing presence") # Change the presence to away and '@ me' await self.set_activity(False) From 6ad489183f7e6589888f6594d82285792eba0f3b Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Feb 2022 19:01:52 -0500 Subject: [PATCH 61/62] Working on bug in leaving --- bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot.py b/bot.py index 61b81d4..57903f2 100644 --- a/bot.py +++ b/bot.py @@ -125,7 +125,7 @@ class Bot(commands.Bot): print("Disconnecting") # Disconnect the client from the voice channel #await ctx.voice_client.disconnect() - self.voice_connection.disconnect() + await self.voice_connection.disconnect() print("Changing presence") # Change the presence to away and '@ me' await self.set_activity(False) From ba237d2d53c30aff5d276c4ace9fff2702cb5b91 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Feb 2022 19:09:48 -0500 Subject: [PATCH 62/62] Working on bug in leaving --- bot.py | 16 ++++++++++------ sound.py | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/bot.py b/bot.py index 57903f2..24e9e25 100644 --- a/bot.py +++ b/bot.py @@ -88,15 +88,15 @@ class Bot(commands.Bot): await ctx.send(f"Ok {str(member).capitalize()}, I'm joining {channel}") # Join the voice channel with the audio stream - self.voice_connection = await channel.connect() + voice_connection = await channel.connect() # Create an audio stream from selected device self.streamHandler = sound.PCMStream(self.DEVICE_ID) # Start the audio stream - self.streamHandler.play() + await self.streamHandler.play() # Play the stream - self.voice_connection.play(discord.PCMAudio(self.streamHandler)) + voice_connection.play(discord.PCMAudio(self.streamHandler)) # Start the SDR and begin playing to the audio stream self.start_sdr() @@ -121,20 +121,24 @@ class Bot(commands.Bot): if self.Bot_Connected: print("Cleaning up") # Stop the sound handlers - self.streamHandler.pause() + await self.streamHandler.pause() + print("Disconnecting") # Disconnect the client from the voice channel - #await ctx.voice_client.disconnect() - await self.voice_connection.disconnect() + await ctx.voice_client.disconnect() + print("Changing presence") # Change the presence to away and '@ me' await self.set_activity(False) + # Stop the SDR so it can cool off print("Stopping SDR") self.stop_sdr() + print("Unlocking the bot") # 'Unlock' the bot self.Bot_Connected = False + print("Sending Goodbye") await ctx.send(f"Goodbye {str(member).capitalize()}.") else: diff --git a/sound.py b/sound.py index 8be70b2..c33d97e 100644 --- a/sound.py +++ b/sound.py @@ -28,12 +28,12 @@ class PCMStream: self.stream.close(ignore_errors=True) # Stops the current running stream but does not destroy it - def pause(self): + async def pause(self): if self.stream.active: self.stream.stop(ignore_errors=True) # Plays the stream - def play(self): + async def play(self): if not self.stream.active: self.stream.start()