mirror of
https://github.com/SpudGunMan/meshing-around.git
synced 2026-08-07 09:22:44 +02:00
Merge pull request #247 from SpudGunMan/copilot/enhancement-basic-scheduler
Add scheduler support for news, RSS, marine weather, system info, tide, and solar refactored some other logic around scheduler and also the update and installer
This commit is contained in:
+3
-1
@@ -309,7 +309,9 @@ message = "MeshBot says Hello! DM for more info."
|
||||
# enable overides the above and uses the motd as the message
|
||||
schedulerMotd = False
|
||||
# value can be min,hour,day,mon,tue,wed,thu,fri,sat,sun.
|
||||
# value can also be 'joke' (min/interval) or 'weather' (time/day) or 'link' (hour/interval) for special auto messages
|
||||
# value can also be 'joke' (min/interval), 'weather' (time/day), 'link' (hour/interval) for special auto messages
|
||||
# or 'news' (hour/interval), 'readrss' (hour/interval), 'mwx' (time/day), 'sysinfo' (hour/interval),
|
||||
# 'tide' (time/day), 'solar' (time/day) for automated information broadcasts
|
||||
# 'custom' for module/scheduler.py custom schedule examples
|
||||
value =
|
||||
# interval to use when time is not set (e.g. every 2 days)
|
||||
|
||||
@@ -14,8 +14,24 @@ def setup_custom_schedules(send_message, tell_joke, welcome_message, handle_wxc,
|
||||
5. Make sure to uncomment (delete the single #) the example schedules down at the end of the file to enable them
|
||||
Python is sensitive to indentation so be careful when editing this file.
|
||||
https://thonny.org is included on pi's image and is a simple IDE to use for editing python files.
|
||||
|
||||
Available functions you can import and use:
|
||||
- tell_joke() - Returns a random joke
|
||||
- welcome_message - A welcome message string
|
||||
- handle_wxc(message_from_id, deviceID, cmd, days=None) - Weather information
|
||||
- handleNews(message_from_id, deviceID, message, isDM) - News reader
|
||||
- get_rss_feed(msg) - RSS feed reader
|
||||
- handle_mwx(message_from_id, deviceID, cmd) - Marine weather
|
||||
- sysinfo(message, message_from_id, deviceID, isDM) - System information
|
||||
- handle_tide(message_from_id, deviceID, channel_number) - Tide information
|
||||
- handle_sun(message_from_id, deviceID, channel_number) - Sun information
|
||||
- MOTD - Message of the day string
|
||||
"""
|
||||
try:
|
||||
# Import additional functions for scheduling (optional, depending on your needs)
|
||||
from mesh_bot import handleNews, sysinfo, handle_mwx, handle_tide, handle_sun
|
||||
from modules.rss import get_rss_feed
|
||||
|
||||
# Example task functions, modify as needed the channel and interface parameters default to schedulerChannel and schedulerInterface
|
||||
def send_joke(channel, interface):
|
||||
## uses system.send_message to send the result of tell_joke()
|
||||
@@ -49,6 +65,30 @@ def setup_custom_schedules(send_message, tell_joke, welcome_message, handle_wxc,
|
||||
## uses system.send_message to send message of the day string which can be updated in runtime
|
||||
send_message(MOTD, channel, 0, interface)
|
||||
|
||||
def send_news(channel, interface):
|
||||
## uses system.send_message to send the result of handleNews()
|
||||
send_message(handleNews(0, interface, 'readnews', False), channel, 0, interface)
|
||||
|
||||
def send_rss(channel, interface):
|
||||
## uses system.send_message to send the result of get_rss_feed()
|
||||
send_message(get_rss_feed(''), channel, 0, interface)
|
||||
|
||||
def send_marine_weather(channel, interface):
|
||||
## uses system.send_message to send the result of handle_mwx()
|
||||
send_message(handle_mwx(0, interface, 'mwx'), channel, 0, interface)
|
||||
|
||||
def send_sysinfo(channel, interface):
|
||||
## uses system.send_message to send the result of sysinfo()
|
||||
send_message(sysinfo('', 0, interface, False), channel, 0, interface)
|
||||
|
||||
def send_tide(channel, interface):
|
||||
## uses system.send_message to send the result of handle_tide()
|
||||
send_message(handle_tide(0, interface, channel), channel, 0, interface)
|
||||
|
||||
def send_sun(channel, interface):
|
||||
## uses system.send_message to send the result of handle_sun()
|
||||
send_message(handle_sun(0, interface, channel), channel, 0, interface)
|
||||
|
||||
### Send a joke every 2 minutes
|
||||
#schedule.every(2).minutes.do(lambda: send_joke(schedulerChannel, schedulerInterface))
|
||||
### Send a good morning message every day at 9 AM
|
||||
@@ -67,6 +107,18 @@ def setup_custom_schedules(send_message, tell_joke, welcome_message, handle_wxc,
|
||||
#schedule.every().day.at("13:00").do(lambda: send_motd(schedulerChannel, schedulerInterface))
|
||||
### Send bbslink message every 2 days at 10 AM
|
||||
#schedule.every(2).days.at("10:00").do(lambda: send_message("bbslink MeshBot looking for peers", schedulerChannel, 0, schedulerInterface))
|
||||
### Send news updates every 6 hours
|
||||
#schedule.every(6).hours.do(lambda: send_news(schedulerChannel, schedulerInterface))
|
||||
### Send RSS feed every day at 9 AM
|
||||
#schedule.every().day.at("09:00").do(lambda: send_rss(schedulerChannel, schedulerInterface))
|
||||
### Send marine weather every day at 6 AM
|
||||
#schedule.every().day.at("06:00").do(lambda: send_marine_weather(schedulerChannel, schedulerInterface))
|
||||
### Send system information every day at 12 PM
|
||||
#schedule.every().day.at("12:00").do(lambda: send_sysinfo(schedulerChannel, schedulerInterface))
|
||||
### Send tide information every day at 5 AM
|
||||
#schedule.every().day.at("05:00").do(lambda: send_tide(schedulerChannel, schedulerInterface))
|
||||
### Send sun information every day at 6 AM
|
||||
#schedule.every().day.at("06:00").do(lambda: send_sun(schedulerChannel, schedulerInterface))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error setting up custom schedules: {e}")
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
# Set ownership and permissions for Meshing Around application
|
||||
|
||||
# Check if run as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Please run as root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Use first argument as user, or default to meshbot
|
||||
TARGET_USER="${1:-meshbot}"
|
||||
|
||||
# Check if user exists
|
||||
if ! id "$TARGET_USER" &>/dev/null; then
|
||||
echo "User '$TARGET_USER' does not exist."
|
||||
read -p "Would you like to use the current user ($(logname)) instead? [y/N]: " yn
|
||||
if [[ "$yn" =~ ^[Yy]$ ]]; then
|
||||
TARGET_USER="$(logname)"
|
||||
echo "Using current user: $TARGET_USER"
|
||||
if ! id "$TARGET_USER" &>/dev/null; then
|
||||
echo "Current user '$TARGET_USER' does not exist or cannot be determined."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Exiting."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Setting ownership to $TARGET_USER:$TARGET_USER"
|
||||
|
||||
chown -R "$TARGET_USER:$TARGET_USER" "/opt/meshing-around/-around"
|
||||
chown -R "$TARGET_USER:$TARGET_USER" "/opt/meshing-around/-around/logs"
|
||||
chown -R "$TARGET_USER:$TARGET_USER" "/opt/meshing-around/-around/data"
|
||||
chown "$TARGET_USER:$TARGET_USER" "/opt/meshing-around/-around/config.ini"
|
||||
chmod 640 "/opt/meshing-around/-around/config.ini"
|
||||
chmod 750 "/opt/meshing-around/-around/logs"
|
||||
chmod 750 "/opt/meshing-around/-around/data"
|
||||
|
||||
echo "Permissions and ownership have been set."
|
||||
@@ -287,6 +287,11 @@ echo "Added user $whoami to dialout, tty, and bluetooth groups"
|
||||
|
||||
sudo chown -R "$whoami:$whoami" "$program_path/logs"
|
||||
sudo chown -R "$whoami:$whoami" "$program_path/data"
|
||||
sudo chown "$whoami:$whoami" "$program_path/config.ini"
|
||||
sudo chmod 640 "$program_path/config.ini"
|
||||
echo "Permissions set for meshbot on config.ini"
|
||||
sudo chmod 750 "$program_path/logs"
|
||||
sudo chmod 750 "$program_path/data"
|
||||
echo "Permissions set for meshbot on logs and data directories"
|
||||
|
||||
# check and see if some sort of NTP is running
|
||||
|
||||
+50
-2
@@ -541,17 +541,23 @@ Configure in `[scheduler]` section of `config.ini`.
|
||||
See modules/custom_scheduler.py for advanced scheduling using python
|
||||
|
||||
**Purpose:**
|
||||
`scheduler.py` provides automated scheduling for Mesh Bot, allowing you to send messages, jokes, weather updates, and custom actions at specific times or intervals.
|
||||
`scheduler.py` provides automated scheduling for Mesh Bot, allowing you to send messages, jokes, weather updates, news, RSS feeds, marine weather, system info, tide info, sun info, and custom actions at specific times or intervals.
|
||||
|
||||
**How to Use:**
|
||||
- The scheduler is configured via your bot’s settings or commands, specifying what to send, when, and on which channel/interface.
|
||||
- Supports daily, weekly, hourly, and minutely schedules, as well as special jobs like jokes and weather.
|
||||
- Supports daily, weekly, hourly, and minutely schedules, as well as special jobs like jokes, weather, news, RSS feeds, marine weather, system info, tide info, and sun info.
|
||||
- For advanced automation, you can define your own schedules in `etc/custom_scheduler.py` (copied to `modules/custom_scheduler.py` at install).
|
||||
|
||||
**Features:**
|
||||
- **Basic Scheduling:** Send messages on a set schedule (e.g., every day at 09:00, every Monday at noon, every hour, etc.).
|
||||
- **Joke Scheduler:** Automatically send jokes every x min
|
||||
- **Weather Scheduler:** Send weather updates at time of day, daily.
|
||||
- **News Scheduler:** Send news updates at specified intervals.
|
||||
- **RSS Scheduler:** Send RSS feed updates at specified intervals.
|
||||
- **Marine Weather Scheduler:** Send marine weather forecasts at time of day, daily.
|
||||
- **System Info Scheduler:** Send system information at specified intervals.
|
||||
- **Tide Scheduler:** Send tide information at time of day, daily.
|
||||
- **Sun Scheduler:** Send sun information (sunrise/sunset) at time of day, daily.
|
||||
- **Custom Scheduler:** run your own scheduled jobs by editing `custom_scheduler.py`.
|
||||
- **Logging:** All scheduling actions are logged for debugging and monitoring.
|
||||
|
||||
@@ -618,6 +624,48 @@ You can schedule messages or actions using the following options in your configu
|
||||
- Time: `08:00`
|
||||
- → Sends a weather update daily at 8:00a.
|
||||
|
||||
#### **news**
|
||||
- Schedules the bot to send news updates at the specified interval (in hours).
|
||||
- **Example:**
|
||||
- Option: `news`
|
||||
- Interval: `6`
|
||||
- → Sends news updates every 6 hours.
|
||||
|
||||
#### **readrss**
|
||||
- Schedules the bot to send RSS feed updates at the specified interval (in hours).
|
||||
- **Example:**
|
||||
- Option: `readrss`
|
||||
- Interval: `4`
|
||||
- → Sends RSS feed updates every 4 hours.
|
||||
|
||||
#### **mwx**
|
||||
- Schedules the bot to send marine weather updates at the specified time of day, daily.
|
||||
- **Example:**
|
||||
- Option: `mwx`
|
||||
- Time: `06:00`
|
||||
- → Sends marine weather updates daily at 6:00a.
|
||||
|
||||
#### **sysinfo**
|
||||
- Schedules the bot to send system information at the specified interval (in hours).
|
||||
- **Example:**
|
||||
- Option: `sysinfo`
|
||||
- Interval: `12`
|
||||
- → Sends system information every 12 hours.
|
||||
|
||||
#### **tide**
|
||||
- Schedules the bot to send tide information at the specified time of day, daily.
|
||||
- **Example:**
|
||||
- Option: `tide`
|
||||
- Time: `05:00`
|
||||
- → Sends tide information daily at 5:00a.
|
||||
|
||||
#### **solar**
|
||||
- Schedules the bot to send sun information (sunrise/sunset) at the specified time of day, daily.
|
||||
- **Example:**
|
||||
- Option: `solar`
|
||||
- Time: `06:00`
|
||||
- → Sends sun information daily at 6:00a.
|
||||
|
||||
---
|
||||
|
||||
### Days of the Week
|
||||
|
||||
+41
-7
@@ -1,11 +1,9 @@
|
||||
# modules/scheduler.py 2025 meshing-around
|
||||
# Scheduler setup for Mesh Bot
|
||||
# Scheduler module for mesh_bot
|
||||
import asyncio
|
||||
import schedule
|
||||
from datetime import datetime
|
||||
from functools import partial
|
||||
from modules.log import logger
|
||||
from modules.settings import MOTD
|
||||
from modules.system import send_message
|
||||
|
||||
async def run_scheduler_loop(interval=1):
|
||||
@@ -80,7 +78,11 @@ def setup_scheduler(
|
||||
handle_riverFlow,
|
||||
handle_tide,
|
||||
handle_satpass,
|
||||
handleNews,
|
||||
handle_mwx,
|
||||
sysinfo,
|
||||
)
|
||||
from modules.rss import get_rss_feed
|
||||
except ImportError as e:
|
||||
logger.warning(f"Some mesh_bot schedule features are unavailable by option disable in config.ini: {e} comment out the use of these methods in your custom_scheduler.py")
|
||||
|
||||
@@ -103,8 +105,10 @@ def setup_scheduler(
|
||||
if any(option in schedulerValue for option in basicOptions):
|
||||
if schedulerValue == 'day':
|
||||
if schedulerTime:
|
||||
# Specific time each day
|
||||
schedule.every().day.at(schedulerTime).do(send_sched_msg)
|
||||
else:
|
||||
# Every N days
|
||||
schedule.every(schedulerIntervalInt).days.do(send_sched_msg)
|
||||
elif 'mon' in schedulerValue and schedulerTime:
|
||||
schedule.every().monday.at(schedulerTime).do(send_sched_msg)
|
||||
@@ -127,19 +131,49 @@ def setup_scheduler(
|
||||
logger.debug(f"System: Starting the basic scheduler to send '{scheduler_message}' on schedule '{schedulerValue}' every {schedulerIntervalInt} interval at time '{schedulerTime}' on Device:{schedulerInterface} Channel:{schedulerChannel}")
|
||||
elif 'joke' in schedulerValue:
|
||||
schedule.every(schedulerIntervalInt).minutes.do(
|
||||
partial(send_message, tell_joke(), schedulerChannel, 0, schedulerInterface)
|
||||
lambda: send_message(tell_joke(), schedulerChannel, 0, schedulerInterface)
|
||||
)
|
||||
logger.debug(f"System: Starting the joke scheduler to send a joke every {schedulerIntervalInt} minutes on Device:{schedulerInterface} Channel:{schedulerChannel}")
|
||||
elif 'link' in schedulerValue:
|
||||
schedule.every(schedulerIntervalInt).hours.do(
|
||||
send_message("bbslink MeshBot looking for peers", schedulerChannel, 0, schedulerInterface)
|
||||
lambda: send_message("bbslink MeshBot looking for peers", schedulerChannel, 0, schedulerInterface)
|
||||
)
|
||||
logger.debug(f"System: Starting the link scheduler to send link messages every {schedulerIntervalInt} hours on Device:{schedulerInterface} Channel:{schedulerChannel}")
|
||||
elif 'weather' in schedulerValue:
|
||||
schedule.every().day.at(schedulerTime).do(
|
||||
partial(send_message, handle_wxc(0, schedulerInterface, 'wx', days=1), schedulerChannel, 0, schedulerInterface)
|
||||
lambda: send_message(handle_wxc(0, schedulerInterface, 'wx', days=1), schedulerChannel, 0, schedulerInterface)
|
||||
)
|
||||
logger.debug(f"System: Starting the weather scheduler to send weather updates every {schedulerIntervalInt} hours on Device:{schedulerInterface} Channel:{schedulerChannel}")
|
||||
elif 'news' in schedulerValue:
|
||||
schedule.every(schedulerIntervalInt).hours.do(
|
||||
lambda: send_message(handleNews(0, schedulerInterface, 'readnews', False), schedulerChannel, 0, schedulerInterface)
|
||||
)
|
||||
logger.debug(f"System: Starting the news scheduler to send news updates every {schedulerIntervalInt} hours on Device:{schedulerInterface} Channel:{schedulerChannel}")
|
||||
elif 'readrss' in schedulerValue:
|
||||
schedule.every(schedulerIntervalInt).hours.do(
|
||||
lambda: send_message(get_rss_feed(''), schedulerChannel, 0, schedulerInterface)
|
||||
)
|
||||
logger.debug(f"System: Starting the RSS scheduler to send RSS feeds every {schedulerIntervalInt} hours on Device:{schedulerInterface} Channel:{schedulerChannel}")
|
||||
elif 'mwx' in schedulerValue:
|
||||
schedule.every().day.at(schedulerTime).do(
|
||||
lambda: send_message(handle_mwx(0, schedulerInterface, 'mwx'), schedulerChannel, 0, schedulerInterface)
|
||||
)
|
||||
logger.debug(f"System: Starting the marine weather scheduler to send marine weather updates at {schedulerTime} on Device:{schedulerInterface} Channel:{schedulerChannel}")
|
||||
elif 'sysinfo' in schedulerValue:
|
||||
schedule.every(schedulerIntervalInt).hours.do(
|
||||
lambda: send_message(sysinfo('', 0, schedulerInterface, False), schedulerChannel, 0, schedulerInterface)
|
||||
)
|
||||
logger.debug(f"System: Starting the sysinfo scheduler to send system information every {schedulerIntervalInt} hours on Device:{schedulerInterface} Channel:{schedulerChannel}")
|
||||
elif 'tide' in schedulerValue:
|
||||
schedule.every().day.at(schedulerTime).do(
|
||||
lambda: send_message(handle_tide(0, schedulerInterface, schedulerChannel), schedulerChannel, 0, schedulerInterface)
|
||||
)
|
||||
logger.debug(f"System: Starting the tide scheduler to send tide information at {schedulerTime} on Device:{schedulerInterface} Channel:{schedulerChannel}")
|
||||
elif 'solar' in schedulerValue:
|
||||
schedule.every().day.at(schedulerTime).do(
|
||||
lambda: send_message(handle_sun(0, schedulerInterface, schedulerChannel), schedulerChannel, 0, schedulerInterface)
|
||||
)
|
||||
logger.debug(f"System: Starting the scheduler to send solar information at {schedulerTime} on Device:{schedulerInterface} Channel:{schedulerChannel}")
|
||||
elif 'custom' in schedulerValue:
|
||||
try:
|
||||
from modules.custom_scheduler import setup_custom_schedules # type: ignore
|
||||
@@ -151,7 +185,7 @@ def setup_scheduler(
|
||||
lambda: logger.info("System: Scheduled Broadcast Enabled Reminder")
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning("Custom scheduler file not found or failed to import. cp etc/custom_scheduler.py modules/custom_scheduler.py")
|
||||
logger.warning("Custom scheduler file not found or failed to import. cp etc/custom_scheduler.template modules/custom_scheduler.py")
|
||||
except Exception as e:
|
||||
logger.error(f"System: Scheduler Error {e}")
|
||||
return True
|
||||
@@ -63,6 +63,24 @@ if [[ -f "modules/custom_scheduler.py" ]]; then
|
||||
echo "Including custom_scheduler.py in backup..."
|
||||
cp modules/custom_scheduler.py data/
|
||||
fi
|
||||
# Check config.ini ownership and permissions
|
||||
if [[ -f "config.ini" ]]; then
|
||||
owner=$(stat -f "%Su" config.ini)
|
||||
perms=$(stat -f "%A" config.ini)
|
||||
echo "config.ini is owned by: $owner"
|
||||
echo "config.ini permissions: $perms"
|
||||
if [[ "$owner" == "root" ]]; then
|
||||
echo "Warning: config.ini is owned by root check out the etc/set-permissions.sh script"
|
||||
fi
|
||||
if [[ $(stat -f "%Lp" config.ini) =~ .*[7,6,2]$ ]]; then
|
||||
echo "Warning: config.ini is world-writable or world-readable! check out the etc/set-permissions.sh script"
|
||||
fi
|
||||
|
||||
echo "Including config.ini in backup..."
|
||||
|
||||
cp config.ini data/config.backup
|
||||
fi
|
||||
#create the tar.gz backup
|
||||
tar -czf "$backup_file" "$path2backup"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Backup failed."
|
||||
@@ -70,7 +88,6 @@ else
|
||||
echo "Backup of ${path2backup} completed: ${backup_file}"
|
||||
fi
|
||||
|
||||
|
||||
# Build a config_new.ini file merging user config with new defaults
|
||||
echo "Merging configuration files..."
|
||||
python3 script/configMerge.py > ini_merge_log.txt 2>&1
|
||||
|
||||
Reference in New Issue
Block a user