neuronum 7.0.4__py3-none-any.whl → 8.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of neuronum might be problematic. Click here for more details.

neuronum/neuronum.py CHANGED
@@ -1,17 +1,31 @@
1
1
  import aiohttp
2
- from typing import Optional, AsyncGenerator
3
- import ssl
2
+ from typing import AsyncGenerator
4
3
  import websockets
5
4
  import json
6
5
  import asyncio
7
-
8
- class Cell:
9
- def __init__(self, host: str, password: str, network: str, synapse: str):
10
- self.host = host
11
- self.password = password
12
- self.network = network
13
- self.synapse = synapse
6
+ import base64
7
+ import os
8
+ from pathlib import Path
9
+ from websockets.exceptions import ConnectionClosed
10
+ from cryptography.hazmat.primitives.asymmetric import ec
11
+ from cryptography.hazmat.primitives import serialization, hashes
12
+ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
13
+ from cryptography.hazmat.primitives.kdf.hkdf import HKDF
14
+ from cryptography.hazmat.backends import default_backend
15
+
16
+
17
+ class Node:
18
+ def __init__(self, id: str, private_key: str, public_key: str):
19
+ self.node_id = id
20
+ self.private_key_path = private_key
21
+ self.public_key_path = public_key
14
22
  self.queue = asyncio.Queue()
23
+ self.host = self._load_host()
24
+ self.network = self._load_network()
25
+ self.synapse = self._load_synapse()
26
+ self.password = self._load_password()
27
+ self._private_key = self._load_private_key()
28
+ self._public_key = self._load_public_key()
15
29
 
16
30
 
17
31
  def to_dict(self) -> dict:
@@ -21,186 +35,298 @@ class Cell:
21
35
  "synapse": self.synapse
22
36
  }
23
37
 
38
+ def _load_private_key(self):
39
+ try:
40
+ with open(self.private_key_path, "rb") as f:
41
+ private_key = serialization.load_pem_private_key(
42
+ f.read(),
43
+ password=None,
44
+ backend=default_backend()
45
+ )
46
+ print("Private key loaded successfully.")
47
+ return private_key
48
+ except FileNotFoundError:
49
+ print(f"Private key file not found at {self.private_key_path}.")
50
+ return None
51
+
52
+ def _load_host(self):
53
+ credentials_folder_path = Path.home() / ".neuronum"
54
+ env_path = credentials_folder_path / ".env"
55
+
56
+ env_data = {}
57
+
58
+ try:
59
+ with open(env_path, "r") as f:
60
+ for line in f:
61
+ key, value = line.strip().split("=")
62
+ env_data[key] = value
63
+
64
+ host = env_data.get("HOST", "")
65
+ return host
66
+ except FileNotFoundError:
67
+ print(f"Cell Host not found")
68
+ return None
69
+
24
70
 
25
- def __repr__(self) -> str:
26
- return f"Cell(host={self.host}, password={self.password}, network={self.network}, synapse={self.synapse})"
27
-
28
-
29
- async def stream(self, label: str, data: dict, stx: Optional[str] = None, retry_delay: int = 3):
30
- context = ssl.create_default_context()
31
- context.check_hostname = True
32
- context.verify_mode = ssl.CERT_REQUIRED
33
-
34
- while True:
35
- try:
36
- reader, writer = await asyncio.open_connection(self.network, 55555, ssl=context, server_hostname=self.network)
37
-
38
- credentials = f"{self.host}\n{self.password}\n{self.synapse}\n{stx}\n"
39
- writer.write(credentials.encode("utf-8"))
40
- await writer.drain()
41
-
42
- response = await reader.read(1024)
43
- response_text = response.decode("utf-8").strip()
44
-
45
- if "Authentication successful" not in response_text:
46
- print("Authentication failed, retrying...")
47
- writer.close()
48
- await writer.wait_closed()
49
- await asyncio.sleep(retry_delay)
50
- continue
51
-
52
- stream_payload = {
53
- "label": label,
54
- "data": data,
55
- }
56
-
57
- writer.write(json.dumps(stream_payload).encode("utf-8"))
58
- await writer.drain()
59
-
60
- response = await reader.read(1024)
61
- response_text = response.decode("utf-8").strip()
62
-
63
- if response_text == "Sent":
64
- print(f"Success: {response_text} - {stream_payload}")
65
- break
66
- else:
67
- print(f"Error sending: {stream_payload}")
68
-
69
- except (ssl.SSLError, ConnectionError) as e:
70
- print(f"Connection error: {e}, retrying...")
71
- await asyncio.sleep(retry_delay)
72
-
73
- except Exception as e:
74
- print(f"Unexpected error: {e}, retrying...")
75
- await asyncio.sleep(retry_delay)
71
+ def _load_password(self):
72
+ credentials_folder_path = Path.home() / ".neuronum"
73
+ env_path = credentials_folder_path / ".env"
76
74
 
77
- finally:
78
- if 'writer' in locals():
79
- writer.close()
80
- await writer.wait_closed()
75
+ env_data = {}
81
76
 
77
+ try:
78
+ with open(env_path, "r") as f:
79
+ for line in f:
80
+ key, value = line.strip().split("=")
81
+ env_data[key] = value
82
82
 
83
- async def sync(self, stx: Optional[str] = None) -> AsyncGenerator[str, None]:
84
- full_url = f"wss://{self.network}/sync/{stx}"
83
+ host = env_data.get("PASSWORD", "")
84
+ return host
85
+ except FileNotFoundError:
86
+ print(f"Cell Password not found")
87
+ return None
88
+
89
+ def _load_synapse(self):
90
+ credentials_folder_path = Path.home() / ".neuronum"
91
+ env_path = credentials_folder_path / ".env"
92
+
93
+ env_data = {}
94
+
95
+ try:
96
+ with open(env_path, "r") as f:
97
+ for line in f:
98
+ key, value = line.strip().split("=")
99
+ env_data[key] = value
100
+
101
+ host = env_data.get("SYNAPSE", "")
102
+ return host
103
+ except FileNotFoundError:
104
+ print(f"Cell Synapse not found")
105
+ return None
106
+
107
+ def _load_network(self):
108
+ credentials_folder_path = Path.home() / ".neuronum"
109
+ env_path = credentials_folder_path / ".env"
110
+
111
+ env_data = {}
112
+
113
+ try:
114
+ with open(env_path, "r") as f:
115
+ for line in f:
116
+ key, value = line.strip().split("=")
117
+ env_data[key] = value
118
+
119
+ host = env_data.get("NETWORK", "")
120
+ return host
121
+ except FileNotFoundError:
122
+ print(f"Cell Network not found")
123
+ return None
124
+
125
+
126
+ def _load_public_key(self):
127
+ try:
128
+ with open(self.public_key_path, "rb") as f:
129
+ public_key = serialization.load_pem_public_key(
130
+ f.read(),
131
+ backend=default_backend()
132
+ )
133
+ print("Public key loaded successfully.")
134
+ print(public_key)
135
+ return public_key
136
+ except FileNotFoundError:
137
+ print(f"Public key file not found at {self.public_key_path}. Deriving from private key.")
138
+ if self._private_key:
139
+ return self._private_key.public_key()
140
+ else:
141
+ return None
142
+
143
+
144
+ def get_public_key_jwk(self):
145
+ public_key = self._load_public_key()
146
+ if not public_key:
147
+ print("Public key not loaded. Cannot generate JWK.")
148
+ return None
149
+
150
+ print("Public key loaded successfully.")
151
+ public_numbers = public_key.public_numbers()
152
+
153
+ x_bytes = public_numbers.x.to_bytes((public_numbers.x.bit_length() + 7) // 8, 'big')
154
+ y_bytes = public_numbers.y.to_bytes((public_numbers.y.bit_length() + 7) // 8, 'big')
155
+
156
+ return {
157
+ "kty": "EC",
158
+ "crv": "P-256",
159
+ "x": base64.urlsafe_b64encode(x_bytes).rstrip(b'=').decode('utf-8'),
160
+ "y": base64.urlsafe_b64encode(y_bytes).rstrip(b'=').decode('utf-8')
161
+ }
162
+
163
+
164
+ def _decrypt_with_ecdh_aesgcm(self, ephemeral_public_key_bytes, nonce, ciphertext):
165
+ try:
166
+ ephemeral_public_key = ec.EllipticCurvePublicKey.from_encoded_point(
167
+ ec.SECP256R1(), ephemeral_public_key_bytes
168
+ )
169
+
170
+ shared_secret = self._private_key.exchange(ec.ECDH(), ephemeral_public_key)
171
+
172
+ derived_key = HKDF(
173
+ algorithm=hashes.SHA256(),
174
+ length=32,
175
+ salt=None,
176
+ info=b'handshake data'
177
+ ).derive(shared_secret)
178
+
179
+ aesgcm = AESGCM(derived_key)
180
+ plaintext_bytes = aesgcm.decrypt(nonce, ciphertext, None)
181
+ return json.loads(plaintext_bytes.decode())
182
+
183
+ except Exception as e:
184
+ print(f"Decryption failed: {e}")
185
+ return None
186
+
187
+
188
+ async def sync(self) -> AsyncGenerator[str, None]:
189
+ full_url = f"wss://{self.network}/sync/{self.node_id}"
85
190
  auth_payload = {
86
191
  "host": self.host,
87
192
  "password": self.password,
88
193
  "synapse": self.synapse,
89
194
  }
90
-
91
195
  while True:
92
196
  try:
93
197
  async with websockets.connect(full_url) as ws:
94
198
  await ws.send(json.dumps(auth_payload))
95
199
  print("Connected to WebSocket.")
96
-
97
200
  while True:
98
201
  try:
99
202
  raw_operation = await ws.recv()
100
203
  operation = json.loads(raw_operation)
101
- yield operation
102
-
204
+
205
+ if "encrypted" in operation.get("data", {}):
206
+ encrypted_data = operation["data"]["encrypted"]
207
+
208
+ ephemeral_public_key_b64 = encrypted_data["ephemeralPublicKey"]
209
+ ephemeral_public_key_b64 += '=' * ((4 - len(ephemeral_public_key_b64) % 4) % 4)
210
+ ephemeral_public_key_bytes = base64.urlsafe_b64decode(ephemeral_public_key_b64)
211
+
212
+ nonce_b64 = encrypted_data["nonce"]
213
+ nonce_b64 += '=' * ((4 - len(nonce_b64) % 4) % 4)
214
+ nonce = base64.urlsafe_b64decode(nonce_b64)
215
+
216
+ ciphertext_b64 = encrypted_data["ciphertext"]
217
+ ciphertext_b64 += '=' * ((4 - len(ciphertext_b64) % 4) % 4)
218
+ ciphertext = base64.urlsafe_b64decode(ciphertext_b64)
219
+
220
+ decrypted_data = self._decrypt_with_ecdh_aesgcm(
221
+ ephemeral_public_key_bytes, nonce, ciphertext
222
+ )
223
+
224
+ if decrypted_data:
225
+ operation["data"].update(decrypted_data)
226
+ operation["data"].pop("encrypted")
227
+ yield operation
228
+ else:
229
+ print("Failed to decrypt incoming data. Skipping...")
230
+ else:
231
+ yield operation
103
232
  except asyncio.TimeoutError:
104
233
  print("No data received. Continuing...")
105
- except websockets.exceptions.ConnectionClosedError as e:
106
- print(f"Connection closed with error: {e}. Reconnecting...")
234
+ except ConnectionClosed as e:
235
+ if e.code == 1000:
236
+ print(f"WebSocket closed cleanly (code 1000). Reconnecting...")
237
+ else:
238
+ print(f"Connection closed with error code {e.code}: {e.reason}. Reconnecting...")
107
239
  break
108
240
  except Exception as e:
109
241
  print(f"Unexpected error in recv loop: {e}")
110
242
  break
111
-
112
243
  except websockets.exceptions.WebSocketException as e:
113
244
  print(f"WebSocket error occurred: {e}. Retrying in 5 seconds...")
114
245
  except Exception as e:
115
246
  print(f"General error occurred: {e}. Retrying in 5 seconds...")
116
-
117
247
  await asyncio.sleep(3)
118
-
119
248
 
120
- async def create_tx(self, descr: str, key_values: dict, stx: str, label: str, partners: list):
121
- url = f"https://{self.network}/api/create_tx"
122
249
 
123
- TX = {
124
- "descr": descr,
125
- "key_values": key_values,
126
- "stx": stx,
127
- "label": label,
128
- "partners": partners,
250
+ async def list_nodes(self):
251
+ full_url = f"https://{self.network}/api/list_nodes"
252
+ list_nodes_payload = {
129
253
  "cell": self.to_dict()
130
254
  }
131
-
132
255
  async with aiohttp.ClientSession() as session:
133
256
  try:
134
- async with session.post(url, json=TX) as response:
257
+ async with session.post(full_url, json=list_nodes_payload) as response:
135
258
  response.raise_for_status()
136
259
  data = await response.json()
137
- return data["txID"]
138
-
260
+ return data.get("Nodes", [])
139
261
  except aiohttp.ClientError as e:
140
262
  print(f"Error sending request: {e}")
141
263
  except Exception as e:
142
264
  print(f"Unexpected error: {e}")
143
-
144
-
145
- async def delete_tx(self, txID: str):
146
- url = f"https://{self.network}/api/delete_tx"
147
-
148
- TX = {
149
- "txID": txID,
150
- "cell": self.to_dict()
265
+
266
+
267
+ def _load_public_key_from_jwk(self, jwk):
268
+ try:
269
+ print(jwk)
270
+ x = base64.urlsafe_b64decode(jwk['x'] + '==')
271
+ y = base64.urlsafe_b64decode(jwk['y'] + '==')
272
+ public_numbers = ec.EllipticCurvePublicNumbers(
273
+ int.from_bytes(x, 'big'),
274
+ int.from_bytes(y, 'big'),
275
+ ec.SECP256R1()
276
+ )
277
+ return public_numbers.public_key(default_backend())
278
+ except (KeyError, ValueError, TypeError) as e:
279
+ print(f"Error loading public key from JWK string: {e}")
280
+ return None
281
+
282
+
283
+ def _encrypt_with_ecdh_aesgcm(self, public_key, plaintext_dict):
284
+ ephemeral_private = ec.generate_private_key(ec.SECP256R1())
285
+ ephemeral_public = ephemeral_private.public_key()
286
+ shared_secret = ephemeral_private.exchange(ec.ECDH(), public_key)
287
+ derived_key = HKDF(
288
+ algorithm=hashes.SHA256(),
289
+ length=32,
290
+ salt=None,
291
+ info=b'handshake data'
292
+ ).derive(shared_secret)
293
+ aesgcm = AESGCM(derived_key)
294
+ nonce = os.urandom(12)
295
+ plaintext_bytes = json.dumps(plaintext_dict).encode()
296
+ ciphertext = aesgcm.encrypt(nonce, plaintext_bytes, None)
297
+ ephemeral_public_bytes = ephemeral_public.public_bytes(
298
+ serialization.Encoding.X962,
299
+ serialization.PublicFormat.UncompressedPoint
300
+ )
301
+ return {
302
+ 'ciphertext': base64.b64encode(ciphertext).decode(),
303
+ 'nonce': base64.b64encode(nonce).decode(),
304
+ 'ephemeralPublicKey': base64.b64encode(ephemeral_public_bytes).decode()
151
305
  }
152
-
153
- async with aiohttp.ClientSession() as session:
154
- try:
155
- async with session.post(url, json=TX) as response:
156
- response.raise_for_status()
157
- data = await response.json()
158
- print(f"Response from Neuronum: {data}")
159
- return data
160
-
161
- except aiohttp.ClientError as e:
162
- print(f"Error sending request: {e}")
163
- except Exception as e:
164
- print(f"Unexpected error: {e}")
165
306
 
166
307
 
167
- async def activate_tx(self, txID: str, data: dict):
168
- url = f"https://{self.network}/api/activate_tx/{txID}"
169
- TX = {
170
- "data": data,
171
- "cell": self.to_dict()
172
- }
173
-
174
- async with aiohttp.ClientSession() as session:
308
+ async def tx_response(self, transmitter_id: str, data: dict, client_public_key_str):
309
+ if isinstance(client_public_key_str, str):
175
310
  try:
176
- async with session.post(url, json=TX) as response:
177
- response.raise_for_status()
178
- data = await response.json()
179
- if data.get("success") == True:
180
- if "json" in data.get("response"):
181
- return data.get("response").get("json")
182
- elif "html" in data.get("response"):
183
- return "Info: HTML response available. Please activate TX in browser."
184
- else:
185
- return "Info: Response received but contains no usable content."
186
- else:
187
- print(data["success"], data["message"])
188
-
189
- except aiohttp.ClientError as e:
190
- print(f"Error sending request: {e}")
191
- except Exception as e:
192
- print(f"Unexpected error: {e}")
193
-
194
-
195
- async def tx_response(self, txID: str, client: str, data: dict):
196
- url = f"https://{self.network}/api/tx_response/{txID}"
197
-
311
+ client_public_key_jwk = json.loads(client_public_key_str)
312
+ except json.JSONDecodeError:
313
+ print("Failed to decode client public key from string. Aborting response.")
314
+ return
315
+ elif isinstance(client_public_key_str, dict):
316
+ client_public_key_jwk = client_public_key_str
317
+ else:
318
+ print("Invalid type for client public key. Expected str or dict. Aborting response.")
319
+ return
320
+ public_key = self._load_public_key_from_jwk(client_public_key_jwk)
321
+ if not public_key:
322
+ print("Failed to load public key. Aborting response.")
323
+ return
324
+ encrypted_payload = self._encrypt_with_ecdh_aesgcm(public_key, data)
325
+ url = f"https://{self.network}/api/tx_response/{transmitter_id}"
198
326
  tx_response = {
199
- "client": client,
200
- "data": data,
327
+ "data": encrypted_payload,
201
328
  "cell": self.to_dict()
202
329
  }
203
-
204
330
  async with aiohttp.ClientSession() as session:
205
331
  try:
206
332
  for _ in range(2):
@@ -208,312 +334,7 @@ class Cell:
208
334
  response.raise_for_status()
209
335
  data = await response.json()
210
336
  print(data["message"])
211
-
212
- except aiohttp.ClientError as e:
213
- print(f"Error sending request: {e}")
214
- except Exception as e:
215
- print(f"Unexpected error: {e}")
216
-
217
-
218
- async def create_ctx(self, descr: str, partners: list):
219
- url = f"https://{self.network}/api/create_ctx"
220
-
221
- CTX = {
222
- "descr": descr,
223
- "partners": partners,
224
- "cell": self.to_dict()
225
- }
226
-
227
- async with aiohttp.ClientSession() as session:
228
- try:
229
- async with session.post(url, json=CTX) as response:
230
- response.raise_for_status()
231
- data = await response.json()
232
- return data["ctxID"]
233
-
234
- except aiohttp.ClientError as e:
235
- print(f"Error sending request: {e}")
236
- except Exception as e:
237
- print(f"Unexpected error: {e}")
238
-
239
-
240
- async def delete_ctx(self, ctxID: str):
241
- url = f"https://{self.network}/api/delete_ctx"
242
-
243
- CTX = {
244
- "ctxID": ctxID,
245
- "cell": self.to_dict()
246
- }
247
-
248
- async with aiohttp.ClientSession() as session:
249
- try:
250
- async with session.post(url, json=CTX) as response:
251
- response.raise_for_status()
252
- data = await response.json()
253
- print(f"Response from Neuronum: {data}")
254
- return data
255
-
256
- except aiohttp.ClientError as e:
257
- print(f"Error sending request: {e}")
258
- except Exception as e:
259
- print(f"Unexpected error: {e}")
260
-
261
-
262
- async def create_stx(self, descr: str, partners: list):
263
- url = f"https://{self.network}/api/create_stx"
264
-
265
- STX = {
266
- "descr": descr,
267
- "partners": partners,
268
- "cell": self.to_dict()
269
- }
270
-
271
- async with aiohttp.ClientSession() as session:
272
- try:
273
- async with session.post(url, json=STX) as response:
274
- response.raise_for_status()
275
- data = await response.json()
276
- return data["stxID"]
277
-
278
- except aiohttp.ClientError as e:
279
- print(f"Error sending request: {e}")
280
- except Exception as e:
281
- print(f"Unexpected error: {e}")
282
-
283
-
284
- async def delete_stx(self, stxID: str):
285
- url = f"https://{self.network}/api/delete_stx"
286
-
287
- STX = {
288
- "stxID": stxID,
289
- "cell": self.to_dict()
290
- }
291
-
292
- async with aiohttp.ClientSession() as session:
293
- try:
294
- async with session.post(url, json=STX) as response:
295
- response.raise_for_status()
296
- data = await response.json()
297
- print(f"Response from Neuronum: {data}")
298
- return data
299
-
300
- except aiohttp.ClientError as e:
301
- print(f"Error sending request: {e}")
302
- except Exception as e:
303
- print(f"Unexpected error: {e}")
304
-
305
-
306
- async def list_cells(self):
307
- full_url = f"https://{self.network}/api/list_cells"
308
-
309
- list_cells_payload = {
310
- "cell": self.to_dict()
311
- }
312
-
313
- async with aiohttp.ClientSession() as session:
314
- try:
315
- async with session.get(full_url, json=list_cells_payload) as response:
316
- response.raise_for_status()
317
- data = await response.json()
318
- return data.get("Cells", [])
319
-
320
- except aiohttp.ClientError as e:
321
- print(f"Error sending request: {e}")
322
- except Exception as e:
323
- print(f"Unexpected error: {e}")
324
-
325
-
326
- async def list_tx(self):
327
- full_url = f"https://{self.network}/api/list_tx"
328
-
329
- list_tx_payload = {
330
- "cell": self.to_dict()
331
- }
332
-
333
- async with aiohttp.ClientSession() as session:
334
- try:
335
- async with session.get(full_url, json=list_tx_payload) as response:
336
- response.raise_for_status()
337
- data = await response.json()
338
- return data.get("Transmitters", [])
339
-
340
- except aiohttp.ClientError as e:
341
- print(f"Error sending request: {e}")
342
- except Exception as e:
343
- print(f"Unexpected error: {e}")
344
-
345
-
346
- async def list_ctx(self):
347
- full_url = f"https://{self.network}/api/list_ctx"
348
-
349
- list_ctx_payload = {
350
- "cell": self.to_dict()
351
- }
352
-
353
- async with aiohttp.ClientSession() as session:
354
- try:
355
- async with session.get(full_url, json=list_ctx_payload) as response:
356
- response.raise_for_status()
357
- data = await response.json()
358
- return data.get("Circuits", [])
359
-
360
- except aiohttp.ClientError as e:
361
- print(f"Error sending request: {e}")
362
- except Exception as e:
363
- print(f"Unexpected error: {e}")
364
-
365
-
366
- async def list_stx(self):
367
- full_url = f"https://{self.network}/api/list_stx"
368
-
369
- list_stx_payload = {
370
- "cell": self.to_dict()
371
- }
372
-
373
- async with aiohttp.ClientSession() as session:
374
- try:
375
- async with session.get(full_url, json=list_stx_payload) as response:
376
- response.raise_for_status()
377
- data = await response.json()
378
- return data.get("Streams", [])
379
-
380
- except aiohttp.ClientError as e:
381
- print(f"Error sending request: {e}")
382
- except Exception as e:
383
- print(f"Unexpected error: {e}")
384
-
385
-
386
- async def list_nodes(self):
387
- full_url = f"https://{self.network}/api/list_nodes"
388
-
389
- list_nodes_payload = {
390
- "cell": self.to_dict()
391
- }
392
-
393
- async with aiohttp.ClientSession() as session:
394
- try:
395
- async with session.get(full_url, json=list_nodes_payload) as response:
396
- response.raise_for_status()
397
- data = await response.json()
398
- return data.get("Nodes", [])
399
-
400
337
  except aiohttp.ClientError as e:
401
338
  print(f"Error sending request: {e}")
402
339
  except Exception as e:
403
- print(f"Unexpected error: {e}")
404
-
405
-
406
- async def store(self, label: str, data: dict, ctx: Optional[str] = None):
407
- full_url = f"https://{self.network}/api/store_in_ctx/{ctx}" if ctx else f"https://{self.network}/api/store"
408
-
409
- store_payload = {
410
- "label": label,
411
- "data": data,
412
- "cell": self.to_dict()
413
- }
414
-
415
- async with aiohttp.ClientSession() as session:
416
- try:
417
- async with session.post(full_url, json=store_payload) as response:
418
- response.raise_for_status()
419
- data = await response.json()
420
- print(f"Response from Neuronum: {data}")
421
- return data
422
-
423
- except aiohttp.ClientError as e:
424
- print(f"Error sending request: {e}")
425
- except Exception as e:
426
- print(f"Unexpected error: {e}")
427
-
428
-
429
- async def load(self, label: str, ctx: Optional[str] = None):
430
- full_url = f"https://{self.network}/api/load_from_ctx/{ctx}" if ctx else f"https://{self.network}/api/load"
431
-
432
- load_payload = {
433
- "label": label,
434
- "cell": self.to_dict()
435
- }
436
-
437
- async with aiohttp.ClientSession() as session:
438
- try:
439
- async with session.post(full_url, json=load_payload) as response:
440
- response.raise_for_status()
441
- data = await response.json()
442
- return data
443
-
444
- except aiohttp.ClientError as e:
445
- print(f"Error sending request: {e}")
446
- except Exception as e:
447
- print(f"Unexpected error: {e}")
448
-
449
-
450
- async def delete(self, label: str, ctx: Optional[str] = None):
451
- full_url = f"https://{self.network}/api/delete_from_ctx/{ctx}" if ctx else f"https://{self.network}/api/delete"
452
-
453
- delete_payload = {
454
- "label": label,
455
- "cell": self.to_dict()
456
- }
457
-
458
- async with aiohttp.ClientSession() as session:
459
- try:
460
- async with session.post(full_url, json=delete_payload) as response:
461
- response.raise_for_status()
462
- data = await response.json()
463
- print(f"Response from Neuronum: {data}")
464
- return data
465
-
466
- except aiohttp.ClientError as e:
467
- print(f"Error sending request: {e}")
468
- except Exception as e:
469
- print(f"Unexpected error: {e}")
470
-
471
-
472
- async def clear(self, ctx: Optional[str] = None):
473
- full_url = f"https://{self.network}/api/clear_ctx/{ctx}" if ctx else f"https://{self.network}/api/clear"
474
-
475
- clear_payload = {
476
- "cell": self.to_dict()
477
- }
478
-
479
- async with aiohttp.ClientSession() as session:
480
- try:
481
- async with session.post(full_url, json=clear_payload) as response:
482
- response.raise_for_status()
483
- data = await response.json()
484
- print(f"Response from Neuronum: {data}")
485
- return data
486
-
487
- except aiohttp.ClientError as e:
488
- print(f"Error sending request: {e}")
489
- except Exception as e:
490
- print(f"Unexpected error: {e}")
491
-
492
-
493
- async def notify(self, receiver: str, title: str, message: str):
494
- full_url = f"https://{self.network}/api/notify"
495
-
496
- notify_payload = {
497
- "receiver": receiver,
498
- "notification": {
499
- "title": title,
500
- "message": message
501
- },
502
- "cell": self.to_dict()
503
- }
504
-
505
- async with aiohttp.ClientSession() as session:
506
- try:
507
- async with session.post(full_url, json=notify_payload) as response:
508
- response.raise_for_status()
509
- data = await response.json()
510
- print(f"Notification sent successfully: {data}")
511
- return data
512
-
513
- except aiohttp.ClientError as e:
514
- print(f"HTTP error while sending notification: {e}")
515
- except Exception as e:
516
- print(f"Unexpected error: {e}")
517
-
518
-
519
- __all__ = ['Cell']
340
+ print(f"Unexpected error: {e}")