diff --git a/.gitignore b/.gitignore index 6769e21..68bc17f 100644 --- a/.gitignore +++ b/.gitignore @@ -157,4 +157,4 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ \ No newline at end of file +#.idea/ diff --git a/CHANGELOG.md b/CHANGELOG.md index dfbdddc..6c5556b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,7 @@ **Implemented enhancements:** -- add site\_id [\#74](https://github.com/JarbasHiveMind/HiveMind-core/pull/74) ([JarbasAl](https://github.com/JarbasAl)) +- add site_id [\#74](https://github.com/JarbasHiveMind/HiveMind-core/pull/74) ([JarbasAl](https://github.com/JarbasAl)) ## [V0.13.0a0](https://github.com/JarbasHiveMind/HiveMind-core/tree/V0.13.0a0) (2023-09-08) @@ -100,7 +100,7 @@ **Merged pull requests:** -- filter allowed\_types [\#71](https://github.com/JarbasHiveMind/HiveMind-core/pull/71) ([emphasize](https://github.com/emphasize)) +- filter allowed_types [\#71](https://github.com/JarbasHiveMind/HiveMind-core/pull/71) ([emphasize](https://github.com/emphasize)) - add typehints [\#70](https://github.com/JarbasHiveMind/HiveMind-core/pull/70) ([emphasize](https://github.com/emphasize)) - \[requirements\] Add missing pyOpenSSL [\#69](https://github.com/JarbasHiveMind/HiveMind-core/pull/69) ([goldyfruit](https://github.com/goldyfruit)) @@ -130,7 +130,7 @@ - Xdg [\#51](https://github.com/JarbasHiveMind/HiveMind-core/pull/51) ([JarbasAl](https://github.com/JarbasAl)) - V2 [\#50](https://github.com/JarbasHiveMind/HiveMind-core/pull/50) ([JarbasAl](https://github.com/JarbasAl)) - Refactor/hivemind presence [\#49](https://github.com/JarbasHiveMind/HiveMind-core/pull/49) ([JarbasAl](https://github.com/JarbasAl)) -- refactor/deprecate\_sql [\#48](https://github.com/JarbasHiveMind/HiveMind-core/pull/48) ([JarbasAl](https://github.com/JarbasAl)) +- refactor/deprecate_sql [\#48](https://github.com/JarbasHiveMind/HiveMind-core/pull/48) ([JarbasAl](https://github.com/JarbasAl)) - launcher scripts / deprecate mail param / increase RSA key size [\#46](https://github.com/JarbasHiveMind/HiveMind-core/pull/46) ([Joanguitar](https://github.com/Joanguitar)) - move to asyncio [\#41](https://github.com/JarbasHiveMind/HiveMind-core/pull/41) ([JarbasAl](https://github.com/JarbasAl)) @@ -162,7 +162,7 @@ - migrate to pycryptodomex [\#30](https://github.com/JarbasHiveMind/HiveMind-core/pull/30) ([JarbasAl](https://github.com/JarbasAl)) - Add instructions to README [\#27](https://github.com/JarbasHiveMind/HiveMind-core/pull/27) ([ChanceNCounter](https://github.com/ChanceNCounter)) -- migrate from jarbas\_utils to ovos\_utils [\#24](https://github.com/JarbasHiveMind/HiveMind-core/pull/24) ([JarbasAl](https://github.com/JarbasAl)) +- migrate from jarbas_utils to ovos_utils [\#24](https://github.com/JarbasHiveMind/HiveMind-core/pull/24) ([JarbasAl](https://github.com/JarbasAl)) - revert cryptodomex requirement change [\#21](https://github.com/JarbasHiveMind/HiveMind-core/pull/21) ([JarbasAl](https://github.com/JarbasAl)) - Switch from pycryptodome to pycryptodomex [\#12](https://github.com/JarbasHiveMind/HiveMind-core/pull/12) ([j1nx](https://github.com/j1nx)) - escalate [\#8](https://github.com/JarbasHiveMind/HiveMind-core/pull/8) ([JarbasAl](https://github.com/JarbasAl)) @@ -171,6 +171,4 @@ - Feat/emulation [\#5](https://github.com/JarbasHiveMind/HiveMind-core/pull/5) ([JarbasAl](https://github.com/JarbasAl)) - refactor + http support [\#4](https://github.com/JarbasHiveMind/HiveMind-core/pull/4) ([JarbasAl](https://github.com/JarbasAl)) - - -\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* +\* _This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)_ diff --git a/examples/fakecroft.py b/examples/fakecroft.py index 57d9a53..73e1b91 100644 --- a/examples/fakecroft.py +++ b/examples/fakecroft.py @@ -3,12 +3,16 @@ from ovos_utils.log import LOG from ovos_utils.messagebus import FakeBus from hivemind_listener.service import MessageBusEventHandler -from hivemind_listener.protocol import HiveMindListenerProtocol, \ - HiveMindListenerInternalProtocol, HiveMindClientConnection +from hivemind_listener.protocol import ( + HiveMindListenerProtocol, + HiveMindListenerInternalProtocol, + HiveMindClientConnection, +) class HiveMindFakeCroftProtocol(HiveMindListenerProtocol): - """ Fake ovos-core instance, not actually connected to a messagebus""" + """Fake ovos-core instance, not actually connected to a messagebus""" + peer: str = "fakecroft:0.0.0.0" def bind(self, websocket, bus=None): @@ -17,28 +21,31 @@ def bind(self, websocket, bus=None): bus = FakeBus() self.internal_protocol = HiveMindListenerInternalProtocol(bus) - def handle_incoming_mycroft(self, message: Message, client: HiveMindClientConnection): + def handle_incoming_mycroft( + self, message: Message, client: HiveMindClientConnection + ): """ message (Message): mycroft bus message object """ super().handle_inject_mycroft_msg(message, client) answer = "mycroft is dead! long live mycroft!" - payload = HiveMessage(HiveMessageType.BUS, - message.reply("speak", {"utterance": answer})) + payload = HiveMessage( + HiveMessageType.BUS, message.reply("speak", {"utterance": answer}) + ) client.send(payload) def on_ready(): - LOG.info('FakeCroft started!') + LOG.info("FakeCroft started!") -def on_error(e='Unknown'): - LOG.info('FakeCroft failed to start ({})'.format(repr(e))) +def on_error(e="Unknown"): + LOG.info("FakeCroft failed to start ({})".format(repr(e))) def on_stopping(): - LOG.info('FakeCroft is shutting down...') + LOG.info("FakeCroft is shutting down...") def main(ready_hook=on_ready, error_hook=on_error, stopping_hook=on_stopping): @@ -46,17 +53,17 @@ def main(ready_hook=on_ready, error_hook=on_error, stopping_hook=on_stopping): from tornado import web, ioloop from ovos_config import Configuration - LOG.info('Starting FakeCroft...') + LOG.info("Starting FakeCroft...") try: - websocket_configs = Configuration()['websocket'] + websocket_configs = Configuration()["websocket"] except KeyError as ke: - LOG.error('No websocket configs found ({})'.format(repr(ke))) + LOG.error("No websocket configs found ({})".format(repr(ke))) raise - host = websocket_configs.get('host') - port = websocket_configs.get('port') - route = websocket_configs.get('route') + host = websocket_configs.get("host") + port = websocket_configs.get("port") + route = websocket_configs.get("route") port = 5678 route = "/" diff --git a/hivemind_core/database.py b/hivemind_core/database.py index 368f549..17b1d90 100644 --- a/hivemind_core/database.py +++ b/hivemind_core/database.py @@ -7,27 +7,36 @@ def cast_to_client_obj(): - valid_kwargs: Iterable[str] = ("client_id", "api_key", "name", - "description", "is_admin", "last_seen", - "blacklist", "allowed_types", "crypto_key", - "password", "can_broadcast", "can_escalate", - "can_propagate") + valid_kwargs: Iterable[str] = ( + "client_id", + "api_key", + "name", + "description", + "is_admin", + "last_seen", + "blacklist", + "allowed_types", + "crypto_key", + "password", + "can_broadcast", + "can_escalate", + "can_propagate", + ) def _handler(func): - def _cast(ret): if ret is None or isinstance(ret, Client): return ret if isinstance(ret, list): return [_cast(r) for r in ret] if isinstance(ret, dict): - if not all((k in valid_kwargs - for k in ret.keys())): + if not all((k in valid_kwargs for k in ret.keys())): raise RuntimeError(f"{func} returned a dict with unknown keys") return Client(**ret) raise TypeError( - "cast_to_client_obj decorator can only be used in functions that return None, dict, Client or a list of those types") + "cast_to_client_obj decorator can only be used in functions that return None, dict, Client or a list of those types" + ) @wraps(func) def call_function(*args, **kwargs): @@ -40,21 +49,22 @@ def call_function(*args, **kwargs): class Client: - def __init__(self, - client_id: int, - api_key: str, - name: str = "", - description: str = "", - is_admin: bool = False, - last_seen: float = -1, - blacklist: Optional[Dict[str, List[str]]] = None, - allowed_types: Optional[List[str]] = None, - crypto_key: Optional[str] = None, - password: Optional[str] = None, - can_broadcast: bool = True, - can_escalate: bool = True, - can_propagate: bool = True): - + def __init__( + self, + client_id: int, + api_key: str, + name: str = "", + description: str = "", + is_admin: bool = False, + last_seen: float = -1, + blacklist: Optional[Dict[str, List[str]]] = None, + allowed_types: Optional[List[str]] = None, + crypto_key: Optional[str] = None, + password: Optional[str] = None, + can_broadcast: bool = True, + can_escalate: bool = True, + can_propagate: bool = True, + ): self.client_id = client_id self.description = description self.api_key = api_key @@ -63,11 +73,7 @@ def __init__(self, self.is_admin = is_admin self.crypto_key = crypto_key self.password = password - self.blacklist = blacklist or { - "messages": [], - "skills": [], - "intents": [] - } + self.blacklist = blacklist or {"messages": [], "skills": [], "intents": []} self.allowed_types = allowed_types or ["recognizer_loop:utterance"] if "recognizer_loop:utterance" not in self.allowed_types: self.allowed_types.append("recognizer_loop:utterance") @@ -155,9 +161,7 @@ def change_name(self, new_name: str, key: str) -> bool: self.update_item(item_id, user) return True - def change_blacklist(self, - blacklist: Union[str, Dict[str, Any]], - key: str) -> bool: + def change_blacklist(self, blacklist: Union[str, Dict[str, Any]], key: str) -> bool: if isinstance(blacklist, dict): blacklist = json.dumps(blacklist) user = self.get_client_by_api_key(key) @@ -186,15 +190,16 @@ def get_clients_by_name(self, name: str) -> List[Client]: return self.search_by_value("name", name) @cast_to_client_obj() - def add_client(self, - name: str, - key: str = "", - admin: bool = False, - blacklist: Optional[Dict[str, Any]] = None, - allowed_types: Optional[List[str]] = None, - crypto_key: Optional[str] = None, - password: Optional[str] = None) -> Client: - + def add_client( + self, + name: str, + key: str = "", + admin: bool = False, + blacklist: Optional[Dict[str, Any]] = None, + allowed_types: Optional[List[str]] = None, + crypto_key: Optional[str] = None, + password: Optional[str] = None, + ) -> Client: user = self.get_client_by_api_key(key) item_id = self.get_item_id(user) if crypto_key is not None: @@ -214,11 +219,16 @@ def add_client(self, user["password"] = password self.update_item(item_id, user) else: - user = Client(api_key=key, name=name, - blacklist=blacklist, crypto_key=crypto_key, - client_id=self.total_clients() + 1, - is_admin=admin, password=password, - allowed_types=allowed_types) + user = Client( + api_key=key, + name=name, + blacklist=blacklist, + crypto_key=crypto_key, + client_id=self.total_clients() + 1, + is_admin=admin, + password=password, + allowed_types=allowed_types, + ) self.add_item(user) return user @@ -226,11 +236,11 @@ def total_clients(self) -> int: return len(self) def __enter__(self): - """ Context handler """ + """Context handler""" return self def __exit__(self, _type, value, traceback): - """ Commits changes and Closes the session """ + """Commits changes and Closes the session""" try: self.commit() except Exception as e: diff --git a/hivemind_core/protocol.py b/hivemind_core/protocol.py index 36ce398..aa56b92 100644 --- a/hivemind_core/protocol.py +++ b/hivemind_core/protocol.py @@ -13,7 +13,12 @@ from hivemind_bus_client.message import HiveMessage, HiveMessageType from hivemind_bus_client.serialization import decode_bitstring, get_bitstring -from hivemind_bus_client.util import decrypt_bin, encrypt_bin, decrypt_from_json, encrypt_as_json +from hivemind_bus_client.util import ( + decrypt_bin, + encrypt_bin, + decrypt_from_json, + encrypt_as_json, +) class ProtocolVersion(IntEnum): @@ -43,7 +48,8 @@ class HiveMindNodeType(str, Enum): @dataclass class HiveMindClientConnection: - """ represents a connection to the hivemind listener """ + """represents a connection to the hivemind listener""" + key: str ip: str loop: ioloop.IOLoop @@ -54,8 +60,12 @@ class HiveMindClientConnection: pswd_handshake: Optional[PasswordHandShake] = None socket: Optional[WebSocketHandler] = None crypto_key: Optional[str] = None - blacklist: List[str] = field(default_factory=list) # list of ovos message_type to never be sent to this client - allowed_types: List[str] = field(default_factory=list) # list of ovos message_type to allow to be sent from this client + blacklist: List[str] = field( + default_factory=list + ) # list of ovos message_type to never be sent to this client + allowed_types: List[str] = field( + default_factory=list + ) # list of ovos message_type to allow to be sent from this client binarize: bool = False site_id: str = "unknown" can_broadcast: bool = True @@ -76,22 +86,27 @@ def send(self, message: HiveMessage): _msg_type = message.payload.msg_type if _msg_type in self.blacklist: - return LOG.debug(f"message type {_msg_type} " - f"is blacklisted for {self.peer}") + return LOG.debug( + f"message type {_msg_type} " f"is blacklisted for {self.peer}" + ) LOG.debug(f"sending to {self.peer}: {message.msg_type}") if message.msg_type == HiveMessageType.BUS: LOG.debug(f"mycroft_type {_msg_type}") payload = message.serialize() # json string is_bin = False - if self.crypto_key and message.msg_type not in [HiveMessageType.HANDSHAKE, - HiveMessageType.HELLO]: + if self.crypto_key and message.msg_type not in [ + HiveMessageType.HANDSHAKE, + HiveMessageType.HELLO, + ]: if self.binarize: payload = get_bitstring(message.msg_type, message.payload).bytes payload = encrypt_bin(self.crypto_key, payload) is_bin = True else: - payload = encrypt_as_json(self.crypto_key, payload) # still a json string + payload = encrypt_as_json( + self.crypto_key, payload + ) # still a json string LOG.debug(f"encrypted payload: {len(payload)}") else: LOG.debug(f"sent unencrypted!") @@ -120,7 +135,7 @@ def decode(self, payload: str) -> HiveMessage: return HiveMessage(**payload) def authorize(self, message: Message) -> bool: - """ parse the message being injected into ovos-core bus + """parse the message being injected into ovos-core bus if this client is not authorized to inject it return False""" if message.msg_type not in self.allowed_types: return False @@ -132,7 +147,8 @@ def authorize(self, message: Message) -> bool: @dataclass() class HiveMindListenerInternalProtocol: - """ this class handles all interactions between a hivemind listener and a ovos-core messagebus""" + """this class handles all interactions between a hivemind listener and a ovos-core messagebus""" + bus: MessageBusClient def register_bus_handlers(self): @@ -146,7 +162,7 @@ def clients(self) -> Dict[str, HiveMindClientConnection]: # mycroft handlers - from master -> slave def handle_send(self, message: Message): - """ ovos wants to send a HiveMessage + """ovos wants to send a HiveMessage a device can be both a master and a slave, downstream messages are handled here @@ -157,9 +173,7 @@ def handle_send(self, message: Message): peer = message.data.get("peer") msg_type = message.data["msg_type"] - hmessage = HiveMessage(msg_type, - payload=payload, - target_peers=[peer]) + hmessage = HiveMessage(msg_type, payload=payload, target_peers=[peer]) if msg_type in [HiveMessageType.PROPAGATE, HiveMessageType.BROADCAST]: # this message is meant to be sent to all slave nodes @@ -180,12 +194,15 @@ def handle_send(self, message: Message): client.send(hmessage) else: LOG.error("That client is not connected") - self.bus.emit(message.forward( - "hive.client.send.error", - {"error": "That client is not connected", "peer": peer})) + self.bus.emit( + message.forward( + "hive.client.send.error", + {"error": "That client is not connected", "peer": peer}, + ) + ) def handle_internal_mycroft(self, message: str): - """ forward internal messages to clients if they are the target + """forward internal messages to clients if they are the target here is where the client isolation happens, clients only get responses to their own messages""" @@ -208,10 +225,12 @@ def handle_internal_mycroft(self, message: str): # forward internal messages to clients if they are the target LOG.debug(f"{message.msg_type} - destination: {peer}") message.context["source"] = "hive" - msg = HiveMessage(HiveMessageType.BUS, - source_peer=peer, - target_peers=target_peers, - payload=message) + msg = HiveMessage( + HiveMessageType.BUS, + source_peer=peer, + target_peers=target_peers, + payload=message, + ) client.send(msg) @@ -246,23 +265,32 @@ def get_bus(self, client: HiveMindClientConnection): def handle_new_client(self, client: HiveMindClientConnection): LOG.debug(f"new client: {client.peer}") self.clients[client.peer] = client - message = Message("hive.client.connect", - {"ip": client.ip, "session_id": client.sess.session_id}, - {"source": client.peer}) + message = Message( + "hive.client.connect", + {"ip": client.ip, "session_id": client.sess.session_id}, + {"source": client.peer}, + ) bus = self.get_bus(client) bus.emit(message) - min_version = ProtocolVersion.ONE if client.crypto_key is None and self.require_crypto \ + min_version = ( + ProtocolVersion.ONE + if client.crypto_key is None and self.require_crypto else ProtocolVersion.ZERO + ) max_version = ProtocolVersion.ONE - msg = HiveMessage(HiveMessageType.HELLO, - payload={"pubkey": client.handshake.pubkey, - # allows any node to verify messages are signed with this - "peer": client.peer, # this identifies the connected client in ovos message.context - "node_id": self.peer, - "session_id": client.sess.session_id}) + msg = HiveMessage( + HiveMessageType.HELLO, + payload={ + "pubkey": client.handshake.pubkey, + # allows any node to verify messages are signed with this + "peer": client.peer, # this identifies the connected client in ovos message.context + "node_id": self.peer, + "session_id": client.sess.session_id, + }, + ) LOG.debug(f"saying HELLO to: {client.peer}") client.send(msg) @@ -274,9 +302,11 @@ def handle_new_client(self, client: HiveMindClientConnection): "min_protocol_version": min_version, "max_protocol_version": max_version, "binarize": True, # report we support the binarization scheme - "preshared_key": client.crypto_key is not None, # do we have a pre-shared key (V0 proto) - "password": client.pswd_handshake is not None, # is password available (V1 proto, replaces pre-shared key) - "crypto_required": self.require_crypto # do we allow unencrypted payloads + "preshared_key": client.crypto_key + is not None, # do we have a pre-shared key (V0 proto) + "password": client.pswd_handshake + is not None, # is password available (V1 proto, replaces pre-shared key) + "crypto_required": self.require_crypto, # do we allow unencrypted payloads } msg = HiveMessage(HiveMessageType.HANDSHAKE, payload) LOG.debug(f"starting {client.peer} HANDSHAKE: {payload}") @@ -288,25 +318,31 @@ def handle_client_disconnected(self, client: HiveMindClientConnection): if client.peer in self.clients: self.clients.pop(client.peer) client.socket.close() - message = Message("hive.client.disconnect", - {"ip": client.ip}, - {"source": client.peer, "session": client.sess.serialize()}) + message = Message( + "hive.client.disconnect", + {"ip": client.ip}, + {"source": client.peer, "session": client.sess.serialize()}, + ) bus = self.get_bus(client) bus.emit(message) def handle_invalid_key_connected(self, client: HiveMindClientConnection): LOG.error("Client provided an invalid api key") - message = Message("hive.client.connection.error", - {"error": "invalid api key", "peer": client.peer}, - {"source": client.peer}) + message = Message( + "hive.client.connection.error", + {"error": "invalid api key", "peer": client.peer}, + {"source": client.peer}, + ) bus = self.get_bus(client) bus.emit(message) def handle_invalid_protocol_version(self, client: HiveMindClientConnection): LOG.error("Client does not satisfy protocol requirements") - message = Message("hive.client.connection.error", - {"error": "protocol error", "peer": client.peer}, - {"source": client.peer}) + message = Message( + "hive.client.connection.error", + {"error": "protocol error", "peer": client.peer}, + {"source": client.peer}, + ) bus = self.get_bus(client) bus.emit(message) @@ -344,19 +380,24 @@ def handle_message(self, message: HiveMessage, client: HiveMindClientConnection) self.handle_unknown_message(message, client) # HiveMind protocol messages - from slave -> master - def handle_unknown_message(self, message: HiveMessage, client: HiveMindClientConnection): - """ message handler for non default message types, subclasses can + def handle_unknown_message( + self, message: HiveMessage, client: HiveMindClientConnection + ): + """message handler for non default message types, subclasses can handle their own types here message (HiveMessage): HiveMind message object """ - def handle_binary_message(self, message: HiveMessage, client: HiveMindClientConnection): + def handle_binary_message( + self, message: HiveMessage, client: HiveMindClientConnection + ): assert message.msg_type == HiveMessageType.BINARY # TODO - def handle_handshake_message(self, message: HiveMessage, - client: HiveMindClientConnection): + def handle_handshake_message( + self, message: HiveMessage, client: HiveMindClientConnection + ): LOG.debug("handshake received, generating session key") payload = message.payload if "site_id" in payload: @@ -409,13 +450,16 @@ def handle_handshake_message(self, message: HiveMessage, msg = HiveMessage(HiveMessageType.HANDSHAKE, payload) client.send(msg) # client can recreate crypto_key on his side now - def handle_bus_message(self, message: HiveMessage, - client: HiveMindClientConnection): + def handle_bus_message( + self, message: HiveMessage, client: HiveMindClientConnection + ): self.handle_inject_mycroft_msg(message.payload, client) if self.mycroft_bus_callback: self.mycroft_bus_callback(message.payload) - def handle_broadcast_message(self, message: HiveMessage, client: HiveMindClientConnection): + def handle_broadcast_message( + self, message: HiveMessage, client: HiveMindClientConnection + ): """ message (HiveMessage): HiveMind message object """ @@ -447,8 +491,9 @@ def _unpack_message(self, message: HiveMessage, client: HiveMindClientConnection pload.remove_target_peer(client.peer) return pload - def handle_propagate_message(self, message: HiveMessage, - client: HiveMindClientConnection): + def handle_propagate_message( + self, message: HiveMessage, client: HiveMindClientConnection + ): """ message (HiveMessage): HiveMind message object """ @@ -475,15 +520,21 @@ def handle_propagate_message(self, message: HiveMessage, self.clients[peer].send(payload) # send to other masters - message = Message("hive.send.upstream", payload, - {"destination": "hive", - "source": self.peer, - "session": client.sess.serialize()}) + message = Message( + "hive.send.upstream", + payload, + { + "destination": "hive", + "source": self.peer, + "session": client.sess.serialize(), + }, + ) bus = self.get_bus(client) bus.emit(message) - def handle_escalate_message(self, message: HiveMessage, - client: HiveMindClientConnection): + def handle_escalate_message( + self, message: HiveMessage, client: HiveMindClientConnection + ): """ message (HiveMessage): HiveMind message object """ @@ -506,22 +557,29 @@ def handle_escalate_message(self, message: HiveMessage, self.escalate_callback(payload) # send to other masters - message = Message("hive.send.upstream", payload, - {"destination": "hive", - "source": self.peer, - "session": client.sess.serialize()}) + message = Message( + "hive.send.upstream", + payload, + { + "destination": "hive", + "source": self.peer, + "session": client.sess.serialize(), + }, + ) bus = self.get_bus(client) bus.emit(message) # HiveMind mycroft bus messages - from slave -> master def update_slave_session(self, message: Message, client: HiveMindClientConnection): - """ slave injected a message, master decides what the session is unconditionally (active skills etc) + """slave injected a message, master decides what the session is unconditionally (active skills etc) handle special message that influence session per client and update HM session as needed here """ message.context["session"] = client.sess.serialize() return message - def handle_inject_mycroft_msg(self, message: Message, client: HiveMindClientConnection): + def handle_inject_mycroft_msg( + self, message: Message, client: HiveMindClientConnection + ): """ message (Message): mycroft bus message object """ @@ -538,7 +596,9 @@ def handle_inject_mycroft_msg(self, message: Message, client: HiveMindClientConn if message.msg_type == "speak": message.context["destination"] = ["audio"] elif message.context.get("destination") is None: - message.context["destination"] = "skills" # ensure not treated as a broadcast + message.context[ + "destination" + ] = "skills" # ensure not treated as a broadcast # send client message to internal mycroft bus LOG.info(f"Forwarding message to mycroft bus from client: {client.peer}") diff --git a/hivemind_core/scripts.py b/hivemind_core/scripts.py index 93ef320..404327f 100644 --- a/hivemind_core/scripts.py +++ b/hivemind_core/scripts.py @@ -22,9 +22,13 @@ def hmcore_cmds(): def add_client(name, access_key, password, crypto_key): key = crypto_key if key: - print("WARNING: crypto key is deprecated, use password instead if your client supports it") - print("WARNING: for security the encryption key should be randomly generated\n" - "Defining your own key is discouraged") + print( + "WARNING: crypto key is deprecated, use password instead if your client supports it" + ) + print( + "WARNING: for security the encryption key should be randomly generated\n" + "Defining your own key is discouraged" + ) if len(key) != 16: print("Encryption key needs to be exactly 16 characters!") raise ValueError @@ -49,7 +53,9 @@ def add_client(name, access_key, password, crypto_key): print("Password:", password) print("Encryption Key:", key) - print("WARNING: Encryption Key is deprecated, only use if your client does not support password") + print( + "WARNING: Encryption Key is deprecated, only use if your client does not support password" + ) @hmcore_cmds.command(help="allow message types sent from a client", name="allow-msg") @@ -65,9 +71,11 @@ def allow_msg(msg_type, node_id): _choices = [] for client in ClientDatabase(): if client["client_id"] != -1: - table.add_row(str(client["client_id"]), - client["name"], - str(client.get("allowed_types", []))) + table.add_row( + str(client["client_id"]), + client["name"], + str(client.get("allowed_types", [])), + ) _choices.append(str(client["client_id"])) if not _choices: @@ -77,8 +85,10 @@ def allow_msg(msg_type, node_id): console = Console() console.print(table) _exit = str(max(int(i) for i in _choices) + 1) - node_id = Prompt.ask(f"To which client you want to add '{msg_type}'? ({_exit}='Exit')", - choices=_choices + [_exit]) + node_id = Prompt.ask( + f"To which client you want to add '{msg_type}'? ({_exit}='Exit')", + choices=_choices + [_exit], + ) if node_id == _exit: console.print("User exit", style="red") exit() @@ -101,8 +111,9 @@ def allow_msg(msg_type, node_id): break -@hmcore_cmds.command(help="remove credentials for a client (numeric unique ID)", - name="delete-client") +@hmcore_cmds.command( + help="remove credentials for a client (numeric unique ID)", name="delete-client" +) @click.argument("node_id", required=True, type=int) def delete_client(node_id): with ClientDatabase() as db: @@ -134,28 +145,67 @@ def list_clients(): with ClientDatabase() as db: for x in db: if x["client_id"] != -1: - table.add_row(str(x["client_id"]), x["name"], x["api_key"], x["password"], x["crypto_key"]) + table.add_row( + str(x["client_id"]), + x["name"], + x["api_key"], + x["password"], + x["crypto_key"], + ) console.print(table) @hmcore_cmds.command(help="start listening for HiveMind connections", name="listen") +@click.option( + "--ovos_bus_address", + help="Open Voice OS bus address", + type=str, + default="127.0.0.1", +) +@click.option( + "--ovos_bus_port", help="Open Voice OS bus port number", type=int, default=8181 +) @click.option("--port", help="HiveMind port number", type=int, default=5678) @click.option("--ssl", help="use wss://", type=bool, default=False) -@click.option("--cert_dir", help="HiveMind SSL certificate directory", type=str, default=f"{xdg_data_home()}/hivemind") -@click.option("--cert_name", help="HiveMind SSL certificate file name", type=str, default="hivemind") -def listen(port: int, ssl: bool, cert_dir: str, cert_name: str): +@click.option( + "--cert_dir", + help="HiveMind SSL certificate directory", + type=str, + default=f"{xdg_data_home()}/hivemind", +) +@click.option( + "--cert_name", + help="HiveMind SSL certificate file name", + type=str, + default="hivemind", +) +def listen( + ovos_bus_address: str, + ovos_bus_port: int, + port: int, + ssl: bool, + cert_dir: str, + cert_name: str, +): from hivemind_core.service import HiveMindService + ovos_bus_config = { + "address": ovos_bus_address, + "port": ovos_bus_port, + } + websocket_config = { "host": "0.0.0.0", "port": port, "ssl": ssl, "cert_dir": cert_dir, - "cert_name": cert_name + "cert_name": cert_name, } - service = HiveMindService(websocket_config=websocket_config) + service = HiveMindService( + ovos_bus_config=ovos_bus_config, websocket_config=websocket_config + ) service.run() diff --git a/hivemind_core/service.py b/hivemind_core/service.py index 9c2b77e..50206f9 100644 --- a/hivemind_core/service.py +++ b/hivemind_core/service.py @@ -24,13 +24,18 @@ from hivemind_bus_client.identity import NodeIdentity from hivemind_bus_client.message import HiveMessageType from hivemind_core.database import ClientDatabase -from hivemind_core.protocol import HiveMindListenerProtocol, HiveMindClientConnection, HiveMindNodeType +from hivemind_core.protocol import ( + HiveMindListenerProtocol, + HiveMindClientConnection, + HiveMindNodeType, +) from hivemind_ggwave import GGWaveMaster from hivemind_presence import LocalPresence -def create_self_signed_cert(cert_dir=f"{xdg_data_home()}/hivemind", - name="hivemind") -> Tuple[str, str]: +def create_self_signed_cert( + cert_dir=f"{xdg_data_home()}/hivemind", name="hivemind" +) -> Tuple[str, str]: """ If name.crt and name.key don't exist in cert_dir, create a new self-signed cert and key pair and write them into that directory. @@ -41,8 +46,7 @@ def create_self_signed_cert(cert_dir=f"{xdg_data_home()}/hivemind", key_path = join(cert_dir, KEY_FILE) makedirs(cert_dir, exist_ok=True) - if not exists(join(cert_dir, CERT_FILE)) \ - or not exists(join(cert_dir, KEY_FILE)): + if not exists(join(cert_dir, CERT_FILE)) or not exists(join(cert_dir, KEY_FILE)): # create a key pair k = crypto.PKey() k.generate_key(crypto.TYPE_RSA, 2048) @@ -61,36 +65,36 @@ def create_self_signed_cert(cert_dir=f"{xdg_data_home()}/hivemind", cert.set_issuer(cert.get_subject()) cert.set_pubkey(k) # TODO don't use sha1 - cert.sign(k, 'sha1') + cert.sign(k, "sha1") if not exists(cert_dir): makedirs(cert_dir) - open(cert_path, "wb").write( - crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) + open(cert_path, "wb").write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) open(join(cert_dir, KEY_FILE), "wb").write( - crypto.dump_privatekey(crypto.FILETYPE_PEM, k)) + crypto.dump_privatekey(crypto.FILETYPE_PEM, k) + ) return cert_path, key_path def on_ready(): - LOG.info('HiveMind bus service ready!') + LOG.info("HiveMind bus service ready!") def on_alive(): - LOG.info('HiveMind bus service alive') + LOG.info("HiveMind bus service alive") def on_started(): - LOG.info('HiveMind bus service started!') + LOG.info("HiveMind bus service started!") -def on_error(e='Unknown'): - LOG.info('HiveMind bus failed to start ({})'.format(repr(e))) +def on_error(e="Unknown"): + LOG.info("HiveMind bus failed to start ({})".format(repr(e))) def on_stopping(): - LOG.info('HiveMind bus is shutting down...') + LOG.info("HiveMind bus is shutting down...") class MessageBusEventHandler(WebSocketHandler): @@ -105,7 +109,10 @@ def decode_auth(auth) -> Tuple[str, str]: def on_message(self, message): message = self.client.decode(message) - if message.msg_type == HiveMessageType.BUS and message.payload.msg_type == "recognizer_loop:b64_audio": + if ( + message.msg_type == HiveMessageType.BUS + and message.payload.msg_type == "recognizer_loop:b64_audio" + ): LOG.info(f"received {self.client.peer} sent base64 audio for STT") else: LOG.info(f"received {self.client.peer} message: {message}") @@ -118,9 +125,15 @@ def open(self): # in regular handshake an asymmetric key pair is used handshake = HandShake(HiveMindService.identity.private_key) - self.client = HiveMindClientConnection(key=key, name=name, - ip=self.request.remote_ip, socket=self, sess=Session(), - handshake=handshake, loop=self.protocol.loop) + self.client = HiveMindClientConnection( + key=key, + name=name, + ip=self.request.remote_ip, + socket=self, + sess=Session(), + handshake=handshake, + loop=self.protocol.loop, + ) with ClientDatabase() as users: user = users.get_client_by_api_key(key) @@ -142,11 +155,15 @@ def open(self): self.client.node_type = HiveMindNodeType.NODE # TODO . placeholder - if not self.client.crypto_key and \ - not self.protocol.handshake_enabled \ - and self.protocol.require_crypto: - LOG.error("No pre-shared crypto key for client and handshake disabled, " - "but configured to require crypto!") + if ( + not self.client.crypto_key + and not self.protocol.handshake_enabled + and self.protocol.require_crypto + ): + LOG.error( + "No pre-shared crypto key for client and handshake disabled, " + "but configured to require crypto!" + ) # clients requiring handshake support might fail here self.protocol.handle_invalid_protocol_version(self.client) self.close() @@ -166,45 +183,62 @@ def check_origin(self, origin) -> bool: class HiveMindService: identity = NodeIdentity() - def __init__(self, - alive_hook: Callable = on_alive, - started_hook: Callable = on_started, - ready_hook: Callable = on_ready, - error_hook: Callable = on_error, - stopping_hook: Callable = on_stopping, - websocket_config: Optional[Dict[str, Any]] = None, - protocol=HiveMindListenerProtocol, - bus=None, - ws_handler=MessageBusEventHandler): - - websocket_config = websocket_config or \ - Configuration().get('hivemind_websocket', {}) - callbacks = StatusCallbackMap(on_started=started_hook, - on_alive=alive_hook, - on_ready=ready_hook, - on_error=error_hook, - on_stopping=stopping_hook) + def __init__( + self, + alive_hook: Callable = on_alive, + started_hook: Callable = on_started, + ready_hook: Callable = on_ready, + error_hook: Callable = on_error, + stopping_hook: Callable = on_stopping, + websocket_config: Optional[Dict[str, Any]] = None, + ovos_bus_config: Optional[Dict[str, Any]] = None, + protocol=HiveMindListenerProtocol, + bus=None, + ws_handler=MessageBusEventHandler, + ): + websocket_config = websocket_config or Configuration().get( + "hivemind_websocket", {} + ) + callbacks = StatusCallbackMap( + on_started=started_hook, + on_alive=alive_hook, + on_ready=ready_hook, + on_error=error_hook, + on_stopping=stopping_hook, + ) self._proto = protocol self._ws_handler = ws_handler if bus: self.bus = bus else: - self.bus = MessageBusClient(emitter=EventEmitter()) + self.ovos_bus_address = ovos_bus_config.get("address") or "127.0.0.1" + self.ovos_bus_port = ovos_bus_config.get("port") or 8181 + self.bus = MessageBusClient( + host=self.ovos_bus_address, + port=self.ovos_bus_port, + emitter=EventEmitter(), + ) self.bus.run_in_thread() self.bus.connected_event.wait() - self.status = ProcessStatus('HiveMind', callback_map=callbacks) - self.host = websocket_config.get('host') or "0.0.0.0" - self.port = websocket_config.get('port') or 5678 - self.ssl = websocket_config.get('ssl', False) - self.cert_dir = websocket_config.get('cert_dir') or f"{xdg_data_home()}/hivemind" - self.cert_name = websocket_config.get('cert_name') or "hivemind" # name + ".crt"/".key" - - self.presence = LocalPresence(name=self.identity.name, - service_type=HiveMindNodeType.MIND, - upnp=websocket_config.get('upnp', False), - port=self.port, - zeroconf=websocket_config.get('zeroconf', False)) + self.status = ProcessStatus("HiveMind", callback_map=callbacks) + self.host = websocket_config.get("host") or "0.0.0.0" + self.port = websocket_config.get("port") or 5678 + self.ssl = websocket_config.get("ssl", False) + self.cert_dir = ( + websocket_config.get("cert_dir") or f"{xdg_data_home()}/hivemind" + ) + self.cert_name = ( + websocket_config.get("cert_name") or "hivemind" + ) # name + ".crt"/".key" + + self.presence = LocalPresence( + name=self.identity.name, + service_type=HiveMindNodeType.MIND, + upnp=websocket_config.get("upnp", False), + port=self.port, + zeroconf=websocket_config.get("zeroconf", False), + ) try: # TODO - silent_mode should be controlled via external events # to start enrolling new devices on demand @@ -231,7 +265,9 @@ def run(self): KEY_FILE = f"{self.cert_dir}/{self.cert_name}.key" if not os.path.isfile(KEY_FILE): LOG.info(f"generating self-signed SSL certificate") - CERT_FILE, KEY_FILE = create_self_signed_cert(self.cert_dir, self.cert_name) + CERT_FILE, KEY_FILE = create_self_signed_cert( + self.cert_dir, self.cert_name + ) LOG.debug("using ssl key at " + KEY_FILE) LOG.debug("using ssl certificate at " + CERT_FILE) ssl_options = {"certfile": CERT_FILE, "keyfile": KEY_FILE} diff --git a/readme.md b/readme.md index c8026c3..0b18dfc 100644 --- a/readme.md +++ b/readme.md @@ -45,12 +45,14 @@ Usage: hivemind-core listen [OPTIONS] start listening for HiveMind connections Options: - --host TEXT HiveMind host - --port INTEGER HiveMind port number - --ssl BOOLEAN use wss:// - --cert_dir TEXT HiveMind SSL certificate directory - --cert_name TEXT HiveMind SSL certificate file name - --help Show this message and exit. + --host TEXT HiveMind host + --port INTEGER HiveMind port number + --ovos_bus_address TEXT Open Voice OS bus address + --ovos_bus_port INTEGER Open Voice OS bus port + --ssl BOOLEAN use wss:// + --cert_dir TEXT HiveMind SSL certificate directory + --cert_name TEXT HiveMind SSL certificate file name + --help Show this message and exit. $ hivemind-core delete-client --help @@ -72,10 +74,12 @@ Options: ``` +By default HiveMind listens for the Open Voice OS bus on `127.0.0.1` which should not be changed when running as the same place. In some cases such as Kubernetes when the HiveMind Listener and Open Voice OS bus are in different pods, the HiveMind Listener should be able to connect to the pod address by using the `ovos_bus_address` and `ovos_bus_port` options. + # Protocol | Protocol Version | 0 | 1 | -|----------------------|-----|-----| +| -------------------- | --- | --- | | json serialization | yes | yes | | binary serialization | no | yes | | pre-shared AES key | yes | yes | @@ -83,7 +87,6 @@ Options: | PGP handshake | no | yes | | zlib compression | no | yes | - some clients such as HiveMind-Js do not yet support protocol V1 # HiveMind components @@ -112,4 +115,3 @@ some clients such as HiveMind-Js do not yet support protocol V1 ## Minds - [NodeRed](https://github.com/OpenJarbas/HiveMind-NodeRed) - diff --git a/scripts/bump_alpha.py b/scripts/bump_alpha.py index bc18619..b91c437 100644 --- a/scripts/bump_alpha.py +++ b/scripts/bump_alpha.py @@ -15,4 +15,4 @@ if line.startswith(version_var_name): print(f"{version_var_name} = {new_version}") else: - print(line.rstrip('\n')) + print(line.rstrip("\n")) diff --git a/scripts/bump_build.py b/scripts/bump_build.py index 3307ebc..0010f59 100644 --- a/scripts/bump_build.py +++ b/scripts/bump_build.py @@ -18,4 +18,4 @@ elif line.startswith(alpha_var_name): print(f"{alpha_var_name} = 0") else: - print(line.rstrip('\n')) + print(line.rstrip("\n")) diff --git a/scripts/bump_major.py b/scripts/bump_major.py index 175f81e..47e7cee 100644 --- a/scripts/bump_major.py +++ b/scripts/bump_major.py @@ -24,4 +24,4 @@ elif line.startswith(alpha_var_name): print(f"{alpha_var_name} = 0") else: - print(line.rstrip('\n')) + print(line.rstrip("\n")) diff --git a/scripts/bump_minor.py b/scripts/bump_minor.py index 9f3590d..bf26bbe 100644 --- a/scripts/bump_minor.py +++ b/scripts/bump_minor.py @@ -21,4 +21,4 @@ elif line.startswith(alpha_var_name): print(f"{alpha_var_name} = 0") else: - print(line.rstrip('\n')) + print(line.rstrip("\n")) diff --git a/scripts/remove_alpha.py b/scripts/remove_alpha.py index c52a4f0..64a43ef 100644 --- a/scripts/remove_alpha.py +++ b/scripts/remove_alpha.py @@ -10,4 +10,4 @@ if line.startswith(alpha_var_name): print(f"{alpha_var_name} = 0") else: - print(line.rstrip('\n')) + print(line.rstrip("\n")) diff --git a/setup.py b/setup.py index e8deba5..3e23544 100644 --- a/setup.py +++ b/setup.py @@ -5,23 +5,22 @@ def get_version(): - """ Find the version of the package""" + """Find the version of the package""" version = None - version_file = os.path.join(BASEDIR, 'hivemind_core', 'version.py') + version_file = os.path.join(BASEDIR, "hivemind_core", "version.py") major, minor, build, alpha = (None, None, None, None) with open(version_file) as f: for line in f: - if 'VERSION_MAJOR' in line: - major = line.split('=')[1].strip() - elif 'VERSION_MINOR' in line: - minor = line.split('=')[1].strip() - elif 'VERSION_BUILD' in line: - build = line.split('=')[1].strip() - elif 'VERSION_ALPHA' in line: - alpha = line.split('=')[1].strip() - - if ((major and minor and build and alpha) or - '# END_VERSION_BLOCK' in line): + if "VERSION_MAJOR" in line: + major = line.split("=")[1].strip() + elif "VERSION_MINOR" in line: + minor = line.split("=")[1].strip() + elif "VERSION_BUILD" in line: + build = line.split("=")[1].strip() + elif "VERSION_ALPHA" in line: + alpha = line.split("=")[1].strip() + + if (major and minor and build and alpha) or "# END_VERSION_BLOCK" in line: break version = f"{major}.{minor}.{build}" if int(alpha) > 0: @@ -30,30 +29,29 @@ def get_version(): def required(requirements_file): - """ Read requirements file and remove comments and empty lines. """ - with open(os.path.join(BASEDIR, requirements_file), 'r') as f: + """Read requirements file and remove comments and empty lines.""" + with open(os.path.join(BASEDIR, requirements_file), "r") as f: requirements = f.read().splitlines() - if 'MYCROFT_LOOSE_REQUIREMENTS' in os.environ: - print('USING LOOSE REQUIREMENTS!') - requirements = [r.replace('==', '>=').replace('~=', '>=') for r in requirements] - return [pkg for pkg in requirements - if pkg.strip() and not pkg.startswith("#")] + if "MYCROFT_LOOSE_REQUIREMENTS" in os.environ: + print("USING LOOSE REQUIREMENTS!") + requirements = [ + r.replace("==", ">=").replace("~=", ">=") for r in requirements + ] + return [pkg for pkg in requirements if pkg.strip() and not pkg.startswith("#")] setup( - name='jarbas_hive_mind', + name="jarbas_hive_mind", version=get_version(), - packages=['hivemind_core'], + packages=["hivemind_core"], include_package_data=True, install_requires=required("requirements.txt"), - url='https://github.com/JarbasHiveMind/HiveMind-core', - license='MIT', - author='jarbasAI', - author_email='jarbasai@mailfence.com', - description='Mesh Networking utilities for OpenVoiceOS', + url="https://github.com/JarbasHiveMind/HiveMind-core", + license="MIT", + author="jarbasAI", + author_email="jarbasai@mailfence.com", + description="Mesh Networking utilities for OpenVoiceOS", entry_points={ - 'console_scripts': [ - 'hivemind-core=hivemind_core.scripts:hmcore_cmds' - ] - } + "console_scripts": ["hivemind-core=hivemind_core.scripts:hmcore_cmds"] + }, ) diff --git a/test/unittests/test_bus.py b/test/unittests/test_bus.py index 9a97580..ec4ac7f 100644 --- a/test/unittests/test_bus.py +++ b/test/unittests/test_bus.py @@ -9,6 +9,7 @@ # TODO - rewrite tests + def get_hive(): # TODO add/mock db for the test key = "dummy_key" @@ -43,24 +44,44 @@ def get_hive(): sleep(1) - mid = FakeMycroft(MID_PORT, connection=HiveNodeClient( - key=key, crypto_key=crypto_key, port=MASTER_PORT, ssl=False)) + mid = FakeMycroft( + MID_PORT, + connection=HiveNodeClient( + key=key, crypto_key=crypto_key, port=MASTER_PORT, ssl=False + ), + ) mid.start() - mid2 = FakeMycroft(MID2_PORT, connection=HiveNodeClient( - key=key, crypto_key=crypto_key, port=MASTER_PORT, ssl=False)) + mid2 = FakeMycroft( + MID2_PORT, + connection=HiveNodeClient( + key=key, crypto_key=crypto_key, port=MASTER_PORT, ssl=False + ), + ) mid2.start() sleep(1) - end = FakeMycroft(END_PORT, connection=HiveNodeClient( - key=key, crypto_key=crypto_key, port=MID_PORT, ssl=False)) + end = FakeMycroft( + END_PORT, + connection=HiveNodeClient( + key=key, crypto_key=crypto_key, port=MID_PORT, ssl=False + ), + ) end.start() - end2 = FakeMycroft(END2_PORT, connection=HiveNodeClient( - key=key, crypto_key=crypto_key, port=MID_PORT, ssl=False)) + end2 = FakeMycroft( + END2_PORT, + connection=HiveNodeClient( + key=key, crypto_key=crypto_key, port=MID_PORT, ssl=False + ), + ) end2.start() - end3 = FakeMycroft(END3_PORT, connection=HiveNodeClient( - key=key, crypto_key=crypto_key, port=MID2_PORT, ssl=False)) + end3 = FakeMycroft( + END3_PORT, + connection=HiveNodeClient( + key=key, crypto_key=crypto_key, port=MID2_PORT, ssl=False + ), + ) end3.start() sleep(10) # allow hive to fully connect @@ -90,9 +111,7 @@ def test_connections(self): class TestHiveBus(TestCase): - def test_midtomaster(self): - # # Master # * @@ -122,8 +141,9 @@ def handle_mid2_bus(message): mid.register_downstream_handlers() mid2.register_downstream_handlers() - pload = HiveMessage(msg_type=HiveMessageType.BUS, - payload=Message("test", {"ping": "pong"})) + pload = HiveMessage( + msg_type=HiveMessageType.BUS, payload=Message("test", {"ping": "pong"}) + ) mid.connection.emit(pload) sleep(2) @@ -167,8 +187,9 @@ def handle_mid2_bus(message): mid.register_downstream_handlers() mid2.register_downstream_handlers() - pload = HiveMessage(msg_type=HiveMessageType.BUS, - payload=Message("test", {"ping": "pong"})) + pload = HiveMessage( + msg_type=HiveMessageType.BUS, payload=Message("test", {"ping": "pong"}) + ) end3.connection.emit(pload) sleep(2) @@ -225,8 +246,9 @@ def handle_end3_bus(message): end2.register_upstream_handlers() end2.register_upstream_handlers() - pload = HiveMessage(msg_type=HiveMessageType.BUS, - payload=Message("test", {"ping": "pong"})) + pload = HiveMessage( + msg_type=HiveMessageType.BUS, payload=Message("test", {"ping": "pong"}) + ) master.interface.send(pload, mid.connection.peer) sleep(0.5) @@ -283,8 +305,9 @@ def handle_end3_bus(message): end2.register_upstream_handlers() end2.register_upstream_handlers() - pload = HiveMessage(msg_type=HiveMessageType.BUS, - payload=Message("test", {"ping": "pong"})) + pload = HiveMessage( + msg_type=HiveMessageType.BUS, payload=Message("test", {"ping": "pong"}) + ) mid.interface.send(pload, end.connection.peer) sleep(0.5) @@ -301,9 +324,7 @@ def handle_end3_bus(message): class TestEscalate(TestCase): - def test_midtomaster(self): - # # Master # * @@ -333,9 +354,11 @@ def handle_mid2_escalate(message): mid.register_downstream_handlers() mid2.register_downstream_handlers() - pload = HiveMessage(msg_type=HiveMessageType.THIRDPRTY, - payload=Message("test", {"ping": "pong"})) - pload = HiveMessage(msg_type=HiveMessageType.ESCALATE, payload=pload) + pload = HiveMessage( + msg_type=HiveMessageType.THIRDPRTY, + payload=Message("test", {"ping": "pong"}), + ) + pload = HiveMessage(msg_type=HiveMessageType.ESCALATE, payload=pload) mid.connection.emit(pload) sleep(2) @@ -381,8 +404,10 @@ def handle_mid2_escalate(message): mid.register_downstream_handlers() mid2.register_downstream_handlers() - pload = HiveMessage(msg_type=HiveMessageType.THIRDPRTY, - payload=Message("test", {"ping": "pong"})) + pload = HiveMessage( + msg_type=HiveMessageType.THIRDPRTY, + payload=Message("test", {"ping": "pong"}), + ) pload = HiveMessage(msg_type=HiveMessageType.ESCALATE, payload=pload) end.connection.emit(pload) sleep(2) @@ -428,8 +453,10 @@ def handle_mid2_escalate(message): mid.register_downstream_handlers() mid2.register_downstream_handlers() - pload = HiveMessage(msg_type=HiveMessageType.THIRDPRTY, - payload=Message("test", {"ping": "pong"})) + pload = HiveMessage( + msg_type=HiveMessageType.THIRDPRTY, + payload=Message("test", {"ping": "pong"}), + ) pload = HiveMessage(msg_type=HiveMessageType.ESCALATE, payload=pload) end3.connection.emit(pload) sleep(2) @@ -447,7 +474,6 @@ def handle_mid2_escalate(message): class TestHiveBroadcast(TestCase): - def test_master(self): # Master # / \ @@ -490,8 +516,10 @@ def handle_end3_broadcast(message): end2.register_upstream_handlers() end3.register_upstream_handlers() - pload = HiveMessage(msg_type=HiveMessageType.THIRDPRTY, - payload=Message("test", {"ping": "pong"})) + pload = HiveMessage( + msg_type=HiveMessageType.THIRDPRTY, + payload=Message("test", {"ping": "pong"}), + ) master.interface.broadcast(pload) sleep(1) @@ -550,8 +578,10 @@ def handle_end3_broadcast(message): end2.register_upstream_handlers() end3.register_upstream_handlers() - pload = HiveMessage(msg_type=HiveMessageType.THIRDPRTY, - payload=Message("test", {"ping": "pong"})) + pload = HiveMessage( + msg_type=HiveMessageType.THIRDPRTY, + payload=Message("test", {"ping": "pong"}), + ) mid.interface.broadcast(pload) sleep(1) @@ -572,9 +602,7 @@ def handle_end3_broadcast(message): @skip("TODO Fix me") class TestPropagate(TestCase): - def test_mid(self): - # # Master # * \ @@ -628,9 +656,11 @@ def handle_end3_propagate(message): end2.register_upstream_handlers() end3.register_upstream_handlers() - pload = HiveMessage(msg_type=HiveMessageType.THIRDPRTY, - payload=Message("test", {"ping": "pong"})) - pload = HiveMessage(msg_type=HiveMessageType.PROPAGATE, payload=pload) + pload = HiveMessage( + msg_type=HiveMessageType.THIRDPRTY, + payload=Message("test", {"ping": "pong"}), + ) + pload = HiveMessage(msg_type=HiveMessageType.PROPAGATE, payload=pload) mid.connection.emit(pload) sleep(2) @@ -702,8 +732,10 @@ def handle_end3_propagate(message): end2.register_upstream_handlers() end3.register_upstream_handlers() - pload = HiveMessage(msg_type=HiveMessageType.THIRDPRTY, - payload=Message("test", {"ping": "pong"})) + pload = HiveMessage( + msg_type=HiveMessageType.THIRDPRTY, + payload=Message("test", {"ping": "pong"}), + ) pload = HiveMessage(msg_type=HiveMessageType.PROPAGATE, payload=pload) end.connection.emit(pload) sleep(2) @@ -778,8 +810,10 @@ def handle_end3_propagate(message): end2.register_upstream_handlers() end3.register_upstream_handlers() - pload = HiveMessage(msg_type=HiveMessageType.THIRDPRTY, - payload=Message("test", {"ping": "pong"})) + pload = HiveMessage( + msg_type=HiveMessageType.THIRDPRTY, + payload=Message("test", {"ping": "pong"}), + ) pload = HiveMessage(msg_type=HiveMessageType.PROPAGATE, payload=pload) end3.connection.emit(pload) sleep(2) @@ -797,4 +831,3 @@ def handle_end3_propagate(message): continue self.assertEqual(message.msg_type, HiveMessageType.BUS) self.assertTrue(isinstance(message.payload, Message)) - diff --git a/test/unittests/test_db.py b/test/unittests/test_db.py index 5fbce6e..fd8c1d5 100644 --- a/test/unittests/test_db.py +++ b/test/unittests/test_db.py @@ -5,9 +5,7 @@ class TestDB(TestCase): - def test_add_entry(self): - key = os.urandom(8).hex() access_key = os.urandom(16).hex() password = None @@ -15,9 +13,7 @@ def test_add_entry(self): with ClientDatabase() as db: n = db.total_clients() name = f"HiveMind-Node-{n}" - user = db.add_client(name, access_key, - crypto_key=key, - password=password) + user = db.add_client(name, access_key, crypto_key=key, password=password) # verify data self.assertTrue(isinstance(user, Client)) self.assertEqual(user.name, name) @@ -39,4 +35,3 @@ def test_add_entry(self): self.assertEqual(node_id, -1) user = db.get_client_by_api_key(access_key) self.assertIsNone(user) -