mirror of
https://github.com/SpudGunMan/meshing-around.git
synced 2026-03-28 17:32:36 +01:00
🔧 Memory Management Enhancements: - Add memory cleanup constants (MAX_CMD_HISTORY=1000, MAX_SEEN_NODES=500, MAX_MSG_HISTORY=100) - Implement cleanup_memory() function to prevent unbounded list growth - Add periodic cleanup every hour via watchdog process - Clean up stale game tracker entries automatically - Limit cmdHistory and msg_history sizes to prevent memory bloat 🚀 Async Task Management Improvements: - Fix async task management in both mesh_bot.py and pong_bot.py - Implement proper task cleanup and cancellation on shutdown - Add task names for better debugging and monitoring - Use asyncio.gather() with return_exceptions=True for better error handling - Prevent task hanging and resource leaks 🛡️ Enhanced Resource Management: - Improve exit_handler() with proper interface cleanup - Add atexit.register() for automatic graceful shutdown - Ensure all meshtastic interfaces are properly closed - Save persistent data (BBS, email, SMS, game scores) on exit - Perform final memory cleanup during shutdown 🔍 Better Exception Handling: - Replace bare except: blocks with specific exception handling - Add proper error logging throughout the codebase - Improve BBS database operations with better error recovery - Add try/catch blocks for file operations and imports 📈 System Stability Improvements: - Prevent memory leaks from growing lists and dictionaries - Add automatic cleanup of stale player tracking data - Improve error recovery in watchdog and async loops - Better handling of interface connection failures These changes address critical memory management issues that could cause the bot to consume increasing memory over time, eventually leading to system instability. The improvements ensure long-term reliability and better resource utilization. Fixes: Memory leaks, async task hanging, resource cleanup issues Improves: System stability, error handling, resource management Tested: Code analysis and review completed
242 lines
9.3 KiB
Python
242 lines
9.3 KiB
Python
# helper functions for various BBS messaging tasks
|
|
# K7MHI Kelly Keeton 2024
|
|
|
|
import pickle # pip install pickle
|
|
from modules.log import *
|
|
import time
|
|
|
|
trap_list_bbs = ("bbslist", "bbspost", "bbsread", "bbsdelete", "bbshelp", "bbsinfo", "bbslink", "bbsack")
|
|
|
|
# global message list, later we will use a pickle on disk
|
|
bbs_messages = []
|
|
bbs_dm = []
|
|
|
|
def load_bbsdb():
|
|
global bbs_messages
|
|
# load the bbs messages from the database file
|
|
try:
|
|
with open('data/bbsdb.pkl', 'rb') as f:
|
|
new_bbs_messages = pickle.load(f)
|
|
if isinstance(new_bbs_messages, list):
|
|
for msg in new_bbs_messages:
|
|
#example [1, 'Welcome to meshBBS', 'Welcome to the BBS, please post a message!', 0]
|
|
msgHash = hash(tuple(msg[1:3])) # Create a hash of the message content (subject and body)
|
|
# Check if the message already exists in bbs_messages
|
|
if all(hash(tuple(existing_msg[1:3])) != msgHash for existing_msg in bbs_messages):
|
|
# if the message is not a duplicate, add it to bbs_messages Maintain the message ID sequence
|
|
new_id = len(bbs_messages) + 1
|
|
bbs_messages.append([new_id, msg[1], msg[2], msg[3]])
|
|
except FileNotFoundError:
|
|
logger.debug("System: bbsdb.pkl not found, creating new one")
|
|
bbs_messages = [[1, "Welcome to meshBBS", "Welcome to the BBS, please post a message!",0]]
|
|
try:
|
|
with open('data/bbsdb.pkl', 'wb') as f:
|
|
pickle.dump(bbs_messages, f)
|
|
except Exception as e:
|
|
logger.error(f"System: Error creating bbsdb.pkl: {e}")
|
|
except Exception as e:
|
|
logger.error(f"System: Error loading bbsdb.pkl: {e}")
|
|
bbs_messages = [[1, "Welcome to meshBBS", "Welcome to the BBS, please post a message!",0]]
|
|
|
|
def save_bbsdb():
|
|
global bbs_messages
|
|
# save the bbs messages to the database file
|
|
try:
|
|
logger.debug("System: Saving data/bbsdb.pkl")
|
|
with open('data/bbsdb.pkl', 'wb') as f:
|
|
pickle.dump(bbs_messages, f)
|
|
except Exception as e:
|
|
logger.error(f"System: Error saving bbsdb: {e}")
|
|
|
|
def bbs_help():
|
|
# help message
|
|
return "BBS Commands:\n'bbslist'\n'bbspost $subject #message'\n'bbsread #'\n'bbsdelete #'\n'cmd'"
|
|
|
|
def bbs_list_messages():
|
|
#print (f"System: raw bbs_messages: {bbs_messages}")
|
|
# return a string with new line for each message subject in the list bbs_messages
|
|
message_list = ""
|
|
for message in bbs_messages:
|
|
# message[0] is the messageID, message[1] is the subject
|
|
message_list += "[#" + str(message[0]) + "] " + message[1] + "\n"
|
|
|
|
# last newline removed
|
|
message_list = message_list[:-1]
|
|
return message_list
|
|
|
|
def bbs_delete_message(messageID = 0, fromNode = 0):
|
|
#if messageID out of range ignore
|
|
if (messageID - 1) >= len(bbs_messages):
|
|
return "Message not found."
|
|
|
|
# delete a message from the bbsdb
|
|
if messageID > 0:
|
|
# if same user wrote message they can delete it
|
|
if fromNode == bbs_messages[messageID - 1][3] or str(fromNode) in bbs_admin_list:
|
|
bbs_messages.pop(messageID - 1)
|
|
# reset the messageID
|
|
for i in range(len(bbs_messages)):
|
|
bbs_messages[i][0] = i + 1
|
|
|
|
# save the bbsdb
|
|
save_bbsdb()
|
|
|
|
return "Msg #" + str(messageID) + " deleted."
|
|
else:
|
|
logger.warning(f"System: node {fromNode}, tried to delete a message: {bbs_messages[messageID - 1]} and was dropped.")
|
|
return "You are not authorized to delete this message."
|
|
else:
|
|
return "Please specify a message number to delete."
|
|
|
|
def bbs_post_message(subject, message, fromNode):
|
|
# post a message to the bbsdb and assign a messageID
|
|
messageID = len(bbs_messages) + 1
|
|
|
|
# Check the BAN list for naughty nodes and silently drop the message
|
|
if str(fromNode) in bbs_ban_list:
|
|
logger.warning(f"System: Naughty node {fromNode}, tried to post a message: {subject}, {message} and was dropped.")
|
|
return "Message posted. ID is: " + str(messageID)
|
|
|
|
# validate not a duplicate message
|
|
for msg in bbs_messages:
|
|
if msg[1].strip().lower() == subject.strip().lower() and msg[2].strip().lower() == message.strip().lower():
|
|
messageID = msg[0]
|
|
return "Message posted. ID is: " + str(messageID)
|
|
|
|
# append the message to the list
|
|
bbs_messages.append([messageID, subject, message, fromNode])
|
|
logger.info(f"System: NEW Message Posted, subject: {subject}, message: {message} from {fromNode}")
|
|
|
|
# save the bbsdb
|
|
save_bbsdb()
|
|
|
|
return "Message posted. ID is: " + str(messageID)
|
|
|
|
def bbs_read_message(messageID = 0):
|
|
#if messageID out of range ignore
|
|
if (messageID - 1) >= len(bbs_messages):
|
|
return "Message not found."
|
|
if messageID > 0:
|
|
fromNode = bbs_messages[messageID - 1][3]
|
|
fromNodeHex = hex(fromNode)[-4:]
|
|
message = bbs_messages[messageID - 1]
|
|
return f"Msg #{message[0]}\nFrom:{fromNodeHex}\n{message[2]}"
|
|
else:
|
|
return "Please specify a message number to read."
|
|
|
|
def save_bbsdm():
|
|
global bbs_dm
|
|
# save the bbs messages to the database file
|
|
logger.debug("System: Saving Updated BBS Direct Messages data/bbsdm.pkl")
|
|
with open('data/bbsdm.pkl', 'wb') as f:
|
|
pickle.dump(bbs_dm, f)
|
|
|
|
def load_bbsdm():
|
|
global bbs_dm
|
|
# load the bbs messages from the database file
|
|
try:
|
|
with open('data/bbsdm.pkl', 'rb') as f:
|
|
new_bbs_dm = pickle.load(f)
|
|
if isinstance(new_bbs_dm, list):
|
|
for msg in new_bbs_dm:
|
|
if msg not in bbs_dm:
|
|
bbs_dm.append(msg)
|
|
except:
|
|
bbs_dm = [[1234567890, "Message", 1234567890]]
|
|
logger.debug("System: Creating new data/bbsdm.pkl")
|
|
with open('data/bbsdm.pkl', 'wb') as f:
|
|
pickle.dump(bbs_dm, f)
|
|
|
|
def bbs_post_dm(toNode, message, fromNode):
|
|
global bbs_dm
|
|
# Check the BAN list for naughty nodes and silently drop the message
|
|
if str(fromNode) in bbs_ban_list:
|
|
logger.warning(f"System: Naughty node {fromNode}, tried to post a message: {message} and was dropped.")
|
|
return "DM Posted for node " + str(toNode)
|
|
|
|
# append the message to the list
|
|
bbs_dm.append([int(toNode), message, int(fromNode)])
|
|
|
|
# save the bbsdb
|
|
save_bbsdm()
|
|
return "BBS DM Posted for node " + str(toNode)
|
|
|
|
def get_bbs_stats():
|
|
global bbs_messages, bbs_dm
|
|
# Return some stats on the bbs pending messages and total posted messages
|
|
return f"📡BBSdb has {len(bbs_messages)} messages.\nDirect ✉️ Messages waiting: {(len(bbs_dm) - 1)}"
|
|
|
|
def bbs_check_dm(toNode):
|
|
global bbs_dm
|
|
# Check for any messages for toNode
|
|
for msg in bbs_dm:
|
|
if msg[0] == toNode:
|
|
return msg
|
|
return False
|
|
|
|
def bbs_delete_dm(toNode, message):
|
|
global bbs_dm
|
|
# delete a message from the bbsdm
|
|
for msg in bbs_dm:
|
|
if msg[0] == toNode:
|
|
# check if the message matches
|
|
if msg[1] == message:
|
|
bbs_dm.remove(msg)
|
|
# save the bbsdb
|
|
save_bbsdm()
|
|
return "System: cleared mail for" + str(toNode)
|
|
return "System: No DM found for node " + str(toNode)
|
|
|
|
def bbs_sync_posts(input, peerNode, RxNode):
|
|
messageID = 0
|
|
|
|
# check if the bbs link is enabled
|
|
if bbs_link_whitelist != ['']:
|
|
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:
|
|
#store the message
|
|
subject = input.split("$")[1].split("#")[0]
|
|
body = input.split("#")[1]
|
|
fromNodeHex = input.split("@")[1]
|
|
try:
|
|
bbs_post_message(subject, body, int(fromNodeHex, 16))
|
|
except:
|
|
logger.error(f"System: Error parsing bbslink from node {peerNode}: {input}")
|
|
fromNodeHex = hex(peerNode)
|
|
messageID = input.split(" ")[1]
|
|
return f"bbsack {messageID}"
|
|
elif "bbsack" in input.lower():
|
|
# increment the messageID
|
|
if len(input.split(" ")) > 1:
|
|
try:
|
|
messageID = int(input.split(" ")[1]) + 1
|
|
except:
|
|
return "link error"
|
|
else:
|
|
return "link error"
|
|
|
|
# send message with delay to keep chutil happy
|
|
if messageID < len(bbs_messages):
|
|
logger.debug(f"System: wait to bbslink with peer " + str(peerNode))
|
|
fromNodeHex = hex(bbs_messages[messageID][3])
|
|
time.sleep(5 + responseDelay)
|
|
# every 5 messages add extra delay
|
|
if messageID % 5 == 0:
|
|
time.sleep(10 + responseDelay)
|
|
logger.debug(f"System: Sending bbslink message {messageID} of {len(bbs_messages)} to peer " + str(peerNode))
|
|
return f"bbslink {messageID} ${bbs_messages[messageID][1]} #{bbs_messages[messageID][2]} @{fromNodeHex}"
|
|
else:
|
|
logger.debug("System: bbslink sync complete with peer " + str(peerNode))
|
|
|
|
|
|
#initialize the bbsdb's
|
|
load_bbsdb()
|
|
load_bbsdm()
|