forked from iarv/meshing-around
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb051f4225 | ||
|
|
61c5be1a08 | ||
|
|
bc7d47b2a7 | ||
|
|
24bcd5cbf9 | ||
|
|
8407512b0f | ||
|
|
6f4e8615a3 | ||
|
|
314d36e0dc | ||
|
|
27accb0d4a | ||
|
|
fd84505ad1 | ||
|
|
8f75b13c4d | ||
|
|
31d05f8aa7 | ||
|
|
cdfe4bb844 | ||
|
|
f30e9cd8b8 | ||
|
|
931bc7b9f7 | ||
|
|
049c0d5ad7 | ||
|
|
a5f1e452e4 | ||
|
|
d89cd8598d | ||
|
|
d4e3ea60e3 | ||
|
|
b98bc8429a | ||
|
|
4bb7c9296a | ||
|
|
bb7b5b1c90 | ||
|
|
c400f6f998 | ||
|
|
fce6c0b2e4 | ||
|
|
0d0288ba18 | ||
|
|
c25d7bc8de | ||
|
|
d42fa72d54 | ||
|
|
bc7176c1cf | ||
|
|
15d454f93a |
70
README.md
70
README.md
@@ -11,8 +11,8 @@ Welcome to the Mesh Bot project! This feature-rich bot is designed to enhance yo
|
||||
- **Customizable Triggers**: Monitor group channels for specific keywords and set custom responses.
|
||||
|
||||
### Network Tools
|
||||
- **Enhance and build local mesh**: Ping allow for message delivery testing with more realistic packets vs. telemetry
|
||||
- **Test node hardware**: `test` will send incremental data into the radio buffer for overall length of message testing
|
||||
- **Build, Test Local Mesh**: Ping allow for message delivery testing with more realistic packets vs. telemetry
|
||||
- **Test Node Hardware**: `test` will send incremental sized data into the radio buffer for overall length of message testing
|
||||
|
||||
### Dual Radio/Node Support
|
||||
- **Simultaneous Monitoring**: Monitor two networks at the same time.
|
||||
@@ -47,6 +47,7 @@ Welcome to the Mesh Bot project! This feature-rich bot is designed to enhance yo
|
||||
|
||||
### File Monitor Alerts
|
||||
- **File Mon**: Monitor a flat/text file for changes, brodcast the contents of the message to mesh channel.
|
||||
- **News File**: on request of news the contents of the file is returned.
|
||||
|
||||
### Data Reporting
|
||||
- **HTML Generator**: Visualize bot traffic and data flows with a built-in HTML generator for [data reporting](logs/README.md).
|
||||
@@ -162,7 +163,7 @@ lheardCmdIgnoreNodes = # command history ignore list ex: 2813308004,4258675309
|
||||
|
||||
### Sentry Settings
|
||||
|
||||
Sentry Bot detects anyone coming close to the bot-node.
|
||||
Sentry Bot detects anyone coming close to the bot-node. uses the Location Lat/Lon value.
|
||||
|
||||
```ini
|
||||
SentryEnabled = True # detect anyone close to the bot
|
||||
@@ -181,20 +182,6 @@ enabled = True
|
||||
repeater_channels = [2, 3]
|
||||
```
|
||||
|
||||
### Radio Monitoring
|
||||
A module allowing a Hamlib compatible radio to connect to the bot. When functioning, it will message the configured channel with a message of in use. **Requires hamlib/rigctld to be running as a service.**
|
||||
|
||||
```ini
|
||||
[radioMon]
|
||||
enabled = False
|
||||
rigControlServerAddress = localhost:4532
|
||||
sigWatchBroadcastCh = 2 # channel to broadcast to can be 2,3
|
||||
signalDetectionThreshold = -10 # minimum SNR as reported by radio via hamlib
|
||||
signalHoldTime = 10 # hold time for high SNR
|
||||
signalCooldown = 5 # the following are combined to reset the monitor
|
||||
signalCycleLimit = 5
|
||||
```
|
||||
|
||||
### Ollama (LLM/AI) Settings
|
||||
For Ollama to work, the command line `ollama run 'model'` needs to work properly. Ensure you have enough RAM and your GPU is working as expected. The default model for this project is set to `gemma2:2b`. Ollama can be remote [Ollama Server](https://github.com/ollama/ollama/blob/main/docs/faq.md#how-do-i-configure-ollama-server)
|
||||
|
||||
@@ -214,13 +201,30 @@ llmContext_fromGoogle = True # enable context from google search results helps w
|
||||
googleSearchResults = 3 # number of google search results to include in the context more results = more compute time
|
||||
```
|
||||
|
||||
### Radio Monitoring
|
||||
A module allowing a Hamlib compatible radio to connect to the bot. When functioning, it will message the configured channel with a message of in use. **Requires hamlib/rigctld to be running as a service.**
|
||||
|
||||
```ini
|
||||
[radioMon]
|
||||
enabled = False
|
||||
rigControlServerAddress = localhost:4532
|
||||
sigWatchBroadcastCh = 2 # channel to broadcast to can be 2,3
|
||||
signalDetectionThreshold = -10 # minimum SNR as reported by radio via hamlib
|
||||
signalHoldTime = 10 # hold time for high SNR
|
||||
signalCooldown = 5 # the following are combined to reset the monitor
|
||||
signalCycleLimit = 5
|
||||
```
|
||||
|
||||
### File Monitoring
|
||||
Some dev notes for ideas of use
|
||||
|
||||
```ini
|
||||
[fileMon]
|
||||
enabled = True
|
||||
filemon_enabled = True
|
||||
file_path = alert.txt
|
||||
broadcastCh = 2,4
|
||||
enable_read_news = False
|
||||
news_file_path = news.txt
|
||||
```
|
||||
#### NOAA EAS
|
||||
To Alert on Mesh with the NOAA EAS API you can set the channels and enable, checks every 30min
|
||||
@@ -233,11 +237,14 @@ wxAlertBroadcastCh = 2,4
|
||||
```
|
||||
|
||||
To Monitor EAS with no internet connection see the following notes
|
||||
- [samedec](https://crates.io/crates/samedec) rust decoder much like multimon-ng
|
||||
- [sameold](https://crates.io/crates/sameold) rust SAME message translator much like EAS2Text and dsame3
|
||||
|
||||
no examples yet for these tools
|
||||
|
||||
- [EAS2Text](https://github.com/A-c0rN/EAS2Text)
|
||||
- depends on [multimon-ng](https://github.com/EliasOenal/multimon-ng) or [direwolf](https://github.com/wb2osz/direwolf)
|
||||
- [dsame3](https://github.com/jamieden/dsame3) // recomend not using anything but the sample file for basic work
|
||||
- this can be used with a rtl-sdr to capture alerts
|
||||
- depends on [multimon-ng](https://github.com/EliasOenal/multimon-ng), [direwolf](https://github.com/wb2osz/direwolf), [samedec](https://crates.io/crates/samedec) rust decoder much like multimon-ng
|
||||
- [dsame3](https://github.com/jamieden/dsame3)
|
||||
- has a sample .ogg file for testing alerts
|
||||
|
||||
The following example shell command can pipe the data using [etc/eas_alert_parser.py](etc/eas_alert_parser.py) to alert.txt
|
||||
@@ -249,8 +256,17 @@ The following example shell command will pipe rtl_sdr to alert.txt
|
||||
rtl_fm -f 162425000 -s 22050 | multimon-ng -t raw -a EAS /dev/stdin | python eas_alert_parser.py
|
||||
```
|
||||
|
||||
#### Newspaper on mesh
|
||||
a newspaper could be built by external scripts. could use Ollama to compile text via news web pages and write news.txt
|
||||
|
||||
### Scheduler
|
||||
The Scheduler is enabled in the `settings.py` by setting `scheduler_enabled = True`. The actions and settings are via code only at this time. See mesh_bot.py around line [1050](https://github.com/SpudGunMan/meshing-around/blob/e94581936530c76ea43500eebb43f32ba7ed5e19/mesh_bot.py#L1050) to edit the schedule. See [schedule documentation](https://schedule.readthedocs.io/en/stable/) for more.
|
||||
In the config.ini enable the module
|
||||
```ini
|
||||
[scheduler]
|
||||
# enable or disable the scheduler module
|
||||
enabled = True
|
||||
```
|
||||
The actions are via code only at this time. See mesh_bot.py around line [1050](https://github.com/SpudGunMan/meshing-around/blob/e94581936530c76ea43500eebb43f32ba7ed5e19/mesh_bot.py#L1050) to edit the schedule. See [schedule documentation](https://schedule.readthedocs.io/en/stable/) for more. Recomend to backup changes so they dont get lost.
|
||||
|
||||
```python
|
||||
#Send WX every Morning at 08:00 using handle_wxc function to channel 2 on device 1
|
||||
@@ -261,11 +277,16 @@ schedule.every().wednesday.at("19:00").do(lambda: send_message("Net Starting Now
|
||||
```
|
||||
|
||||
#### BBS Link
|
||||
The scheduler also handles the BBL Link Brodcast message
|
||||
The scheduler also handles the BBL Link Brodcast message, this would be an esxample of a mesh-admin channel on 8 being used to pass BBS post traffic between two bots as the initator, one direction pull.
|
||||
```python
|
||||
# Send bbslink looking for peers every other day at 10:00 using send_message function to channel 8 on device 1
|
||||
schedule.every(2).days.at("10:00").do(lambda: send_message("bbslink MeshBot looking for peers", 8, 0, 1))
|
||||
```
|
||||
```ini
|
||||
bbslink_enabled = True
|
||||
# list of whitelisted nodes numbers ex: 2813308004,4258675309 empty list allows all
|
||||
bbslink_whitelist =
|
||||
```
|
||||
|
||||
### MQTT Notes
|
||||
There is no direct support for MQTT in the code, however, reports from Discord are that using [meshtasticd](https://meshtastic.org/docs/hardware/devices/linux-native-hardware/) with no radio and attaching the bot to the software node, which is MQTT-linked, allows routing. There also seems to be a quicker way to enable MQTT by having your bot node with the enabled [serial](https://meshtastic.org/docs/configuration/module/serial/) module with echo enabled and MQTT uplink and downlink. These two methods have been mentioned as allowing MQTT routing for the project.
|
||||
@@ -319,7 +340,7 @@ sudo apt-get install fonts-noto-color-emoji
|
||||
| Command | Description | ✅ Works Off-Grid |
|
||||
|---------|-------------|-
|
||||
| `ping`, `ack` | Return data for signal. Example: `ping 15 #DrivingI5` (activates auto-ping every 20 seconds for count 15) | ✅ |
|
||||
| `test` | Returns like ping but also can be used to test the limits of data buffers `test 4` sends data to the maxBuffer limit (default 225) | ✅ |
|
||||
| `test` | Returns like ping but also can be used to test the limits of data buffers `test 4` sends data to the maxBuffer limit (default 220) | ✅ |
|
||||
| `whereami` | Returns the address of the sender's location if known |
|
||||
| `whoami` | Returns details of the node asking, also returned when position exchanged 📍 | ✅ |
|
||||
| `motd` | Displays the message of the day or sets it. Example: `motd $New Message Of the day` | ✅ |
|
||||
@@ -355,6 +376,7 @@ sudo apt-get install fonts-noto-color-emoji
|
||||
| `wiki:` | Searches Wikipedia and returns the first few sentences of the first result if a match. Example: `wiki: lora radio` |
|
||||
| `askai` and `ask:` | Ask Ollama LLM AI for a response. Example: `askai what temp do I cook chicken` | ✅ |
|
||||
| `messages` | Replays the last messages heard, like Store and Forward | ✅ |
|
||||
| `readnews` | returns the contents of a file (news.txt, by default) via the chunker on air | ✅ |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -94,6 +94,10 @@ enabled = True
|
||||
bbs_ban_list =
|
||||
# list of admin nodes numbers ex: 2813308004,4258675309
|
||||
bbs_admin_list =
|
||||
# enable bbs synchronization with other nodes
|
||||
bbslink_enabled = False
|
||||
# list of whitelisted nodes numbers ex: 2813308004,4258675309 empty list allows all
|
||||
bbslink_whitelist =
|
||||
|
||||
# location module
|
||||
[location]
|
||||
@@ -122,7 +126,11 @@ enabled = False
|
||||
# and rebroadcasted on the same channel on the other device/node/interface
|
||||
# with great power comes great responsibility, danger could be lurking in use of this feature
|
||||
# if you have the two nodes on the same radio configurations, you could create a feedback loop
|
||||
repeater_channels =
|
||||
repeater_channels =
|
||||
|
||||
[scheduler]
|
||||
# enable or disable the scheduler module
|
||||
enabled = False
|
||||
|
||||
[radioMon]
|
||||
# using Hamlib rig control will monitor and alert on channel use
|
||||
@@ -139,9 +147,11 @@ signalCooldown = 5
|
||||
signalCycleLimit = 5
|
||||
|
||||
[fileMon]
|
||||
enabled = False
|
||||
filemon_enabled = False
|
||||
file_path = alert.txt
|
||||
broadcastCh = 2
|
||||
enable_read_news = False
|
||||
news_file_path = news.txt
|
||||
|
||||
[messagingSettings]
|
||||
# delay in seconds for response to avoid message collision
|
||||
|
||||
@@ -9,32 +9,40 @@ from EAS2Text import EAS2Text
|
||||
buff=[] # store messages for writing
|
||||
seen=set()
|
||||
pattern = re.compile(r'ZCZC.*?NWS-')
|
||||
|
||||
# alternate regex for parsing multimon-ng output
|
||||
#reg = r"^.*?(NNNN|ZCZC)(?:-([A-Za-z0-9]{3})-([A-Za-z0-9]{3})-((?:-?[0-9]{6})+)\+([0-9]{4})-([0-9]{7})-(.{8})-)?.*?$"
|
||||
#prog = re.compile(reg, re.MULTILINE)
|
||||
#groups = prog.match(sameData).groups()
|
||||
|
||||
while True:
|
||||
try:
|
||||
# Handle piped input
|
||||
line=input().strip()
|
||||
inp=input().strip()
|
||||
except EOFError:
|
||||
break
|
||||
# only want EAS lines
|
||||
if line.startswith("EAS:") or line.startswith("EAS (part):"):
|
||||
content=line.split(maxsplit=1)[1]
|
||||
if content=="NNNN": # end of EAS message
|
||||
# write if we have something
|
||||
if buff:
|
||||
print("writing")
|
||||
with open("alert.txt","w") as fh:
|
||||
fh.write('\n'.join(buff))
|
||||
# prepare for new data
|
||||
buff.clear()
|
||||
seen.clear()
|
||||
elif content in seen:
|
||||
# don't need repeats'
|
||||
continue
|
||||
else:
|
||||
# check for national weather service
|
||||
match=pattern.search(content)
|
||||
if match:
|
||||
seen.add(content)
|
||||
msg=EAS2Text(content).EASText
|
||||
print("got message", msg)
|
||||
buff.append(msg)
|
||||
# potentially take multiple lines in one buffered input
|
||||
for line in inp.splitlines():
|
||||
# only want EAS lines
|
||||
if line.startswith("EAS:") or line.startswith("EAS (part):"):
|
||||
content=line.split(":", maxsplit=1)[1].strip()
|
||||
if content=="NNNN": # end of EAS message
|
||||
# write if we have something
|
||||
if buff:
|
||||
print("writing")
|
||||
with open("alert.txt","w") as fh:
|
||||
fh.write('\n'.join(buff))
|
||||
# prepare for new data
|
||||
buff.clear()
|
||||
seen.clear()
|
||||
elif content in seen:
|
||||
# don't need repeats
|
||||
continue
|
||||
else:
|
||||
# check for national weather service
|
||||
match=pattern.search(content)
|
||||
if match:
|
||||
seen.add(content)
|
||||
msg=EAS2Text(content).EASText
|
||||
print("got message", msg)
|
||||
buff.append(msg)
|
||||
@@ -57,7 +57,7 @@ def parse_log_file(file_path):
|
||||
if multiLogReader:
|
||||
# set file_path to the cwd of the default project ../log
|
||||
log_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'logs')
|
||||
log_files = glob.glob(os.path.join(log_dir, 'meshbot*.log'))
|
||||
log_files = glob.glob(os.path.join(log_dir, 'meshbot.log.*'))
|
||||
print(f"Checking log files: {log_files}")
|
||||
|
||||
if log_files:
|
||||
@@ -745,7 +745,7 @@ def generate_main_html(log_data, system_info):
|
||||
"""
|
||||
template = Template(html_template)
|
||||
return template.safe_substitute(
|
||||
date=datetime.now().strftime('%Y_%m_%d'),
|
||||
date=datetime.now().strftime('%Y-%m-%d'),
|
||||
command_data=json.dumps(log_data['command_counts']),
|
||||
message_data=json.dumps(log_data['message_types']),
|
||||
activity_data=json.dumps(log_data['hourly_activity']),
|
||||
|
||||
@@ -58,7 +58,7 @@ def parse_log_file(file_path):
|
||||
if multiLogReader:
|
||||
# set file_path to the cwd of the default project ../log
|
||||
log_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'logs')
|
||||
log_files = glob.glob(os.path.join(log_dir, 'meshbot*.log'))
|
||||
log_files = glob.glob(os.path.join(log_dir, 'meshbot.log.*'))
|
||||
print(f"Checking log files: {log_files}")
|
||||
|
||||
if log_files:
|
||||
@@ -1036,7 +1036,7 @@ options: {
|
||||
|
||||
template = Template(html_template)
|
||||
return template.safe_substitute(
|
||||
date=datetime.now().strftime('%Y_%m_%d'),
|
||||
date=datetime.now().strftime('%Y-%m-%d'),
|
||||
command_data=json.dumps(log_data['command_counts']),
|
||||
message_data=json.dumps(log_data['message_types']),
|
||||
activity_data=json.dumps(log_data['hourly_activity']),
|
||||
|
||||
51
mesh_bot.py
51
mesh_bot.py
@@ -28,7 +28,7 @@ def auto_response(message, snr, rssi, hop, pkiStatus, message_from_id, channel_n
|
||||
"ack": lambda: handle_ping(message_from_id, deviceID, message, hop, snr, rssi, isDM, channel_number),
|
||||
"ask:": lambda: handle_llm(message_from_id, channel_number, deviceID, message, publicChannel),
|
||||
"askai": lambda: handle_llm(message_from_id, channel_number, deviceID, message, publicChannel),
|
||||
"bbslink": lambda: bbs_sync_posts(message, message_from_id, deviceID),
|
||||
"bbsack": lambda: bbs_sync_posts(message, message_from_id, deviceID),
|
||||
"bbsdelete": lambda: handle_bbsdelete(message, message_from_id),
|
||||
"bbshelp": bbs_help,
|
||||
"bbsinfo": lambda: get_bbs_stats(),
|
||||
@@ -57,6 +57,7 @@ def auto_response(message, snr, rssi, hop, pkiStatus, message_from_id, channel_n
|
||||
"ping": lambda: handle_ping(message_from_id, deviceID, message, hop, snr, rssi, isDM, channel_number),
|
||||
"pinging": lambda: handle_ping(message_from_id, deviceID, message, hop, snr, rssi, isDM, channel_number),
|
||||
"pong": lambda: "🏓PING!!🛜",
|
||||
"readnews": lambda: read_news(),
|
||||
"rlist": lambda: handle_repeaterQuery(message_from_id, deviceID, channel_number),
|
||||
"sitrep": lambda: handle_lheard(message, message_from_id, deviceID, isDM),
|
||||
"solar": lambda: drap_xray_conditions() + "\n" + solar_conditions(),
|
||||
@@ -160,15 +161,20 @@ def handle_ping(message_from_id, deviceID, message, hop, snr, rssi, isDM, chann
|
||||
multiPingList.pop(i)
|
||||
msg = "🛑 auto-ping"
|
||||
|
||||
# set inital pingCount
|
||||
try:
|
||||
pingCount = int(message.split(" ")[1])
|
||||
if pingCount == 123 or pingCount == 1234:
|
||||
pingCount = 1
|
||||
if pingCount > 51:
|
||||
pingCount = 50
|
||||
except:
|
||||
# if 3 or more entries (2 or more active), throttle the multi-ping for congestion
|
||||
if len(multiPingList) > 2:
|
||||
msg = "🚫⛔️ auto-ping, service busy. ⏳Try again soon."
|
||||
pingCount = -1
|
||||
else:
|
||||
# set inital pingCount
|
||||
try:
|
||||
pingCount = int(message.split(" ")[1])
|
||||
if pingCount == 123 or pingCount == 1234:
|
||||
pingCount = 1
|
||||
if pingCount > 51:
|
||||
pingCount = 50
|
||||
except:
|
||||
pingCount = -1
|
||||
|
||||
if pingCount > 1:
|
||||
multiPingList.append({'message_from_id': message_from_id, 'count': pingCount + 1, 'type': type, 'deviceID': deviceID, 'channel_number': channel_number, 'startCount': pingCount})
|
||||
@@ -895,7 +901,7 @@ def onReceive(packet, interface):
|
||||
|
||||
hop = f"{hop_count} hops"
|
||||
|
||||
if message_string == help_message or message_string == welcome_message or "CMD?:" in message_string:
|
||||
if help_message in message_string or welcome_message in message_string or "CMD?:" in message_string:
|
||||
# ignore help and welcome messages
|
||||
logger.warning(f"Got Own Welcome/Help header. From: {get_name_from_number(message_from_id, 'long', rxNode)}")
|
||||
return
|
||||
@@ -916,15 +922,19 @@ def onReceive(packet, interface):
|
||||
if games_enabled and (hop == "Direct" or hop_count < game_hop_limit):
|
||||
playingGame = checkPlayingGame(message_from_id, message_string, rxNode, channel_number)
|
||||
else:
|
||||
playingGame = False
|
||||
logger.warning(f"Device:{rxNode} Ignoring Request to Play Game: {message_string} From: {get_name_from_number(message_from_id, 'long', rxNode)} with hop count: {hop}")
|
||||
send_message(f"Your hop count exceeds safe playable distance at {hop_count} hops", channel_number, message_from_id, rxNode)
|
||||
if games_enabled:
|
||||
logger.warning(f"Device:{rxNode} Ignoring Request to Play Game: {message_string} From: {get_name_from_number(message_from_id, 'long', rxNode)} with hop count: {hop}")
|
||||
send_message(f"Your hop count exceeds safe playable distance at {hop_count} hops", channel_number, message_from_id, rxNode)
|
||||
time.sleep(responseDelay)
|
||||
else:
|
||||
playingGame = False
|
||||
|
||||
if not playingGame:
|
||||
if llm_enabled:
|
||||
# respond with LLM
|
||||
llm = handle_llm(message_from_id, channel_number, rxNode, message_string, publicChannel)
|
||||
send_message(llm, channel_number, message_from_id, rxNode)
|
||||
time.sleep(responseDelay)
|
||||
else:
|
||||
# respond with welcome message on DM
|
||||
logger.warning(f"Device:{rxNode} Ignoring DM: {message_string} From: {get_name_from_number(message_from_id, 'long', rxNode)}")
|
||||
@@ -1018,6 +1028,11 @@ async def start_rx():
|
||||
logger.debug("System: Logging System Logs to disk")
|
||||
if bbs_enabled:
|
||||
logger.debug(f"System: BBS Enabled, {bbsdb} has {len(bbs_messages)} messages. Direct Mail Messages waiting: {(len(bbs_dm) - 1)}")
|
||||
if bbs_link_enabled:
|
||||
if len(bbs_link_whitelist) > 0:
|
||||
logger.debug(f"System: BBS Link Enabled with {len(bbs_link_whitelist)} peers")
|
||||
else:
|
||||
logger.debug(f"System: BBS Link Enabled allowing all")
|
||||
if solar_conditions_enabled:
|
||||
logger.debug("System: Celestial Telemetry Enabled")
|
||||
if location_enabled:
|
||||
@@ -1045,11 +1060,16 @@ async def start_rx():
|
||||
logger.debug(f"System: Radio Detection Enabled using rigctld at {rigControlServerAddress} brodcasting to channels: {sigWatchBroadcastCh} for {get_freq_common_name(get_hamlib('f'))}")
|
||||
if file_monitor_enabled:
|
||||
logger.debug(f"System: File Monitor Enabled for {file_monitor_file_path}, broadcasting to channels: {file_monitor_broadcastCh}")
|
||||
if read_news_enabled:
|
||||
logger.debug(f"System: File Monitor News Reader Enabled for {news_file_path}")
|
||||
if wxAlertBroadcastEnabled:
|
||||
logger.debug(f"System: Weather Alert Broadcast Enabled on channels {wxAlertBroadcastChannel}")
|
||||
if scheduler_enabled:
|
||||
# Examples of using the scheduler, Times here are in 24hr format
|
||||
# https://schedule.readthedocs.io/en/stable/
|
||||
|
||||
# Reminder Scheduler is enabled every Monday at noon send a log message
|
||||
schedule.every().monday.at("12:00").do(lambda: logger.info("System: Scheduled Broadcast Reminder"))
|
||||
|
||||
# Good Morning Every day at 09:00 using send_message function to channel 2 on device 1
|
||||
#schedule.every().day.at("09:00").do(lambda: send_message("Good Morning", 2, 0, 1))
|
||||
@@ -1066,6 +1086,9 @@ async def start_rx():
|
||||
# Send a joke every 6 hours using tell_joke function to channel 2 on device 1
|
||||
#schedule.every(6).hours.do(lambda: send_message(tell_joke(), 2, 0, 1))
|
||||
|
||||
# Send a joke every 2 minutes using tell_joke function to channel 2 on device 1
|
||||
#schedule.every(2).minutes.do(lambda: send_message(tell_joke(), 2, 0, 1))
|
||||
|
||||
# Send the Welcome Message every other day at 08:00 using send_message function to channel 2 on device 1
|
||||
#schedule.every(2).days.at("08:00").do(lambda: send_message(welcome_message, 2, 0, 1))
|
||||
|
||||
@@ -1074,8 +1097,6 @@ async def start_rx():
|
||||
|
||||
# Send bbslink looking for peers every other day at 10:00 using send_message function to channel 3 on device 1
|
||||
#schedule.every(2).days.at("10:00").do(lambda: send_message("bbslink MeshBot looking for peers", 3, 0, 1))
|
||||
|
||||
#
|
||||
logger.debug("System: Starting the broadcast scheduler")
|
||||
await BroadcastScheduler()
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import pickle # pip install pickle
|
||||
from modules.log import *
|
||||
import time
|
||||
|
||||
trap_list_bbs = ("bbslist", "bbspost", "bbsread", "bbsdelete", "bbshelp", "bbsinfo", "bbslink", "bbsack")
|
||||
|
||||
@@ -164,6 +165,15 @@ def bbs_delete_dm(toNode, message):
|
||||
|
||||
def bbs_sync_posts(input, peerNode, RxNode):
|
||||
messageID = 0
|
||||
|
||||
# check if the bbs link is enabled
|
||||
if bbs_link_whitelist is not None:
|
||||
if str(peerNode) not in bbs_link_whitelist:
|
||||
logger.warning(f"System: BBS Link is disabled for node {peerNode}.")
|
||||
return "System: BBS Link is disabled for your node."
|
||||
if bbs_link_enabled == False:
|
||||
return "System: BBS Link is disabled."
|
||||
|
||||
# respond when another bot asks for the bbs posts to sync
|
||||
if "bbslink" in input.lower():
|
||||
if "$" in input and "#" in input:
|
||||
@@ -178,8 +188,12 @@ def bbs_sync_posts(input, peerNode, RxNode):
|
||||
ack = int(input.split(" ")[1])
|
||||
messageID = int(ack) + 1
|
||||
|
||||
# send message
|
||||
# send message with delay to keep chutil happy
|
||||
if messageID < len(bbs_messages):
|
||||
time.sleep(5 + responseDelay)
|
||||
# every 5 messages add extra delay
|
||||
if messageID % 5 == 0:
|
||||
time.sleep(10 + responseDelay)
|
||||
return f"bbslink {messageID} ${bbs_messages[messageID][1]} #{bbs_messages[messageID][2]}"
|
||||
else:
|
||||
logger.debug("System: bbslink sync complete with peer " + str(peerNode))
|
||||
|
||||
@@ -5,15 +5,22 @@ from modules.log import *
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
trap_list_filemon = ("readnews",)
|
||||
|
||||
def read_file(file_monitor_file_path):
|
||||
try:
|
||||
with open(file_monitor_file_path, 'r') as f:
|
||||
content = f.read()
|
||||
return content
|
||||
except Exception as e:
|
||||
logger.warning(f"FileMon: Error reading file: {file_monitor_file_path}")
|
||||
return None
|
||||
|
||||
def read_news():
|
||||
# read the news file on demand
|
||||
return read_file(news_file_path)
|
||||
|
||||
async def watch_file():
|
||||
def read_file(file_monitor_file_path):
|
||||
try:
|
||||
with open(file_monitor_file_path, 'r') as f:
|
||||
content = f.read()
|
||||
return content
|
||||
except Exception as e:
|
||||
logger.warning(f"FileMon: Error reading file: {file_monitor_file_path}")
|
||||
return None
|
||||
|
||||
if not os.path.exists(file_monitor_file_path):
|
||||
return None
|
||||
|
||||
@@ -378,8 +378,10 @@ def alertBrodcast():
|
||||
# get the latest weather alerts and broadcast them if there are any
|
||||
global wxAlertCache
|
||||
currentAlert = getWeatherAlerts(latitudeValue, longitudeValue)
|
||||
|
||||
if currentAlert == ERROR_FETCHING_DATA or currentAlert == NO_DATA_NOGPS or currentAlert == NO_ALERTS:
|
||||
# check if any reason to discard the alerts
|
||||
if currentAlert == ERROR_FETCHING_DATA or currentAlert == NO_DATA_NOGPS:
|
||||
return False
|
||||
elif currentAlert == NO_ALERTS:
|
||||
wxAlertCache = ""
|
||||
return False
|
||||
# broadcast the alerts send to wxBrodcastCh
|
||||
|
||||
@@ -5,7 +5,7 @@ import configparser
|
||||
# messages
|
||||
NO_DATA_NOGPS = "No location data: does your device have GPS?"
|
||||
ERROR_FETCHING_DATA = "error fetching data"
|
||||
WELCOME_MSG = 'MeshBot, here for you like a friend who is not. Try sending: ping @foo or, cmd? for more'
|
||||
WELCOME_MSG = 'MeshBot, here for you like a friend who is not. Try sending: ping @foo or, CMD? for more'
|
||||
MOTD = 'Thanks for using MeshBOT! Have a good day!'
|
||||
NO_ALERTS = "No weather alerts found."
|
||||
|
||||
@@ -24,7 +24,6 @@ max_retry_count1 = 4 # max retry count for interface 1
|
||||
max_retry_count2 = 4 # max retry count for interface 2
|
||||
retry_int1 = False
|
||||
retry_int2 = False
|
||||
scheduler_enabled = False # enable the scheduler currently config via code only
|
||||
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
|
||||
@@ -78,6 +77,10 @@ if 'fileMon' not in config:
|
||||
config['fileMon'] = {'enabled': 'False', 'file_path': 'alert.txt', 'broadcastCh': '2'}
|
||||
config.write(open(config_file, 'w'))
|
||||
|
||||
if 'scheduler' not in config:
|
||||
config['scheduler'] = {'enabled': 'False'}
|
||||
config.write(open(config_file, 'w'))
|
||||
|
||||
# interface1 settings
|
||||
interface1_type = config['interface'].get('type', 'serial')
|
||||
port1 = config['interface'].get('port', '')
|
||||
@@ -153,11 +156,16 @@ try:
|
||||
bbsdb = config['bbs'].get('bbsdb', 'data/bbsdb.pkl')
|
||||
bbs_ban_list = config['bbs'].get('bbs_ban_list', '').split(',')
|
||||
bbs_admin_list = config['bbs'].get('bbs_admin_list', '').split(',')
|
||||
bbs_link_enabled = config['bbs'].getboolean('bbslink_enabled', False)
|
||||
bbs_link_whitelist = config['bbs'].get('bbslink_whitelist', '').split(',')
|
||||
|
||||
# repeater
|
||||
repeater_enabled = config['repeater'].getboolean('enabled', False)
|
||||
repeater_channels = config['repeater'].get('repeater_channels', '').split(',')
|
||||
|
||||
# scheduler
|
||||
scheduler_enabled = config['scheduler'].getboolean('enabled', False)
|
||||
|
||||
# radio monitoring
|
||||
radio_detection_enabled = config['radioMon'].getboolean('enabled', False)
|
||||
rigControlServerAddress = config['radioMon'].get('rigControlServerAddress', 'localhost:4532') # default localhost:4532
|
||||
@@ -168,9 +176,11 @@ try:
|
||||
signalCycleLimit = config['radioMon'].getint('signalCycleLimit', 5) # default 5 cycles, used with SIGNAL_COOLDOWN
|
||||
|
||||
# file monitor
|
||||
file_monitor_enabled = config['fileMon'].getboolean('enabled', False)
|
||||
file_monitor_enabled = config['fileMon'].getboolean('filemon_enabled', False)
|
||||
file_monitor_file_path = config['fileMon'].get('file_path', 'alert.txt') # default alert.txt
|
||||
file_monitor_broadcastCh = config['fileMon'].getint('broadcastCh', 2) # default 2
|
||||
read_news_enabled = config['fileMon'].getboolean('enable_read_news', False) # default disabled
|
||||
news_file_path = config['fileMon'].get('news_file_path', 'news.txt') # default news.txt
|
||||
|
||||
# games
|
||||
game_hop_limit = config['messagingSettings'].getint('game_hop_limit', 5) # default 3 hops
|
||||
|
||||
@@ -158,8 +158,6 @@ else:
|
||||
# Scheduled Broadcast Configuration
|
||||
if scheduler_enabled:
|
||||
import schedule # pip install schedule
|
||||
# Reminder Scheduler is enabled every Monday at noon send a log message
|
||||
schedule.every().monday.at("12:00").do(lambda: logger.info("System: Scheduled Broadcast Reminder"))
|
||||
|
||||
# Sentry Configuration
|
||||
if sentry_enabled:
|
||||
@@ -176,8 +174,11 @@ if radio_detection_enabled:
|
||||
from modules.radio import * # from the spudgunman/meshing-around repo
|
||||
|
||||
# File Monitor Configuration
|
||||
if file_monitor_enabled:
|
||||
if file_monitor_enabled or read_news_enabled:
|
||||
from modules.filemon import * # from the spudgunman/meshing-around repo
|
||||
if read_news_enabled:
|
||||
trap_list = trap_list + trap_list_filemon # items readnews
|
||||
help_message = help_message + ", readmail"
|
||||
|
||||
# BLE dual interface prevention
|
||||
if interface1_type == 'ble' and interface2_type == 'ble':
|
||||
@@ -641,9 +642,12 @@ def handleMultiPing(nodeID=0, deviceID=1):
|
||||
|
||||
|
||||
def handleWxBroadcast(deviceID=1):
|
||||
# only allow API call every 30 minutes
|
||||
# only allow API call every 20 minutes
|
||||
# the watchdog will call this function 3 times, seeing possible throttling on the API
|
||||
clock = datetime.now()
|
||||
if clock.minute % 30 != 0:
|
||||
if clock.minute % 20 != 0:
|
||||
return False
|
||||
if clock.second > 17:
|
||||
return False
|
||||
|
||||
# check for alerts
|
||||
|
||||
Reference in New Issue
Block a user