361 Commits
GUI ... master

Author SHA1 Message Date
0cea0f901f Merge pull request 'Core Update' (#12) from core_update into master
Reviewed-on: #12
2022-11-26 22:07:54 -05:00
Logan Cusano
c6d120982d Cherry pick 'Update to use intents' to remove NGv2 sections 2022-11-26 22:04:43 -05:00
Logan Cusano
71a8914ac7 Updated requirements.txt 2022-11-26 22:00:18 -05:00
Logan Cusano
f36c73b30b Sledgehammer config approach 2022-11-26 21:59:52 -05:00
b2a73726a0 Merge pull request 'V3_GQRX_Improvements' (#11) from V3_GQRX_Improvements into master
Reviewed-on: http://git.vpn.cusano.net/Discord_Bot_Gang/Discord-Radio-Bot/pulls/11
2022-04-16 20:52:43 -04:00
Logan Cusano
a094ba581d //WIP v3
GQRX Changes
- Better error handling; GQRX config dupes some options for an unknown reason
2022-04-10 13:53:59 -04:00
Logan Cusano
62f9e1abaa //WIP v3
GQRX Changes
- Ensure AGC is enabled whenever the bot starts
2022-04-09 22:34:56 -04:00
Logan Cusano
e4e04e429f //WIP v3
GQRX Config Template
- Added switch to allow AGC
2022-04-09 22:27:26 -04:00
Logan Cusano
0db4f62afc //WIP v3
Clear Messages Changes
- Bug
- More error checking
- Limit the user to 100 messages
- More help text
2022-04-09 22:22:44 -04:00
Logan Cusano
4d5c1183d6 //WIP v3
Clear Messages Changes
- Bug
2022-04-09 22:15:37 -04:00
Logan Cusano
9dba89a60b //WIP v3
Clear Messages Changes
- Check for negative numbers... Gino
2022-04-09 22:14:21 -04:00
Logan Cusano
dcf0de8351 //WIP v3
Clear Messages Changes
- Added help message
2022-04-09 22:09:24 -04:00
Logan Cusano
bfb049e268 //WIP v3
Clear Messages Changes
- Default clear to 2 messages (instantiation and the previous message)
2022-04-09 22:05:32 -04:00
Logan Cusano
b0d83def35 //WIP v3
Clear Messages Changes
- No "#0000" in username output
2022-04-09 22:03:52 -04:00
Logan Cusano
8af7a70b65 //WIP v3
Clear Messages Changes
- Mentioning user ID not name
2022-04-09 22:02:04 -04:00
Logan Cusano
8d090c5c67 //WIP v3
Clear Messages Changes
- Better logging
- Better output
2022-04-09 21:58:34 -04:00
Logan Cusano
35e19ee83f //WIP v3
Clear Messages Changes
- New approach
2022-04-09 21:39:44 -04:00
Logan Cusano
44d027ab8c Merge branch 'master' into V3_GQRX_Improvements 2022-04-09 21:29:12 -04:00
Logan Cusano
31d5f8139c New Module to Clear Channel Messages 2022-04-09 21:28:21 -04:00
Logan Cusano
3837f7abd6 //WIP V3
Update todo
2022-04-09 21:27:49 -04:00
Logan Cusano
b7f0939787 //WIP V3
GQRX Changes
- Sending output to devnull, except for error
2022-04-09 04:32:12 -04:00
Logan Cusano
cccd974adc //WIP V3
GQRX Changes
- Changed repeating log entry to debug
2022-04-09 03:39:45 -04:00
Logan Cusano
f4b14c26fc Merge branch 'master' into V3_GQRX_Improvements 2022-04-09 03:27:23 -04:00
Logan Cusano
a483f83b4b //WIP V3
Bot Changes
- Increased loop delay when waiting for GQRX to start
Noisegate Change
- Display whether the noisegate is active or not in the logs
2022-04-09 02:36:48 -04:00
Logan Cusano
458ab5be95 //WIP V3
Bot Changes
- Never used new radio config function when loading config
2022-04-09 02:25:46 -04:00
Logan Cusano
dbbd5b6fe3 //WIP V3
Bot Changes
- Awaiting new async 'use_current_radio_config' function
2022-04-09 02:23:46 -04:00
Logan Cusano
63b37d14cb //WIP V3
Bot Changes
- Improved handlers
- Added new function to use the newly set or loaded settings
- Any change to frequency, squelch, mode, or noisegate threshold will show no profile
    - The user is responsible for saving their changes

Readme Changes
- Update with notes on the bot change above
2022-04-09 02:21:53 -04:00
Logan Cusano
2dabf1c98d //WIP V3
GQRX Changes
- Resetting tel_conn when closing GQRX
2022-04-09 01:57:24 -04:00
Logan Cusano
5551aa6355 //WIP V3
Default Radio Settings Update
- One location for default radio settings

GQRX Changes
- Default settings
- Simpler check to see if GQRX is started
- Working process handler
2022-04-09 01:52:37 -04:00
Logan Cusano
7ee5d92852 forgot each item needed to be in the list, no spaces 2022-04-09 01:21:33 -04:00
Logan Cusano
5a3729627b GQRX Changes
- Checks for GQRX config file
    - If one exists, reset the 'crashed' option
    - If one does not, create one from the template
- New templates package
    - Useful for storing large files to be called on later
2022-04-09 01:13:06 -04:00
Logan Cusano
e4201a2746 Fixed Logging 2022-04-08 21:46:28 -04:00
Logan Cusano
bb56566d37 Merge branch 'master' into V3_GQRX_Improvements 2022-04-08 21:35:07 -04:00
Logan Cusano
35e90aa290 Created a 'debug' log file to keep one log file clean 2022-04-08 21:32:31 -04:00
Logan Cusano
99ec1d07aa Merge remote-tracking branch 'origin/V3_GQRX_Improvements' into V3_GQRX_Improvements 2022-04-01 01:59:51 -04:00
Logan Cusano
bbca25ce3f Getting closer on the GQRX Handler. Doesn't close properly 2022-04-01 01:55:14 -04:00
Logan Cusano
676936eb6f More intelligent init of the handler 2022-04-01 01:10:12 -04:00
Logan Cusano
8debd690f1 Better logging 2022-04-01 01:08:54 -04:00
Logan Cusano
5d1a58ee87 Var got set before the error check 2022-04-01 01:07:23 -04:00
Logan Cusano
fd62da0c6f Silly bug 2022-04-01 01:05:57 -04:00
Logan Cusano
b972d09a65 Handling the delay in GQRX opening and being 'usable'
Handling the delay in GQRX opening and being 'usable'
2022-04-01 01:00:32 -04:00
Logan Cusano
21aacde130 Moved the main inits to when the bot is run 2022-04-01 00:51:34 -04:00
Logan Cusano
e26ae52399 Wrong location of the logger 2022-04-01 00:50:28 -04:00
Logan Cusano
319de5cf42 Initial changes to handle GQRX process 2022-04-01 00:46:34 -04:00
5e6b6f02d9 Merge pull request 'New logging system' (#10) from V3_Better_Logging into master
Reviewed-on: http://git.vpn.cusano.net/Discord_Bot_Gang/Discord-Radio-Bot/pulls/10
2022-04-01 00:02:08 -04:00
Logan Cusano
25ad3b076c Wait for the bot to connect before playing audio 2022-03-31 22:52:10 -04:00
Logan Cusano
d3a18c2421 BUG Would try to join when the bot was disconnected 2022-03-31 18:23:45 -04:00
Logan Cusano
6170719233 Update todo 2022-03-31 03:00:58 -04:00
Logan Cusano
c8503c856e Update todo with GQRX commands 2022-03-31 02:47:07 -04:00
Logan Cusano
471f668cdb Flipped the handlers 2022-03-31 02:32:14 -04:00
Logan Cusano
579987d571 Forgot to update new var name 2022-03-31 02:30:46 -04:00
Logan Cusano
6efca8f0fa Added a callback to itself so if it cuts out, it should restart 2022-03-31 02:28:54 -04:00
Logan Cusano
9e60444bc6 More specific log output 2022-03-31 02:27:21 -04:00
Logan Cusano
9280278ca7 Ignore the new ./logs directory 2022-03-30 01:38:22 -04:00
Logan Cusano
c07220f434 Update readme 2022-03-30 01:37:54 -04:00
Logan Cusano
d650ae4350 Never updated the global sensitivity, so the profile would be updated with this value 2022-03-30 01:35:43 -04:00
Logan Cusano
cbeb90a0f4 Remove closing 2022-03-30 01:31:19 -04:00
Logan Cusano
5c0d0f1f7b Merge remote-tracking branch 'origin/V3_Better_Logging' into V3_Better_Logging 2022-03-30 00:37:17 -04:00
Logan Cusano
6135bd81de Specified output name
Specified output name
2022-03-30 00:37:01 -04:00
Logan Cusano
dc178e54af Specified output name 2022-03-30 00:33:13 -04:00
Logan Cusano
f124853332 Specified output name 2022-03-30 00:20:36 -04:00
Logan Cusano
331753ccbf Merge remote-tracking branch 'origin/V3_Better_Logging' into V3_Better_Logging 2022-03-29 23:39:28 -04:00
Logan Cusano
fd8fb1ab3d undoing verbosity change 2022-03-29 23:36:46 -04:00
Logan Cusano
5b2d0f9135 Merge branch 'V3_Better_Logging' of git.vpn.cusano.net:Discord_Bot_Gang/Discord-Radio-Bot into V3_Better_Logging 2022-03-29 23:31:13 -04:00
Logan Cusano
1357a0509c Merge branch 'V3_Better_Logging' of git.vpn.cusano.net:Discord_Bot_Gang/Discord-Radio-Bot into V3_Better_Logging 2022-03-29 23:20:44 -04:00
Logan Cusano
ac4e4349df OCD change 2022-03-30 03:17:37 +00:00
Logan Cusano
9af6fc254c Added logs to a folder, so it doesn't polute the root 2022-03-30 03:17:37 +00:00
Logan Cusano
c1ecd092c6 Added logging 2022-03-30 03:17:36 +00:00
Logan Cusano
34c5fe9c5b Increase verbosity 2022-03-30 03:17:36 +00:00
Logan Cusano
65b0f25921 New logging 2022-03-30 03:17:36 +00:00
a8fbd094e1 Update '.gitignore'
Better directory management
2022-03-29 23:15:14 -04:00
Logan Cusano
834a74c7f0 Merge branch 'V3_Better_Logging' of git.vpn.cusano.net:Discord_Bot_Gang/Discord-Radio-Bot into V3_Better_Logging 2022-03-29 23:13:09 -04:00
Logan Cusano
d2bf7822cf OCD change 2022-03-29 23:08:40 -04:00
Logan Cusano
22d36c5f19 Added logs to a folder, so it doesn't polute the root 2022-03-29 23:06:43 -04:00
Logan Cusano
a9cfa89655 Added logging 2022-03-29 22:56:20 -04:00
Logan Cusano
44e0941a6a Increase verbosity 2022-03-29 22:42:16 -04:00
Logan Cusano
e6d4c9f467 Merge remote-tracking branch 'origin/V3_Better_Logging' into V3_Better_Logging 2022-03-29 22:39:25 -04:00
Logan Cusano
acc76db5f9 Merge branch 'V3_Better_Logging' of git.vpn.cusano.net:Discord_Bot_Gang/Discord-Radio-Bot into V3_Better_Logging 2022-03-29 16:45:03 -04:00
Logan Cusano
1ed5436138 New logging 2022-03-29 20:44:52 +00:00
Logan Cusano
f8f6cb7e52 Stopping the voice connection after the fadeout and change fadeout duration 2022-03-29 16:39:48 -04:00
Logan Cusano
458d8d12a4 Merge branch 'V3_Better_Logging' of git.vpn.cusano.net:Discord_Bot_Gang/Discord-Radio-Bot into V3_Better_Logging 2022-03-28 01:58:23 -04:00
Logan Cusano
319e92b897 New logging 2022-03-28 05:56:20 +00:00
Logan Cusano
997c24949b Updated todo 2022-03-28 01:55:30 -04:00
Logan Cusano
326da757cf No pipes or STDIN/STDOUT 2022-03-28 01:46:50 -04:00
Logan Cusano
c741288b87 New logging 2022-03-28 01:27:37 -04:00
d97e51d960 Merge pull request 'Noisegate V2' (#8) from NoiseGateV2 into master
Reviewed-on: http://git.vpn.cusano.net/Discord_Bot_Gang/Discord-Radio-Bot/pulls/8
2022-03-28 00:13:06 -04:00
Logan Cusano
8c5cc65a50 no new line at the end of display profiles 2022-03-27 15:05:46 -04:00
Logan Cusano
63ca0fec63 silly bug 2022-03-27 15:04:55 -04:00
Logan Cusano
8e191531d0 Reads profiles without noisegate sensitivity value 2022-03-27 15:03:41 -04:00
Logan Cusano
83c2971742 No spaces in profiles 2022-03-27 15:00:58 -04:00
Logan Cusano
1a705c2b6c KeyError instead of NameError ~507 bot.py 2022-03-27 14:56:56 -04:00
Logan Cusano
3a2d9531f9 Removing circular import 2022-03-27 14:55:11 -04:00
Logan Cusano
54e3a3a116 Remove dupe file 2022-03-27 14:50:42 -04:00
Logan Cusano
0d32d3fa7e rebase master into NoiseGateV2 2022-03-27 14:48:42 -04:00
Logan Cusano
05e81aea1d Better output 2022-03-27 14:48:40 -04:00
Logan Cusano
69c7d4d548 idk 2022-03-27 14:48:39 -04:00
Logan Cusano
79c651be30 idk 2022-03-27 14:48:38 -04:00
Logan Cusano
4eb73c3832 idk 2022-03-27 14:48:37 -04:00
Logan Cusano
f90c84184a idk 2022-03-27 14:48:36 -04:00
Logan Cusano
831c19e7e1 idk 2022-03-27 14:48:34 -04:00
Logan Cusano
97aed314ba idk 2022-03-27 14:48:33 -04:00
Logan Cusano
f64a3dc517 idk 2022-03-27 14:48:32 -04:00
Logan Cusano
5d7a3b2d0b idk 2022-03-27 14:48:31 -04:00
Logan Cusano
0c53e58fab idk 2022-03-27 14:48:30 -04:00
Logan Cusano
8c7075b76c idk 2022-03-27 14:48:28 -04:00
Logan Cusano
dd9643363b idk 2022-03-27 14:48:27 -04:00
Logan Cusano
7cca55a836 Trying to use pipe 2022-03-27 14:48:26 -04:00
Logan Cusano
5fe98bfe0e Trying to use stdin, executable and shell 2022-03-27 14:48:25 -04:00
Logan Cusano
6049af3a25 Trying to use stdout/stderr 2022-03-27 14:48:24 -04:00
Logan Cusano
c03521de7f Using a list with extend instead of append 2022-03-27 14:48:22 -04:00
Logan Cusano
e5b3359571 Using a list with extend instead of append 2022-03-27 14:48:21 -04:00
Logan Cusano
492026ed8c Using a list with extend instead of append 2022-03-27 14:48:20 -04:00
Logan Cusano
61f113c475 Using a list with extend instead of append 2022-03-27 14:48:19 -04:00
Logan Cusano
bbe461255a idk 2022-03-27 14:48:18 -04:00
Logan Cusano
7d161baeda no space in debug emendation 2022-03-27 14:48:16 -04:00
Logan Cusano
f68e794d02 popen needs string? 2022-03-27 14:48:15 -04:00
Logan Cusano
9986eadaaf popen needs string? 2022-03-27 14:48:14 -04:00
Logan Cusano
404ddfc773 Updated p25 opening 2022-03-27 14:48:13 -04:00
Logan Cusano
f94a900c44 Added profile commands for the bot 2022-03-27 14:43:33 -04:00
Logan Cusano
721ef932ce Better output 2022-03-27 14:18:16 -04:00
Logan Cusano
4ab14d2e5d idk 2022-03-27 14:08:53 -04:00
Logan Cusano
9635e11434 idk 2022-03-27 14:06:36 -04:00
Logan Cusano
8fa42d819a idk 2022-03-27 14:03:40 -04:00
Logan Cusano
5a8efa8884 idk 2022-03-27 14:01:49 -04:00
Logan Cusano
e0dba3be4e idk 2022-03-27 13:52:37 -04:00
Logan Cusano
4e839eecb4 idk 2022-03-27 13:50:24 -04:00
Logan Cusano
81dc305197 idk 2022-03-27 13:49:02 -04:00
Logan Cusano
203bee0734 idk 2022-03-27 13:48:07 -04:00
Logan Cusano
7e2cf932dc idk 2022-03-27 13:46:44 -04:00
Logan Cusano
102e0ae473 idk 2022-03-27 13:43:52 -04:00
Logan Cusano
33ea596ab4 idk 2022-03-27 13:40:00 -04:00
Logan Cusano
f965c5de05 Trying to use pipe 2022-03-27 13:32:49 -04:00
Logan Cusano
0bc0d8e031 Trying to use stdin, executable and shell 2022-03-27 13:29:15 -04:00
Logan Cusano
92ab4992cb Trying to use stdout/stderr 2022-03-27 13:19:29 -04:00
Logan Cusano
da29c0eaaa Using a list with extend instead of append 2022-03-27 13:12:25 -04:00
Logan Cusano
30d539312e Using a list with extend instead of append 2022-03-27 13:03:00 -04:00
Logan Cusano
9a225010be Using a list with extend instead of append 2022-03-27 13:00:31 -04:00
Logan Cusano
92bd4ffb1f Using a list with extend instead of append 2022-03-27 12:49:08 -04:00
Logan Cusano
bf8adda80b idk 2022-03-27 05:20:00 -04:00
Logan Cusano
f239e87b0a no space in debug emendation 2022-03-27 05:18:23 -04:00
Logan Cusano
136009c07e popen needs string? 2022-03-27 05:17:03 -04:00
Logan Cusano
5efb44164a popen needs string? 2022-03-27 05:11:49 -04:00
Logan Cusano
6fd30b9609 Updated p25 opening 2022-03-27 04:37:29 -04:00
Logan Cusano
c1084deadf Update requirements from Dan's Update 2022-03-27 01:24:36 -04:00
Logan Cusano
cf79050b96 Possible fix for Nonetype error on Greada 2022-03-27 01:16:53 -04:00
Logan Cusano
22d6d5d3d7 Added a function to allow users to change the noisegate threshold 2022-03-27 01:15:17 -04:00
Logan Cusano
24adc47109 Dan's Update
- New classes for noisegate & audio stream
- Ability to manipulate raw data in real-time
2022-03-27 00:50:42 -04:00
Logan Cusano
0329ff7a81 New attempt 2022-03-26 03:07:24 -04:00
Logan Cusano
48f6812f68 Printing volume value 2022-03-26 02:42:42 -04:00
Logan Cusano
b444236dc9 Printing volume value 2022-03-26 02:39:08 -04:00
Logan Cusano
287eb0e981 Merge remote-tracking branch 'origin/NoiseGateV2' into NoiseGateV2 2022-03-26 02:34:39 -04:00
Logan Cusano
7a9dbfb53a Actually connecting the noisegate 2022-03-26 02:34:01 -04:00
Logan Cusano
a13cf36e15 Actually connecting the noisegate 2022-03-26 02:24:06 -04:00
Logan Cusano
465c0e00ea Added noisegate to the 'sound' file and integrated it with its function 2022-03-26 01:35:02 -04:00
Logan Cusano
12aa4d6f51 Merge branch 'master' into NoiseGateV2 2022-03-20 03:15:37 -04:00
Logan Cusano
2dacab263b Rename to resolve conflict 2022-03-20 03:05:33 -04:00
Logan Cusano
126ba907af Testing OP25 Improvements
- New system of handling thread
2022-03-19 01:18:45 -04:00
Logan Cusano
4f230f8d33 Testing OP25 Improvements
- Introduced threading (again..)
2022-03-19 00:46:13 -04:00
Logan Cusano
1257b713fc Testing OP25 Improvements
- Disabled shell
2022-03-19 00:39:10 -04:00
Logan Cusano
4f7ec14807 Testing OP25 Improvements
- Changed output to be ignored
2022-03-19 00:32:03 -04:00
Logan Cusano
ec7880ed1e Testing OP25 Improvements 2022-03-19 00:27:55 -04:00
Logan Cusano
e4cb8647ee Replaced arbitrary sleep with a wait until response on all functions 2022-03-19 00:19:02 -04:00
Logan Cusano
a205bd2d8a BUG; Used string, not byte string 2022-03-19 00:17:36 -04:00
Logan Cusano
8355b98da5 Replaced arbitrary sleep with a wait until response 2022-03-19 00:16:24 -04:00
Logan Cusano
c3fb20f12e Increased sleep time 2022-03-19 00:07:06 -04:00
Logan Cusano
5ebbaca54b GQRX bug; Never created connection 2022-03-17 23:30:40 -04:00
Logan Cusano
efe8eda66f Improved GQRX handler speed 2022-03-17 23:28:59 -04:00
Logan Cusano
9f4473ffcc Improved the display of profiles 2022-03-17 23:21:29 -04:00
Logan Cusano
759f05eb0c Improved the display of profiles 2022-03-17 23:18:17 -04:00
Logan Cusano
0d51b47146 BUGFIX in display all profiles 2022-03-17 23:15:02 -04:00
Logan Cusano
ff26cbc0b1 Removed ping in favor of 'ModuleProbe' module 2022-03-17 23:13:07 -04:00
Logan Cusano
9456b91d09 Added WYA command to display where the bot is 2022-03-17 23:10:13 -04:00
Logan Cusano
b534e2b731 Typo 2022-03-17 22:41:37 -04:00
fdeb337ca5 Merge branch 'NoiseGateV2' into master 2022-03-17 16:52:12 -04:00
Logan Cusano
56a3bdd0ea Default the OP25 HTTP server to enabled, caused issues with the shell with it disabled.
Need to look into perhaps using the `shell=True` parameter
2022-03-17 00:12:15 -04:00
Logan Cusano
830d299a01 Update gitignore to ignore all pycache folders 2022-03-17 00:09:39 -04:00
676969a8fe Merge pull request 'Moved the startup ping response to a module, so it will only reply if the modules are enabled' (#6) from ModuleProbe into master
Reviewed-on: http://git.vpn.cusano.net/Discord_Bot_Gang/Discord-Radio-Bot/pulls/6
2022-03-17 00:00:46 -04:00
Logan Cusano
760ebd7e43 Added a call to set squelch to 0 as all settings are being changed, so there is no sudden burst of noise 2022-03-16 23:59:42 -04:00
Logan Cusano
c564a145b5 Moved the startup ping response to a module, so it will only reply if the modules are enabled 2022-03-16 23:52:17 -04:00
Logan Cusano
db0922bf68 Updated TODO 2022-03-16 02:20:47 -04:00
Logan Cusano
897e53d4c0 Changed the order of operations on boot so squelch is changed first 2022-03-16 02:20:35 -04:00
Logan Cusano
a1f9869e8e Undo revert 2022-03-16 01:34:02 -04:00
Logan Cusano
73fa560040 Removed noisegate (again) 2022-03-16 01:29:09 -04:00
Logan Cusano
1f30e05e46 Revert "Cleanup"
This reverts commit 78d9b9e307.
2022-03-16 01:26:24 -04:00
Logan Cusano
56ee57e96a Merge remote-tracking branch 'origin/master' 2022-03-16 01:15:47 -04:00
Logan Cusano
5cc8a8c118 Changed to sync sleep 2022-03-16 01:15:22 -04:00
26909bce0f Merge pull request 'Merge from master' (#5) from master into NoiseGateV2
Reviewed-on: http://git.vpn.cusano.net/Discord_Bot_Gang/Discord-Radio-Bot/pulls/5
2022-02-27 21:47:07 -05:00
fc1a742933 Merge branch 'NoiseGateV2' into master 2022-02-27 21:46:31 -05:00
Logan Cusano
78d9b9e307 Cleanup 2022-02-27 21:43:28 -05:00
Logan Cusano
ba237d2d53 Working on bug in leaving 2022-02-27 19:09:48 -05:00
Logan Cusano
6ad489183f Working on bug in leaving 2022-02-27 19:01:52 -05:00
Logan Cusano
b721fa3df4 Working on bug in leaving 2022-02-27 19:00:55 -05:00
Logan Cusano
7682d8c72c Working on bug in leaving 2022-02-27 18:44:02 -05:00
Logan Cusano
706f4a31df Working on bug in leaving 2022-02-27 18:40:36 -05:00
Logan Cusano
7fa11a4abe Working on bug in leaving 2022-02-27 18:36:18 -05:00
Logan Cusano
17c528c153 Working on bug in leaving 2022-02-27 17:29:08 -05:00
Logan Cusano
f9594e2afe Error catching
- PCMStream.clean_up
- PCMStream.read
2022-02-27 17:25:10 -05:00
Logan Cusano
167c06f331 Error catching
- PCMStream.clean_up
- PCMStream.read
2022-02-27 17:21:53 -05:00
Logan Cusano
137d0ae097 Error catching for cleaning up 2022-02-27 17:17:23 -05:00
Logan Cusano
002288d632 Error catching for cleaning up 2022-02-27 17:14:31 -05:00
Logan Cusano
59a3ca22ea Debug output for leaving 2022-02-27 17:11:59 -05:00
Logan Cusano
fdb9bb519c Grammar fix
Comma in `Ok <member>*,*\n<message>`
2022-02-27 17:09:06 -05:00
Logan Cusano
a5f0a4b193 BUGFIX appending to dict, fixed 2022-02-27 16:30:38 -05:00
Logan Cusano
62968654d2 v2.0.1
- Args
- Better configs
- Other shit too, idr
2022-02-27 13:40:21 -05:00
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
a3a592d854 New Noisegate attempt 2022-02-14 20:13:23 -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
26 changed files with 2388 additions and 198 deletions

14
.gitignore vendored
View File

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

View File

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

214
NoiseGatev2.py Normal file
View File

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

View File

@@ -1,13 +1,19 @@
# 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
## Requirements
- Python 3.X
- *See `requirements.txt`*
- GQRX (if you want the bot to be able to control the sdr)
- Any SDR app you want, as long as it plays audio
## Setup
1. Install the pip packages found in the ```Requirements.txt``` file
- *Optional if you are using OP25* Run `echo 0 > /sys/module/usbcore/parameters/usbfs_memory_mb`
3. Run ```main.py``` with Python
4. Follow the prompts in the terminal
5. Redirect your audio to the selected device
6. You're all set!
5. Ensure your audio is playing on the selected device
5You'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 +23,9 @@ 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)
**Notes for readme leter on**
- Users need to save profile after any change in discord
-

56
TODO.md Normal file
View File

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

597
bot.py
View File

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

239
gqrxHandler.py Normal file
View File

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

122
main.py
View File

@@ -1,7 +1,9 @@
import logging
import os
import time
import bot
from BotResources import check_if_config_exists, write_config_file, read_config_file
import argparse
import BotResources
# Jorn
#token = 'OTE1MDY0OTk2OTk0NjMzNzI5.YaWKsA.Y9yaCGg_VXRL_qQVbs05vo7gSAc'
@@ -9,50 +11,130 @@ 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"
LOGGER = logging.getLogger("Discord_Radio_Bot.Main")
class BotDeviceNotFound(Exception):
def __init__(self, device):
print(f"Unable to find the device: {device}")
LOGGER.debug(f"Unable to find the device: {device}")
try:
os.remove('./config.ini')
except OSError:
print("Config file not found, restarting.")
LOGGER.warning("Config file not found, restarting.")
#os.execv(__file__, sys.argv)
def main():
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)
def main(**passed_config):
# Don't create a config if the user passed parameters
if len(passed_config.keys()) == 0:
LOGGER.info('Checking config file...')
if not BotResources.check_if_config_exists():
LOGGER.warning("No config file exists, please enter this information now")
BotResources.write_config_file(init=True)
config = read_config_file()
config = BotResources.read_config_file()
print('Starting Bot...')
if not config:
LOGGER.warning("No config file exists, please enter this information now")
BotResources.write_config_file(init=True)
config = BotResources.read_config_file()
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'])
# Overwrite config options if they were passed
if len(passed_config.keys()) == 0:
for sub in config:
# checking if key present in other dictionary
if sub in passed_config and passed_config[sub]:
config[sub] = passed_config[sub]
print(f"Verifying audio device:\t{config['Device Name']}")
LOGGER.info('Starting Bot...')
if not discord_bot_client.check_device():
discord_bot_client = bot.Bot(Token=config['Bot Token'], Device_ID=config['Device ID'],
Device_Name=config['Device Name'], Mention_Group=config['Mention Group'],
Channel_ID=config['Channel ID'], Handler=config['Handler'])
LOGGER.debug(f"Verifying audio device:\t{config['Device Name']}")
if not discord_bot_client.check_device(_override=bool("Device ID" in passed_config.keys())):
raise BotDeviceNotFound(config['Device Name'])
print("Bot started!")
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__':
cmd_args = cmd_arguments()
BotResources.init_global_logger()
if not all(cmd_args.values()):
LOGGER.debug("Passed arguments:")
for arg in cmd_args:
if cmd_args[arg]:
LOGGER.debug(f"\t{arg}:\t{cmd_args[arg]}")
try:
print('Starting...')
LOGGER.info('Starting...')
while True:
try:
main()
main(**cmd_args)
except BotDeviceNotFound:
print("Restarting...")
LOGGER.info("Restarting...")
time.sleep(2)
except KeyboardInterrupt:
print("Exiting...")
LOGGER.error("Exiting...")

View File

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

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

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

View File

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

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()

93
op25Handler.py Normal file
View File

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

BIN
opus/libopus_aarcch64.so Normal file

Binary file not shown.

BIN
opus/libopus_armv7l.so Normal file

Binary file not shown.

View File

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

View File

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

0
templates/__init__.py Normal file
View File

View File

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