feat: add auto-reply on payment (#82)
This commit is contained in:
parent
d26ff891ce
commit
1a3fc62106
3 changed files with 70 additions and 28 deletions
|
|
@ -218,6 +218,8 @@ class Stall(PartialStall, Nostrable):
|
||||||
class ProductConfig(BaseModel):
|
class ProductConfig(BaseModel):
|
||||||
description: Optional[str]
|
description: Optional[str]
|
||||||
currency: Optional[str]
|
currency: Optional[str]
|
||||||
|
use_autoreply: Optional[bool] = False
|
||||||
|
autoreply_message: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
class PartialProduct(BaseModel):
|
class PartialProduct(BaseModel):
|
||||||
|
|
|
||||||
69
services.py
69
services.py
|
|
@ -1,3 +1,4 @@
|
||||||
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from typing import List, Optional, Tuple
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
|
|
@ -176,6 +177,8 @@ async def handle_order_paid(order_id: str, merchant_pubkey: str):
|
||||||
success, message = await update_products_for_order(merchant, order)
|
success, message = await update_products_for_order(merchant, order)
|
||||||
await notify_client_of_order_status(order, merchant, success, message)
|
await notify_client_of_order_status(order, merchant, success, message)
|
||||||
|
|
||||||
|
await autoreply_for_products_in_order(merchant, order)
|
||||||
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.warning(ex)
|
logger.warning(ex)
|
||||||
|
|
||||||
|
|
@ -202,32 +205,8 @@ async def notify_client_of_order_status(
|
||||||
else:
|
else:
|
||||||
dm_content = f"Order cannot be fulfilled. Reason: {message}"
|
dm_content = f"Order cannot be fulfilled. Reason: {message}"
|
||||||
|
|
||||||
dm_event = merchant.build_dm_event(dm_content, order.public_key)
|
dm_type = DirectMessageType.ORDER_PAID_OR_SHIPPED.value if success else DirectMessageType.PLAIN_TEXT.value
|
||||||
|
await send_dm(merchant, order.public_key, dm_type, dm_content)
|
||||||
dm = PartialDirectMessage(
|
|
||||||
event_id=dm_event.id,
|
|
||||||
event_created_at=dm_event.created_at,
|
|
||||||
message=dm_content,
|
|
||||||
public_key=order.public_key,
|
|
||||||
type=DirectMessageType.ORDER_PAID_OR_SHIPPED.value
|
|
||||||
if success
|
|
||||||
else DirectMessageType.PLAIN_TEXT.value,
|
|
||||||
)
|
|
||||||
dm_reply = await create_direct_message(merchant.id, dm)
|
|
||||||
|
|
||||||
await nostr_client.publish_nostr_event(dm_event)
|
|
||||||
|
|
||||||
await websocketUpdater(
|
|
||||||
merchant.id,
|
|
||||||
json.dumps(
|
|
||||||
{
|
|
||||||
"type": f"dm:{dm.type}",
|
|
||||||
"customerPubkey": order.public_key,
|
|
||||||
"dm": dm_reply.dict(),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def update_products_for_order(
|
async def update_products_for_order(
|
||||||
merchant: Merchant, order: Order
|
merchant: Merchant, order: Order
|
||||||
|
|
@ -247,6 +226,44 @@ async def update_products_for_order(
|
||||||
|
|
||||||
return True, "ok"
|
return True, "ok"
|
||||||
|
|
||||||
|
async def autoreply_for_products_in_order(
|
||||||
|
merchant: Merchant, order: Order
|
||||||
|
) -> Tuple[bool, str]:
|
||||||
|
product_ids = [i.product_id for i in order.items]
|
||||||
|
|
||||||
|
products = await get_products_by_ids(merchant.id, product_ids)
|
||||||
|
products_with_autoreply = [p for p in products if p.config.use_autoreply]
|
||||||
|
|
||||||
|
for p in products_with_autoreply:
|
||||||
|
dm_content = p.config.autoreply_message
|
||||||
|
await send_dm(merchant, order.public_key, DirectMessageType.PLAIN_TEXT.value, dm_content)
|
||||||
|
await asyncio.sleep(1) # do not send all autoreplies at once
|
||||||
|
|
||||||
|
|
||||||
|
async def send_dm(merchant: Merchant, other_pubkey: str, type: str, dm_content: str,):
|
||||||
|
dm_event = merchant.build_dm_event(dm_content, other_pubkey)
|
||||||
|
|
||||||
|
dm = PartialDirectMessage(
|
||||||
|
event_id=dm_event.id,
|
||||||
|
event_created_at=dm_event.created_at,
|
||||||
|
message=dm_content,
|
||||||
|
public_key=other_pubkey,
|
||||||
|
type=type
|
||||||
|
)
|
||||||
|
dm_reply = await create_direct_message(merchant.id, dm)
|
||||||
|
|
||||||
|
await nostr_client.publish_nostr_event(dm_event)
|
||||||
|
|
||||||
|
await websocketUpdater(
|
||||||
|
merchant.id,
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"type": f"dm:{dm.type}",
|
||||||
|
"customerPubkey": other_pubkey,
|
||||||
|
"dm": dm_reply.dict(),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
async def compute_products_new_quantity(
|
async def compute_products_new_quantity(
|
||||||
merchant_id: str, product_ids: List[str], items: List[OrderItem]
|
merchant_id: str, product_ids: List[str], items: List[OrderItem]
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,28 @@
|
||||||
:mask="stall.currency != 'sat' ? '#.##' : '#'" fill-mask="0" reverse-fill-mask></q-input>
|
:mask="stall.currency != 'sat' ? '#.##' : '#'" fill-mask="0" reverse-fill-mask></q-input>
|
||||||
<q-input filled dense v-model.number="productDialog.data.quantity" type="number" label="Quantity"></q-input>
|
<q-input filled dense v-model.number="productDialog.data.quantity" type="number" label="Quantity"></q-input>
|
||||||
|
|
||||||
|
|
||||||
|
<q-expansion-item group="advanced" icon="settings" label="Advanced options">
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="row q-mb-sm">
|
||||||
|
<div class="col">
|
||||||
|
<q-checkbox v-model="productDialog.data.config.use_autoreply" dense label="Autoreply when paid" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row q-mb-sm q-ml-sm">
|
||||||
|
<div class="col">
|
||||||
|
<q-input v-model="productDialog.data.config.autoreply_message" filled dense type="textarea" rows="5"
|
||||||
|
label="Autoreply message" hint="It can include link to a digital asset">
|
||||||
|
</q-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-expansion-item>
|
||||||
|
|
||||||
|
|
||||||
<div class="row q-mt-lg">
|
<div class="row q-mt-lg">
|
||||||
<q-btn v-if="productDialog.data.id" type="submit"
|
<q-btn v-if="productDialog.data.id" type="submit"
|
||||||
:label="productDialog.data.pending ? 'Restore Product' : 'Update Product'" unelevated
|
:label="productDialog.data.pending ? 'Restore Product' : 'Update Product'" unelevated
|
||||||
|
|
@ -164,6 +186,8 @@
|
||||||
|
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</q-form>
|
</q-form>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
|
@ -191,8 +215,7 @@
|
||||||
There are no products to be restored.
|
There are no products to be restored.
|
||||||
</div>
|
</div>
|
||||||
<div class="row q-mt-lg">
|
<div class="row q-mt-lg">
|
||||||
<q-btn @click="restoreAllPendingProducts" v-close-popup flat color="green"
|
<q-btn @click="restoreAllPendingProducts" v-close-popup flat color="green">Restore All</q-btn>
|
||||||
>Restore All</q-btn>
|
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
|
||||||
</div>
|
</div>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue