161 lines
6.1 KiB
Python
161 lines
6.1 KiB
Python
import os
|
|
from datetime import datetime, timezone
|
|
import json
|
|
import time
|
|
|
|
from libs.fspn.utils.wrapper_util import threaded
|
|
from libs.app.common.logging import get_logger
|
|
from libs.fspn.utils.sha256_util import hash_file, hash_bytes, hash_string
|
|
from libs.noSys.noSysModule import NoSysModule
|
|
from libs.noSys.events import Events as nosys_events, DynamicEvents as nosys_dynamic_events
|
|
from libs.noSys.peers import Peer
|
|
from libs.p2post.p2post import P2post
|
|
from .networks import Networks
|
|
from .dataManager import DataManager
|
|
from .p2privateApiBlueprint import Blueprint
|
|
|
|
logger = get_logger()
|
|
|
|
class P2private(NoSysModule):
|
|
def __init__(self, noSys):
|
|
super().__init__(noSys)
|
|
self.nosys_core.subscribe_event(nosys_events.USER_ADDED, self.on_user_added)
|
|
|
|
self.data = DataManager()
|
|
self.p2post:P2post = None
|
|
self.networks_module:Networks = None
|
|
self.requested_signatures = {}
|
|
|
|
self.friends_state = {}
|
|
|
|
def setup(self):
|
|
self.p2post = self.nosys_core.modules.get("p2post", "p2post")
|
|
self.networks_module = self.nosys_core.modules.get("p2private", "networks")
|
|
self.nosys_core.modules.api.register_blueprint(Blueprint(self).blueprint)
|
|
|
|
def on_nosys_ready(self, event):
|
|
pass
|
|
|
|
def add_friend(self, pubkey, relays):
|
|
self.data.add_friend(pubkey, relays)
|
|
self.set_friend_state(pubkey)
|
|
|
|
def on_user_added(self, event):
|
|
user_id:str = event.user_id
|
|
self.manage_friends(user_id)
|
|
|
|
@threaded
|
|
def manage_friends(self, my_user):
|
|
for friend in self.data.list_friends():
|
|
self.set_friend_state(friend["pubkey"])
|
|
while True:
|
|
for friend in self.data.list_friends():
|
|
self.manage_friend(friend)
|
|
time.sleep(10)
|
|
|
|
def set_friend_state(self, friend_pubkey):
|
|
# TODO Add friends inside users object data
|
|
self.friends_state[friend_pubkey] = {
|
|
"id": friend_pubkey,
|
|
"status": "dis", # TODO Enum
|
|
}
|
|
|
|
def manage_friend(self, user):
|
|
friend_id = user["pubkey"]
|
|
state = self.friends_state[friend_id]
|
|
|
|
if state["status"] == "dis":
|
|
print("FRIEND IS DISCONNECTED")
|
|
for relay in user["relays"]:
|
|
if self.networks_module.network_states.get(relay):
|
|
print(f"Connected to relay {relay}")
|
|
# TODO Check if friend is on in the network and send message to connect to a rendezvous
|
|
elif state["status"] == "con":
|
|
print("FRIEND IS CONNECTED")
|
|
|
|
def on_module_message(self, event):
|
|
handler_action = getattr(self, 'on_'+event.data.get("action"))
|
|
handler_action(event)
|
|
|
|
def create_message(self, from_user, to_user, content, medias):
|
|
medias_data = []
|
|
for media in medias:
|
|
if media["type"] == "local":
|
|
file_hash = os.path.splitext(os.path.basename(media["file_path"]))[0]
|
|
medias_data.append({"type":"local", "hash":file_hash})
|
|
self.p2post.data.add_media(file_hash, media["file_path"])
|
|
else:
|
|
medias_data.append({"type":media["type"], "url":media["url"]})
|
|
|
|
current_utc_datetime = datetime.now(timezone.utc)
|
|
utc_timestamp = current_utc_datetime.timestamp()
|
|
message = {
|
|
"timestamp": utc_timestamp,
|
|
"from": from_user,
|
|
"to": to_user,
|
|
"content": content, #TODO Hash it
|
|
"medias": medias_data
|
|
}
|
|
message_serialized = json.dumps(message, sort_keys=True, separators=(",", ":"))
|
|
|
|
message["hash"] = hash_bytes(message_serialized.encode('utf-8'))
|
|
request_id = self.nosys_core.modules.pmc.sign(message["hash"], from_user, self.signature_callback, f"New Message {message}")
|
|
self.requested_signatures[request_id] = message
|
|
logger.debug(f"Message waiting signature {request_id}: {message}")
|
|
return message["hash"]
|
|
|
|
def signature_callback(self, request_id, signature):
|
|
message = self.requested_signatures.get(request_id)
|
|
if signature and message:
|
|
message["signature"] = signature
|
|
self.send_message(message)
|
|
|
|
def send_message(self, message):
|
|
self.data.add_message(message)
|
|
direct_connections = self.nosys_core.peers.get_by_peer_user_id(message["to"])
|
|
if direct_connections:
|
|
logger.debug(f"Friend {message['to']} direct connected")
|
|
for peer in direct_connections:
|
|
# TODO Maybe check 'from user', peers can set to just receive message from the user in connection and networks
|
|
self.send_private_message(message, peer.id)
|
|
else:
|
|
logger.debug(f"Friend {message['to']} not direct connected. Posting message to user relays.")
|
|
to_user = message["to"]
|
|
relay_networks = self.get_friend_relay_networks(to_user) # TODO get from store
|
|
if not relay_networks:
|
|
logger.error(f"Not found a network relay to friend {to_user}")
|
|
# TODO Update message status
|
|
else:
|
|
self.networks_module.post_network_message(message, relay_networks)
|
|
|
|
def send_private_message(self, message, peer_id):
|
|
payload = {'action':'private_message','message':message}
|
|
self.nosys_core.dispatcher.send_message(payload, peer_id, self.id)
|
|
|
|
def on_private_message(self, event):
|
|
data = event.data
|
|
message = data["message"]
|
|
if not self.data.get_message(message["hash"]):
|
|
self.data.add_message(message)
|
|
logger.debug(f"New message received {message}")
|
|
else:
|
|
logger.debug("Message already exists")
|
|
|
|
# Read receipts ???? Talk to friends about it
|
|
|
|
def get_friend_relay_networks(self, user_id):
|
|
user = self.data.get_friend(user_id)
|
|
if user:
|
|
return user["relays"]
|
|
return []
|
|
|
|
def on_module_connection(self, event):
|
|
peer:Peer = event.peer
|
|
peer_user = peer.connection.security.peer_user
|
|
state = self.friends_state.get(peer_user)
|
|
if state:
|
|
state["status"] = "con"
|
|
|
|
|
|
|
|
|