Conversation service basic functionality implemented

This commit is contained in:
Mark Qvist 2021-05-13 16:38:59 +02:00
parent e7339cbabc
commit 6db7ea7fe0

View File

@ -1,48 +1,147 @@
import os import os
import RNS import RNS
import LXMF import LXMF
import shutil
from nomadnet.Directory import DirectoryEntry
class Conversation: class Conversation:
cached_conversations = {}
created_callback = None
@staticmethod @staticmethod
def ingest(lxmessage, app): def ingest(lxmessage, app, originator = False, delegate = None):
source_hash_path = RNS.hexrep(lxmessage.source_hash, delimit=False) if originator:
source_hash = lxmessage.destination_hash
else:
source_hash = lxmessage.source_hash
source_hash_path = RNS.hexrep(source_hash, delimit=False)
conversation_path = app.conversationpath + "/" + source_hash_path conversation_path = app.conversationpath + "/" + source_hash_path
if not os.path.isdir(conversation_path): if not os.path.isdir(conversation_path):
os.makedirs(conversation_path) os.makedirs(conversation_path)
if Conversation.created_callback != None:
Conversation.created_callback()
lxmessage.write_to_directory(conversation_path) ingested_path = lxmessage.write_to_directory(conversation_path)
if RNS.hexrep(source_hash, delimit=False) in Conversation.cached_conversations:
conversation = Conversation.cached_conversations[RNS.hexrep(source_hash, delimit=False)]
conversation.scan_storage()
return ingested_path
@staticmethod @staticmethod
def conversation_list(app): def conversation_list(app):
conversations = [] conversations = []
for entry in os.listdir(app.conversationpath): for dirname in os.listdir(app.conversationpath):
if len(entry) == RNS.Identity.TRUNCATED_HASHLENGTH//8*2 and os.path.isdir(app.conversationpath + "/" + entry): if len(dirname) == RNS.Identity.TRUNCATED_HASHLENGTH//8*2 and os.path.isdir(app.conversationpath + "/" + dirname):
try: try:
source_hash_text = dirname
source_hash = bytes.fromhex(dirname)
app_data = RNS.Identity.recall_app_data(source_hash)
display_name = app.directory.display_name(source_hash)
if display_name == None and app_data:
display_name = app_data.decode("utf-8")
trust_level = app.directory.trust_level(source_hash, display_name)
entry = (source_hash_text, display_name, trust_level)
conversations.append(entry) conversations.append(entry)
except Exception as e: except Exception as e:
RNS.log("Error while loading conversation "+str(entry)+", skipping it. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("Error while loading conversation "+str(dirname)+", skipping it. The contained exception was: "+str(e), RNS.LOG_ERROR)
conversations.sort(key=lambda e: (-e[2], e[1], e[0]), reverse=False)
return conversations return conversations
@staticmethod
def cache_conversation(conversation):
Conversation.cached_conversations[conversation.source_hash] = conversation
@staticmethod
def delete_conversation(source_hash_path, app):
conversation_path = app.conversationpath + "/" + source_hash_path
try:
if os.path.isdir(conversation_path):
shutil.rmtree(conversation_path)
except Exception as e:
RNS.log("Could not remove conversation at "+str(conversation_path)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
def __init__(self, source_hash, app): def __init__(self, source_hash, app):
self.app = app
self.source_hash = source_hash self.source_hash = source_hash
self.send_destination = None
self.messages = []
self.messages_path = app.conversationpath + "/" + source_hash self.messages_path = app.conversationpath + "/" + source_hash
self.messages_load_time = None self.messages_load_time = None
self.messages = []
self.source_known = False self.source_known = False
self.source_trusted = False self.source_trusted = False
self.source_blocked = False self.source_blocked = False
self.__changed_callback = None
self.source_identity = RNS.Identity.recall(bytes.fromhex(self.source_hash))
if self.source_identity:
self.source_known = True
self.send_destination = RNS.Destination(self.source_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "lxmf", "delivery")
self.scan_storage()
self.trust_level = app.directory.trust_level(bytes.fromhex(self.source_hash))
Conversation.cache_conversation(self)
def scan_storage(self):
old_len = len(self.messages)
self.messages = []
for filename in os.listdir(self.messages_path): for filename in os.listdir(self.messages_path):
if len(filename) == RNS.Identity.HASHLENGTH//8*2: if len(filename) == RNS.Identity.HASHLENGTH//8*2:
message_path = self.messages_path + "/" + filename message_path = self.messages_path + "/" + filename
self.messages.append(ConversationMessage(message_path)) self.messages.append(ConversationMessage(message_path))
if self.__changed_callback != None:
self.__changed_callback(self)
def __str__(self): def __str__(self):
return self.source_hash string = self.source_hash
if self.source_identity:
if self.source_identity.app_data:
# TODO: Sanitise for viewing
string += " | "+self.source.source_identity.app_data.decode("utf-8")
return string
def register_changed_callback(self, callback):
self.__changed_callback = callback
def send(self, content="", title=""):
if self.send_destination:
dest = self.send_destination
source = self.app.lxmf_destination
lxm = LXMF.LXMessage(dest, source, content, desired_method=LXMF.LXMessage.DIRECT)
lxm.register_delivery_callback(self.message_delivered)
self.app.message_router.handle_outbound(lxm)
message_path = Conversation.ingest(lxm, self.app, originator=True)
self.messages.append(ConversationMessage(message_path))
else:
# TODO: Implement
# Alter UI so message cannot be sent until there is a path, or LXMF propagation is implemented
RNS.log("Destination unknown")
def message_delivered(self, message):
message_path = Conversation.ingest(message, self.app, originator=True)
class ConversationMessage: class ConversationMessage:
def __init__(self, file_path): def __init__(self, file_path):
@ -63,6 +162,12 @@ class ConversationMessage:
self.loaded = False self.loaded = False
self.lxm = None self.lxm = None
def get_timestamp(self):
if not self.loaded:
self.load()
return self.timestamp
def get_title(self): def get_title(self):
if not self.loaded: if not self.loaded:
self.load() self.load()