mirror of
https://github.com/SpudGunMan/meshing-around.git
synced 2026-03-28 17:32:36 +01:00
299 lines
11 KiB
Python
299 lines
11 KiB
Python
# helper functions for various BBS messaging tasks
|
|
# K7MHI Kelly Keeton 2024
|
|
|
|
import pickle # pip install pickle
|
|
from modules.log import logger
|
|
from modules.settings import bbs_admin_list, bbs_ban_list, MESSAGE_CHUNK_SIZE, bbs_link_enabled, bbs_link_whitelist, responseDelay
|
|
import time
|
|
from datetime import datetime
|
|
|
|
useSynchCompression = False
|
|
|
|
if useSynchCompression:
|
|
import zlib
|
|
from modules.system import send_raw_bytes
|
|
|
|
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
|
|
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:
|
|
msgHash = hash(tuple(msg[1:3]))
|
|
if all(hash(tuple(existing_msg[1:3])) != msgHash for existing_msg in bbs_messages):
|
|
new_id = len(bbs_messages) + 1
|
|
bbs_messages.append([new_id, msg[1], msg[2], msg[3]])
|
|
return True # Loaded successfully, regardless of whether new messages were added
|
|
return False # File existed but did not contain a valid list of messages (possibly corrupted)
|
|
except FileNotFoundError:
|
|
# create a new bbsdb.pkl with a welcome message
|
|
# template ([messageID, subject, message, fromNode, now, thread, replyto])
|
|
bbs_messages = [[1, "Welcome to meshBBS", "Welcome to the BBS, please post a message!",0,time.strftime('%Y-%m-%d %H:%M:%S'),0,0]]
|
|
logger.debug("System: bbsdb.pkl not found, creating new one")
|
|
try:
|
|
with open('data/bbsdb.pkl', 'wb') as f:
|
|
pickle.dump(bbs_messages, f)
|
|
return True
|
|
except Exception as e:
|
|
logger.error(f"System: Error creating bbsdb.pkl: {e}")
|
|
return False
|
|
except Exception as e:
|
|
logger.error(f"System: Error loading bbsdb.pkl: {e}")
|
|
return False
|
|
|
|
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, threadID=0, replytoID=0):
|
|
# post a message to the bbsdb
|
|
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
thread = threadID
|
|
replyto = replytoID
|
|
# 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 message length isnt three times the MESSAGE_CHUNK_SIZE
|
|
if len(message) > (3 * MESSAGE_CHUNK_SIZE):
|
|
return "Message too long, max length is " + str(3 * MESSAGE_CHUNK_SIZE) + " characters."
|
|
# 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)
|
|
# validate its not overlength by keeping in chunker limit
|
|
# append the message to the list
|
|
bbs_messages.append([messageID, subject, message, fromNode, now, thread, replyto])
|
|
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)
|
|
|
|
# validate message length isnt three times the MESSAGE_CHUNK_SIZE
|
|
if len(message) > (3 * MESSAGE_CHUNK_SIZE):
|
|
return "Message too long, max length is " + str(3 * MESSAGE_CHUNK_SIZE) + " characters."
|
|
# validate not a duplicate message
|
|
for msg in bbs_dm:
|
|
if msg[0] == int(toNode) and msg[1].strip().lower() == message.strip().lower():
|
|
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 compress_data(data_to_compress):
|
|
# Prepare message as bytes
|
|
compressed = zlib.compress(data_to_compress.encode('utf-8'))
|
|
return compressed
|
|
|
|
def decompress_data(data_bytes):
|
|
try:
|
|
decompressed = zlib.decompress(data_bytes)
|
|
msg = decompressed.decode('utf-8')
|
|
return msg
|
|
except Exception as e:
|
|
logger.warning(f"Error decompressing data: {e}")
|
|
return False
|
|
|
|
def bbs_receive_compressed(data_bytes, fromNode, RxNode):
|
|
try:
|
|
decompressed = zlib.decompress(data_bytes)
|
|
msg = decompressed.decode('utf-8')
|
|
|
|
bbs_sync_posts(msg, fromNode, RxNode)
|
|
|
|
return msg
|
|
except Exception as e:
|
|
logger.error(f"Error decompressing BBS message: {e}")
|
|
return None
|
|
|
|
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))
|
|
msg = f"bbslink {messageID} ${bbs_messages[messageID][1]} #{bbs_messages[messageID][2]} @{fromNodeHex}"
|
|
if useSynchCompression:
|
|
compressed = compress_data(msg)
|
|
send_raw_bytes(peerNode, compressed)
|
|
logger.debug("System: Sent compressed bbslink message to peer " + str(peerNode))
|
|
else:
|
|
return msg
|
|
else:
|
|
logger.debug("System: bbslink sync complete with peer " + str(peerNode))
|
|
|
|
|
|
#initialize the bbsdb's
|
|
load_bbsdb()
|
|
load_bbsdm()
|