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.
@@ -1,288 +1,22 @@
1
- import ast
2
1
  import json
3
2
  import logging
4
- import re
5
- from typing import Any, Dict, List, Optional, Tuple, Union, cast
6
-
7
- from agentstr.nostr import (
8
- EventId,
9
- Keys,
10
- NostrClient,
11
- ProductData,
12
- ShippingCost,
13
- ShippingMethod,
14
- StallData,
15
- )
3
+ import time
4
+ from typing import Any, List, Optional, Tuple, Union
5
+
6
+ from agentstr.models import AgentProfile, MerchantProduct, MerchantStall
7
+ from agentstr.nostr import EventId, NostrClient
16
8
 
17
9
  try:
18
- from phi.tools import Toolkit
10
+ from agno.tools import Toolkit
19
11
  except ImportError:
20
- raise ImportError(
21
- "`phidata` not installed. Please install using `pip install phidata`"
22
- )
23
-
24
- from pydantic import BaseModel, ConfigDict, Field, validate_call
25
-
26
-
27
- class Profile:
28
-
29
- logger = logging.getLogger("Profile")
30
- WEB_URL: str = "https://primal.net/p/"
31
-
32
- def __init__(self, name: str, about: str, picture: str, nsec: Optional[str] = None):
33
- """Initialize the profile.
34
-
35
- Args:
36
- name: Name for the merchant
37
- about: brief description about the merchant
38
- picture: url to a png file with a picture for the merchant
39
- nsec: optional private key to be used by this Merchant
40
- """
41
-
42
- # Set log handling for MerchantProfile
43
- if not Profile.logger.hasHandlers():
44
- console_handler = logging.StreamHandler()
45
- console_handler.setLevel(logging.INFO)
46
- formatter = logging.Formatter(
47
- "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
48
- )
49
- console_handler.setFormatter(formatter)
50
- Profile.logger.addHandler(console_handler)
51
-
52
- self.name = name
53
- self.about = about
54
- self.picture = picture
55
-
56
- if nsec:
57
- self.private_key = nsec
58
- keys = Keys.parse(self.private_key)
59
- self.public_key = keys.public_key().to_bech32()
60
- Profile.logger.info(
61
- f"Pre-defined private key reused for {self.name}: {self.private_key}"
62
- )
63
- Profile.logger.info(
64
- f"Pre-defined public key reused for {self.name}: {self.public_key}"
65
- )
66
- else:
67
- keys = Keys.generate()
68
- self.private_key = keys.secret_key().to_bech32()
69
- self.public_key = keys.public_key().to_bech32()
70
- Profile.logger.info(
71
- f"New private key created for {self.name}: {self.private_key}"
72
- )
73
- Profile.logger.info(
74
- f"New public key created for {self.name}: {self.public_key}"
75
- )
76
-
77
- self.url = str(self.WEB_URL) + str(self.public_key)
78
-
79
- def __str__(self) -> str:
80
- return (
81
- "Merchant Profile:\n"
82
- "Name = {}\n"
83
- "Description = {}\n"
84
- "Picture = {}\n"
85
- "URL = {}\n"
86
- "Private key = {}\n"
87
- "Public key = {}".format(
88
- self.name,
89
- self.about,
90
- self.picture,
91
- self.url,
92
- self.private_key,
93
- self.public_key,
94
- )
95
- )
96
-
97
- def to_dict(self) -> dict:
98
- return {
99
- "name": self.name,
100
- "description": self.about,
101
- "picture": self.picture,
102
- "public key": self.public_key,
103
- "private key": self.private_key,
104
- }
105
-
106
- def get_about(self) -> str:
107
- """
108
- Returns a description of the Merchant
109
-
110
- Returns:
111
- str: description of the Merchant
112
- """
113
- return self.about
114
-
115
- def get_name(self) -> str:
116
- """
117
- Returns the Merchant's name
12
+ raise ImportError("`agno` not installed. Please install using `pip install agno`")
118
13
 
119
- Returns:
120
- str: Merchant's name
121
- """
122
- return self.name
123
-
124
- def get_picture(self) -> str:
125
- """
126
- Returns the picture associated with the Merchant.
127
-
128
- Returns:
129
- str: URL to the picture associated with the Merchant
130
- """
131
- return self.picture
132
-
133
- def get_private_key(self) -> str:
134
- """
135
- Returns the private key.
136
-
137
- Returns:
138
- str: private key in bech32 format
139
- """
140
- return str(self.private_key)
141
-
142
- def get_public_key(self) -> str:
143
- """
144
- Returns the public key.
14
+ from pydantic import BaseModel, ConfigDict
145
15
 
146
- Returns:
147
- str: public key in bech32 format
148
- """
149
- return str(self.public_key)
150
-
151
- def get_url(self) -> str:
152
- return str(self.url)
153
-
154
-
155
- class MerchantProduct(BaseModel):
156
- model_config = ConfigDict(arbitrary_types_allowed=True)
157
-
158
- id: str
159
- stall_id: str
160
- name: str
161
- description: str
162
- images: List[str]
163
- currency: str
164
- price: float
165
- quantity: int
166
- shipping: List[ShippingCost]
167
- categories: Optional[List[str]] = []
168
- specs: Optional[List[List[str]]] = []
169
-
170
- @classmethod
171
- def from_product_data(cls, product: ProductData) -> "MerchantProduct":
172
- return cls(
173
- id=product.id,
174
- stall_id=product.stall_id,
175
- name=product.name,
176
- description=product.description,
177
- images=product.images,
178
- currency=product.currency,
179
- price=product.price,
180
- quantity=product.quantity,
181
- shipping=product.shipping,
182
- categories=product.categories if product.categories is not None else [],
183
- specs=product.specs if product.specs is not None else [],
184
- )
185
-
186
- def to_product_data(self) -> ProductData:
187
- return ProductData(
188
- id=self.id,
189
- stall_id=self.stall_id,
190
- name=self.name,
191
- description=self.description,
192
- images=self.images,
193
- currency=self.currency,
194
- price=self.price,
195
- quantity=self.quantity,
196
- shipping=self.shipping,
197
- categories=self.categories,
198
- specs=self.specs,
199
- )
200
-
201
- def to_dict(self) -> dict:
202
- """
203
- Returns a dictionary representation of the MerchantProduct.
204
- ShippingCost class is not serializable, so we need to convert it to a dictionary.
205
-
206
- Returns:
207
- dict: dictionary representation of the MerchantProduct
208
- """
209
- shipping_dicts = []
210
- for shipping in self.shipping:
211
- shipping_dicts.append({"id": shipping.id, "cost": shipping.cost})
212
-
213
- return {
214
- "id": self.id,
215
- "stall_id": self.stall_id,
216
- "name": self.name,
217
- "description": self.description,
218
- "images": self.images,
219
- "currency": self.currency,
220
- "price": self.price,
221
- "quantity": self.quantity,
222
- "shipping": shipping_dicts,
223
- "categories": self.categories,
224
- "specs": self.specs,
225
- }
226
-
227
-
228
- class MerchantStall(BaseModel):
229
- model_config = ConfigDict(arbitrary_types_allowed=True)
230
-
231
- id: str
232
- name: str
233
- description: str
234
- currency: str
235
- shipping: List[ShippingMethod]
236
-
237
- @classmethod
238
- def from_stall_data(cls, stall: StallData) -> "MerchantStall":
239
- return cls(
240
- id=stall.id(),
241
- name=stall.name(),
242
- description=stall.description(),
243
- currency=stall.currency(),
244
- shipping=stall.shipping(),
245
- )
246
-
247
- def to_stall_data(self) -> StallData:
248
- return StallData(
249
- self.id,
250
- self.name,
251
- self.description,
252
- self.currency,
253
- self.shipping, # No conversion needed
254
- )
255
16
 
256
- def to_dict(self) -> dict:
257
- """
258
- Returns a dictionary representation of the MerchantStall.
259
- ShippingMethod class is not serializable, so we need to convert it to a dictionary.
260
- We can only access cost and id from the ShippingMethod class. We can't access name or regions.
261
-
262
- Returns:
263
- dict: dictionary representation of the MerchantStall
264
- """
265
- shipping_dicts = []
266
- for shipping in self.shipping:
267
- shipping_dicts.append(
268
- {
269
- "cost": shipping.get_shipping_cost().cost,
270
- "id": shipping.get_shipping_cost().id,
271
- }
272
- )
273
-
274
- return {
275
- "id": self.id,
276
- "name": self.name,
277
- "description": self.description,
278
- "currency": self.currency,
279
- "shipping zones": [shipping_dicts],
280
- }
281
-
282
-
283
- class Merchant(Toolkit):
17
+ class MerchantTools(Toolkit):
284
18
  """
285
- Merchant is a toolkit that allows a merchant to publish products and stalls to Nostr.
19
+ MerchantTools is a toolkit that allows a merchant to publish products and stalls to Nostr.
286
20
 
287
21
  TBD:
288
22
  - Better differentiation between products and stalls in the database and products and stalls published.
@@ -301,7 +35,7 @@ class Merchant(Toolkit):
301
35
 
302
36
  def __init__(
303
37
  self,
304
- merchant_profile: Profile,
38
+ merchant_profile: AgentProfile,
305
39
  relay: str,
306
40
  stalls: List[MerchantStall],
307
41
  products: List[MerchantProduct],
@@ -317,9 +51,7 @@ class Merchant(Toolkit):
317
51
  super().__init__(name="merchant")
318
52
  self.relay = relay
319
53
  self.merchant_profile = merchant_profile
320
- self._nostr_client = NostrClient(
321
- self.relay, self.merchant_profile.get_private_key()
322
- )
54
+ self._nostr_client = NostrClient(relay, merchant_profile.get_private_key())
323
55
 
324
56
  # initialize the Product DB with no event id
325
57
  self.product_db = [(product, None) for product in products]
@@ -327,7 +59,7 @@ class Merchant(Toolkit):
327
59
  # initialize the Stall DB with no event id
328
60
  self.stall_db = [(stall, None) for stall in stalls]
329
61
 
330
- # Register wrapped versions of the methods
62
+ # Register methods
331
63
  self.register(self.get_profile)
332
64
  self.register(self.get_relay)
333
65
  self.register(self.get_products)
@@ -347,19 +79,25 @@ class Merchant(Toolkit):
347
79
 
348
80
  def get_profile(self) -> str:
349
81
  """
350
- Retrieves merchant profile in JSON format
82
+ Get the merchant profile in JSON format
351
83
 
352
84
  Returns:
353
85
  str: merchant profile in JSON format
354
86
  """
355
- return json.dumps(self.merchant_profile.to_dict())
87
+ return json.dumps(self.merchant_profile.to_json())
356
88
 
357
89
  def get_relay(self) -> str:
90
+ """
91
+ Get the Nostr relay the merchant is using
92
+
93
+ Returns:
94
+ str: Nostr relay
95
+ """
358
96
  return self.relay
359
97
 
360
98
  def get_products(self) -> str:
361
99
  """
362
- Retrieves all the merchant products
100
+ Get all the merchant products
363
101
 
364
102
  Returns:
365
103
  str: JSON string containing all products
@@ -368,7 +106,7 @@ class Merchant(Toolkit):
368
106
 
369
107
  def get_stalls(self) -> str:
370
108
  """
371
- Retrieves all the merchant stalls in JSON format
109
+ Get all the merchant stalls in JSON format
372
110
 
373
111
  Returns:
374
112
  str: JSON string containing all stalls
@@ -379,10 +117,13 @@ class Merchant(Toolkit):
379
117
  self,
380
118
  ) -> str:
381
119
  """
382
- Publishes or updates all products in the Merchant's Product DB
120
+ Publishes or updates to Nostrall products in the Merchant's Product DB
383
121
 
384
122
  Returns:
385
123
  str: JSON array with status of all product publishing operations
124
+
125
+ Raises:
126
+ ValueError: if NostrClient is not initialized
386
127
  """
387
128
 
388
129
  if self._nostr_client is None:
@@ -393,9 +134,9 @@ class Merchant(Toolkit):
393
134
  for i, (product, _) in enumerate(self.product_db):
394
135
  try:
395
136
  # Convert MerchantProduct to ProductData for nostr_client
396
- product_data = product.to_product_data()
137
+ # product_data = product.to_product_data()
397
138
  # Publish using the SDK's synchronous method
398
- event_id = self._nostr_client.publish_product(product_data)
139
+ event_id = self._nostr_client.publish_product(product)
399
140
  self.product_db[i] = (product, event_id)
400
141
  results.append(
401
142
  {
@@ -404,8 +145,10 @@ class Merchant(Toolkit):
404
145
  "product_name": product.name,
405
146
  }
406
147
  )
148
+ # Pause for 0.5 seconds to avoid rate limiting
149
+ time.sleep(0.5)
407
150
  except Exception as e:
408
- Profile.logger.error(f"Unable to publish product {product}. Error {e}")
151
+ logging.error(f"Unable to publish product {product}. Error {e}")
409
152
  results.append(
410
153
  {"status": "error", "message": str(e), "product_name": product.name}
411
154
  )
@@ -416,10 +159,13 @@ class Merchant(Toolkit):
416
159
  self,
417
160
  ) -> str:
418
161
  """
419
- Publishes or updates all stalls managed by the merchant and adds the corresponding EventId to the Stall DB
162
+ Publishes or updates to Nostr all stalls managed by the merchant and adds the corresponding EventId to the Stall DB
420
163
 
421
164
  Returns:
422
165
  str: JSON array with status of all stall publishing operations
166
+
167
+ Raises:
168
+ ValueError: if NostrClient is not initialized
423
169
  """
424
170
  if self._nostr_client is None:
425
171
  raise ValueError("NostrClient not initialized")
@@ -427,9 +173,9 @@ class Merchant(Toolkit):
427
173
 
428
174
  for i, (stall, _) in enumerate(self.stall_db):
429
175
  try:
430
- # Convert MerchantStall to StallData for nostr_client
431
- stall_data = stall.to_stall_data()
432
- event_id = self._nostr_client.publish_stall(stall_data)
176
+ # We don't need to convert MerchantStall to StallData for nostr_client
177
+ # stall_data = stall.to_stall_data()
178
+ event_id = self._nostr_client.publish_stall(stall)
433
179
  self.stall_db[i] = (stall, event_id)
434
180
  results.append(
435
181
  {
@@ -438,8 +184,10 @@ class Merchant(Toolkit):
438
184
  "stall_name": stall.name,
439
185
  }
440
186
  )
187
+ # Pause for 0.5 seconds to avoid rate limiting
188
+ time.sleep(0.5)
441
189
  except Exception as e:
442
- Profile.logger.error(f"Unable to publish stall {stall}. Error {e}")
190
+ logging.error(f"Unable to publish stall {stall}. Error {e}")
443
191
  results.append(
444
192
  {"status": "error", "message": str(e), "stall_name": stall.name}
445
193
  )
@@ -448,13 +196,16 @@ class Merchant(Toolkit):
448
196
 
449
197
  def publish_new_product(self, product: MerchantProduct) -> str:
450
198
  """
451
- Publishes a new product that is not currently in the Merchant's Product DB and adds it to the Product DB
199
+ Publishes to Nostra new product that is not currently in the Merchant's Product DB and adds it to the Product DB
452
200
 
453
201
  Args:
454
202
  product: MerchantProduct to be published
455
203
 
456
204
  Returns:
457
205
  str: JSON string with status of the operation
206
+
207
+ Raises:
208
+ ValueError: if NostrClient is not initialized
458
209
  """
459
210
  if self._nostr_client is None:
460
211
  raise ValueError("NostrClient not initialized")
@@ -480,12 +231,15 @@ class Merchant(Toolkit):
480
231
 
481
232
  def publish_product_by_name(self, arguments: str) -> str:
482
233
  """
483
- Publishes or updates a given product from the Merchant's Product DB
234
+ Publishes or updates to Nostra given product from the Merchant's Product DB
484
235
  Args:
485
236
  arguments: JSON string that may contain {"name": "product_name"} or just "product_name"
486
237
 
487
238
  Returns:
488
239
  str: JSON string with status of the operation
240
+
241
+ Raises:
242
+ ValueError: if NostrClient is not initialized
489
243
  """
490
244
  if self._nostr_client is None:
491
245
  raise ValueError("NostrClient not initialized")
@@ -508,9 +262,9 @@ class Merchant(Toolkit):
508
262
  if product.name == name:
509
263
  try:
510
264
  # Convert MerchantProduct to ProductData for nostr_client
511
- product_data = product.to_product_data()
265
+ # product_data = product.to_product_data()
512
266
  # Publish using the SDK's synchronous method
513
- event_id = self._nostr_client.publish_product(product_data)
267
+ event_id = self._nostr_client.publish_product(product)
514
268
  # Update the product_db with the new event_id
515
269
  self.product_db[i] = (product, event_id)
516
270
  return json.dumps(
@@ -520,6 +274,8 @@ class Merchant(Toolkit):
520
274
  "product_name": product.name,
521
275
  }
522
276
  )
277
+ # Pause for 0.5 seconds to avoid rate limiting
278
+ time.sleep(0.5)
523
279
  except Exception as e:
524
280
  return json.dumps(
525
281
  {
@@ -540,7 +296,7 @@ class Merchant(Toolkit):
540
296
 
541
297
  def publish_products_by_stall_name(self, arguments: Union[str, dict]) -> str:
542
298
  """
543
- Publishes or updates all products sold by the merchant in a given stall
299
+ Publishes or updates to Nostr all products sold by the merchant in a given stall
544
300
 
545
301
  Args:
546
302
  arguments: str or dict with the stall name. Can be in formats:
@@ -550,6 +306,9 @@ class Merchant(Toolkit):
550
306
 
551
307
  Returns:
552
308
  str: JSON array with status of all product publishing operations
309
+
310
+ Raises:
311
+ ValueError: if NostrClient is not initialized
553
312
  """
554
313
  if self._nostr_client is None:
555
314
  raise ValueError("NostrClient not initialized")
@@ -604,8 +363,8 @@ class Merchant(Toolkit):
604
363
  for i, (product, _) in enumerate(self.product_db):
605
364
  if product.stall_id == stall_id:
606
365
  try:
607
- product_data = product.to_product_data()
608
- event_id = self._nostr_client.publish_product(product_data)
366
+ # product_data = product.to_product_data()
367
+ event_id = self._nostr_client.publish_product(product)
609
368
  self.product_db[i] = (product, event_id)
610
369
  results.append(
611
370
  {
@@ -615,6 +374,8 @@ class Merchant(Toolkit):
615
374
  "stall_name": stall_name,
616
375
  }
617
376
  )
377
+ # Pause for 0.5 seconds to avoid rate limiting
378
+ time.sleep(0.5)
618
379
  except Exception as e:
619
380
  results.append(
620
381
  {
@@ -645,7 +406,7 @@ class Merchant(Toolkit):
645
406
 
646
407
  def publish_profile(self) -> str:
647
408
  """
648
- Publishes the profile on Nostr
409
+ Publishes the profile to Nostr
649
410
 
650
411
  Returns:
651
412
  str: JSON of the event that published the profile
@@ -668,22 +429,25 @@ class Merchant(Toolkit):
668
429
 
669
430
  def publish_new_stall(self, stall: MerchantStall) -> str:
670
431
  """
671
- Publishes a new stall that is not currently in the Merchant's Stall DB and adds it to the Stall DB
432
+ Publishes to Nostr a new stall that is not currently in the Merchant's Stall DB and adds it to the Stall DB
672
433
 
673
434
  Args:
674
435
  stall: MerchantStall to be published
675
436
 
676
437
  Returns:
677
438
  str: JSON string with status of the operation
439
+
440
+ Raises:
441
+ ValueError: if NostrClient is not initialized
678
442
  """
679
443
  if self._nostr_client is None:
680
444
  raise ValueError("NostrClient not initialized")
681
445
 
682
446
  try:
683
- # Convert to StallData for SDK
684
- stall_data = stall.to_stall_data()
685
- # Publish using the SDK's synchronous method
686
- event_id = self._nostr_client.publish_stall(stall_data)
447
+ # We don't ned to convert to StallData. nostr_client.publish_stall() accepts a MerchantStall
448
+ # stall_data = stall.to_stall_data()
449
+ # Publish using the synchronous method
450
+ event_id = self._nostr_client.publish_stall(stall)
687
451
  # we need to add the stall event id to the stall db
688
452
  self.stall_db.append((stall, event_id))
689
453
  return json.dumps(
@@ -699,6 +463,21 @@ class Merchant(Toolkit):
699
463
  )
700
464
 
701
465
  def publish_stall_by_name(self, arguments: Union[str, dict]) -> str:
466
+ """
467
+ Publishes or updates to Nostr a given stall by name
468
+
469
+ Args:
470
+ arguments: str or dict with the stall name. Can be in formats:
471
+ - {"name": "stall_name"}
472
+ - {"arguments": "{\"name\": \"stall_name\"}"}
473
+ - "stall_name"
474
+
475
+ Returns:
476
+ str: JSON array with status of the operation
477
+
478
+ Raises:
479
+ ValueError: if NostrClient is not initialized
480
+ """
702
481
  if self._nostr_client is None:
703
482
  raise ValueError("NostrClient not initialized")
704
483
 
@@ -735,8 +514,9 @@ class Merchant(Toolkit):
735
514
  for i, (stall, _) in enumerate(self.stall_db):
736
515
  if stall.name == stall_name:
737
516
  try:
738
- stall_data = stall.to_stall_data()
739
- event_id = self._nostr_client.publish_stall(stall_data)
517
+ # We are not passing StallData to nostr_client.publish_stall() anymore
518
+ # stall_data = stall.to_stall_data()
519
+ event_id = self._nostr_client.publish_stall(stall)
740
520
  self.stall_db[i] = (stall, event_id)
741
521
  return json.dumps(
742
522
  {
@@ -745,6 +525,8 @@ class Merchant(Toolkit):
745
525
  "stall_name": stall.name,
746
526
  }
747
527
  )
528
+ # Pause for 0.5 seconds to avoid rate limiting
529
+ time.sleep(0.5)
748
530
  except Exception as e:
749
531
  return json.dumps(
750
532
  [
@@ -774,10 +556,13 @@ class Merchant(Toolkit):
774
556
 
775
557
  def remove_all_products(self) -> str:
776
558
  """
777
- Removes all published products from Nostr
559
+ Removes from Nostr all products published by the merchant
778
560
 
779
561
  Returns:
780
562
  str: JSON array with status of all product removal operations
563
+
564
+ Raises:
565
+ ValueError: if NostrClient is not initialized
781
566
  """
782
567
  if self._nostr_client is None:
783
568
  raise ValueError("NostrClient not initialized")
@@ -810,6 +595,8 @@ class Merchant(Toolkit):
810
595
  "event_id": str(event_id),
811
596
  }
812
597
  )
598
+ # Pause for 0.5 seconds to avoid rate limiting
599
+ time.sleep(0.5)
813
600
  except Exception as e:
814
601
  results.append(
815
602
  {"status": "error", "message": str(e), "product_name": product.name}
@@ -819,10 +606,13 @@ class Merchant(Toolkit):
819
606
 
820
607
  def remove_all_stalls(self) -> str:
821
608
  """
822
- Removes all stalls and their products from Nostr
609
+ Removes from Nostr all stalls from the merchant and their corresponding products
823
610
 
824
611
  Returns:
825
612
  str: JSON array with status of all removal operations
613
+
614
+ Raises:
615
+ ValueError: if NostrClient is not initialized
826
616
  """
827
617
  if self._nostr_client is None:
828
618
  raise ValueError("NostrClient not initialized")
@@ -897,6 +687,8 @@ class Merchant(Toolkit):
897
687
  "event_id": str(stall_event_id),
898
688
  }
899
689
  )
690
+ # Pause for 0.5 seconds to avoid rate limiting
691
+ time.sleep(0.5)
900
692
  except Exception as e:
901
693
  results.append(
902
694
  {"status": "error", "message": str(e), "stall_name": stall_name}
@@ -906,13 +698,16 @@ class Merchant(Toolkit):
906
698
 
907
699
  def remove_product_by_name(self, arguments: str) -> str:
908
700
  """
909
- Deletes a product with the given name from Nostr
701
+ Removes from Nostr a product with the given name
910
702
 
911
703
  Args:
912
704
  arguments: JSON string that may contain {"name": "product_name"} or just "product_name"
913
705
 
914
706
  Returns:
915
707
  str: JSON string with status of the operation
708
+
709
+ Raises:
710
+ ValueError: if NostrClient is not initialized
916
711
  """
917
712
  if self._nostr_client is None:
918
713
  raise ValueError("NostrClient not initialized")
@@ -949,6 +744,8 @@ class Merchant(Toolkit):
949
744
  )
950
745
  # Remove the event_id, keeping the product in the database
951
746
  self.product_db[i] = (product, None)
747
+ # Pause for 0.5 seconds to avoid rate limiting
748
+ time.sleep(0.5)
952
749
  return json.dumps(
953
750
  {
954
751
  "status": "success",
@@ -972,7 +769,8 @@ class Merchant(Toolkit):
972
769
  )
973
770
 
974
771
  def remove_stall_by_name(self, arguments: Union[str, dict]) -> str:
975
- """Remove a stall and its products by name
772
+ """
773
+ Remove from Nostr a stall and its products by name
976
774
 
977
775
  Args:
978
776
  arguments: str or dict with the stall name. Can be in formats:
@@ -982,6 +780,9 @@ class Merchant(Toolkit):
982
780
 
983
781
  Returns:
984
782
  str: JSON array with status of the operation
783
+
784
+ Raises:
785
+ ValueError: if NostrClient is not initialized
985
786
  """
986
787
  if self._nostr_client is None:
987
788
  raise ValueError("NostrClient not initialized")
@@ -1063,6 +864,8 @@ class Merchant(Toolkit):
1063
864
  "event_id": str(event_id),
1064
865
  }
1065
866
  )
867
+ # Pause for 0.5 seconds to avoid rate limiting
868
+ time.sleep(0.5)
1066
869
  except Exception as e:
1067
870
  results.append(
1068
871
  {
@@ -1117,15 +920,15 @@ class Merchant(Toolkit):
1117
920
  [{"status": "error", "message": str(e), "stall_name": "unknown"}]
1118
921
  )
1119
922
 
1120
- def get_event_id(self, response: Any) -> str:
1121
- """Convert any response to a string event ID.
923
+ # def get_event_id(self, response: Any) -> str:
924
+ # """Convert any response to a string event ID.
1122
925
 
1123
- Args:
1124
- response: Response that might contain an event ID
926
+ # Args:
927
+ # response: Response that might contain an event ID
1125
928
 
1126
- Returns:
1127
- str: String representation of event ID or empty string if None
1128
- """
1129
- if response is None:
1130
- return ""
1131
- return str(response)
929
+ # Returns:
930
+ # str: String representation of event ID or empty string if None
931
+ # """
932
+ # if response is None:
933
+ # return ""
934
+ # return str(response)