forked from iarv/meshing-around
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0d024d770 | ||
|
|
9b633502e6 | ||
|
|
ac1a007ba4 | ||
|
|
09cf6f585c | ||
|
|
916719f1c5 | ||
|
|
11a6dc3cf0 | ||
|
|
c160678e79 | ||
|
|
0c9fd919ab | ||
|
|
e17dc79896 | ||
|
|
06d6855d92 | ||
|
|
66f937a645 | ||
|
|
f4985b744a | ||
|
|
7ae6174f96 | ||
|
|
d44fdd4462 | ||
|
|
3dd6da4684 | ||
|
|
a229b57964 | ||
|
|
5e045b6447 | ||
|
|
1e328d4f4d | ||
|
|
879d141844 | ||
|
|
7daf8c4c33 | ||
|
|
3e6d1f5c6f | ||
|
|
32deea9e3b | ||
|
|
793fabcdb8 | ||
|
|
41efbc6189 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -8,7 +8,8 @@ config.ini
|
||||
venv/
|
||||
|
||||
# logs
|
||||
logs/*.log
|
||||
logs/
|
||||
install_notes.txt
|
||||
|
||||
# modified .service files
|
||||
etc/*.service
|
||||
@@ -18,3 +19,6 @@ __pycache__/
|
||||
|
||||
# rag data
|
||||
data/rag/*
|
||||
|
||||
# qrz db
|
||||
data/qrz.db
|
||||
|
||||
@@ -44,6 +44,7 @@ Welcome to the Mesh Bot project! This feature-rich bot is designed to enhance yo
|
||||
|
||||
### Fun and Games
|
||||
- **Built-in Games**: Enjoy games like DopeWars, Lemonade Stand, BlackJack, and VideoPoker.
|
||||
- **FCC ARRL QuizBot**: The exam question pool quiz-bot.
|
||||
- **Command-Based Gameplay**: Issue `games` to display help and start playing.
|
||||
|
||||
### Radio Frequency Monitoring
|
||||
@@ -132,6 +133,8 @@ The following settings determine how the bot responds. By default, the bot will
|
||||
respond_by_dm_only = True
|
||||
defaultChannel = 0
|
||||
ignoreDefaultChannel = False # ignoreDefaultChannel, the bot will ignore the default channel set above
|
||||
ignoreChannels = # ignoreChannels is a comma separated list of channels to ignore, e.g. 4,5
|
||||
cmdBang = False # require ! to be the first character in a command
|
||||
```
|
||||
|
||||
### Location Settings
|
||||
@@ -425,6 +428,7 @@ There is no direct support for MQTT in the code, however, reports from Discord a
|
||||
| `blackjack` | Plays Blackjack (Casino 21) | ✅ |
|
||||
| `dopewars` | Plays the classic drug trader game | ✅ |
|
||||
| `golfsim` | Plays a 9-hole Golf Simulator | ✅ |
|
||||
| `hamtest` | FCC/ARRL Quiz `hamtest general` or `hamtest extra` and `score` | ✅ |
|
||||
| `hangman` | Plays the classic word guess game | ✅ |
|
||||
| `joke` | Tells a joke | ✅ |
|
||||
| `lemonstand` | Plays the classic Lemonade Stand finance game | ✅ |
|
||||
@@ -447,6 +451,7 @@ I used ideas and snippets from other responder bots and want to call them out!
|
||||
- [Video Poker Terminal Game](https://github.com/devtronvarma/Video-Poker-Terminal-Game)
|
||||
- [Python Mastermind](https://github.com/pwdkramer/pythonMastermind/)
|
||||
- [Golf](https://github.com/danfriedman30/pythongame)
|
||||
- ARRL Question Pool Data from https://github.com/russolsen/ham_radio_question_pool
|
||||
|
||||
### Special Thanks
|
||||
- **xdep**: For the reporting tools.
|
||||
@@ -458,6 +463,7 @@ I used ideas and snippets from other responder bots and want to call them out!
|
||||
- **WH6GXZ nurse dude**: For bashing on installer
|
||||
- **Josh**: For more bashing on installer!
|
||||
- **dj505**: trying it on windows!
|
||||
- **mikecarper**: ideas, and testing. hamtest
|
||||
- **Cisien, bitflip, **Woof**, **propstg**, **Josh** and Hailo1999**: For testing and feature ideas on Discord and GitHub.
|
||||
- **Meshtastic Discord Community**: For tossing out ideas and testing code.
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@ autoPingInChannel = False
|
||||
defaultChannel = 0
|
||||
# ignoreDefaultChannel, the bot will ignore the default channel set above
|
||||
ignoreDefaultChannel = False
|
||||
# ignoreChannels is a comma separated list of channels to ignore, e.g. 4,5
|
||||
ignoreChannels =
|
||||
# require ! to be the first character in a command
|
||||
cmdBang = False
|
||||
|
||||
# motd is reset to this value on boot
|
||||
motd = Thanks for using MeshBOT! Have a good day!
|
||||
@@ -255,6 +259,7 @@ videopoker = True
|
||||
mastermind = True
|
||||
golfsim = True
|
||||
hangman = True
|
||||
hamtest = True
|
||||
|
||||
[messagingSettings]
|
||||
# delay in seconds for response to avoid message collision
|
||||
@@ -266,6 +271,6 @@ MESSAGE_CHUNK_SIZE = 160
|
||||
# Request Acknowledgement of message OTA
|
||||
wantAck = False
|
||||
# Max limit buffer for radio testing. 233 is hard limit 2.5+ firmware
|
||||
maxBuffer = 220
|
||||
maxBuffer = 200
|
||||
|
||||
|
||||
|
||||
7226
data/hamradio/extra.json
Normal file
7226
data/hamradio/extra.json
Normal file
File diff suppressed because it is too large
Load Diff
5126
data/hamradio/general.json
Normal file
5126
data/hamradio/general.json
Normal file
File diff suppressed because it is too large
Load Diff
4934
data/hamradio/technician.json
Normal file
4934
data/hamradio/technician.json
Normal file
File diff suppressed because it is too large
Load Diff
68
mesh_bot.py
68
mesh_bot.py
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/python3
|
||||
# Meshtastic Autoresponder MESH Bot
|
||||
# K7MHI Kelly Keeton 2024
|
||||
# K7MHI Kelly Keeton 2025
|
||||
|
||||
try:
|
||||
from pubsub import pub
|
||||
@@ -15,7 +15,7 @@ from modules.log import *
|
||||
from modules.system import *
|
||||
|
||||
# list of commands to remove from the default list for DM only
|
||||
restrictedCommands = ["blackjack", "videopoker", "dopewars", "lemonstand", "golfsim", "mastermind", "hangman"]
|
||||
restrictedCommands = ["blackjack", "videopoker", "dopewars", "lemonstand", "golfsim", "mastermind", "hangman", "hamtest"]
|
||||
restrictedResponse = "🤖only available in a Direct Message📵" # "" for none
|
||||
|
||||
# Global Variables
|
||||
@@ -57,6 +57,7 @@ def auto_response(message, snr, rssi, hop, pkiStatus, message_from_id, channel_n
|
||||
"games": lambda: gamesCmdList,
|
||||
"globalthermonuclearwar": lambda: handle_gTnW(),
|
||||
"golfsim": lambda: handleGolf(message, message_from_id, deviceID),
|
||||
"hamtest": lambda: handleHamtest(message, message_from_id, deviceID),
|
||||
"hangman": lambda: handleHangman(message, message_from_id, deviceID),
|
||||
"hfcond": hf_band_conditions,
|
||||
"history": lambda: handle_history(message, message_from_id, deviceID, isDM),
|
||||
@@ -114,6 +115,10 @@ def auto_response(message, snr, rssi, hop, pkiStatus, message_from_id, channel_n
|
||||
# check the message for commands words list, processed after system.messageTrap
|
||||
for key in command_handler:
|
||||
word = message_lower.split(' ')
|
||||
if cmdBang:
|
||||
# strip the !
|
||||
if word[0].startswith("!"):
|
||||
word[0] = word[0][1:]
|
||||
if key in word:
|
||||
# append all the commands found in the message to the cmds list
|
||||
cmds.append({'cmd': key, 'index': message_lower.index(key)})
|
||||
@@ -687,6 +692,41 @@ def handleHangman(message, nodeID, deviceID):
|
||||
time.sleep(responseDelay + 1)
|
||||
return msg
|
||||
|
||||
def handleHamtest(message, nodeID, deviceID):
|
||||
global hamtestTracker
|
||||
index = 0
|
||||
msg = ''
|
||||
response = message.split(' ')
|
||||
for i in range(len(hamtestTracker)):
|
||||
if hamtestTracker[i]['nodeID'] == nodeID:
|
||||
hamtestTracker[i]["last_played"] = time.time()
|
||||
index = i+1
|
||||
break
|
||||
|
||||
if not index:
|
||||
hamtestTracker.append({"nodeID": nodeID,"last_played": time.time()})
|
||||
|
||||
if "end" in response[0].lower():
|
||||
msg = hamtest.endGame(nodeID)
|
||||
elif "score" in response[0].lower():
|
||||
msg = hamtest.getScore(nodeID)
|
||||
|
||||
if "hamtest" in response[0].lower():
|
||||
if len(response) > 1:
|
||||
if "gen" in response[1].lower():
|
||||
msg = hamtest.newGame(nodeID, 'general')
|
||||
elif "ex" in response[1].lower():
|
||||
msg = hamtest.newGame(nodeID, 'extra')
|
||||
else:
|
||||
msg = hamtest.newGame(nodeID, 'technician')
|
||||
|
||||
# if the message is an answer A B C or D upper or lower case
|
||||
if response[0].upper() in ['A', 'B', 'C', 'D']:
|
||||
msg = hamtest.answer(nodeID, response[0])
|
||||
|
||||
time.sleep(responseDelay + 1)
|
||||
return msg
|
||||
|
||||
def handle_riverFlow(message, message_from_id, deviceID):
|
||||
location = get_node_location(message_from_id, deviceID)
|
||||
userRiver = message.lower()
|
||||
@@ -815,6 +855,8 @@ def sysinfo(message, message_from_id, deviceID):
|
||||
return "sysinfo command returns system information."
|
||||
else:
|
||||
if enable_runShellCmd and file_monitor_enabled:
|
||||
# get the system information from the shell script
|
||||
# this is an example of how to run a shell script and return the data
|
||||
shellData = call_external_script(None, "script/sysEnv.sh").rstrip()
|
||||
return get_sysinfo(message_from_id, deviceID) + "\n" + shellData
|
||||
else:
|
||||
@@ -1012,6 +1054,7 @@ def checkPlayingGame(message_from_id, message_string, rxNode, channel_number):
|
||||
(mindTracker, "MasterMind", handleMmind) if 'mindTracker' in globals() else None,
|
||||
(golfTracker, "GolfSim", handleGolf) if 'golfTracker' in globals() else None,
|
||||
(hangmanTracker, "Hangman", handleHangman) if 'hangmanTracker' in globals() else None,
|
||||
(hamtestTracker, "HamTest", handleHamtest) if 'hamtestTracker' in globals() else None,
|
||||
]
|
||||
trackers = [tracker for tracker in trackers if tracker is not None]
|
||||
|
||||
@@ -1036,6 +1079,7 @@ def onReceive(packet, interface):
|
||||
replyIDset = False
|
||||
emojiSeen = False
|
||||
isDM = False
|
||||
playingGame = False
|
||||
|
||||
if DEBUGpacket:
|
||||
# Debug print the interface object
|
||||
@@ -1228,11 +1272,17 @@ def onReceive(packet, interface):
|
||||
else:
|
||||
# message is on a channel
|
||||
if messageTrap(message_string):
|
||||
# message is for us to respond to
|
||||
# message is for us to respond to, or is it...
|
||||
if ignoreDefaultChannel and channel_number == publicChannel:
|
||||
logger.debug(f"System: ignoreDefaultChannel CMD:{message_string} From: {get_name_from_number(message_from_id, 'short', rxNode)}")
|
||||
logger.debug(f"System: Ignoring CMD:{message_string} From: {get_name_from_number(message_from_id, 'short', rxNode)} Default Channel:{channel_number}")
|
||||
elif str(message_from_id) in bbs_ban_list:
|
||||
logger.debug(f"System: Ignoring CMD:{message_string} From: {get_name_from_number(message_from_id, 'short', rxNode)} Cantankerous Node")
|
||||
elif str(channel_number) in ignoreChannels:
|
||||
logger.debug(f"System: Ignoring CMD:{message_string} From: {get_name_from_number(message_from_id, 'short', rxNode)} Ignored Channel:{channel_number}")
|
||||
elif cmdBang and not message_string.startswith("!"):
|
||||
logger.debug(f"System: Ignoring CMD:{message_string} From: {get_name_from_number(message_from_id, 'short', rxNode)} Didnt sound like they meant it")
|
||||
else:
|
||||
# message is for bot to respond to
|
||||
# message is for bot to respond to, seriously this time..
|
||||
logger.info(f"Device:{rxNode} Channel:{channel_number} " + CustomFormatter.green + "ReceivedChannel: " + CustomFormatter.white + f"{message_string} " + CustomFormatter.purple +\
|
||||
"From: " + CustomFormatter.white + f"{get_name_from_number(message_from_id, 'long', rxNode)}")
|
||||
if useDMForResponse:
|
||||
@@ -1375,6 +1425,8 @@ async def start_rx():
|
||||
logger.debug(f"System: QRZ Welcome/Hello Enabled")
|
||||
if checklist_enabled:
|
||||
logger.debug(f"System: CheckList Module Enabled")
|
||||
if ignoreChannels != []:
|
||||
logger.debug(f"System: Ignoring Channels: {ignoreChannels}")
|
||||
if enableSMTP:
|
||||
if enableImap:
|
||||
logger.debug(f"System: SMTP Email Alerting Enabled using IMAP")
|
||||
@@ -1392,6 +1444,12 @@ async def start_rx():
|
||||
|
||||
# Send WX every Morning at 08:00 using handle_wxc function to channel 2 on device 1
|
||||
#schedule.every().day.at("08:00").do(lambda: send_message(handle_wxc(0, 1, 'wx'), 2, 0, 1))
|
||||
|
||||
# Send Weather Channel Notice Wed. Noon on channel 2, device 1
|
||||
#schedule.every().wednesday.at("12:00").do(lambda: send_message("Weather alerts available on 'Alerts' channel with default 'AQ==' key.", 2, 0, 1))
|
||||
|
||||
# Send config URL for Medium Fast Network Use every other day at 10:00 to default channel 2 on device 1
|
||||
#schedule.every(2).days.at("10:00").do(lambda: send_message("Join us on Medium Fast https://meshtastic.org/e/#CgcSAQE6AggNEg4IARAEOAFAA0gBUB5oAQ", 2, 0, 1))
|
||||
|
||||
# Send a Net Starting Now Message Every Wednesday at 19:00 using send_message function to channel 2 on device 1
|
||||
#schedule.every().wednesday.at("19:00").do(lambda: send_message("Net Starting Now", 2, 0, 1))
|
||||
|
||||
@@ -17,12 +17,12 @@ def read_file(file_monitor_file_path, random_line_only=False):
|
||||
return "🐝buzz 💐buzz buzz🍯"
|
||||
if random_line_only:
|
||||
# read a random line from the file
|
||||
with open(file_monitor_file_path, 'r') as f:
|
||||
with open(file_monitor_file_path, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
return random.choice(lines)
|
||||
else:
|
||||
# read the whole file
|
||||
with open(file_monitor_file_path, 'r') as f:
|
||||
with open(file_monitor_file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
return content
|
||||
except Exception as e:
|
||||
@@ -37,7 +37,7 @@ def read_news():
|
||||
def write_news(content, append=False):
|
||||
# write the news file on demand
|
||||
try:
|
||||
with open(news_file_path, 'a' if append else 'w') as f:
|
||||
with open(news_file_path, 'a' if append else 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
logger.info(f"FileMon: Updated {news_file_path}")
|
||||
return True
|
||||
@@ -76,7 +76,7 @@ def call_external_script(message, script="script/runShell.sh"):
|
||||
logger.warning(f"FileMon: Script not found: {script_path}")
|
||||
return "sorry I can't do that"
|
||||
|
||||
output = os.popen(f"bash {script_path} {message}").read()
|
||||
output = os.popen(f"bash {script_path} {message}", encoding='utf-8').read()
|
||||
return output
|
||||
except Exception as e:
|
||||
logger.warning(f"FileMon: Error calling external script: {e}")
|
||||
|
||||
142
modules/games/hamtest.py
Normal file
142
modules/games/hamtest.py
Normal file
@@ -0,0 +1,142 @@
|
||||
# hamradio test module for meshbot DE K7MHI 2025
|
||||
# depends on the JSON question data files from https://github.com/russolsen/ham_radio_question_pool
|
||||
|
||||
# data files which are expected to be in ../../data/hamradio/ similar to the following:
|
||||
# https://raw.githubusercontent.com/russolsen/ham_radio_question_pool/refs/heads/master/technician-2022-2026/technician.json
|
||||
# https://raw.githubusercontent.com/russolsen/ham_radio_question_pool/refs/heads/master/general-2023-2027/general.json
|
||||
# https://raw.githubusercontent.com/russolsen/ham_radio_question_pool/refs/heads/master/extra-2024-2028/extra.json
|
||||
|
||||
import json
|
||||
import random
|
||||
import os
|
||||
from modules.log import *
|
||||
|
||||
class HamTest:
|
||||
def __init__(self):
|
||||
self.questions = {}
|
||||
self.load_questions()
|
||||
self.game = {}
|
||||
|
||||
def load_questions(self):
|
||||
for level in ['technician', 'general', 'extra']:
|
||||
try:
|
||||
with open(f'{os.path.dirname(__file__)}/../../data/hamradio/{level}.json', encoding='utf-8') as f:
|
||||
self.questions[level] = json.load(f)
|
||||
except FileNotFoundError:
|
||||
logger.error(f"File not found: ../../data/hamradio/{level}.json")
|
||||
self.questions[level] = []
|
||||
except json.JSONDecodeError:
|
||||
logger.error(f"Error decoding JSON from file: ../../data/hamradio/{level}.json")
|
||||
self.questions[level] = []
|
||||
|
||||
def newGame(self, id, level='technician'):
|
||||
msg = f"📻New {level} quiz started, 'end' to exit."
|
||||
if id in self.game:
|
||||
level = self.game[id]['level']
|
||||
self.game[id] = {
|
||||
'level': level,
|
||||
'score': 0,
|
||||
'total': 0,
|
||||
'errors': [],
|
||||
'qId': None,
|
||||
'question': None,
|
||||
'answers': None,
|
||||
'correct': None
|
||||
}
|
||||
# set the pool needed for the game
|
||||
if self.game[id]['level'] == 'extra':
|
||||
self.game[id]['total'] = 50
|
||||
else:
|
||||
self.game[id]['total'] = 35
|
||||
|
||||
# randomize the questions
|
||||
random.shuffle(self.questions[level])
|
||||
|
||||
msg += f"\n{self.nextQuestion(id)}"
|
||||
return msg
|
||||
|
||||
def nextQuestion(self, id):
|
||||
level = self.game[id]['level']
|
||||
# if question has the word figure in it, skip it
|
||||
question = random.choice(self.questions[level])
|
||||
while 'figure' in question['question'].lower():
|
||||
question = random.choice(self.questions[level])
|
||||
|
||||
self.game[id]['question'] = question['question']
|
||||
self.game[id]['answers'] = question['answers']
|
||||
self.game[id]['correct'] = question['correct']
|
||||
self.game[id]['qId'] = question['id']
|
||||
self.game[id]['total'] -= 1
|
||||
|
||||
if self.game[id]['total'] == 0:
|
||||
return self.endGame(id)
|
||||
|
||||
# ask the question and return answers in A, B, C, D format
|
||||
msg = f"{self.game[id]['question']}\n"
|
||||
for i, answer in enumerate(self.game[id]['answers']):
|
||||
msg += f"{chr(65+i)}. {answer}\n"
|
||||
return msg
|
||||
|
||||
def answer(self, id, answer):
|
||||
if id not in self.game:
|
||||
return "No game in progress"
|
||||
if self.game[id]['correct'] == ord(answer.upper()) - 65:
|
||||
self.game[id]['score'] += 1
|
||||
return f"Correct👍\n" + self.nextQuestion(id)
|
||||
else:
|
||||
# record the section of the question for study aid
|
||||
section = self.game[id]['qId'][:3]
|
||||
self.game[id]['errors'].append(section)
|
||||
# provide the correct answer
|
||||
answer = [self.game[id]['correct']]
|
||||
return f"Wrong.⛔️ Correct is {chr(65+self.game[id]['correct'])}\n" + self.nextQuestion(id)
|
||||
|
||||
def getScore(self, id):
|
||||
if id not in self.game:
|
||||
return "No game in progress"
|
||||
score = self.game[id]['score']
|
||||
total = self.game[id]['total']
|
||||
level = self.game[id]['level']
|
||||
if self.game[id]['errors']:
|
||||
areaofstudy = max(set(self.game[id]['errors']), key = self.game[id]['errors'].count)
|
||||
else:
|
||||
areaofstudy = "None"
|
||||
|
||||
if level == 'extra':
|
||||
pool = 50
|
||||
else:
|
||||
pool = 35
|
||||
|
||||
return f"Score: {score}/{pool}\nQuestions left: {total}\nArea of study: {areaofstudy}"
|
||||
|
||||
def endGame(self, id):
|
||||
if id not in self.game:
|
||||
return "No game in progress"
|
||||
|
||||
score = self.game[id]['score']
|
||||
level = self.game[id]['level']
|
||||
|
||||
if level == 'extra':
|
||||
# passing score for extra is 37 out of 50
|
||||
passing = 37
|
||||
else:
|
||||
# passing score for technician and general is 26 out of 35
|
||||
passing = 26
|
||||
|
||||
if score >= passing:
|
||||
msg = f"Game over. Score: {score} 73! 🎉You passed the {level} exam."
|
||||
else:
|
||||
# find the most common section of the questions missed
|
||||
if self.game[id]['errors']:
|
||||
areaofstudy = max(set(self.game[id]['errors']), key = self.game[id]['errors'].count)
|
||||
else:
|
||||
areaofstudy = "None"
|
||||
msg = f"Game over. Score: {score} 73! 😿You did not pass the {level} exam. \nYou may want to study {areaofstudy}."
|
||||
|
||||
# remove the game[id] from the list
|
||||
del self.game[id]
|
||||
return msg
|
||||
|
||||
hamtestTracker = []
|
||||
hamtest = HamTest()
|
||||
|
||||
@@ -513,7 +513,6 @@ def getIpawsAlert(lat=0, lon=0, shortAlerts = False):
|
||||
if info.getElementsByTagName("description") and info.getElementsByTagName("description")[0].childNodes:
|
||||
description = info.getElementsByTagName("description")[0].childNodes[0].nodeValue
|
||||
else:
|
||||
logger.debug(f"System: report this to discord - iPAWS No description for alert: {headline}")
|
||||
description = headline
|
||||
|
||||
area_table = info.getElementsByTagName("area")[0]
|
||||
|
||||
@@ -26,7 +26,6 @@ max_retry_count2 = 4 # max retry count for interface 2
|
||||
retry_int1 = False
|
||||
retry_int2 = False
|
||||
wiki_return_limit = 3 # limit the number of sentences returned off the first paragraph first hit
|
||||
playingGame = False
|
||||
GAMEDELAY = 28800 # 8 hours in seconds for game mode holdoff
|
||||
cmdHistory = [] # list to hold the last commands
|
||||
seenNodes = [] # list to hold the last seen nodes
|
||||
@@ -196,7 +195,9 @@ try:
|
||||
# general
|
||||
useDMForResponse = config['general'].getboolean('respond_by_dm_only', True)
|
||||
publicChannel = config['general'].getint('defaultChannel', 0) # the meshtastic public channel
|
||||
ignoreChannels = config['general'].get('ignoreChannels', '').split(',') # ignore these channels
|
||||
ignoreDefaultChannel = config['general'].getboolean('ignoreDefaultChannel', False)
|
||||
cmdBang = config['general'].getboolean('cmdBang', False) # default off
|
||||
zuluTime = config['general'].getboolean('zuluTime', False) # aka 24 hour time
|
||||
log_messages_to_file = config['general'].getboolean('LogMessagesToFile', False) # default off
|
||||
log_backup_count = config['general'].getint('LogBackupCount', 32) # default 32 days
|
||||
@@ -332,6 +333,7 @@ try:
|
||||
mastermind_enabled = config['games'].getboolean('mastermind', True)
|
||||
golfSim_enabled = config['games'].getboolean('golfSim', True)
|
||||
hangman_enabled = config['games'].getboolean('hangman', True)
|
||||
hamtest_enabled = config['games'].getboolean('hamtest', True)
|
||||
|
||||
# messaging settings
|
||||
responseDelay = config['messagingSettings'].getfloat('responseDelay', 0.7) # default 0.7
|
||||
|
||||
@@ -90,6 +90,13 @@ if location_enabled:
|
||||
else:
|
||||
# NOAA only features
|
||||
help_message = help_message + ", wxa, tide, ealert"
|
||||
|
||||
# NOAA alerts needs location module
|
||||
if wxAlertBroadcastEnabled or emergencyAlertBrodcastEnabled:
|
||||
from modules.locationdata import * # from the spudgunman/meshing-around repo
|
||||
# limited subset, this should be done better but eh..
|
||||
trap_list = trap_list + ("wx", "wxc", "wxa", "wxalert", "ea", "ealert")
|
||||
help_message = help_message + ", wxalert, ealert"
|
||||
|
||||
# BBS Configuration
|
||||
if bbs_enabled:
|
||||
@@ -157,6 +164,11 @@ if hangman_enabled:
|
||||
trap_list = trap_list + ("hangman",)
|
||||
games_enabled = True
|
||||
|
||||
if hamtest_enabled:
|
||||
from modules.games.hamtest import * # from the spudgunman/meshing-around repo
|
||||
trap_list = trap_list + ("hamtest",)
|
||||
games_enabled = True
|
||||
|
||||
# Games Configuration
|
||||
if games_enabled is True:
|
||||
help_message = help_message + ", games"
|
||||
@@ -179,6 +191,8 @@ if games_enabled is True:
|
||||
gamesCmdList += "golfSim, "
|
||||
if hangman_enabled:
|
||||
gamesCmdList += "hangman, "
|
||||
if hamtest_enabled:
|
||||
gamesCmdList += "hamTest, "
|
||||
gamesCmdList = gamesCmdList[:-2] # remove the last comma
|
||||
else:
|
||||
gamesCmdList = ""
|
||||
@@ -652,6 +666,8 @@ def messageTrap(msg):
|
||||
# if word in message is in the trap list, return True
|
||||
if t.lower() == m.lower():
|
||||
return True
|
||||
if cmdBang and m.startswith("!"):
|
||||
return True
|
||||
# if no trap words found, run a search for near misses like ping? or cmd?
|
||||
for m in message_list:
|
||||
for t in range(len(trap_list)):
|
||||
@@ -713,6 +729,7 @@ def handleAlertBroadcast(deviceID=1):
|
||||
alertDe = NO_ALERTS
|
||||
alertFema = NO_ALERTS
|
||||
wxAlert = NO_ALERTS
|
||||
alertWx = False
|
||||
# only allow API call every 20 minutes
|
||||
# the watchdog will call this function 3 times, seeing possible throttling on the API
|
||||
clock = datetime.now()
|
||||
@@ -736,7 +753,7 @@ def handleAlertBroadcast(deviceID=1):
|
||||
|
||||
# format alert
|
||||
if alertWx:
|
||||
wxAlert = f"🚨 {alertWx[1]} EAS WX ALERT: {alertWx[0]}"
|
||||
wxAlert = f"🚨 {alertWx[1]} EAS-WX ALERT: {alertWx[0]}"
|
||||
else:
|
||||
wxAlert = False
|
||||
|
||||
|
||||
Reference in New Issue
Block a user