Files
nosys_libs/p2private/p2private.py
2026-01-25 13:55:46 +10:00

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"