mirror of
https://github.com/markqvist/NomadNet.git
synced 2024-10-01 01:26:07 -04:00
188 lines
6.6 KiB
Python
188 lines
6.6 KiB
Python
|
# Simple message board that can be hosted on a NomadNet node, messages can be posted by 'conversing' with a unique peer, all messages are then forwarded to the message board.
|
||
|
# https://github.com/chengtripp/lxmf_messageboard
|
||
|
|
||
|
import RNS
|
||
|
import LXMF
|
||
|
import os, time
|
||
|
from queue import Queue
|
||
|
import RNS.vendor.umsgpack as msgpack
|
||
|
|
||
|
display_name = "NomadNet Message Board"
|
||
|
max_messages = 20
|
||
|
|
||
|
def setup_lxmf():
|
||
|
if os.path.isfile(identitypath):
|
||
|
identity = RNS.Identity.from_file(identitypath)
|
||
|
RNS.log('Loaded identity from file', RNS.LOG_INFO)
|
||
|
else:
|
||
|
RNS.log('No Primary Identity file found, creating new...', RNS.LOG_INFO)
|
||
|
identity = RNS.Identity()
|
||
|
identity.to_file(identitypath)
|
||
|
|
||
|
return identity
|
||
|
|
||
|
def lxmf_delivery(message):
|
||
|
# Do something here with a received message
|
||
|
RNS.log("A message was received: "+str(message.content.decode('utf-8')))
|
||
|
|
||
|
message_content = message.content.decode('utf-8')
|
||
|
source_hash_text = RNS.hexrep(message.source_hash, delimit=False)
|
||
|
|
||
|
#Create username (just first 5 char of your addr)
|
||
|
username = source_hash_text[0:5]
|
||
|
|
||
|
RNS.log('Username: {}'.format(username), RNS.LOG_INFO)
|
||
|
|
||
|
time_string = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(message.timestamp))
|
||
|
new_message = '{} {}: {}\n'.format(time_string, username, message_content)
|
||
|
|
||
|
# Push message to board
|
||
|
# First read message board (if it exists
|
||
|
if os.path.isfile(boardpath):
|
||
|
f = open(boardpath, "rb")
|
||
|
message_board = msgpack.unpack(f)
|
||
|
f.close()
|
||
|
else:
|
||
|
message_board = []
|
||
|
|
||
|
#Check we aren't doubling up (this can sometimes happen if there is an error initially and it then gets fixed)
|
||
|
if new_message not in message_board:
|
||
|
# Append our new message to the list
|
||
|
message_board.append(new_message)
|
||
|
|
||
|
# Prune the message board if needed
|
||
|
while len(message_board) > max_messages:
|
||
|
RNS.log('Pruning Message Board')
|
||
|
message_board.pop(0)
|
||
|
|
||
|
# Now open the board and write the updated list
|
||
|
f = open(boardpath, "wb")
|
||
|
msgpack.pack(message_board, f)
|
||
|
f.close()
|
||
|
|
||
|
# Send reply
|
||
|
message_reply = '{}_{}_Your message has been added to the messageboard'.format(source_hash_text, time.time())
|
||
|
q.put(message_reply)
|
||
|
|
||
|
def announce_now(lxmf_destination):
|
||
|
lxmf_destination.announce()
|
||
|
|
||
|
def send_message(destination_hash, message_content):
|
||
|
try:
|
||
|
# Make a binary destination hash from a hexadecimal string
|
||
|
destination_hash = bytes.fromhex(destination_hash)
|
||
|
|
||
|
except Exception as e:
|
||
|
RNS.log("Invalid destination hash", RNS.LOG_ERROR)
|
||
|
return
|
||
|
|
||
|
# Check that size is correct
|
||
|
if not len(destination_hash) == RNS.Reticulum.TRUNCATED_HASHLENGTH//8:
|
||
|
RNS.log("Invalid destination hash length", RNS.LOG_ERROR)
|
||
|
|
||
|
else:
|
||
|
# Length of address was correct, let's try to recall the
|
||
|
# corresponding Identity
|
||
|
destination_identity = RNS.Identity.recall(destination_hash)
|
||
|
|
||
|
if destination_identity == None:
|
||
|
# No path/identity known, we'll have to abort or request one
|
||
|
RNS.log("Could not recall an Identity for the requested address. You have probably never received an announce from it. Try requesting a path from the network first. In fact, let's do this now :)", RNS.LOG_ERROR)
|
||
|
RNS.Transport.request_path(destination_hash)
|
||
|
RNS.log("OK, a path was requested. If the network knows a path, you will receive an announce with the Identity data shortly.", RNS.LOG_INFO)
|
||
|
|
||
|
else:
|
||
|
# We know the identity for the destination hash, let's
|
||
|
# reconstruct a destination object.
|
||
|
lxmf_destination = RNS.Destination(destination_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "lxmf", "delivery")
|
||
|
|
||
|
# Create a new message object
|
||
|
lxm = LXMF.LXMessage(lxmf_destination, local_lxmf_destination, message_content, title="Reply", desired_method=LXMF.LXMessage.DIRECT)
|
||
|
|
||
|
# You can optionally tell LXMF to try to send the message
|
||
|
# as a propagated message if a direct link fails
|
||
|
lxm.try_propagation_on_fail = True
|
||
|
|
||
|
# Send it
|
||
|
message_router.handle_outbound(lxm)
|
||
|
|
||
|
def announce_check():
|
||
|
if os.path.isfile(announcepath):
|
||
|
f = open(announcepath, "r")
|
||
|
announce = int(f.readline())
|
||
|
f.close()
|
||
|
else:
|
||
|
RNS.log('failed to open announcepath', RNS.LOG_DEBUG)
|
||
|
announce = 1
|
||
|
|
||
|
if announce > int(time.time()):
|
||
|
RNS.log('Recent announcement', RNS.LOG_DEBUG)
|
||
|
else:
|
||
|
f = open(announcepath, "w")
|
||
|
next_announce = int(time.time()) + 1800
|
||
|
f.write(str(next_announce))
|
||
|
f.close()
|
||
|
announce_now(local_lxmf_destination)
|
||
|
RNS.log('Announcement sent, expr set 1800 seconds', RNS.LOG_INFO)
|
||
|
|
||
|
#Setup Paths and Config Files
|
||
|
userdir = os.path.expanduser("~")
|
||
|
|
||
|
if os.path.isdir("/etc/nomadmb") and os.path.isfile("/etc/nomadmb/config"):
|
||
|
configdir = "/etc/nomadmb"
|
||
|
elif os.path.isdir(userdir+"/.config/nomadmb") and os.path.isfile(userdir+"/.config/nomadmb/config"):
|
||
|
configdir = userdir+"/.config/nomadmb"
|
||
|
else:
|
||
|
configdir = userdir+"/.nomadmb"
|
||
|
|
||
|
storagepath = configdir+"/storage"
|
||
|
if not os.path.isdir(storagepath):
|
||
|
os.makedirs(storagepath)
|
||
|
|
||
|
identitypath = configdir+"/storage/identity"
|
||
|
announcepath = configdir+"/storage/announce"
|
||
|
boardpath = configdir+"/storage/board"
|
||
|
|
||
|
# Message Queue
|
||
|
q = Queue(maxsize = 5)
|
||
|
|
||
|
# Start Reticulum and print out all the debug messages
|
||
|
reticulum = RNS.Reticulum(loglevel=RNS.LOG_VERBOSE)
|
||
|
|
||
|
# Create a Identity.
|
||
|
current_identity = setup_lxmf()
|
||
|
|
||
|
# Init the LXMF router
|
||
|
message_router = LXMF.LXMRouter(identity = current_identity, storagepath = configdir)
|
||
|
|
||
|
# Register a delivery destination (for yourself)
|
||
|
# In this example we use the same Identity as we used
|
||
|
# to instantiate the LXMF router. It could be a different one,
|
||
|
# but it can also just be the same, depending on what you want.
|
||
|
local_lxmf_destination = message_router.register_delivery_identity(current_identity, display_name=display_name)
|
||
|
|
||
|
# Set a callback for when a message is received
|
||
|
message_router.register_delivery_callback(lxmf_delivery)
|
||
|
|
||
|
# Announce node properties
|
||
|
|
||
|
RNS.log('LXMF Router ready to receive on: {}'.format(RNS.prettyhexrep(local_lxmf_destination.hash)), RNS.LOG_INFO)
|
||
|
announce_check()
|
||
|
|
||
|
while True:
|
||
|
|
||
|
# Work through internal message queue
|
||
|
for i in list(q.queue):
|
||
|
message_id = q.get()
|
||
|
split_message = message_id.split('_')
|
||
|
destination_hash = split_message[0]
|
||
|
message = split_message[2]
|
||
|
RNS.log('{} {}'.format(destination_hash, message), RNS.LOG_INFO)
|
||
|
send_message(destination_hash, message)
|
||
|
|
||
|
# Check whether we need to make another announcement
|
||
|
announce_check()
|
||
|
|
||
|
#Sleep
|
||
|
time.sleep(10)
|