156 Commits
GUI ... V1

Author SHA1 Message Date
Logan Cusano
c68cdf9d7b Started work on argparse 2022-02-18 23:45:18 -05:00
Logan Cusano
4d742886b5 Updated TODO 2022-02-18 23:45:03 -05:00
Logan Cusano
f00afa307a Moved the 'Bot Started!' to where the bot is finished loading 2022-02-18 23:36:41 -05:00
Logan Cusano
a67b5f845e BUGFIX OP25
- Handler would stay in the OP25 directory, causing profiles to get saved there
2022-02-18 23:34:51 -05:00
Logan Cusano
d16ccc49fb Added a command to display the current settings 2022-02-18 23:24:33 -05:00
e11a36270a Merge pull request 'module_check_at_startup' (#4) from module_check_at_startup into master
Reviewed-on: http://git.vpn.cusano.net/Discord_Bot_Gang/Discord-Radio-Bot/pulls/4
2022-02-18 23:14:54 -05:00
Logan Cusano
174d3bcb57 BUGFIX - Working on the startup check 2022-02-18 23:12:22 -05:00
Logan Cusano
e36c77a7a5 BUGFIX - Working on the startup check 2022-02-18 23:08:01 -05:00
Logan Cusano
eaaeee7109 Trying an on_message function that gets disabled after the bot is fully loaded 2022-02-18 22:51:51 -05:00
Logan Cusano
57225413fc Trying an on_message function that gets disabled after the bot is fully loaded 2022-02-18 22:43:15 -05:00
Logan Cusano
54c2eeedbf Trying an on_message function that gets disabled after the bot is fully loaded 2022-02-18 22:34:47 -05:00
Logan Cusano
04f3d25fd9 Add the initial revision of 'check for other bots' 2022-02-18 22:19:36 -05:00
Logan Cusano
90ad6f89cd Check the main variable for bot IDs 2022-02-18 22:18:13 -05:00
Logan Cusano
15cfa5bf94 Added check to see if the user sending ping is itself
- This will be important to figure out who is a bot
2022-02-18 20:54:17 -05:00
01bf1d8a91 Merge pull request 'Implement OP25 Handler' (#3) from OP25_Handler into master
Reviewed-on: http://git.vpn.cusano.net/Discord_Bot_Gang/Discord-Radio-Bot/pulls/3
2022-02-18 20:47:08 -05:00
Logan Cusano
eb6d09bed9 Improved help messages 2022-02-18 20:44:36 -05:00
Logan Cusano
0fa0018b66 Improved the loading of handlers 2022-02-18 20:35:21 -05:00
Logan Cusano
fc30c6a183 Implemented a global variable for acceptable handlers 2022-02-18 20:32:57 -05:00
Logan Cusano
0ff0289997 BUGFIG bot
- async sleep is in milliseconds
2022-02-18 20:07:03 -05:00
Logan Cusano
44e6deef3d Removed noisegate remnants 2022-02-18 20:03:29 -05:00
Logan Cusano
28149abaff Removed noisegate remnants 2022-02-18 01:59:47 -05:00
Logan Cusano
34b8fe2c02 BUGFIG bot
- Doesn't leave properly
2022-02-18 01:56:11 -05:00
Logan Cusano
2d6deeb212 BUGFIG bot
- Doesn't close properly
2022-02-18 01:51:53 -05:00
Logan Cusano
4a9cdefa4a BUGFIG bot
- Doesn't close properly
2022-02-18 01:48:19 -05:00
Logan Cusano
d288d067fe BUGFIG bot
- Doesn't close properly
2022-02-18 01:33:56 -05:00
Logan Cusano
570d09fe45 BUGFIG bot
- Doesn't close properly
2022-02-18 01:31:51 -05:00
Logan Cusano
15a592ad35 BUGFIG bot
- Missing activity
2022-02-18 01:29:41 -05:00
Logan Cusano
dcfa2301b8 BUGFIG bot
- Missing activity
2022-02-18 01:28:53 -05:00
Logan Cusano
9ebdb49d37 BUGFIG op25 2022-02-18 01:26:17 -05:00
Logan Cusano
b8ad42f66e BUGFIG op25 2022-02-18 01:25:17 -05:00
Logan Cusano
00d8204fdd BUGFIG op25 2022-02-18 01:23:05 -05:00
Logan Cusano
ff048ac8f4 BUGFIG op25 2022-02-18 01:21:52 -05:00
Logan Cusano
0ca2552567 BUGFIG op25 2022-02-18 01:20:18 -05:00
Logan Cusano
4a996f468f BUGFIG op25 2022-02-18 01:18:54 -05:00
Logan Cusano
364748c3d4 BUGFIG op25 2022-02-18 01:16:44 -05:00
Logan Cusano
300524d4af BUGFIG op25 2022-02-18 01:15:30 -05:00
Logan Cusano
71fd0133a1 BUGFIG op25 2022-02-18 01:14:03 -05:00
Logan Cusano
3c844e71fa BUGFIG op25 2022-02-18 01:10:52 -05:00
Logan Cusano
1e060bcdba BUGFIG op25 2022-02-18 01:08:50 -05:00
Logan Cusano
8a765ad58e BUGFIG op25 2022-02-18 01:00:33 -05:00
Logan Cusano
15ce6a7aea BUGFIG op25 2022-02-18 00:58:59 -05:00
Logan Cusano
4dd7539c5b BUGFIG never started OP25 2022-02-18 00:50:21 -05:00
Logan Cusano
65363e8238 Added debug http server to ensure the app is running 2022-02-18 00:46:44 -05:00
Logan Cusano
c4edfae8ee BUGFIX op25Handler.py
- Padded error and out pipes
2022-02-18 00:42:43 -05:00
Logan Cusano
cf756e32e6 BUGFIX op25Handler.py
- Possible shell issue
2022-02-17 01:37:07 -05:00
Logan Cusano
882fc59400 Added Support for OP25 2022-02-17 01:29:53 -05:00
Logan Cusano
6fcf0dcc64 update .gitignore 2022-02-17 00:51:59 -05:00
Logan Cusano
ae92057986 Added third key for Brent 2022-02-17 00:50:39 -05:00
Logan Cusano
1997d5ca5d Remove Noise Gate from master (moved to its own branch) 2022-02-17 00:50:12 -05:00
Logan Cusano
6b9e48d722 Added activity if there is no handler selected 2022-02-13 15:25:50 -05:00
Logan Cusano
720ff6b5c2 Update readme 2022-02-12 20:11:59 -05:00
Logan Cusano
ea205749e5 Update NoiseGate 2022-02-12 20:05:44 -05:00
Logan Cusano
dda6b64e14 Added 'lock' when bot joins to avoid double join or leave when not joined error 2022-02-12 20:02:24 -05:00
Logan Cusano
e1ca0bfd99 Updated variable names to be more accurate 2022-02-12 20:01:08 -05:00
Logan Cusano
18ceb0074e Improve noise gate 2022-02-12 19:59:38 -05:00
Logan Cusano
f4cf8715e9 update requirements 2022-02-12 19:58:47 -05:00
Logan Cusano
73f532ec02 update .gitignore 2022-02-12 19:58:25 -05:00
Logan Cusano
60530baeab Add PyNaCl to requirements.txt 2022-02-06 18:47:33 -05:00
Logan Cusano
8525f79878 BUGFIX noisegate
- Froze when leaving and joining
- Add aarch64 option for opus
2022-02-06 18:45:25 -05:00
Logan Cusano
181cbd9180 update .gitignore 2022-02-06 18:36:24 -05:00
Logan Cusano
042808951e rm __pycache__ 2022-02-06 23:34:53 +00:00
Logan Cusano
c56ba77dd0 Adding AARCH64 libopus 2022-02-06 23:33:11 +00:00
Logan Cusano
0cac9c8f81 Implement Noisegate function to start and stop playing the audio stream 2022-02-04 12:35:00 -05:00
Logan Cusano
329dc8fff8 Added Handler config option 2022-02-04 12:34:21 -05:00
Logan Cusano
82fd2d3f76 BUGFIX bot
Fixed bug where activity didn't update when saving a profile
2022-02-02 05:00:15 -05:00
Logan Cusano
b46defa94a BUGFIX LinkCop
Blocked commands
2022-01-29 20:48:02 -05:00
Logan Cusano
263ca89481 Improved LinkCop
Now checks if the message was from a blocked channel first
2022-01-29 20:42:21 -05:00
Logan Cusano
7951c09765 Disabled discord2digital 2022-01-29 20:30:50 -05:00
Logan Cusano
afacce854f LinkCop prep for live usage 2022-01-29 20:29:44 -05:00
Logan Cusano
2dfe119bf1 bugfixes for LinkCop 2022-01-29 20:26:09 -05:00
Logan Cusano
621390d380 New Module: LyricCop
It will monitor selected channels for links and move them to the 'links' channel
2022-01-29 18:10:43 -05:00
Logan Cusano
dafc4b729b Introduced logging level (output) 2022-01-22 23:56:23 -05:00
Logan Cusano
de5e7574c8 Ignore more filetypes 2022-01-22 23:30:58 -05:00
Logan Cusano
73176b35b4 Converted lyrics generator to a class 2022-01-22 23:27:39 -05:00
Logan Cusano
410924c5cc Bugfix: Squelch is now a float 2022-01-16 16:29:51 -05:00
Logan Cusano
2e3f25cb4c Bugfix: Working on SDR RX 2022-01-04 00:48:48 -05:00
Logan Cusano
b2fca67428 Bugfix: Working on SDR RX 2022-01-04 00:45:10 -05:00
Logan Cusano
2210616315 Bugfix: Working on SDR RX 2022-01-04 00:41:29 -05:00
Logan Cusano
9eee7d8b42 Bugfix: Working on SDR RX 2022-01-04 00:37:45 -05:00
Logan Cusano
769da28223 Bugfix: Working on SDR RX 2022-01-04 00:35:28 -05:00
Logan Cusano
521ab15448 Bugfix: Working on SDR RX 2022-01-04 00:32:19 -05:00
Logan Cusano
b7fca9d506 Bugfix: Working on SDR RX 2022-01-04 00:30:31 -05:00
Logan Cusano
a0e05d0360 Bugfix: Working on SDR RX 2022-01-04 00:24:09 -05:00
Logan Cusano
3fb2ba34a2 Bugfix: Working on SDR RX 2022-01-04 00:15:11 -05:00
Logan Cusano
d050ee877a Bugfix: Working on SDR RX 2022-01-04 00:12:48 -05:00
Logan Cusano
f2709cdb47 Bugfix: Working on SDR RX 2022-01-04 00:11:18 -05:00
Logan Cusano
0ecca373c6 Bugfix: Working on SDR RX 2022-01-04 00:07:02 -05:00
Logan Cusano
d30d6bb1f7 Bugfix: Working on SDR RX 2022-01-04 00:02:37 -05:00
Logan Cusano
189d384c57 Bugfix: Working on SDR RX 2022-01-04 00:00:19 -05:00
Logan Cusano
eac3fc4e39 Update: Working on SDR RX
- Switched to using GQRX
- Created a handler to connect over Telnet to local GQRS

TODO
- Need to find a way to close GQRX without closing the application; Can't start the app with the radio listening, so it must be opened manually
2022-01-03 22:54:18 -05:00
Logan Cusano
1c08bc6919 Update: Working on SDR RX 2022-01-02 22:31:28 -05:00
Logan Cusano
f89c3dc1cb Update: WillieTimer 2021-12-31 02:31:35 -05:00
Logan Cusano
6d47a570f8 Update: Working on SDR RX 2021-12-31 02:27:18 -05:00
Logan Cusano
7602469962 Update: Working on SDR RX 2021-12-31 02:21:08 -05:00
Logan Cusano
0a8af22087 Update: Working on SDR RX 2021-12-31 02:18:23 -05:00
Logan Cusano
2d07ba04c3 Update: WillieTimer; Proper disposal of variable during each loop 2021-12-30 00:23:35 -05:00
Logan Cusano
1e674ee75b Update: WillieTimer; Proper disposal of variable during each loop 2021-12-30 00:16:52 -05:00
Logan Cusano
51b7768854 Update: WillieTimer 2021-12-30 00:14:47 -05:00
Logan Cusano
e122acbf37 Bugfix: WillieTimer 2021-12-30 00:05:27 -05:00
Logan Cusano
80f117d635 Bugfix: WillieTimer 2021-12-30 00:00:09 -05:00
Logan Cusano
2521fbc3b2 Bugfix: WillieTimer 2021-12-29 23:56:13 -05:00
Logan Cusano
3d8b53f699 Bugfix: WillieTimer 2021-12-29 23:53:54 -05:00
Logan Cusano
01aa82757f Bugfix: WillieTimer 2021-12-29 23:49:28 -05:00
Logan Cusano
d9fcb7d0f8 Bugfix: WillieTimer 2021-12-29 23:48:35 -05:00
Logan Cusano
0cc199de83 Bugfix: WillieTimer 2021-12-29 23:47:02 -05:00
Logan Cusano
39df84d7a5 Bugfix: WillieTimer 2021-12-29 23:38:07 -05:00
Logan Cusano
40c925f301 Bugfix: WillieTimer 2021-12-29 23:32:18 -05:00
Logan Cusano
99f38e175b Bugfix: WillieTimer, no id of nonetype 2021-12-29 23:22:28 -05:00
Logan Cusano
d5beff52b1 Removed weights file to save space, AI non functional on RPi 2021-12-29 22:13:01 -05:00
Logan Cusano
366cb05c39 Update: Removed AI reference, RPi doesn't have required libraries 2021-12-29 22:11:03 -05:00
Logan Cusano
87e34f1be7 Update: Updated location of phraseGenerator.py 2021-12-29 21:00:05 -05:00
Logan Cusano
b45746a09a Update: Add previously hidden weights file for generating text 2021-12-29 20:55:53 -05:00
Logan Cusano
4cde5e2e93 Update: WillieTimer RNN textgen function and better behavior with generating text 2021-12-29 20:54:56 -05:00
Logan Cusano
5d03e7bc0f Bugfix: Profile update low bw would have high-pitched voice 2021-12-29 16:18:54 -05:00
Logan Cusano
e4aba8fba7 Cleanup 2021-12-29 01:08:57 -05:00
Logan Cusano
e08c3239e7 Bugfix: Profile update 2021-12-29 00:54:57 -05:00
Logan Cusano
66c822d352 Bugfix: Profile update 2021-12-29 00:52:02 -05:00
Logan Cusano
49841a04a3 Update: Profile update 2021-12-29 00:50:37 -05:00
Logan Cusano
1229d60d87 Update: Profile update 2021-12-29 00:47:34 -05:00
Logan Cusano
0ab787ef5f Bugfix: Profile update profile name didn't show as the status 2021-12-28 20:53:15 -05:00
Logan Cusano
69f6127cb4 Bugfix: Profile update async problems 2021-12-28 20:51:11 -05:00
Logan Cusano
108a0b5999 Update: TODO 2021-12-28 20:46:19 -05:00
Logan Cusano
ea17c70f93 Update: Added activity change when loading profile 2021-12-28 20:46:00 -05:00
Logan Cusano
9b3f67b2a1 Bugfix: Profile update didn't save to proper file 2021-12-28 20:32:19 -05:00
Logan Cusano
e66c28abbe Update: Profile update 2021-12-28 20:23:01 -05:00
Logan Cusano
ed1abc3020 Update: TODO 2021-12-28 02:39:47 -05:00
Logan Cusano
a3329e3c11 Update: Removed unused import 2021-12-28 02:39:40 -05:00
Logan Cusano
c438cb4141 Update: Spacing 2021-12-28 02:37:34 -05:00
Logan Cusano
a028cad70c Update: TODO 2021-12-28 02:36:53 -05:00
Logan Cusano
fd580e4ad4 Update: Spacing 2021-12-28 02:36:38 -05:00
Logan Cusano
eb9ba7f576 Update: Updated the activity whenever the freq changes 2021-12-28 02:27:25 -05:00
Logan Cusano
8391df4209 Update: Added SDR stop command and stopped SDR when the bot leaves 2021-12-28 02:21:59 -05:00
Logan Cusano
f64d562c5e Update: Updated activity with current frequency 2021-12-28 02:12:41 -05:00
Logan Cusano
2f1ce157c6 Bugfix: No audio on low sample rates
Update: Added grammar to responses
2021-12-28 02:06:14 -05:00
Logan Cusano
4bab5aae5b Bugfix: Init no sample rate 2021-12-28 01:53:55 -05:00
Logan Cusano
1a930594b3 Added commands to change radio parameters while running 2021-12-28 01:49:43 -05:00
Logan Cusano
08d1863a4b Resolved command conflict with core bot 2021-12-28 01:18:52 -05:00
Logan Cusano
695f5dc2c9 Updated start_sdr function 2021-12-28 01:16:41 -05:00
Logan Cusano
7895fd772b Update: bot.py 2021-12-28 00:25:10 -05:00
Logan Cusano
c18c686b91 Updates 2021-12-28 00:10:52 -05:00
Logan Cusano
a97403de98 Bugfix: bot.py 2021-12-27 18:38:21 -05:00
Logan Cusano
66f258f44e Implemented command line SDR integration
TODO:
- Add commands to allow changing Freq
2021-12-27 18:33:11 -05:00
Logan Cusano
9a36f56d28 Merge remote-tracking branch 'origin/master' 2021-12-27 03:34:01 -05:00
Logan Cusano
a5016c3ea6 Updated README.md 2021-12-27 03:33:06 -05:00
Logan Cusano
6326e69075 Init for linux 2021-12-27 02:30:50 -05:00
48df14ba7e Merge pull request 'WillAI-Dev' (#1) from WillAI-Dev into master
Reviewed-on: http://git.vpn.cusano.net/Discord_Bot_Gang/Discord-Radio-Bot/pulls/1
2021-12-27 01:13:12 -05:00
Logan Cusano
d41919e32b Created AI text generator function to fit into WilliBot 2021-12-27 01:09:48 -05:00
Logan Cusano
7ed60e9f70 Ignore Mac system files 2021-12-22 17:50:16 -05:00
Logan Cusano
24cfac7c33 Init ML scripts 2021-12-22 16:50:35 -05:00
Logan Cusano
e3bcc124e1 Update gitignore 2021-12-22 16:50:09 -05:00
Logan Cusano
861eb3d808 Updated to not save development files 2021-12-22 03:06:21 -05:00
Logan Cusano
894237458f Updated to not save development files 2021-12-22 02:29:02 -05:00
Logan Cusano
963e25eca9 - Added activity and status for bot user 2021-12-22 02:28:41 -05:00
Logan Cusano
a60660eb03 - Improved random phrase (will not show a phrase twice)
- Added test function for random phrase
2021-12-22 02:27:28 -05:00
Logan Cusano
92c1639565 Context update 2021-12-22 01:54:42 -05:00
Logan Cusano
e7006cd40a WillieTimer Update
WillieTimer: Added a Phrases.txt for random 4:20 selection
WillieTimer: Updated cog.py to use Phrases.txt
Update gitignore
2021-12-18 04:06:45 -05:00
21 changed files with 1521 additions and 89 deletions

10
.gitignore vendored
View File

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

View File

@@ -2,11 +2,15 @@ import sound
import configparser
from os.path import exists
PDB_ACCEPTABLE_HANDLERS = ['gqrx', 'op25']
PDB_KNOWN_BOT_IDS = [756327271597473863, 915064996994633729, 943742040255115304]
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'):
if config.has_section('Bot_Info') and config.has_section('Device') and config.has_section('Config'):
return True
else:
return False
@@ -23,7 +27,8 @@ def read_config_file():
'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'])
'Channel ID': int(config['Bot_Info']['Channel_ID']),
'Handler': str(config['Config']['Handler'])
}
print("Found config options:")
@@ -42,9 +47,17 @@ def write_config_file(**kwargs):
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']:
@@ -77,7 +90,9 @@ def write_config_file(**kwargs):
def get_device_list():
return sound.query_devices().items()
list_of_devices = sound.query_devices().items()
print(list_of_devices)
return list_of_devices
def get_user_device_selection():
@@ -86,32 +101,38 @@ def get_user_device_selection():
for device, dev_id in device_list:
print(f"{dev_id + 1}\t-\t{device}")
org_device_list.append((dev_id, device))
selected_id = None
while not selected_id:
selected_list_id = None
selected_device = None
while not selected_list_id:
print(f"selected device: {selected_list_id}")
print(device_list)
try:
selected_id = int(input(f"Please select the input device from above:\t")) - 1
selected_list_id = int(input(f"Please select the input device from above:\t"))
except Exception as e:
print(e)
continue
continue
if selected_id and not selected_id + 1 > int(len(device_list)):
if int(selected_list_id) < int(len(device_list)):
print("ford")
continue
elif selected_id > int(len(device_list)):
elif selected_list_id > int(len(device_list)):
print("Out of range, try again...")
selected_id = None
selected_list_id = None
continue
else:
selected_id = None
selected_list_id = None
print("Internal error, try again")
continue
for dev_dict in org_device_list:
if dev_dict[0] == selected_id:
selected_id = dev_dict
print(list(device_list)[selected_list_id-1][0])
if dev_dict[1] == list(device_list)[selected_list_id-1][0]:
selected_device = dev_dict
print(selected_device)
return selected_id
return selected_device
def get_user_token():
@@ -142,4 +163,25 @@ def get_user_mention_channel_id():
else:
print("Length error in ID, please try again")
channel_id = None
continue
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 == '':
return handler
def check_negative(s):
try:
f = float(s)
if (f < 0):
return True
# Otherwise return false
return False
except ValueError:
return False

67
NoiseGate.py Normal file
View File

@@ -0,0 +1,67 @@
import threading
import time
import numpy as np
import discord
import sounddevice as sd
from threading import Thread, Event
noise_gate_trigger = 0 # Set this value for the trigger on the noise-gate
voice_connection = None
audio_stream = None
last_callback_value = 0
class NoiseGate(Thread):
def __init__(self, trigger_value: int = 700):
global noise_gate_trigger
super(NoiseGate, self).__init__()
self.stream = None
self.stop_NG = threading.Event()
self.NG_Started = threading.Event()
noise_gate_trigger = trigger_value
def init_stream(self, num, _voice_connection, _audio_stream):
global voice_connection, audio_stream
self.stream = sd.InputStream(device=num, callback=stream_callback)
voice_connection = _voice_connection
audio_stream = _audio_stream
def run(self) -> None:
self.NG_Started.set()
self.stream.start()
while not self.stop_NG.is_set():
#print("Main loop start")
if last_callback_value >= noise_gate_trigger:
trigger_noise_gate(True)
while last_callback_value >= noise_gate_trigger:
time.sleep(6)
trigger_noise_gate(False)
#print("Main loop end")
self.stream.stop()
self.stream.close()
self.NG_Started.clear()
voice_connection.stop()
print('Thread #%s stopped' % self.ident)
def stream_callback(indata, *args):
global last_callback_value
volume_normalization = np.linalg.norm(indata) * 10
last_callback_value = int(volume_normalization)
#print(f"Callback Value: {last_callback_value}")
def trigger_noise_gate(triggered: bool):
if triggered:
if not voice_connection.is_playing():
voice_connection.play(discord.PCMAudio(audio_stream))
# print("|" * int(volume_normalization / 4))
print("Noise Gate was Triggered")
#time.sleep(10)
else:
if voice_connection.is_playing():
print("Noise Gate stopped")
voice_connection.pause()
# try disconnecting and reconnecting

View File

@@ -1,13 +1,18 @@
# 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.
## 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. Redirect your audio to the selected device
6. You're all set!
## 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
2. Run ```main.py``` with Python
3. Follow the prompts in the terminal
4. Ensure your audio is playing on the selected device
5. You're all set! Ask the bot to join!
### 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.
@@ -17,15 +22,4 @@ 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
- [ ] 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
### [To-Do](https://git.vpn.cusano.net/Discord_Bot_Gang/Discord-Radio-Bot/src/branch/master/TODO.md)

28
TODO.md Normal file
View File

@@ -0,0 +1,28 @@
## To-Do
### Main Bot
- [ ] 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
- [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
- [ ] Transcode radio transmissions to text
### Modules
#### Willie Timer
- [ ] 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
### 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

424
bot.py
View File

@@ -1,66 +1,303 @@
import asyncio
import os
import platform
import discord
import BotResources
import sound
import configparser
from discord.ext import commands
from main import write_config_file
# Init class for bot
class Bot(commands.Bot):
def __init__(self, **kwargs): # bot_token, device_id, device_name, command_prefix='>!'):
def __init__(self, **kwargs):
# If there is no custom command prefix (!help, ?help, etc.), use '>!' but also accept @ mentions
if 'command_prefix' not in kwargs.keys():
kwargs['command_prefix'] = '>!'
commands.Bot.__init__(self, command_prefix=commands.when_mentioned_or(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)
# Init the core bot variables
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']
# 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()
# Init radio parameters
self.profile_name = None
self.freq = "104700000"
self.mode = "wfm"
self.squelch = 0
# 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()
self.check_for_modules()
# Add discord events to the bot
self.add_events()
# Start the bot
def start_bot(self):
self.run(self.BOT_TOKEN)
# Add discord commands to the bot
def add_commands(self):
# 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')
@self.command(help="Use this command to join the bot to your channel", brief="Joins the voice channel that the caller is in")
# 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")
async def join(ctx, *, member: discord.Member = None):
member = member or ctx.author.display_name
await self.wait_until_ready()
discord.opus.load_opus('./opus/libopus.dll')
if discord.opus.is_loaded():
stream = sound.PCMStream()
channel = ctx.author.voice.channel
await ctx.send(f"Ok {member}, I'm joining {channel}")
if not self.Bot_Connected:
# Wait for the bot to be ready to connect
await self.wait_until_ready()
stream.change_device(self.DEVICE_ID)
# Load respective opus library
self.load_opus()
voice_connection = await channel.connect()
voice_connection.play(discord.PCMAudio(stream))
if discord.opus.is_loaded():
channel = ctx.author.voice.channel
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()
# 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)
# Play the stream
voice_connection.play(discord.PCMAudio(self.streamHandler))
# Start the SDR and begin playing to the audio stream
self.start_sdr()
# Change the activity to the channel and band-type being used
await self.set_activity()
# 'Lock' the bot from connecting
self.Bot_Connected = True
else:
# Return that the opus library would not load
await ctx.send("Opus won't load")
else:
await ctx.send("Opus won't load")
await ctx.send(f"{str(member).capitalize()}, I'm already connected")
@self.command(help="Use this command to have the bot leave your channel", brief="Leaves the current voice channel")
@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
self.streamHandler.clean_up()
# Disconnect the client from the voice channel
await ctx.voice_client.disconnect()
# Change the presence to away and '@ me'
await self.set_activity(False)
# Stop the SDR so it can cool off
self.stop_sdr()
# 'Unlock' the bot
self.Bot_Connected = False
# 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.\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
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:
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 to the freq
self.profile_name = None
# If the SDR is started, restart it with the updates
if self.sdr_started:
self.start_sdr()
await self.set_activity()
else:
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. "
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.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()
# 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}'")
@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):
"""Reloads a module."""
member = member or ctx.author.display_name
if self.reload_modules(module):
await ctx.send(f"Ok {member}, I reloaded {module}")
await ctx.send(f"Ok {str(member).capitalize()}, I reloaded {module}")
else:
await ctx.send(f"{member}, something went wrong. Please check the console")
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()
@self.command(name='stopsdr', hidden=True)
async def _stopsdr(ctx, member: discord.Member = None):
self.stop_sdr()
# 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()
print("Bot started!")
@self.event
async def on_message(message):
await self.check_and_reply_to_ping(message)
# 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):
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 < 2:
try:
await self.wait_for("message", check=verify_bot_msg, timeout=1)
except asyncio.exceptions.TimeoutError:
seconds_waited += 1
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":
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
if self.system_os_type == 'Linux_32':
discord.opus.load_opus('./opus/libopus.so')
elif self.system_os_type == 'Linux_64':
discord.opus.load_opus('./opus/libopus_aarcch64.so')
elif self.system_os_type == 'Windows':
discord.opus.load_opus('./opus/libopus.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:
@@ -74,14 +311,19 @@ class Bot(commands.Bot):
else:
return False
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")
# 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")):
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")
@@ -92,3 +334,131 @@ class Bot(commands.Bot):
except Exception as e:
print(e)
return False
# Check and store the OS type of the system for later use
def check_os_type(self):
if os.name == 'nt':
self.system_os_type = 'Windows'
else:
processor = platform.architecture()[0]
if processor == "64bit":
self.system_os_type = 'Linux_64'
elif processor == "32bit":
self.system_os_type = 'Linux_32'
# Check to see if there is only one frequency
def start_sdr(self):
if self.Handler in ['gqrx', 'op25']:
if type(self.freq) == str:
# Single freq sent
# Stop the SDR if it is running
self.stop_sdr()
# Start the radio
print(f"Starting freq: {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)
self.OP25Handler.start()
# 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
if self.Handler == 'op25':
self.OP25Handler.close_op25()
# self.OP25Handler.join()
# Need a way to 'close' GQRX
self.sdr_started = False
# Set the activity of the bot
async def set_activity(self, connected=True):
if connected:
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]}"
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):
config = configparser.SafeConfigParser()
if os.path.exists('./profiles.ini'):
config.read('./profiles.ini')
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)
with open('./profiles.ini', 'w+') as config_file:
config.write(config_file)
self.profile_name = profile_name
if self.sdr_started:
self.start_sdr()
await self.set_activity()
# Load a saved profile into the current settings
async def load_radio_config(self, profile_name):
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 self.sdr_started:
self.start_sdr()
await self.set_activity()
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: {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:
ctx = await self.get_context(message)
await self.invoke(ctx)
return True
else:
await self.process_commands(message)
return False

48
gqrxHandler.py Normal file
View File

@@ -0,0 +1,48 @@
from telnetlib import Telnet
from BotResources import check_negative
from time import sleep
class GQRXHandler():
def __init__(self, hostname: str = "localhost", port: int = 7356):
self.hostname = hostname
self.port = port
self.telnet_connection = None
def create_telnet_connection(self):
print("Creating connection")
tel_conn = Telnet(self.hostname, self.port)
tel_conn.open(self.hostname, self.port)
return tel_conn
def change_freq(self, freq):
tel_conn = self.create_telnet_connection()
print(f"Changing freq to {freq}")
tel_conn.write(bytes(f"F {int(freq)}", 'utf-8'))
sleep(1)
tel_conn.close()
def change_squelch(self, squelch):
tel_conn = self.create_telnet_connection()
if not check_negative(squelch):
squelch = float(-abs(squelch))
print(f"Changing squelch to {squelch}")
tel_conn.write(bytes(f"L SQL {float(squelch)}", 'utf-8'))
sleep(1)
tel_conn.close()
def change_mode(self, mode):
tel_conn = self.create_telnet_connection()
print(f"Changing mode to {mode}")
tel_conn.write(bytes(f"M {str(mode)}", 'utf-8'))
sleep(1)
tel_conn.close()
def set_all_settings(self, mode, squelch, freq):
self.change_mode(mode)
self.change_freq(freq)
self.change_squelch(squelch)

18
main.py
View File

@@ -1,6 +1,7 @@
import os
import time
import bot
import argparse
from BotResources import check_if_config_exists, write_config_file, read_config_file
# Jorn
@@ -9,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"
@@ -22,26 +26,30 @@ 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'],
Mention_Group=config['Mention Group'], Channel_ID=config['Channel ID'])
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():
raise BotDeviceNotFound(config['Device Name'])
print("Bot started!")
discord_bot_client.start_bot()

135
modules/LinkCop/cog.py Normal file
View File

@@ -0,0 +1,135 @@
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]))+)')
### 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
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.append([
235148962103951360 # Carl Bot
])
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):
print('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:
print(f"Link Cop Error: '{err}'")
print(f"Bot or other non-user that has not been whitelisted sent a message")
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)
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)
print(links)
if len(links) > 0:
return links
def setup(bot: commands.Bot):
bot.add_cog(LinkCop(bot))

3
modules/WillieTimer/.gitignore vendored Normal file
View File

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

View File

@@ -0,0 +1,10 @@
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,32 +6,45 @@ 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.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!"
]
self.current_420_phrase = None
self.current_warm_up_phrase = None
self.caller_name = None
#self.PG = PhraseGenerator()
@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 datetime.datetime.now().strftime('%H:%M') in ("04:20", "16:20"):
# If it is 4:20pm
if datetime.datetime.now().strftime('%H:%M') in "16:20":
print(f"It's {datetime.datetime.now().strftime('%H:%M:%S')}!")
channel = self.bot.get_channel(id=self.channel_id)
await channel.send(f"<@&{self.bot_get_role().id}> {choice(self.message_pool)}")
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
@commands.command(help="Use this command to start the background task to wait for 4:20",
brief="Starts the 4:20 clock")
@@ -39,7 +52,10 @@ 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'")
@@ -51,10 +67,11 @@ 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(help="Example '@Greada chchn 757379843792044102'",
@commands.command(name='420chchn', 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
@@ -71,7 +88,7 @@ class WillieTimer(commands.Cog):
await ctx.send(f"{member}, {message} is not a valid channel ID, please try again")
pass
@commands.command(help="Example '@Greada chmtn 'Willies Friends'",
@commands.command(name='420chmtn', 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
@@ -87,12 +104,81 @@ 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):
channel = self.bot.get_channel(id=self.channel_id)
guild = channel.guild
role = discord.utils.get(guild.roles, name=self.mention_group)
role = discord.utils.get(self.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

@@ -0,0 +1,433 @@
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

@@ -0,0 +1,83 @@
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

@@ -0,0 +1,55 @@
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()

56
op25Handler.py Normal file
View File

@@ -0,0 +1,56 @@
import threading
import subprocess
import asyncio
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 start(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 OP25")
cwd = os.getcwd()
print(cwd)
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"])
os.chdir(cwd)
def close_op25(self):
print(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:
print("Terminating OP25")
self.OP25Proc.terminate()
asyncio.sleep(1000)
print(f"Waited {seconds_waited} seconds")
seconds_waited += 1
except Exception as e:
print(e)

BIN
opus/libopus.so Normal file

Binary file not shown.

BIN
opus/libopus_aarcch64.so Normal file

Binary file not shown.

View File

@@ -1,2 +1,3 @@
discord~=1.7.3
sounddevice~=0.4.3
sounddevice~=0.4.3
pynacl~=1.5.0

View File

@@ -7,8 +7,9 @@ sd.default.dtype = "int16"
sd.default.latency = "low"
sd.default.samplerate = 48000
class PCMStream:
globals()
def __init__(self):
self.stream = None
@@ -20,14 +21,18 @@ class PCMStream:
# convert to pcm format
return bytes(data)
def change_device(self, num):
def change_device(self, device_ID):
self.clean_up()
self.stream = sd.RawInputStream(device=device_ID)
self.stream.start()
def clean_up(self):
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):