neuronum 7.0.3__py3-none-any.whl → 8.0.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,30 @@
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
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
+
7
16
 
8
17
  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
18
+ def __init__(self, private_key_path: str, public_key_path: str):
19
+ self.private_key_path = private_key_path
20
+ self.public_key_path = public_key_path
14
21
  self.queue = asyncio.Queue()
22
+ self.host = self._load_host()
23
+ self.network = self._load_network()
24
+ self.synapse = self._load_synapse()
25
+ self.password = self._load_password()
26
+ self._private_key = self._load_private_key()
27
+ self._public_key = self._load_public_key()
15
28
 
16
29
 
17
30
  def to_dict(self) -> dict:
@@ -21,186 +34,424 @@ class Cell:
21
34
  "synapse": self.synapse
22
35
  }
23
36
 
37
+ def _load_private_key(self):
38
+ try:
39
+ with open(self.private_key_path, "rb") as f:
40
+ private_key = serialization.load_pem_private_key(
41
+ f.read(),
42
+ password=None,
43
+ backend=default_backend()
44
+ )
45
+ print("Private key loaded successfully.")
46
+ return private_key
47
+ except FileNotFoundError:
48
+ print(f"Private key file not found at {self.private_key_path}.")
49
+ return None
50
+
51
+ def _load_host(self):
52
+ credentials_folder_path = Path.home() / ".neuronum"
53
+ env_path = credentials_folder_path / ".env"
54
+
55
+ env_data = {}
56
+
57
+ try:
58
+ with open(env_path, "r") as f:
59
+ for line in f:
60
+ key, value = line.strip().split("=")
61
+ env_data[key] = value
62
+
63
+ host = env_data.get("HOST", "")
64
+ return host
65
+ except FileNotFoundError:
66
+ print(f"Cell Host not found")
67
+ return None
68
+
24
69
 
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)
70
+ def _load_password(self):
71
+ credentials_folder_path = Path.home() / ".neuronum"
72
+ env_path = credentials_folder_path / ".env"
76
73
 
77
- finally:
78
- if 'writer' in locals():
79
- writer.close()
80
- await writer.wait_closed()
74
+ env_data = {}
81
75
 
76
+ try:
77
+ with open(env_path, "r") as f:
78
+ for line in f:
79
+ key, value = line.strip().split("=")
80
+ env_data[key] = value
82
81
 
83
- async def sync(self, stx: Optional[str] = None) -> AsyncGenerator[str, None]:
84
- full_url = f"wss://{self.network}/sync/{stx}"
82
+ host = env_data.get("PASSWORD", "")
83
+ return host
84
+ except FileNotFoundError:
85
+ print(f"Cell Password not found")
86
+ return None
87
+
88
+ def _load_synapse(self):
89
+ credentials_folder_path = Path.home() / ".neuronum"
90
+ env_path = credentials_folder_path / ".env"
91
+
92
+ env_data = {}
93
+
94
+ try:
95
+ with open(env_path, "r") as f:
96
+ for line in f:
97
+ key, value = line.strip().split("=")
98
+ env_data[key] = value
99
+
100
+ host = env_data.get("SYNAPSE", "")
101
+ return host
102
+ except FileNotFoundError:
103
+ print(f"Cell Synapse not found")
104
+ return None
105
+
106
+ def _load_network(self):
107
+ credentials_folder_path = Path.home() / ".neuronum"
108
+ env_path = credentials_folder_path / ".env"
109
+
110
+ env_data = {}
111
+
112
+ try:
113
+ with open(env_path, "r") as f:
114
+ for line in f:
115
+ key, value = line.strip().split("=")
116
+ env_data[key] = value
117
+
118
+ host = env_data.get("NETWORK", "")
119
+ return host
120
+ except FileNotFoundError:
121
+ print(f"Cell Network not found")
122
+ return None
123
+
124
+
125
+ def _load_public_key(self):
126
+ try:
127
+ with open(self.public_key_path, "rb") as f:
128
+ public_key = serialization.load_pem_public_key(
129
+ f.read(),
130
+ backend=default_backend()
131
+ )
132
+ print("Public key loaded successfully.")
133
+ print(public_key)
134
+ return public_key
135
+ except FileNotFoundError:
136
+ print(f"Public key file not found at {self.public_key_path}. Deriving from private key.")
137
+ if self._private_key:
138
+ return self._private_key.public_key()
139
+ else:
140
+ return None
141
+
142
+
143
+ def get_public_key_jwk(self):
144
+ public_key = self._load_public_key()
145
+ if not public_key:
146
+ print("Public key not loaded. Cannot generate JWK.")
147
+ return None
148
+
149
+ print("Public key loaded successfully.")
150
+ public_numbers = public_key.public_numbers()
151
+
152
+ x_bytes = public_numbers.x.to_bytes((public_numbers.x.bit_length() + 7) // 8, 'big')
153
+ y_bytes = public_numbers.y.to_bytes((public_numbers.y.bit_length() + 7) // 8, 'big')
154
+
155
+ return {
156
+ "kty": "EC",
157
+ "crv": "P-256",
158
+ "x": base64.urlsafe_b64encode(x_bytes).rstrip(b'=').decode('utf-8'),
159
+ "y": base64.urlsafe_b64encode(y_bytes).rstrip(b'=').decode('utf-8')
160
+ }
161
+
162
+
163
+ def _decrypt_with_ecdh_aesgcm(self, ephemeral_public_key_bytes, nonce, ciphertext):
164
+ try:
165
+ ephemeral_public_key = ec.EllipticCurvePublicKey.from_encoded_point(
166
+ ec.SECP256R1(), ephemeral_public_key_bytes
167
+ )
168
+
169
+ shared_secret = self._private_key.exchange(ec.ECDH(), ephemeral_public_key)
170
+
171
+ derived_key = HKDF(
172
+ algorithm=hashes.SHA256(),
173
+ length=32,
174
+ salt=None,
175
+ info=b'handshake data'
176
+ ).derive(shared_secret)
177
+
178
+ aesgcm = AESGCM(derived_key)
179
+ plaintext_bytes = aesgcm.decrypt(nonce, ciphertext, None)
180
+ return json.loads(plaintext_bytes.decode())
181
+
182
+ except Exception as e:
183
+ print(f"Decryption failed: {e}")
184
+ return None
185
+
186
+
187
+ async def sync(self, node_id: str) -> AsyncGenerator[str, None]:
188
+ full_url = f"wss://{self.network}/sync/{node_id}"
85
189
  auth_payload = {
86
190
  "host": self.host,
87
191
  "password": self.password,
88
192
  "synapse": self.synapse,
89
193
  }
90
-
91
194
  while True:
92
195
  try:
93
196
  async with websockets.connect(full_url) as ws:
94
197
  await ws.send(json.dumps(auth_payload))
95
198
  print("Connected to WebSocket.")
96
-
97
199
  while True:
98
200
  try:
99
201
  raw_operation = await ws.recv()
100
202
  operation = json.loads(raw_operation)
101
- yield operation
102
-
203
+
204
+ if "encrypted" in operation.get("data", {}):
205
+ encrypted_data = operation["data"]["encrypted"]
206
+
207
+ ephemeral_public_key_b64 = encrypted_data["ephemeralPublicKey"]
208
+ ephemeral_public_key_b64 += '=' * ((4 - len(ephemeral_public_key_b64) % 4) % 4)
209
+ ephemeral_public_key_bytes = base64.urlsafe_b64decode(ephemeral_public_key_b64)
210
+
211
+ nonce_b64 = encrypted_data["nonce"]
212
+ nonce_b64 += '=' * ((4 - len(nonce_b64) % 4) % 4)
213
+ nonce = base64.urlsafe_b64decode(nonce_b64)
214
+
215
+ ciphertext_b64 = encrypted_data["ciphertext"]
216
+ ciphertext_b64 += '=' * ((4 - len(ciphertext_b64) % 4) % 4)
217
+ ciphertext = base64.urlsafe_b64decode(ciphertext_b64)
218
+
219
+ decrypted_data = self._decrypt_with_ecdh_aesgcm(
220
+ ephemeral_public_key_bytes, nonce, ciphertext
221
+ )
222
+
223
+ if decrypted_data:
224
+ operation["data"].update(decrypted_data)
225
+ operation["data"].pop("encrypted")
226
+ yield operation
227
+ else:
228
+ print("Failed to decrypt incoming data. Skipping...")
229
+ else:
230
+ yield operation
103
231
  except asyncio.TimeoutError:
104
232
  print("No data received. Continuing...")
105
- except websockets.exceptions.ConnectionClosedError as e:
106
- print(f"Connection closed with error: {e}. Reconnecting...")
233
+ except ConnectionClosed as e:
234
+ if e.code == 1000:
235
+ print(f"WebSocket closed cleanly (code 1000). Reconnecting...")
236
+ else:
237
+ print(f"Connection closed with error code {e.code}: {e.reason}. Reconnecting...")
107
238
  break
108
239
  except Exception as e:
109
240
  print(f"Unexpected error in recv loop: {e}")
110
241
  break
111
-
112
242
  except websockets.exceptions.WebSocketException as e:
113
243
  print(f"WebSocket error occurred: {e}. Retrying in 5 seconds...")
114
244
  except Exception as e:
115
245
  print(f"General error occurred: {e}. Retrying in 5 seconds...")
116
-
117
246
  await asyncio.sleep(3)
118
-
119
247
 
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
248
 
123
- TX = {
124
- "descr": descr,
125
- "key_values": key_values,
126
- "stx": stx,
127
- "label": label,
128
- "partners": partners,
249
+ async def list_nodes(self):
250
+ full_url = f"https://{self.network}/api/list_nodes"
251
+ list_nodes_payload = {
129
252
  "cell": self.to_dict()
130
253
  }
131
-
132
254
  async with aiohttp.ClientSession() as session:
133
255
  try:
134
- async with session.post(url, json=TX) as response:
256
+ async with session.post(full_url, json=list_nodes_payload) as response:
135
257
  response.raise_for_status()
136
258
  data = await response.json()
137
- return data["txID"]
138
-
259
+ return data.get("Nodes", [])
139
260
  except aiohttp.ClientError as e:
140
261
  print(f"Error sending request: {e}")
141
262
  except Exception as e:
142
263
  print(f"Unexpected error: {e}")
143
264
 
144
265
 
145
- async def delete_tx(self, txID: str):
146
- url = f"https://{self.network}/api/delete_tx"
266
+ async def activate_tx(self, node_id: str, data: dict):
267
+ url = f"https://{self.network}/api/activate_tx/{node_id}"
268
+
269
+ nodes = await self.list_nodes()
147
270
 
148
- TX = {
149
- "txID": txID,
150
- "cell": self.to_dict()
151
- }
271
+ target_public_key_pem = None
272
+ target_node = node_id
152
273
 
153
- async with aiohttp.ClientSession() as session:
154
- try:
274
+ for node in nodes:
275
+ node_ids = node.get('config', {}).get('data_gateways', [])
276
+ for node_id_entry in node_ids:
277
+ if node_id_entry.get('node_id') == target_node:
278
+ target_public_key_pem = node.get('config', {}).get('public_key')
279
+ break
280
+ if target_public_key_pem:
281
+ break
282
+
283
+ if not target_public_key_pem:
284
+ print(f"Target node not found or public key is missing.")
285
+ return None
286
+
287
+ try:
288
+ print(target_public_key_pem)
289
+ partner_public_key_jwk = self._load_public_key_from_pem(target_public_key_pem)
290
+ if not partner_public_key_jwk:
291
+ print("Failed to convert public key to JWK format. Aborting.")
292
+ return None
293
+
294
+ partner_public_key = self._load_public_key_from_jwk(partner_public_key_jwk)
295
+ if not partner_public_key:
296
+ print("Failed to load partner's public key. Aborting.")
297
+ return None
298
+
299
+ data_to_encrypt = data.copy()
300
+ data_to_encrypt["publicKey"] = self.get_public_key_jwk()
301
+
302
+ encrypted_payload = self._encrypt_with_ecdh_aesgcm(partner_public_key, data_to_encrypt)
303
+
304
+ TX = {
305
+ "data": {
306
+ "encrypted": encrypted_payload
307
+ },
308
+ "cell": self.to_dict()
309
+ }
310
+
311
+ async with aiohttp.ClientSession() as session:
155
312
  async with session.post(url, json=TX) as response:
313
+ if response.status == 504:
314
+ print(f"Gateway Timeout: No response from transmitter for txID: {node_id}")
315
+ return None
316
+
156
317
  response.raise_for_status()
157
- data = await response.json()
158
- print(f"Response from Neuronum: {data}")
159
- return data
318
+
319
+ response_data = await response.json()
320
+
321
+ if "response" in response_data:
322
+ inner_response = response_data["response"]
323
+
324
+ if "ciphertext" in inner_response:
325
+ ephemeral_public_key_b64 = inner_response["ephemeralPublicKey"]
326
+ ephemeral_public_key_b64 += '=' * ((4 - len(ephemeral_public_key_b64) % 4) % 4)
327
+ ephemeral_public_key_bytes = base64.urlsafe_b64decode(ephemeral_public_key_b64)
328
+
329
+ nonce_b64 = inner_response["nonce"]
330
+ nonce_b64 += '=' * ((4 - len(nonce_b64) % 4) % 4)
331
+ nonce = base64.urlsafe_b64decode(nonce_b64)
332
+
333
+ ciphertext_b64 = inner_response["ciphertext"]
334
+ ciphertext_b64 += '=' * ((4 - len(ciphertext_b64) % 4) % 4)
335
+ ciphertext = base64.urlsafe_b64decode(ciphertext_b64)
336
+
337
+ decrypted_response = self._decrypt_with_ecdh_aesgcm(
338
+ ephemeral_public_key_bytes, nonce, ciphertext
339
+ )
340
+
341
+ if decrypted_response:
342
+ return decrypted_response
343
+ else:
344
+ print("Failed to decrypt server response.")
345
+ return None
346
+ else:
347
+ print("Server response was not encrypted as expected.")
348
+ return inner_response
160
349
 
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
-
350
+ else:
351
+ print("Unexpected response format.")
352
+ return response_data
353
+
354
+ except aiohttp.ClientResponseError as e:
355
+ print(f"HTTP Error: {e.status}, Message: {e.message}, URL: {e.request_info.url}")
356
+ return None
357
+
358
+ except aiohttp.ClientError as e:
359
+ print(f"Connection Error: {e}")
360
+ return None
361
+
362
+ except Exception as e:
363
+ print(f"Unexpected error: {e}")
364
+ return None
365
+
366
+
367
+ def _load_public_key_from_jwk(self, jwk):
368
+ try:
369
+ print(jwk)
370
+ x = base64.urlsafe_b64decode(jwk['x'] + '==')
371
+ y = base64.urlsafe_b64decode(jwk['y'] + '==')
372
+ public_numbers = ec.EllipticCurvePublicNumbers(
373
+ int.from_bytes(x, 'big'),
374
+ int.from_bytes(y, 'big'),
375
+ ec.SECP256R1()
376
+ )
377
+ return public_numbers.public_key(default_backend())
378
+ except (KeyError, ValueError, TypeError) as e:
379
+ print(f"Error loading public key from JWK string: {e}")
380
+ return None
381
+
166
382
 
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()
383
+ def _load_public_key_from_pem(self, pem_string: str):
384
+ try:
385
+ print(pem_string)
386
+ corrected_pem_string = pem_string.replace("-----BEGINPUBLICKEY-----", "-----BEGIN PUBLIC KEY-----")
387
+ corrected_pem_string = corrected_pem_string.replace("-----ENDPUBLICKEY-----", "-----END PUBLIC KEY-----")
388
+
389
+ public_key = serialization.load_pem_public_key(
390
+ corrected_pem_string.encode(),
391
+ backend=default_backend()
392
+ )
393
+
394
+ public_numbers = public_key.public_numbers()
395
+ x_bytes = public_numbers.x.to_bytes((public_numbers.x.bit_length() + 7) // 8, 'big')
396
+ y_bytes = public_numbers.y.to_bytes((public_numbers.y.bit_length() + 7) // 8, 'big')
397
+ return {
398
+ "kty": "EC",
399
+ "crv": "P-256",
400
+ "x": base64.urlsafe_b64encode(x_bytes).rstrip(b'=').decode('utf-8'),
401
+ "y": base64.urlsafe_b64encode(y_bytes).rstrip(b'=').decode('utf-8')
402
+ }
403
+ except Exception as e:
404
+ print(f"Error loading public key from PEM string: {e}")
405
+ return None
406
+
407
+
408
+ def _encrypt_with_ecdh_aesgcm(self, public_key, plaintext_dict):
409
+ ephemeral_private = ec.generate_private_key(ec.SECP256R1())
410
+ ephemeral_public = ephemeral_private.public_key()
411
+ shared_secret = ephemeral_private.exchange(ec.ECDH(), public_key)
412
+ derived_key = HKDF(
413
+ algorithm=hashes.SHA256(),
414
+ length=32,
415
+ salt=None,
416
+ info=b'handshake data'
417
+ ).derive(shared_secret)
418
+ aesgcm = AESGCM(derived_key)
419
+ nonce = os.urandom(12)
420
+ plaintext_bytes = json.dumps(plaintext_dict).encode()
421
+ ciphertext = aesgcm.encrypt(nonce, plaintext_bytes, None)
422
+ ephemeral_public_bytes = ephemeral_public.public_bytes(
423
+ serialization.Encoding.X962,
424
+ serialization.PublicFormat.UncompressedPoint
425
+ )
426
+ return {
427
+ 'ciphertext': base64.b64encode(ciphertext).decode(),
428
+ 'nonce': base64.b64encode(nonce).decode(),
429
+ 'ephemeralPublicKey': base64.b64encode(ephemeral_public_bytes).decode()
172
430
  }
431
+
173
432
 
174
- async with aiohttp.ClientSession() as session:
433
+ async def tx_response(self, transmitter_id: str, data: dict, client_public_key_str):
434
+ if isinstance(client_public_key_str, str):
175
435
  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
-
436
+ client_public_key_jwk = json.loads(client_public_key_str)
437
+ except json.JSONDecodeError:
438
+ print("Failed to decode client public key from string. Aborting response.")
439
+ return
440
+ elif isinstance(client_public_key_str, dict):
441
+ client_public_key_jwk = client_public_key_str
442
+ else:
443
+ print("Invalid type for client public key. Expected str or dict. Aborting response.")
444
+ return
445
+ public_key = self._load_public_key_from_jwk(client_public_key_jwk)
446
+ if not public_key:
447
+ print("Failed to load public key. Aborting response.")
448
+ return
449
+ encrypted_payload = self._encrypt_with_ecdh_aesgcm(public_key, data)
450
+ url = f"https://{self.network}/api/tx_response/{transmitter_id}"
198
451
  tx_response = {
199
- "client": client,
200
- "data": data,
452
+ "data": encrypted_payload,
201
453
  "cell": self.to_dict()
202
454
  }
203
-
204
455
  async with aiohttp.ClientSession() as session:
205
456
  try:
206
457
  for _ in range(2):
@@ -208,312 +459,9 @@ class Cell:
208
459
  response.raise_for_status()
209
460
  data = await response.json()
210
461
  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
462
  except aiohttp.ClientError as e:
341
463
  print(f"Error sending request: {e}")
342
464
  except Exception as e:
343
465
  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
- except aiohttp.ClientError as e:
401
- print(f"Error sending request: {e}")
402
- 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']
466
+
467
+ __all__ = ['Cell']