# modules/scheduler.py 2025 meshing-around # Scheduler module for mesh_bot import asyncio import schedule from datetime import datetime from modules.log import logger from modules.system import send_message async def run_scheduler_loop(interval=1): logger.debug(f"System: Scheduler loop started Tasks: {len(schedule.jobs)}, Details:{extract_schedule_fields(schedule.get_jobs())}") try: last_logged_minute = -1 while True: try: # Log scheduled jobs every 20 minutes now = datetime.now() if now.minute % 20 == 0 and now.minute != last_logged_minute: logger.debug(f"System: Scheduled Tasks {len(schedule.jobs)}, Details:{extract_schedule_fields(schedule.get_jobs())}") last_logged_minute = now.minute schedule.run_pending() except Exception as e: logger.error(f"System: Scheduler loop exception: {e}") await asyncio.sleep(interval) except asyncio.CancelledError: logger.debug("System: Scheduler loop cancelled, shutting down.") def safe_int(val, default=0, type=""): try: return int(val) except (ValueError, TypeError): logger.debug(f"System: Scheduler config {type} error '{val}' to int, using default {default}") return default def extract_schedule_fields(jobs): """ Extracts 'Every ... (last run: [...], next run: ...)' from schedule.get_jobs() output without regex. """ jobs_str = str(jobs) results = [] # Split by '), ' to separate jobs, then add ')' back except last parts = jobs_str.split('), ') for i, part in enumerate(parts): if not part.endswith(')'): part += ')' # Find the start of 'Every' start = part.find('Every') if start != -1: # Find the start of 'do ()' do_idx = part.find('do ') if do_idx != -1: summary = part[start:do_idx].strip() # Find the (last run: ... next run: ...) part paren_idx = part.find('(', do_idx) if paren_idx != -1: summary += ' ' + part[paren_idx:].strip() while '', f_start) if f_end == -1: break func_str = summary[f_start+10:f_end] func_name = func_str.split(' ')[0] summary = summary[:f_start] + func_name + summary[f_end+1:] results.append(summary) return results def setup_scheduler( schedulerMotd, MOTD, schedulerMessage, schedulerChannel, schedulerInterface, schedulerValue, schedulerTime, schedulerInterval): try: # Methods imported from mesh_bot for scheduling tasks from mesh_bot import ( tell_joke, welcome_message, handle_wxc, handle_moon, handle_sun, 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") # Setup the scheduler based on configuration schedulerValue = schedulerValue.lower().strip() schedulerTime = schedulerTime.strip() schedulerInterval = schedulerInterval.strip() schedulerChannel = safe_int(schedulerChannel, 0, type="channel") schedulerInterface = safe_int(schedulerInterface, 1, type="interface") schedulerIntervalInt = safe_int(schedulerInterval, 5, type="interval") try: scheduler_message = MOTD if schedulerMotd else schedulerMessage def send_sched_msg(): send_message(scheduler_message, schedulerChannel, 0, schedulerInterface) # Basic Scheduler Options basicOptions = ['day', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun', 'hour', 'min'] 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) elif 'tue' in schedulerValue and schedulerTime: schedule.every().tuesday.at(schedulerTime).do(send_sched_msg) elif 'wed' in schedulerValue and schedulerTime: schedule.every().wednesday.at(schedulerTime).do(send_sched_msg) elif 'thu' in schedulerValue and schedulerTime: schedule.every().thursday.at(schedulerTime).do(send_sched_msg) elif 'fri' in schedulerValue and schedulerTime: schedule.every().friday.at(schedulerTime).do(send_sched_msg) elif 'sat' in schedulerValue and schedulerTime: schedule.every().saturday.at(schedulerTime).do(send_sched_msg) elif 'sun' in schedulerValue and schedulerTime: schedule.every().sunday.at(schedulerTime).do(send_sched_msg) elif 'hour' in schedulerValue: schedule.every(schedulerIntervalInt).hours.do(send_sched_msg) elif 'min' in schedulerValue: schedule.every(schedulerIntervalInt).minutes.do(send_sched_msg) 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( 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( 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( 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 setup_custom_schedules( send_message, tell_joke, welcome_message, handle_wxc, MOTD, schedulerChannel, schedulerInterface) logger.debug(f"System: Starting the custom_scheduler.py ") schedule.every().monday.at("12:00").do( 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.template modules/custom_scheduler.py") except Exception as e: logger.error(f"System: Scheduler Error {e}") return True