agentstr 0.1.10__py3-none-any.whl → 0.1.12__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
agentstr/nostr.py CHANGED
@@ -1,5 +1,11 @@
1
+ import json
1
2
  import logging
2
- from typing import Optional
3
+ import traceback
4
+ from datetime import timedelta
5
+ from pathlib import Path
6
+ from typing import Dict, List, Optional, Tuple
7
+
8
+ from agentstr.models import MerchantProduct, MerchantStall, NostrProfile
3
9
 
4
10
  try:
5
11
  import asyncio
@@ -10,11 +16,13 @@ except ImportError:
10
16
 
11
17
  try:
12
18
  from nostr_sdk import (
19
+ Alphabet,
13
20
  Client,
14
21
  Coordinate,
15
- Event,
16
22
  EventBuilder,
17
23
  EventId,
24
+ Events,
25
+ Filter,
18
26
  Keys,
19
27
  Kind,
20
28
  Metadata,
@@ -23,8 +31,11 @@ try:
23
31
  PublicKey,
24
32
  ShippingCost,
25
33
  ShippingMethod,
34
+ SingleLetterTag,
26
35
  StallData,
27
36
  Tag,
37
+ TagKind,
38
+ TagStandard,
28
39
  Timestamp,
29
40
  )
30
41
 
@@ -44,8 +55,6 @@ class NostrClient:
44
55
  """
45
56
 
46
57
  logger = logging.getLogger("NostrClient")
47
- ERROR: str = "ERROR"
48
- SUCCESS: str = "SUCCESS"
49
58
 
50
59
  def __init__(
51
60
  self,
@@ -74,6 +83,7 @@ class NostrClient:
74
83
  self.keys = Keys.parse(nsec)
75
84
  self.nostr_signer = NostrSigner.keys(self.keys)
76
85
  self.client = Client(self.nostr_signer)
86
+ self.connected = False
77
87
 
78
88
  def delete_event(self, event_id: EventId, reason: Optional[str] = None) -> EventId:
79
89
  """
@@ -98,7 +108,7 @@ class NostrClient:
98
108
  Publish generic Nostr event to the relay
99
109
 
100
110
  Returns:
101
- EventId: event id if successful or NostrClient.ERROR if unsuccesful
111
+ EventId: event id published
102
112
 
103
113
  Raises:
104
114
  RuntimeError: if the product can't be published
@@ -113,7 +123,7 @@ class NostrClient:
113
123
  text: text to be published as kind 1 event
114
124
 
115
125
  Returns:
116
- EventId: EventId if successful or NostrClient.ERROR if unsuccesful
126
+ EventId: EventId if successful
117
127
 
118
128
  Raises:
119
129
  RuntimeError: if the product can't be published
@@ -121,7 +131,7 @@ class NostrClient:
121
131
  # Run the async publishing function synchronously
122
132
  return asyncio.run(self._async_publish_note(text))
123
133
 
124
- def publish_product(self, product: ProductData) -> EventId:
134
+ def publish_product(self, product: MerchantProduct) -> EventId:
125
135
  """
126
136
  Create or update a NIP-15 Marketplace product with event kind 30018
127
137
 
@@ -129,13 +139,16 @@ class NostrClient:
129
139
  product: product to be published
130
140
 
131
141
  Returns:
132
- EventId: event id if successful or NostrClient.ERROR if unsuccesful
142
+ EventId: event id of the publication event
133
143
 
134
144
  Raises:
135
145
  RuntimeError: if the product can't be published
136
146
  """
137
147
  # Run the async publishing function synchronously
138
- return asyncio.run(self._async_publish_product(product))
148
+ try:
149
+ return asyncio.run(self._async_publish_product(product))
150
+ except Exception as e:
151
+ raise RuntimeError(f"Failed to publish product: {e}")
139
152
 
140
153
  def publish_profile(self, name: str, about: str, picture: str) -> EventId:
141
154
  """
@@ -147,7 +160,7 @@ class NostrClient:
147
160
  picture: url to a png file with a picture for the profile
148
161
 
149
162
  Returns:
150
- EventId: event id if successful or NostrClient.ERROR if unsuccesful
163
+ EventId: event id if successful
151
164
 
152
165
  Raises:
153
166
  RuntimeError: if the profile can't be published
@@ -155,20 +168,166 @@ class NostrClient:
155
168
  # Run the async publishing function synchronously
156
169
  return asyncio.run(self._async_publish_profile(name, about, picture))
157
170
 
158
- def publish_stall(self, stall: StallData) -> EventId:
171
+ def publish_stall(self, stall: MerchantStall) -> EventId:
159
172
  """Publish a stall to nostr
160
173
 
161
174
  Args:
162
175
  stall: stall to be published
163
176
 
164
177
  Returns:
165
- EventId: event id if successful or NostrClient.ERROR if unsuccesful
178
+ EventId: Id of the publication event
179
+
180
+ Raises:
181
+ RuntimeError: if the stall can't be published
166
182
  """
167
183
  try:
168
184
  return asyncio.run(self._async_publish_stall(stall))
169
185
  except Exception as e:
170
- self.logger.error(f"Failed to publish stall: {e}")
171
- return NostrClient.ERROR
186
+ raise RuntimeError(f"Failed to publish stall: {e}")
187
+
188
+ def retrieve_products_from_seller(self, seller: PublicKey) -> List[MerchantProduct]:
189
+ """
190
+ Retrieve all products from a given seller.
191
+
192
+ Args:
193
+ seller: PublicKey of the seller
194
+
195
+ Returns:
196
+ List[MerchantProduct]: list of products from the seller
197
+ """
198
+ products = []
199
+ try:
200
+ events = asyncio.run(self._async_retrieve_products_from_seller(seller))
201
+ events_list = events.to_vec()
202
+ for event in events_list:
203
+ content = json.loads(event.content())
204
+ product_data = ProductData(
205
+ id=content.get("id"),
206
+ stall_id=content.get("stall_id"),
207
+ name=content.get("name"),
208
+ description=content.get("description"),
209
+ images=content.get("images", []),
210
+ currency=content.get("currency"),
211
+ price=content.get("price"),
212
+ quantity=content.get("quantity"),
213
+ specs=content.get("specs", {}),
214
+ shipping=content.get("shipping", []),
215
+ categories=content.get("categories", []),
216
+ )
217
+ products.append(MerchantProduct.from_product_data(product_data))
218
+ return products
219
+ except Exception as e:
220
+ raise RuntimeError(f"Failed to retrieve products: {e}")
221
+
222
+ def retrieve_profile(self, public_key: PublicKey) -> NostrProfile:
223
+ """
224
+ Retrieve a Nostr profile from the relay.
225
+
226
+ Args:
227
+ public_key: bech32 encoded public key of the profile to retrieve
228
+
229
+ Returns:
230
+ NostrProfile: profile of the author
231
+
232
+ Raises:
233
+ RuntimeError: if the profile can't be retrieved
234
+ """
235
+ try:
236
+ return asyncio.run(self._async_retrieve_profile(public_key))
237
+ except Exception as e:
238
+ raise RuntimeError(f"Failed to retrieve profile: {e}")
239
+
240
+ def retrieve_sellers(self) -> set[NostrProfile]:
241
+ """
242
+ Retrieve all sellers from the relay.
243
+ Sellers are npubs who have published a stall.
244
+ Return set may be empty if metadata can't be retrieved for any author.
245
+
246
+ Returns:
247
+ set[NostrProfile]: set of seller profiles (skips authors with missing metadata)
248
+ """
249
+
250
+ sellers: set[NostrProfile] = set()
251
+
252
+ # First we retrieve all stalls from the relay
253
+
254
+ try:
255
+ events = asyncio.run(self._async_retrieve_all_stalls())
256
+ except Exception as e:
257
+ raise RuntimeError(f"Failed to retrieve stalls: {e}")
258
+
259
+ # Now we search for unique npubs from the list of stalls
260
+
261
+ events_list = events.to_vec()
262
+ authors: Dict[PublicKey, NostrProfile] = {}
263
+
264
+ for event in events_list:
265
+ if event.kind() == Kind(30017):
266
+ # Is this event the first time we see this author?
267
+ if event.author() not in authors:
268
+ # First time we see this author. Let's add the profile to the dictionary
269
+ try:
270
+ profile = asyncio.run(
271
+ self._async_retrieve_profile(event.author())
272
+ )
273
+ # Add the profile to the dictionary, associating it with the author's PublicKey
274
+ authors[event.author()] = profile
275
+ except Exception as e:
276
+ # print(
277
+ # f"Failed to retrieve profile for {event.author().to_bech32()}: {e}"
278
+ # )
279
+ continue
280
+
281
+ # Now we add locations from the event locations to the profile
282
+
283
+ for tag in event.tags().to_vec():
284
+ standardized_tag = tag.as_standardized()
285
+ if isinstance(standardized_tag, TagStandard.GEOHASH):
286
+ string_repr = str(standardized_tag)
287
+ extracted_geohash = string_repr.split("=")[1].rstrip(
288
+ ")"
289
+ ) # Splitting and removing the closing parenthesis
290
+ # print(
291
+ # f"Adding location {extracted_geohash} to profile {authors[event.author()].get_name()}"
292
+ # )
293
+ profile = authors[event.author()]
294
+ profile.add_location(extracted_geohash)
295
+ authors[event.author()] = profile
296
+ # print(
297
+ # f"New locations for {authors[event.author()].get_name()}: {authors[event.author()].get_locations()}"
298
+ # )
299
+ # else:
300
+ # print(f"Unknown tag: {standardized_tag}")
301
+
302
+ # once we're done iterating over the events, we return the set of profiles
303
+ return set(authors.values())
304
+
305
+ def retrieve_stalls_from_seller(self, seller: PublicKey) -> List[StallData]:
306
+ """
307
+ Retrieve all stalls from a given seller.
308
+
309
+ Args:
310
+ seller: PublicKey of the seller
311
+
312
+ Returns:
313
+ List[StallData]: list of stalls from the seller
314
+ """
315
+ stalls = []
316
+ try:
317
+ events = asyncio.run(self._async_retrieve_stalls_from_seller(seller))
318
+ events_list = events.to_vec()
319
+ for event in events_list:
320
+ try:
321
+ # Parse the content field instead of the whole event
322
+ content = event.content()
323
+ stall = StallData.from_json(content)
324
+ stalls.append(stall)
325
+ except Exception as e:
326
+ self.logger.warning(f"Failed to parse stall data: {e}")
327
+ continue
328
+ return stalls
329
+ except Exception as e:
330
+ raise RuntimeError(f"Failed to retrieve stalls: {e}")
172
331
 
173
332
  @classmethod
174
333
  def set_logging_level(cls, logging_level: int) -> None:
@@ -186,23 +345,26 @@ class NostrClient:
186
345
  # --*-- async functions for internal use only. Developers should use synchronous functions above
187
346
  # ----------------------------------------------------------------------------------------------
188
347
 
189
- async def _async_connect(self) -> str:
348
+ async def _async_connect(self) -> None:
190
349
  """Asynchronous function to add relay to the NostrClient instance and connect to it.
350
+ TBD: refactor to not return anything if successful and raise an exception if not
191
351
 
192
- Returns:
193
- str: NostrClient.SUCCESS or NostrClient.ERROR
352
+ Raises:
353
+ RuntimeError: if the relay can't be connected to
194
354
  """
195
- try:
196
- await self.client.add_relay(self.relay)
197
- NostrClient.logger.info(f"Relay {self.relay} succesfully added.")
198
- await self.client.connect()
199
- NostrClient.logger.info("Connected to relay.")
200
- return NostrClient.SUCCESS
201
- except Exception as e:
202
- NostrClient.logger.error(
203
- f"Unable to connect to relay {self.relay}. Exception: {e}."
204
- )
205
- return NostrClient.ERROR
355
+
356
+ if not self.connected:
357
+ try:
358
+ await self.client.add_relay(self.relay)
359
+ NostrClient.logger.info(f"Relay {self.relay} succesfully added.")
360
+ await self.client.connect()
361
+ await asyncio.sleep(2) # give time for slower connections
362
+ NostrClient.logger.info("Connected to relay.")
363
+ self.connected = True
364
+ except Exception as e:
365
+ raise RuntimeError(
366
+ f"Unable to connect to relay {self.relay}. Exception: {e}."
367
+ )
206
368
 
207
369
  async def _async_publish_event(self, event_builder: EventBuilder) -> EventId:
208
370
  """
@@ -214,27 +376,35 @@ class NostrClient:
214
376
  Raises:
215
377
  RuntimeError: if the event can't be published
216
378
  """
217
- connected = await self._async_connect()
379
+ try:
380
+ await self._async_connect()
218
381
 
219
- if connected == NostrClient.ERROR:
220
- raise RuntimeError("Unable to connect to the relay")
382
+ # Add debug logging
383
+ NostrClient.logger.debug(f"Attempting to publish event: {event_builder}")
384
+ NostrClient.logger.debug(
385
+ f"Using keys: {self.keys.public_key().to_bech32()}"
386
+ )
221
387
 
222
- try:
388
+ # Wait for connection and try to publish
223
389
  output = await self.client.send_event_builder(event_builder)
224
- if len(output.success) > 0:
225
- NostrClient.logger.info(
226
- f"Event published with event id: {output.id.to_bech32()}"
390
+
391
+ # More detailed error handling
392
+ if not output:
393
+ raise RuntimeError("No output received from send_event_builder")
394
+ if len(output.success) == 0:
395
+ raise RuntimeError(
396
+ f"Event rejected by relay. Reason: {output.message if hasattr(output, 'message') else 'unknown'}"
227
397
  )
228
- return output.id
229
- else:
230
- raise RuntimeError("Unable to publish event")
231
- except Exception as e:
232
- NostrClient.logger.error(
233
- f"NostrClient instance not properly initialized. Exception: {e}."
234
- )
235
- raise RuntimeError(
236
- f"NostrClient instance not properly initialized. Exception: {e}."
398
+
399
+ NostrClient.logger.info(
400
+ f"Event published with event id: {output.id.to_bech32()}"
237
401
  )
402
+ return output.id
403
+
404
+ except Exception as e:
405
+ NostrClient.logger.error(f"Failed to publish event: {str(e)}")
406
+ NostrClient.logger.debug("Event details:", exc_info=True)
407
+ raise RuntimeError(f"Unable to publish event: {str(e)}")
238
408
 
239
409
  async def _async_publish_note(self, text: str) -> EventId:
240
410
  """
@@ -244,7 +414,7 @@ class NostrClient:
244
414
  text: text to be published as kind 1 event
245
415
 
246
416
  Returns:
247
- EventId: event id if successful or NostrClient.ERROR if unsuccesful
417
+ EventId: event id if successful
248
418
 
249
419
  Raises:
250
420
  RuntimeError: if the event can't be published
@@ -252,7 +422,7 @@ class NostrClient:
252
422
  event_builder = EventBuilder.text_note(text)
253
423
  return await self._async_publish_event(event_builder)
254
424
 
255
- async def _async_publish_product(self, product: ProductData) -> EventId:
425
+ async def _async_publish_product(self, product: MerchantProduct) -> EventId:
256
426
  """
257
427
  Asynchronous function to create or update a NIP-15 Marketplace product with event kind 30018
258
428
 
@@ -260,7 +430,7 @@ class NostrClient:
260
430
  product: product to publish
261
431
 
262
432
  Returns:
263
- EventId: event id if successful or NostrClient.ERROR if unsuccesfull
433
+ EventId: event id if successful
264
434
 
265
435
  Raises:
266
436
  RuntimeError: if the product can't be published
@@ -271,7 +441,7 @@ class NostrClient:
271
441
 
272
442
  # EventBuilder.product_data() has a bug with tag handling.
273
443
  # We use the function to create the content field and discard the eventbuilder
274
- bad_event_builder = EventBuilder.product_data(product)
444
+ bad_event_builder = EventBuilder.product_data(product.to_product_data())
275
445
 
276
446
  # create an event from bad_event_builder to extract the content - not broadcasted
277
447
  bad_event = await self.client.sign_event_builder(bad_event_builder)
@@ -296,7 +466,7 @@ class NostrClient:
296
466
  picture: url to a png file with a picture for the profile
297
467
 
298
468
  Returns:
299
- EventId: event id if successful or NostrClient.ERROR if unsuccesful
469
+ EventId: event id if successful
300
470
 
301
471
  Raises:
302
472
  RuntimeError: if the profile can't be published
@@ -308,7 +478,7 @@ class NostrClient:
308
478
  event_builder = EventBuilder.metadata(metadata_content)
309
479
  return await self._async_publish_event(event_builder)
310
480
 
311
- async def _async_publish_stall(self, stall: StallData) -> EventId:
481
+ async def _async_publish_stall(self, stall: MerchantStall) -> EventId:
312
482
  """
313
483
  Asynchronous function to create or update a NIP-15 Marketplace stall with event kind 30017
314
484
 
@@ -316,12 +486,178 @@ class NostrClient:
316
486
  stall: stall to be published
317
487
 
318
488
  Returns:
319
- EventId: event id if successful or NostrClient.ERROR if unsuccesfull
489
+ EventId: Id of the publication event
320
490
 
321
491
  Raises:
322
492
  RuntimeError: if the profile can't be published
323
493
  """
324
494
 
325
- self.logger.info(f"Stall: {stall}")
326
- event_builder = EventBuilder.stall_data(stall)
495
+ # good_event_builder = EventBuilder(Kind(30018), content).tags(
496
+ # [Tag.identifier(product.id), Tag.coordinate(coordinate_tag)]
497
+ # )
498
+
499
+ self.logger.info(f" Merchant Stall: {stall}")
500
+ event_builder = EventBuilder.stall_data(stall.to_stall_data()).tags(
501
+ [
502
+ Tag.custom(
503
+ TagKind.SINGLE_LETTER(SingleLetterTag.lowercase(Alphabet.G)),
504
+ [stall.geohash],
505
+ ),
506
+ ]
507
+ )
327
508
  return await self._async_publish_event(event_builder)
509
+
510
+ async def _async_retrieve_all_stalls(self) -> Events:
511
+ """
512
+ Asynchronous function to retreive all stalls from a relay
513
+ This function is used internally to find Merchants.
514
+
515
+ Returns:
516
+ Events: events containing all stalls.
517
+
518
+ Raises:
519
+ RuntimeError: if the stalls can't be retrieved
520
+ """
521
+ try:
522
+ await self._async_connect()
523
+ except Exception as e:
524
+ raise RuntimeError("Unable to connect to the relay")
525
+
526
+ try:
527
+ filter = Filter().kind(Kind(30017))
528
+ events = await self.client.fetch_events_from(
529
+ urls=[self.relay], filter=filter, timeout=timedelta(seconds=2)
530
+ )
531
+ return events
532
+ except Exception as e:
533
+ raise RuntimeError(f"Unable to retrieve stalls: {e}")
534
+
535
+ async def _async_retrieve_products_from_seller(self, seller: PublicKey) -> Events:
536
+ """
537
+ Asynchronous function to retrieve the products for a given author
538
+
539
+ Args:
540
+ seller: PublicKey of the seller to retrieve the products for
541
+
542
+ Returns:
543
+ Events: list of events containing the products of the seller
544
+
545
+ Raises:
546
+ RuntimeError: if the products can't be retrieved
547
+ """
548
+ try:
549
+ await self._async_connect()
550
+ except Exception as e:
551
+ raise RuntimeError("Unable to connect to the relay")
552
+
553
+ try:
554
+ # print(f"Retrieving products from seller: {seller}")
555
+ filter = Filter().kind(Kind(30018)).authors([seller])
556
+ events = await self.client.fetch_events_from(
557
+ urls=[self.relay], filter=filter, timeout=timedelta(seconds=2)
558
+ )
559
+ return events
560
+ except Exception as e:
561
+ raise RuntimeError(f"Unable to retrieve stalls: {e}")
562
+
563
+ async def _async_retrieve_profile(self, author: PublicKey) -> NostrProfile:
564
+ """
565
+ Asynchronous function to retrieve the profile for a given author
566
+
567
+ Args:
568
+ author: PublicKey of the author to retrieve the profile for
569
+
570
+ Returns:
571
+ NostrProfile: profile of the author
572
+
573
+ Raises:
574
+ RuntimeError: if the profile can't be retrieved
575
+ """
576
+ try:
577
+ await self._async_connect()
578
+ except Exception as e:
579
+ raise RuntimeError("Unable to connect to the relay")
580
+
581
+ try:
582
+ metadata = await self.client.fetch_metadata(
583
+ public_key=author, timeout=timedelta(seconds=2)
584
+ )
585
+ return NostrProfile.from_metadata(metadata, author)
586
+ except Exception as e:
587
+ raise RuntimeError(f"Unable to retrieve metadata: {e}")
588
+
589
+ async def _async_retrieve_stalls_from_seller(self, seller: PublicKey) -> Events:
590
+ """
591
+ Asynchronous function to retrieve the stall for a given author
592
+
593
+ Args:
594
+ seller: PublicKey of the seller to retrieve the stall for
595
+
596
+ Returns:
597
+ Events: list of events containing the stalls of the seller
598
+
599
+ Raises:
600
+ RuntimeError: if the stall can't be retrieved
601
+ """
602
+ try:
603
+ await self._async_connect()
604
+ except Exception as e:
605
+ raise RuntimeError("Unable to connect to the relay")
606
+
607
+ try:
608
+ filter = Filter().kind(Kind(30017)).authors([seller])
609
+ events = await self.client.fetch_events_from(
610
+ urls=[self.relay], filter=filter, timeout=timedelta(seconds=2)
611
+ )
612
+ return events
613
+ except Exception as e:
614
+ raise RuntimeError(f"Unable to retrieve stalls: {e}")
615
+
616
+
617
+ def generate_and_save_keys(env_var: str, env_path: Path) -> Keys:
618
+ """Generate new nostr keys and save the private key to .env file.
619
+
620
+ Args:
621
+ env_var: Name of the environment variable to store the key
622
+ env_path: Path to the .env file. If None, looks for .env in current directory
623
+
624
+ Returns:
625
+ The generated Keys object
626
+ """
627
+ # Generate new keys
628
+ keys = Keys.generate()
629
+ nsec = keys.secret_key().to_bech32()
630
+
631
+ # Determine .env path
632
+ if env_path is None:
633
+ env_path = Path.cwd() / ".env"
634
+
635
+ # Read existing .env content
636
+ env_content = ""
637
+ if env_path.exists():
638
+ with open(env_path, "r") as f:
639
+ env_content = f.read()
640
+
641
+ # Check if the env var already exists
642
+ lines = env_content.splitlines()
643
+ new_lines = []
644
+ var_found = False
645
+
646
+ for line in lines:
647
+ if line.startswith(f"{env_var}="):
648
+ new_lines.append(f"{env_var}={nsec}")
649
+ var_found = True
650
+ else:
651
+ new_lines.append(line)
652
+
653
+ # If var wasn't found, add it
654
+ if not var_found:
655
+ new_lines.append(f"{env_var}={nsec}")
656
+
657
+ # Write back to .env
658
+ with open(env_path, "w") as f:
659
+ f.write("\n".join(new_lines))
660
+ if new_lines: # Add final newline if there's content
661
+ f.write("\n")
662
+
663
+ return keys
agentstr/nostr.pyi ADDED
@@ -0,0 +1,82 @@
1
+ from logging import Logger
2
+ from pathlib import Path
3
+ from typing import ClassVar, List, Optional
4
+
5
+ from nostr_sdk import ( # type: ignore
6
+ Client,
7
+ Event,
8
+ EventBuilder,
9
+ EventId,
10
+ Events,
11
+ Keys,
12
+ Kind,
13
+ Metadata,
14
+ NostrSigner,
15
+ ProductData,
16
+ PublicKey,
17
+ ShippingCost,
18
+ ShippingMethod,
19
+ StallData,
20
+ Tag,
21
+ Timestamp,
22
+ )
23
+
24
+ from agentstr.models import MerchantProduct, MerchantStall, NostrProfile
25
+
26
+ # Re-export all needed types
27
+ __all__ = [
28
+ "Event",
29
+ "EventBuilder",
30
+ "Events",
31
+ "EventId",
32
+ "Keys",
33
+ "Kind",
34
+ "Metadata",
35
+ "ProductData",
36
+ "PublicKey",
37
+ "ShippingCost",
38
+ "ShippingMethod",
39
+ "StallData",
40
+ "Timestamp",
41
+ ]
42
+
43
+ class NostrClient:
44
+ logger: ClassVar[Logger]
45
+ relay: str
46
+ keys: Keys
47
+ nostr_signer: NostrSigner
48
+ client: Client
49
+
50
+ def __init__(self, relay: str, nsec: str) -> None: ...
51
+ def delete_event(
52
+ self, event_id: EventId, reason: Optional[str] = None
53
+ ) -> EventId: ...
54
+ def publish_event(self, event_builder: EventBuilder) -> EventId: ...
55
+ def publish_note(self, text: str) -> EventId: ...
56
+ def publish_product(self, product: MerchantProduct) -> EventId: ...
57
+ def publish_profile(self, name: str, about: str, picture: str) -> EventId: ...
58
+ def publish_stall(self, stall: MerchantStall) -> EventId: ...
59
+ def retrieve_products_from_seller(
60
+ self, seller: PublicKey
61
+ ) -> List[MerchantProduct]: ...
62
+ def retrieve_profile(self, public_key: PublicKey) -> NostrProfile: ...
63
+ def retrieve_stalls_from_seller(self, seller: PublicKey) -> List[StallData]: ...
64
+ def retrieve_sellers(self) -> set[NostrProfile]: ...
65
+ @classmethod
66
+ def set_logging_level(cls, logging_level: int) -> None: ...
67
+ async def _async_connect(self) -> None: ...
68
+ async def _async_publish_event(self, event_builder: EventBuilder) -> EventId: ...
69
+ async def _async_publish_note(self, text: str) -> EventId: ...
70
+ async def _async_publish_product(self, product: MerchantProduct) -> EventId: ...
71
+ async def _async_publish_profile(
72
+ self, name: str, about: str, picture: str
73
+ ) -> EventId: ...
74
+ async def _async_publish_stall(self, stall: MerchantStall) -> EventId: ...
75
+ async def _async_retrieve_all_stalls(self) -> Events: ...
76
+ async def _async_retrieve_products_from_seller(
77
+ self, seller: PublicKey
78
+ ) -> Events: ...
79
+ async def _async_retrieve_profile(self, author: PublicKey) -> NostrProfile: ...
80
+ async def _async_retrieve_stalls_from_seller(self, seller: PublicKey) -> Events: ...
81
+
82
+ def generate_and_save_keys(env_var: str, env_path: Optional[Path] = None) -> Keys: ...
agentstr/py.typed ADDED
File without changes