agentstr 0.1.11__py3-none-any.whl → 0.1.12__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.
- agentstr/__init__.py +20 -13
- agentstr/buyer.py +291 -0
- agentstr/buyer.pyi +31 -0
- agentstr/{marketplace.py → merchant.py} +126 -323
- agentstr/merchant.pyi +37 -0
- agentstr/models.py +381 -0
- agentstr/models.pyi +103 -0
- agentstr/nostr.py +389 -53
- agentstr/nostr.pyi +82 -0
- agentstr/py.typed +0 -0
- {agentstr-0.1.11.dist-info → agentstr-0.1.12.dist-info}/METADATA +36 -53
- agentstr-0.1.12.dist-info/RECORD +15 -0
- agentstr-0.1.11.dist-info/RECORD +0 -8
- {agentstr-0.1.11.dist-info → agentstr-0.1.12.dist-info}/LICENSE +0 -0
- {agentstr-0.1.11.dist-info → agentstr-0.1.12.dist-info}/WHEEL +0 -0
- {agentstr-0.1.11.dist-info → agentstr-0.1.12.dist-info}/top_level.txt +0 -0
@@ -1,288 +1,22 @@
|
|
1
|
-
import ast
|
2
1
|
import json
|
3
2
|
import logging
|
4
|
-
import
|
5
|
-
from typing import Any,
|
6
|
-
|
7
|
-
from agentstr.
|
8
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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:
|
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
|
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
|
-
|
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.
|
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
|
-
|
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
|
-
|
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
|
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(
|
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
|
-
|
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
|
-
#
|
431
|
-
stall_data = stall.to_stall_data()
|
432
|
-
event_id = self._nostr_client.publish_stall(
|
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
|
-
|
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
|
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
|
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(
|
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(
|
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
|
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
|
-
#
|
684
|
-
stall_data = stall.to_stall_data()
|
685
|
-
# Publish using the
|
686
|
-
event_id = self._nostr_client.publish_stall(
|
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
|
-
|
739
|
-
|
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
|
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
|
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
|
-
|
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
|
-
"""
|
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
|
-
|
923
|
+
# def get_event_id(self, response: Any) -> str:
|
924
|
+
# """Convert any response to a string event ID.
|
1122
925
|
|
1123
|
-
|
1124
|
-
|
926
|
+
# Args:
|
927
|
+
# response: Response that might contain an event ID
|
1125
928
|
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
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)
|