This commit is contained in:
Believethehype
2024-03-18 22:50:48 +01:00
parent 3788bf8ec1
commit 7d2c3f3a36
8 changed files with 265 additions and 97 deletions

View File

@@ -126,7 +126,7 @@ class DVM:
print("[" + self.dvm_config.NIP89.NAME + "] Checking Subscription status") print("[" + self.dvm_config.NIP89.NAME + "] Checking Subscription status")
subscription_status = nip88_has_active_subscription(PublicKey.parse(user.npub), subscription_status = nip88_has_active_subscription(PublicKey.parse(user.npub),
self.dvm_config.NIP88.DTAG, self.client, self.dvm_config.NIP88.DTAG, self.client,
self.dvm_config) self.dvm_config.PUBLIC_KEY)
if subscription_status["isActive"]: if subscription_status["isActive"]:
print("User subscribed until: " + str( print("User subscribed until: " + str(
@@ -317,7 +317,7 @@ class DVM:
config=self.dvm_config) config=self.dvm_config)
# a regular note # a regular note
elif not anon: elif not anon and dvm_config.NIP88 is None:
print("[" + self.dvm_config.NIP89.NAME + "] Profile Zap received for DVM balance: " + print("[" + self.dvm_config.NIP89.NAME + "] Profile Zap received for DVM balance: " +
str(invoice_amount) + " Sats from " + str(user.name)) str(invoice_amount) + " Sats from " + str(user.name))
update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client, update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client,

View File

@@ -11,10 +11,12 @@ from nostr_sdk import (Keys, Client, Timestamp, Filter, nip04_decrypt, HandleNot
from nostr_dvm.utils.database_utils import fetch_user_metadata from nostr_dvm.utils.database_utils import fetch_user_metadata
from nostr_dvm.utils.definitions import EventDefinitions from nostr_dvm.utils.definitions import EventDefinitions
from nostr_dvm.utils.dvmconfig import DVMConfig from nostr_dvm.utils.dvmconfig import DVMConfig
from nostr_dvm.utils.nip88_utils import nip88_has_active_subscription
from nostr_dvm.utils.nip89_utils import NIP89Config from nostr_dvm.utils.nip89_utils import NIP89Config
from nostr_dvm.utils.nwc_tools import nwc_zap from nostr_dvm.utils.nwc_tools import nwc_zap
from nostr_dvm.utils.subscription_utils import create_subscription_sql_table, add_to_subscription_sql_table, \ from nostr_dvm.utils.subscription_utils import create_subscription_sql_table, add_to_subscription_sql_table, \
get_from_subscription__sql_table, update_subscription_sql_table get_from_subscription_sql_table, update_subscription_sql_table, get_all_subscriptions_from_sql_table, \
delete_from_subscription_sql_table
from nostr_dvm.utils.zap_utils import create_bolt11_lud16, zaprequest from nostr_dvm.utils.zap_utils import create_bolt11_lud16, zaprequest
@@ -95,13 +97,85 @@ class Subscription:
kind7001eventid = tag.as_vec()[1] kind7001eventid = tag.as_vec()[1]
if kind7001eventid != "": if kind7001eventid != "":
subscription = get_from_subscription__sql_table("db/subscriptions", kind7001eventid) subscription = get_from_subscription_sql_table("db/subscriptions", kind7001eventid)
if subscription is not None: if subscription is not None:
update_subscription_sql_table("db/subscriptions", kind7001eventid, recipient, update_subscription_sql_table("db/subscriptions", kind7001eventid, recipient,
subscription.subscriber, subscription.nwc, subscription.cadence, subscription.subscriber, subscription.nwc, subscription.cadence,
subscription.amount, subscription.begin, subscription.end, subscription.amount, subscription.begin, subscription.end,
subscription.tier_dtag, subscription.zaps, subscription.recipe, False) subscription.tier_dtag, subscription.zaps, subscription.recipe,
False, Timestamp.now().as_secs())
def infer_subscription_end_time(start, cadence):
end = start
if cadence == "daily":
end = start + 60 * 60 * 24
elif cadence == "weekly":
end = start + 60 * 60 * 24 * 7
elif cadence == "monthly":
# TODO check days of month -.-
end = start + 60 * 60 * 24 * 31
elif cadence == "yearly":
# TODO check extra day every 4 years
end = start + 60 * 60 * 24 * 356
return end
def pay_zap_split(nwc, overall_amount, zaps):
overallsplit = 0
for zap in zaps:
overallsplit += int(zap['split'])
zapped_amount = 0
for zap in zaps:
name, nip05, lud16 = fetch_user_metadata(zap['key'], self.client)
splitted_amount = math.floor(
(int(zap['split']) / overallsplit) * int(overall_amount) / 1000)
# invoice = create_bolt11_lud16(lud16, splitted_amount)
# TODO add details about DVM in message
invoice = zaprequest(lud16, splitted_amount, "DVM subscription", None,
PublicKey.parse(zap['key']), self.keys, DVMConfig.RELAY_LIST)
print(invoice)
if invoice is not None:
nwc_event_id = nwc_zap(nwc, invoice, self.keys, zap['relay'])
if nwc_event_id is None:
print("error zapping " + lud16)
else:
zapped_amount = zapped_amount + (splitted_amount * 1000)
print(str(zapped_amount) + "/" + str(overall_amount))
if zapped_amount < overall_amount * 0.8: # TODO how do we handle failed zaps for some addresses? we are ok with 80% for now
success = False
else:
print("Zapped successfully")
success = True
# if no active subscription exists OR the subscription ended, pay
return success
def make_subscription_zap_recipe(event7001, recipient, subscriber, start, end, tier_dtag):
message = "payed by subscription service"
pTag = Tag.parse(["p", recipient])
PTag = Tag.parse(["P", subscriber])
eTag = Tag.parse(["e", event7001])
validTag = Tag.parse(["valid", str(start), str(end)])
tierTag = Tag.parse(["tier", tier_dtag])
alttag = Tag.parse(["alt", "This is a NIP90 DVM Subscription Payment Recipe"])
tags = [pTag, PTag, eTag, validTag, tierTag, alttag]
event = EventBuilder(EventDefinitions.KIND_NIP88_PAYMENT_RECIPE,
message, tags).to_event(self.keys)
dvmconfig = DVMConfig()
signer = NostrSigner.keys(self.keys)
client = Client(signer)
for relay in dvmconfig.RELAY_LIST:
client.add_relay(relay)
client.connect()
recipeid = client.send_event(event)
recipe = recipeid.to_hex()
return recipe
def handle_dm(nostr_event): def handle_dm(nostr_event):
@@ -123,105 +197,59 @@ class Subscription:
tier_dtag = jsonevent['tier_dtag'] tier_dtag = jsonevent['tier_dtag']
start = Timestamp.now().as_secs() start = Timestamp.now().as_secs()
end = Timestamp.now().as_secs()
isactivesubscription = False isactivesubscription = False
recipe = "" recipe = ""
subscription = get_from_subscription__sql_table("db/subscriptions", event7001) subscription = get_from_subscription_sql_table("db/subscriptions", event7001)
if subscription is not None and subscription.end > start: #if subscription is not None and subscription.end > start:
start = subscription.end # start = subscription.end
isactivesubscription = True # isactivesubscription = True
if cadence == "daily":
end = start + 60 * 60 * 24
elif cadence == "weekly":
end = start + 60 * 60 * 24 * 7
elif cadence == "monthly":
# TODO check days of month -.-
end = start + 60 * 60 * 24 * 31
elif cadence == "yearly":
# TODO check extra day every 4 years
end = start + 60 * 60 * 24 * 356
zapsstr = json.dumps(jsonevent['zaps']) zapsstr = json.dumps(jsonevent['zaps'])
print(zapsstr) print(zapsstr)
success = True success = True
if subscription is None or subscription.end <= Timestamp.now().as_secs(): if subscription is None or subscription.end <= Timestamp.now().as_secs():
#rather check nostr if our db is right
subscription_status = nip88_has_active_subscription(
PublicKey.parse(subscriber),
tier_dtag, self.client, recipient)
overallsplit = 0 if not subscription_status["isActive"]:
success = pay_zap_split(nwc, overall_amount, jsonevent['zaps'])
start = Timestamp.now().as_secs()
end = infer_subscription_end_time(start, cadence)
else:
start = Timestamp.now().as_secs()
end = subscription_status["validUntil"]
else:
start = subscription.begin
end = subscription.end
for zap in jsonevent['zaps']:
overallsplit += int(zap['split'])
zapped_amount = 0
for zap in jsonevent['zaps']:
name, nip05, lud16 = fetch_user_metadata(zap['key'], self.client)
splitted_amount = math.floor(
(int(zap['split']) / overallsplit) * int(jsonevent['overall_amount']) / 1000)
# invoice = create_bolt11_lud16(lud16, splitted_amount)
# TODO add details about DVM in message
invoice = zaprequest(lud16, splitted_amount, "DVM subscription", None,
PublicKey.parse(zap['key']), self.keys, DVMConfig.RELAY_LIST)
print(invoice)
if invoice is not None:
nwc_event_id = nwc_zap(nwc, invoice, self.keys, zap['relay'])
if nwc_event_id is None:
print("error zapping " + lud16)
else:
zapped_amount = zapped_amount + (splitted_amount * 1000)
print(str(zapped_amount) + "/" + str(overall_amount))
if zapped_amount < overall_amount * 0.8: # TODO how do we handle failed zaps for some addresses? we are ok with 80% for now
success = False
else:
print("Zapped successfully")
# if no active subscription exists OR the subscription ended, pay
if success: if success:
message = "payed by subscription service" recipe = make_subscription_zap_recipe(event7001, recipient, subscriber, start, end, tier_dtag)
pTag = Tag.parse(["p", recipient])
PTag = Tag.parse(["P", subscriber])
eTag = Tag.parse(["e", event7001])
validTag = Tag.parse(["valid", str(start), str(end)])
tierTag = Tag.parse(["tier", tier_dtag])
alttag = Tag.parse(["alt", "This is a NIP90 DVM Subscription Payment Recipe"])
tags = [pTag, PTag, eTag, validTag, tierTag, alttag]
event = EventBuilder(EventDefinitions.KIND_NIP88_PAYMENT_RECIPE,
message, tags).to_event(self.keys)
dvmconfig = DVMConfig()
signer = NostrSigner.keys(self.keys)
client = Client(signer)
for relay in dvmconfig.RELAY_LIST:
client.add_relay(relay)
client.connect()
recipeid = client.send_event(event)
recipe = recipeid.to_hex()
print("RECIPE " + recipe) print("RECIPE " + recipe)
isactivesubscription = True isactivesubscription = True
if subscription is None: if subscription is None:
add_to_subscription_sql_table("db/subscriptions", event7001, recipient, subscriber, nwc, add_to_subscription_sql_table("db/subscriptions", event7001, recipient, subscriber, nwc,
cadence, overall_amount, start, end, tier_dtag, cadence, overall_amount, start, end, tier_dtag,
zapsstr, recipe, isactivesubscription) zapsstr, recipe, isactivesubscription, Timestamp.now().as_secs())
print("new subscription entry") print("new subscription entry")
else: else:
update_subscription_sql_table("db/subscriptions", event7001, recipient, subscriber, nwc, update_subscription_sql_table("db/subscriptions", event7001, recipient, subscriber, nwc,
cadence, overall_amount, start, end, cadence, overall_amount, start, end,
tier_dtag, zapsstr, recipe, isactivesubscription) tier_dtag, zapsstr, recipe, isactivesubscription,
Timestamp.now().as_secs())
print("updated subscription entry") print("updated subscription entry")
except Exception as e: except Exception as e:
print(e) print(e)
except Exception as e: except Exception as e:
print("Error in Subscriber " + str(e)) print("Error in Subscriber " + str(e))
@@ -230,9 +258,65 @@ class Subscription:
try: try:
while True: while True:
time.sleep(60.0) time.sleep(60.0)
subscriptions = get_all_subscriptions_from_sql_table("db/subscriptions")
print("Checking " + str(len(subscriptions)) + " entries..")
for subscription in subscriptions:
if subscription.active:
if subscription.end < Timestamp.now().as_secs():
# We could directly zap, but let's make another check if our subscription expired
subscription_status = nip88_has_active_subscription(
PublicKey.parse(subscription.subscriber),
subscription.tier_dtag, self.client, subscription.recipent)
if not subscription_status["isActive"] or subscription_status["expires"]:
update_subscription_sql_table("db/subscriptions", subscription.id,
subscription.recipent,
subscription.subscriber, subscription.nwc,
subscription.cadence, subscription.amount,
subscription.begin, subscription.end,
subscription.tier_dtag, subscription.zaps,
subscription.recipe,
False,
Timestamp.now().as_secs())
else:
zaps = json.loads(subscription.zaps)
success = pay_zap_split(subscription.nwc, subscription.amount, zaps)
if success:
end = infer_subscription_end_time(Timestamp.now().as_secs(), subscription.cadence)
recipe = make_subscription_zap_recipe(subscription.id, subscription.recipent,
subscription.subscriber, subscription.begin,
end, subscription.tier_dtag)
else:
end = Timestamp.now().as_secs()
recipe = subscription.recipe
update_subscription_sql_table("db/subscriptions", subscription.id,
subscription.recipent,
subscription.subscriber, subscription.nwc,
subscription.cadence, subscription.amount,
subscription.begin, end,
subscription.tier_dtag, subscription.zaps, recipe,
success,
Timestamp.now().as_secs())
print("updated subscription entry")
print("Checking Subscription") else:
delete_threshold = 60 * 60 * 24 * 365
if subscription.cadence == "daily":
delete_threshold = 60 * 60 * 24 * 7 # After 7 days, delete the subscription, user can make a new one
elif subscription.cadence == "weekly":
delete_threshold = 60 * 60 * 24 * 21 # After 21 days, delete the subscription, user can make a new one
elif subscription.cadence == "monthly":
delete_threshold = 60 * 60 * 24 * 60 # After 60 days, delete the subscription, user can make a new one
elif subscription.cadence == "yearoy":
delete_threshold = 60 * 60 * 24 * 500 # After 500 days, delete the subscription, user can make a new one
if subscription.end < (Timestamp.now().as_secs() - delete_threshold):
delete_from_subscription_sql_table("db/subscriptions", subscription.id)
print("Delete expired subscription")
print(str(Timestamp.now().as_secs()) + " Checking Subscription")
except KeyboardInterrupt: except KeyboardInterrupt:
print('Stay weird!') print('Stay weird!')
os.kill(os.getpid(), signal.SIGTERM) os.kill(os.getpid(), signal.SIGTERM)

View File

@@ -219,7 +219,7 @@ def build_example_subscription(name, identifier, admin_config):
"lud16": dvm_config.LN_ADDRESS, "lud16": dvm_config.LN_ADDRESS,
"encryptionSupported": True, "encryptionSupported": True,
"cashuAccepted": True, "cashuAccepted": True,
"amount": "subscription", "subscription": True,
"nip90Params": { "nip90Params": {
"max_results": { "max_results": {
"required": False, "required": False,

View File

@@ -88,15 +88,16 @@ def nip88_delete_announcement(eid: str, keys: Keys, dtag: str, client: Client, c
send_event(event, client, config) send_event(event, client, config)
def nip88_has_active_subscription(user: PublicKey, tiereventdtag, client: Client, dvm_config): def nip88_has_active_subscription(user: PublicKey, tiereventdtag, client: Client, receiver_public_key_hex):
subscription_status = { subscription_status = {
"isActive": False, "isActive": False,
"validUntil": 0, "validUntil": 0,
"subscriptionId": "", "subscriptionId": "",
"expires": False,
} }
subscriptionfilter = Filter().kind(definitions.EventDefinitions.KIND_NIP88_PAYMENT_RECIPE).pubkey( subscriptionfilter = Filter().kind(definitions.EventDefinitions.KIND_NIP88_PAYMENT_RECIPE).pubkey(
PublicKey.parse(dvm_config.PUBLIC_KEY)).custom_tag(SingleLetterTag.uppercase(Alphabet.P), PublicKey.parse(receiver_public_key_hex)).custom_tag(SingleLetterTag.uppercase(Alphabet.P),
[user.to_hex()]).limit(1) [user.to_hex()]).limit(1)
evts = client.get_events_of([subscriptionfilter], timedelta(seconds=5)) evts = client.get_events_of([subscriptionfilter], timedelta(seconds=5))
if len(evts) > 0: if len(evts) > 0:
@@ -114,6 +115,17 @@ def nip88_has_active_subscription(user: PublicKey, tiereventdtag, client: Client
if subscription_status["validUntil"] > Timestamp.now().as_secs() & matchesdtag: if subscription_status["validUntil"] > Timestamp.now().as_secs() & matchesdtag:
subscription_status["isActive"] = True subscription_status["isActive"] = True
if subscription_status["isActive"]:
cancel_filter = Filter().kind(EventDefinitions.KIND_NIP88_STOP_SUBSCRIPTION_EVENT).author(
user).pubkey(PublicKey.parse(receiver_public_key_hex)).event(EventId.parse(subscription_status["subscriptionId"])).limit(1)
cancel_events = client.get_events_of([cancel_filter], timedelta(seconds=5))
if len(cancel_events) > 0:
if cancel_events[0].created_at().as_secs() > evts[0].created_at().as_secs():
subscription_status["expires"] = True
return subscription_status return subscription_status

View File

@@ -17,6 +17,7 @@ class Subscription:
zaps: str zaps: str
recipe: str recipe: str
active: bool active: bool
lastupdate: int
def create_subscription_sql_table(db): def create_subscription_sql_table(db):
@@ -40,7 +41,8 @@ def create_subscription_sql_table(db):
tier_dtag text, tier_dtag text,
zaps text, zaps text,
recipe text, recipe text,
active boolean active boolean,
lastupdate int
); """) ); """)
@@ -52,23 +54,23 @@ def create_subscription_sql_table(db):
def add_to_subscription_sql_table(db, id, recipient, subscriber, nwc, cadence, amount, begin, end, tier_dtag, zaps, def add_to_subscription_sql_table(db, id, recipient, subscriber, nwc, cadence, amount, begin, end, tier_dtag, zaps,
recipe, active): recipe, active, lastupdate):
try: try:
con = sqlite3.connect(db) con = sqlite3.connect(db)
cur = con.cursor() cur = con.cursor()
data = (id, recipient, subscriber, nwc, cadence, amount, begin, end, tier_dtag, zaps, recipe, active) data = (id, recipient, subscriber, nwc, cadence, amount, begin, end, tier_dtag, zaps, recipe, active, lastupdate)
print(id) print(id)
print(recipient) print(recipient)
print(subscriber) print(subscriber)
print(nwc) print(nwc)
cur.execute("INSERT or IGNORE INTO subscriptions VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", data) cur.execute("INSERT or IGNORE INTO subscriptions VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", data)
con.commit() con.commit()
con.close() con.close()
except Error as e: except Error as e:
print("Error when Adding to DB: " + str(e)) print("Error when Adding to DB: " + str(e))
def get_from_subscription__sql_table(db, id): def get_from_subscription_sql_table(db, id):
try: try:
con = sqlite3.connect(db) con = sqlite3.connect(db)
cur = con.cursor() cur = con.cursor()
@@ -91,6 +93,7 @@ def get_from_subscription__sql_table(db, id):
subscription.zaps = row[9] subscription.zaps = row[9]
subscription.recipe = row[10] subscription.recipe = row[10]
subscription.active = row[11] subscription.active = row[11]
subscription.lastupdate = row[12]
return subscription return subscription
@@ -99,12 +102,59 @@ def get_from_subscription__sql_table(db, id):
return None return None
def update_subscription_sql_table(db, id, recipient, subscriber, nwc, cadence, amount, begin, end, tier_dtag, zaps, def get_all_subscriptions_from_sql_table(db):
recipe, active): try:
con = sqlite3.connect(db)
cursor = con.cursor()
sqlite_select_query = """SELECT * from subscriptions"""
cursor.execute(sqlite_select_query)
records = cursor.fetchall()
subscriptions = []
for row in records:
subscription = Subscription
subscription.id = row[0]
subscription.recipent = row[1]
subscription.subscriber = row[2]
subscription.nwc = row[3]
subscription.cadence = row[4]
subscription.amount = row[5]
subscription.begin = row[6]
subscription.end = row[7]
subscription.tier_dtag = row[8]
subscription.zaps = row[9]
subscription.recipe = row[10]
subscription.active = row[11]
subscription.lastupdate = row[12]
subscriptions.append(subscription)
cursor.close()
return subscriptions
except sqlite3.Error as error:
print("Failed to read data from sqlite table", error)
finally:
if con:
con.close()
#print("The SQLite connection is closed")
def delete_from_subscription_sql_table(db, id):
try: try:
con = sqlite3.connect(db) con = sqlite3.connect(db)
cur = con.cursor() cur = con.cursor()
data = (recipient, subscriber, nwc, cadence, amount, begin, end, tier_dtag, zaps, recipe, active, id) cur.execute("DELETE FROM subscriptions WHERE id=?", (id,))
con.commit()
con.close()
except Error as e:
print(e)
def update_subscription_sql_table(db, id, recipient, subscriber, nwc, cadence, amount, begin, end, tier_dtag, zaps,
recipe, active, lastupdate):
try:
con = sqlite3.connect(db)
cur = con.cursor()
data = (recipient, subscriber, nwc, cadence, amount, begin, end, tier_dtag, zaps, recipe, active, lastupdate, id)
cur.execute(""" UPDATE subscriptions cur.execute(""" UPDATE subscriptions
SET recipient = ? , SET recipient = ? ,
@@ -117,7 +167,8 @@ def update_subscription_sql_table(db, id, recipient, subscriber, nwc, cadence, a
tier_dtag = ?, tier_dtag = ?,
zaps = ?, zaps = ?,
recipe = ?, recipe = ?,
active = ? active = ?,
lastupdate = ?
WHERE id = ?""", data) WHERE id = ?""", data)
con.commit() con.commit()

View File

@@ -757,7 +757,7 @@ export default {
if(!jsonentry.amount){ if(!jsonentry.amount){
jsonentry.amount = "" jsonentry.amount = ""
} }
if(jsonentry.amount === "subscription"){ if(jsonentry.subscription === true){
// if(susbcrition_tier) { // if(susbcrition_tier) {
const filter = new Filter().kind(37001).author(entry.author) const filter = new Filter().kind(37001).author(entry.author)
let tiers = await client.getEventsOf([filter], Duration.fromSecs(5)) let tiers = await client.getEventsOf([filter], Duration.fromSecs(5))

View File

@@ -90,8 +90,8 @@
<div style="margin-left: auto; margin-right: 10px;"> <div style="margin-left: auto; margin-right: 10px;">
<p v-if="dvm.amount.toString().toLowerCase()==='free'" class="badge bg-nostr">Free</p> <p v-if="dvm.amount.toString().toLowerCase()==='free'" class="badge bg-nostr">Free</p>
<p v-if="dvm.amount.toString().toLowerCase()==='flexible'" class="badge bg-nostr2" >Flexible</p> <p v-if="dvm.amount.toString().toLowerCase()==='flexible'" class="badge bg-nostr2" >Flexible</p>
<p v-if="dvm.subscription" class="badge text-white bg-gradient-to-br from-pink-500 to-orange-400">Subscription</p>
<p v-if="dvm.amount.toString().toLowerCase()==='subscription'" class="badge bg-orange-500">Subscription</p>
<p v-if="dvm.amount.toString()===''" ></p> <p v-if="dvm.amount.toString()===''" ></p>
<p v-if="!isNaN(parseInt(dvm.amount))" class="text-sm text-gray-600 rounded" ><div class="flex"><svg style="margin-top:3px" xmlns="http://www.w3.org/2000/svg" width="14" height="16" fill="currentColor" class="bi bi-lightning" viewBox="0 0 16 20"> <p v-if="!isNaN(parseInt(dvm.amount))" class="text-sm text-gray-600 rounded" ><div class="flex"><svg style="margin-top:3px" xmlns="http://www.w3.org/2000/svg" width="14" height="16" fill="currentColor" class="bi bi-lightning" viewBox="0 0 16 20">
<path d="M5.52.359A.5.5 0 0 1 6 0h4a.5.5 0 0 1 .474.658L8.694 6H12.5a.5.5 0 0 1 .395.807l-7 9a.5.5 0 0 1-.873-.454L6.823 9.5H3.5a.5.5 0 0 1-.48-.641zM6.374 1 4.168 8.5H7.5a.5.5 0 0 1 .478.647L6.78 13.04 11.478 7H8a.5.5 0 0 1-.474-.658L9.306 1z"/></svg> {{dvm.amount/1000}}</div></p> <path d="M5.52.359A.5.5 0 0 1 6 0h4a.5.5 0 0 1 .474.658L8.694 6H12.5a.5.5 0 0 1 .395.807l-7 9a.5.5 0 0 1-.873-.454L6.823 9.5H3.5a.5.5 0 0 1-.48-.641zM6.374 1 4.168 8.5H7.5a.5.5 0 0 1 .478.647L6.78 13.04 11.478 7H8a.5.5 0 0 1-.474-.658L9.306 1z"/></svg> {{dvm.amount/1000}}</div></p>

View File

@@ -60,6 +60,9 @@ function set_subscription_props(amount, cadence, dvm) {
this.current_subscription_amount = amount this.current_subscription_amount = amount
this.current_subscription_cadence = cadence this.current_subscription_cadence = cadence
this.current_subscription_dvm = dvm this.current_subscription_dvm = dvm
this.nwcalby = ""
this.nwcmutiny = ""
this.nwc = ""
} }
@@ -905,7 +908,18 @@ const closeNWCModal = () => {
<button v-if="dvm.status !== 'finished' && dvm.status !== 'paid' && dvm.status !== 'payment-required' && dvm.status !== 'subscription-required' && dvm.status !== 'subscription-success' && dvm.status !== 'error' && dvm.status !== 'announced'" className="btn">{{dvm.status}}</button> <button v-if="dvm.status !== 'finished' && dvm.status !== 'paid' && dvm.status !== 'payment-required' && dvm.status !== 'subscription-required' && dvm.status !== 'subscription-success' && dvm.status !== 'error' && dvm.status !== 'announced'" className="btn">{{dvm.status}}</button>
<button v-if="dvm.status === 'finished'" @click="generate_feed(dvm.id)" className="request-Button">Done, again?</button> <button v-if="dvm.status === 'finished' && !dvm.nip88 ||(dvm.nip88 && !dvm.nip88.hasActiveSubscription)" @click="generate_feed(dvm.id)" class="relative inline-flex items-center justify-center p-0.5 mb-2 me-2 overflow-hidden text-sm font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-purple-600 to-blue-500 group-hover:from-purple-600 group-hover:to-blue-500 hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-blue-300 dark:focus:ring-blue-800">
<span class="relative px-5 py-2.5 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-md group-hover:bg-opacity-0">
Done, again?
</span>
</button>
<button v-if="dvm.status === 'finished' && dvm.nip88 && dvm.nip88.hasActiveSubscription" @click="generate_feed(dvm.id);" class="relative inline-flex items-center justify-center p-0.5 mb-2 me-2 overflow-hidden text-sm font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-pink-500 to-orange-400 group-hover:from-pink-500 group-hover:to-orange-400 hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-pink-200 dark:focus:ring-pink-800">
<span class="relative px-5 py-2.5 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-md group-hover:bg-opacity-0">
Done, Again?
</span>
</button>
<button v-if="dvm.status === 'paid'" className="btn">Paid, waiting for DVM..</button> <button v-if="dvm.status === 'paid'" className="btn">Paid, waiting for DVM..</button>
<button v-if="dvm.status === 'error'" className="btn">Error</button> <button v-if="dvm.status === 'error'" className="btn">Error</button>
@@ -922,7 +936,7 @@ const closeNWCModal = () => {
</span> </span>
</button> </button>
<button v-if="dvm.status === 'announced' && dvm.nip88 && dvm.nip88.hasActiveSubscription" @click="generate_feed(dvm.id);"class="relative inline-flex items-center justify-center p-0.5 mb-2 me-2 overflow-hidden text-sm font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-pink-500 to-orange-400 group-hover:from-pink-500 group-hover:to-orange-400 hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-pink-200 dark:focus:ring-pink-800"> <button v-if="dvm.status === 'announced' && dvm.nip88 && dvm.nip88.hasActiveSubscription" @click="generate_feed(dvm.id);" class="relative inline-flex items-center justify-center p-0.5 mb-2 me-2 overflow-hidden text-sm font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-pink-500 to-orange-400 group-hover:from-pink-500 group-hover:to-orange-400 hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-pink-200 dark:focus:ring-pink-800">
<span class="relative px-5 py-2.5 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-md group-hover:bg-opacity-0"> <span class="relative px-5 py-2.5 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-md group-hover:bg-opacity-0">
Request Request
</span> </span>
@@ -939,8 +953,9 @@ const closeNWCModal = () => {
</div> </div>
<div style="margin-left: auto; margin-right: 3px;"> <div style="margin-left: auto; margin-right: 3px;">
<p v-if="dvm.subscription ==='' && dvm.amount.toString().toLowerCase()==='free'" class="badge bg-nostr" >Free</p> <p v-if="!dvm.subscription && dvm.amount.toString().toLowerCase()==='free'" class="badge bg-nostr" >Free</p>
<p v-if="dvm.subscription ==='' && dvm.amount.toString().toLowerCase()==='flexible'" class="badge bg-nostr2" >Flexible</p> <p v-if="!dvm.subscription && dvm.amount.toString().toLowerCase()==='flexible'" class="badge bg-nostr2" >Flexible</p>
<p v-if="dvm.nip88" class="badge text-white bg-gradient-to-br from-pink-500 to-orange-400">Subscription</p>
</div> </div>
<div> <div>
@@ -982,7 +997,7 @@ const closeNWCModal = () => {
</div> </div>
<div class="collapse-content"> <div class="collapse-content">
<button style="margin-top: 20px;" @click="connect_alby_nwc()"> <button v-if="!nwcalby.startsWith('nostr')" style="margin-top: 20px;" @click="connect_alby_nwc()">
<svg width="211" height="40" viewBox="0 0 211 40" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="211" height="40" viewBox="0 0 211 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" width="210" height="40" rx="6" fill="url(#paint0_linear_1_148)"/> <rect x="0.5" width="210" height="40" rx="6" fill="url(#paint0_linear_1_148)"/>
<circle cx="1.575" cy="1.575" r="1.575" transform="matrix(-1 0 0 1 22.1176 13.8575)" fill="black"/> <circle cx="1.575" cy="1.575" r="1.575" transform="matrix(-1 0 0 1 22.1176 13.8575)" fill="black"/>
@@ -1004,6 +1019,7 @@ const closeNWCModal = () => {
</svg> </svg>
</button> </button>
<p style="margin-top: 20px;" v-if="nwcalby.startsWith('nostr')">Connected to Alby Wallet.</p>
</div> </div>
</div> </div>
@@ -1049,7 +1065,12 @@ const closeNWCModal = () => {
<form method="dialog"> <form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"></button> <button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"></button>
<!-- if there is a button in form, it will close the modal --> <!-- if there is a button in form, it will close the modal -->
<button class="btn" @click="store_nwc(); subscribe_to_dvm()">Subscribe</button> <button @click="store_nwc(); subscribe_to_dvm()" class=" relative inline-flex items-center justify-center p-0.5 mb-2 me-2 overflow-hidden text-sm font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-pink-500 to-orange-400 group-hover:from-pink-500 group-hover:to-orange-400 hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-pink-200 dark:focus:ring-pink-800">
<span class="relative px-5 py-2.5 transition-all ease-in duration-75 rounded-md group-hover:bg-opacity-0">
Subscribe
</span>
</button>
</form> </form>
</div> </div>
@@ -1060,7 +1081,7 @@ const closeNWCModal = () => {
<dialog id="subscr" class="modal"> <dialog id="subscr" class="modal">
<div className="modal-box rounded-3xl inner shadow-lg p-6 flex flex-col items-center transition-all duration-1000 bg-gradient-to-br from-pink-500 to-orange-400 "> <div className="modal-box rounded-3xl inner shadow-lg p-6 flex flex-col items-center transition-all duration-1000 bg-gradient-to-br from-pink-500 to-orange-400 ">
<h3 class="font-bold text-lg">Subscribe</h3> <h3 class="font-bold text-lg">Manage your Subscription</h3>
<img style="flex: content" :src="dvm.nip88.image"></img> <img style="flex: content" :src="dvm.nip88.image"></img>
<div class="glass" className="card-body"> <div class="glass" className="card-body">
@@ -1076,10 +1097,10 @@ const closeNWCModal = () => {
</div> </div>
<br> <br>
<h3 v-if="dvm.nip88.hasActiveSubscription && !dvm.nip88.expires ">Subscription renewing at <h3 v-if="dvm.nip88.hasActiveSubscription && !dvm.nip88.expires ">Subscription renewing at
{{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[0].split("-")[2].trim()}}.{{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[0].split("-")[1].trim()}}.{{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[0].split("-")[0].trim().slice(2)}} {{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[1].split("Z")[0].trim()}}</h3> {{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[0].split("-")[2].trim()}}.{{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[0].split("-")[1].trim()}}.{{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[0].split("-")[0].trim().slice(2)}} {{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[1].split("Z")[0].trim()}} GMT</h3>
<h3 v-if="dvm.nip88.hasActiveSubscription && dvm.nip88.expires ">Subscription expires on <h3 v-if="dvm.nip88.hasActiveSubscription && dvm.nip88.expires ">Subscription expires on
{{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[0].split("-")[2].trim()}}.{{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[0].split("-")[1].trim()}}.{{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[0].split("-")[0].trim().slice(2)}} {{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[1].split("Z")[0].trim()}}</h3> {{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[0].split("-")[2].trim()}}.{{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[0].split("-")[1].trim()}}.{{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[0].split("-")[0].trim().slice(2)}} {{Timestamp.fromSecs(parseInt(dvm.nip88.subscribedUntil)).toHumanDatetime().split("T")[1].split("Z")[0].trim()}} GMT</h3>
<h3 v-if="dvm.nip88.hasActiveSubscription && dvm.nip88.expires"> Changed your mind? Resubscribe! The current subscription will continue with a new NWC string</h3> <h3 v-if="dvm.nip88.hasActiveSubscription && dvm.nip88.expires"> Changed your mind? Resubscribe! The current subscription will continue with a new NWC string</h3>
<div v-if="!dvm.nip88.hasActiveSubscription || dvm.nip88.expires" v-for="amount_item in dvm.nip88.amounts"> <div v-if="!dvm.nip88.hasActiveSubscription || dvm.nip88.expires" v-for="amount_item in dvm.nip88.amounts">
<br> <br>