mirror of
https://github.com/believethehype/nostrdvm.git
synced 2025-04-10 04:39:16 +02:00
Merge pull request #23 from believethehype/subscriptions-sdkupdate
Subscriptions sdkupdate
This commit is contained in:
commit
8f9833b0f6
@ -5,7 +5,8 @@ import time
|
||||
from datetime import timedelta
|
||||
|
||||
from nostr_sdk import (Keys, Client, Timestamp, Filter, nip04_decrypt, HandleNotification, EventBuilder, PublicKey,
|
||||
Options, Tag, Event, nip04_encrypt, NostrSigner, EventId, Nip19Event)
|
||||
Options, Tag, Event, nip04_encrypt, NostrSigner, EventId, Nip19Event, Kind, KindEnum,
|
||||
UnsignedEvent, nip59_extract_rumor)
|
||||
|
||||
from nostr_dvm.utils.admin_utils import admin_make_database_updates
|
||||
from nostr_dvm.utils.database_utils import get_or_add_user, update_user_balance, create_sql_table, update_sql_table
|
||||
@ -55,30 +56,48 @@ class Bot:
|
||||
kinds = [EventDefinitions.KIND_NIP90_GENERIC, EventDefinitions.KIND_FEEDBACK]
|
||||
for dvm in self.dvm_config.SUPPORTED_DVMS:
|
||||
if dvm.KIND not in kinds:
|
||||
kinds.append(dvm.KIND + 1000)
|
||||
kinds.append(Kind(dvm.KIND.as_u64() + 1000))
|
||||
dvm_filter = (Filter().kinds(kinds).since(Timestamp.now()))
|
||||
|
||||
self.client.subscribe([zap_filter, dm_filter, dvm_filter])
|
||||
self.client.subscribe([zap_filter, dm_filter, dvm_filter], None)
|
||||
|
||||
create_sql_table(self.dvm_config.DB)
|
||||
admin_make_database_updates(adminconfig=self.admin_config, dvmconfig=self.dvm_config, client=self.client)
|
||||
# add_sql_table_column(dvm_config.DB)
|
||||
|
||||
class NotificationHandler(HandleNotification):
|
||||
client = self.client
|
||||
dvm_config = self.dvm_config
|
||||
keys = self.keys
|
||||
|
||||
def handle(self, relay_url, nostr_event):
|
||||
if (EventDefinitions.KIND_NIP90_EXTRACT_TEXT + 1000 <= nostr_event.kind()
|
||||
<= EventDefinitions.KIND_NIP90_GENERIC + 1000):
|
||||
def handle(self, relay_url, subscription_id, nostr_event):
|
||||
if (EventDefinitions.KIND_NIP90_EXTRACT_TEXT.as_u64() + 1000 <= nostr_event.kind().as_u64()
|
||||
<= EventDefinitions.KIND_NIP90_GENERIC.as_u64() + 1000):
|
||||
handle_nip90_response_event(nostr_event)
|
||||
elif nostr_event.kind() == EventDefinitions.KIND_FEEDBACK:
|
||||
elif nostr_event.kind().as_u64() == EventDefinitions.KIND_FEEDBACK.as_u64():
|
||||
handle_nip90_feedback(nostr_event)
|
||||
elif nostr_event.kind() == EventDefinitions.KIND_DM:
|
||||
handle_dm(nostr_event)
|
||||
elif nostr_event.kind() == EventDefinitions.KIND_ZAP:
|
||||
|
||||
elif nostr_event.kind().as_u64() == EventDefinitions.KIND_ZAP.as_u64():
|
||||
handle_zap(nostr_event)
|
||||
|
||||
elif nostr_event.kind().match_enum(KindEnum.ENCRYPTED_DIRECT_MESSAGE()):
|
||||
try:
|
||||
handle_dm(nostr_event)
|
||||
except Exception as e:
|
||||
print(f"Error during content NIP04 decryption: {e}")
|
||||
elif nostr_event.kind().match_enum(KindEnum.GIFT_WRAP()):
|
||||
print("Decrypting NIP59 event")
|
||||
try:
|
||||
rumor: UnsignedEvent = nip59_extract_rumor(self.keys, nostr_event)
|
||||
if rumor.kind().match_enum(KindEnum.SEALED_DIRECT()):
|
||||
msg = rumor.content()
|
||||
print(f"Received new msg [sealed]: {msg}")
|
||||
self.client.send_sealed_msg(rumor.author(), "Nip44 is not supported yet, but coming soon", None)
|
||||
else:
|
||||
print(f"{rumor.as_json()}")
|
||||
except Exception as e:
|
||||
print(f"Error during content NIP59 decryption: {e}")
|
||||
|
||||
def handle_msg(self, relay_url, msg):
|
||||
return
|
||||
|
||||
@ -190,7 +209,7 @@ class Bot:
|
||||
print("Error in bot " + str(e))
|
||||
|
||||
def handle_nip90_feedback(nostr_event):
|
||||
print(nostr_event.as_json())
|
||||
#print(nostr_event.as_json())
|
||||
try:
|
||||
is_encrypted = False
|
||||
status = ""
|
||||
@ -265,7 +284,7 @@ class Bot:
|
||||
update_sql_table(db=self.dvm_config.DB, npub=user.npub, balance=balance,
|
||||
iswhitelisted=user.iswhitelisted, isblacklisted=user.isblacklisted,
|
||||
nip05=user.nip05, lud16=user.lud16, name=user.name,
|
||||
lastactive=Timestamp.now().as_secs())
|
||||
lastactive=Timestamp.now().as_secs(), subscribed=user.subscribed)
|
||||
evt = EventBuilder.encrypted_direct_msg(self.keys,
|
||||
PublicKey.from_hex(entry["npub"]),
|
||||
"Paid " + str(
|
||||
@ -346,7 +365,7 @@ class Bot:
|
||||
return
|
||||
|
||||
dvms = [x for x in self.dvm_config.SUPPORTED_DVMS if
|
||||
x.PUBLIC_KEY == nostr_event.author().to_hex() and x.KIND == nostr_event.kind() - 1000]
|
||||
x.PUBLIC_KEY == nostr_event.author().to_hex() and x.KIND.as_u64() == nostr_event.kind().as_u64() - 1000]
|
||||
if len(dvms) > 0:
|
||||
dvm = dvms[0]
|
||||
if dvm.dvm_config.EXTERNAL_POST_PROCESS_TYPE != PostProcessFunctionType.NONE:
|
||||
|
270
nostr_dvm/dvm.py
270
nostr_dvm/dvm.py
@ -5,7 +5,7 @@ from datetime import timedelta
|
||||
from sys import platform
|
||||
|
||||
from nostr_sdk import PublicKey, Keys, Client, Tag, Event, EventBuilder, Filter, HandleNotification, Timestamp, \
|
||||
init_logger, LogLevel, Options, nip04_encrypt, NostrSigner
|
||||
init_logger, LogLevel, Options, nip04_encrypt, NostrSigner, Kind, SubscribeAutoCloseOptions
|
||||
|
||||
import time
|
||||
|
||||
@ -13,8 +13,10 @@ from nostr_dvm.utils.definitions import EventDefinitions, RequiredJobToWatch, Jo
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig
|
||||
from nostr_dvm.utils.admin_utils import admin_make_database_updates, AdminConfig
|
||||
from nostr_dvm.utils.backend_utils import get_amount_per_task, check_task_is_supported, get_task
|
||||
from nostr_dvm.utils.database_utils import create_sql_table, get_or_add_user, update_user_balance, update_sql_table
|
||||
from nostr_dvm.utils.database_utils import create_sql_table, get_or_add_user, update_user_balance, update_sql_table, \
|
||||
update_user_subscription
|
||||
from nostr_dvm.utils.mediasource_utils import input_data_file_duration
|
||||
from nostr_dvm.utils.nip88_utils import nip88_has_active_subscription
|
||||
from nostr_dvm.utils.nostr_utils import get_event_by_id, get_referenced_event_by_id, send_event, check_and_decrypt_tags
|
||||
from nostr_dvm.utils.output_utils import build_status_reaction
|
||||
from nostr_dvm.utils.zap_utils import check_bolt11_ln_bits_is_paid, create_bolt11_ln_bits, parse_zap_event_tags, \
|
||||
@ -46,8 +48,7 @@ class DVM:
|
||||
self.jobs_on_hold_list = []
|
||||
pk = self.keys.public_key()
|
||||
|
||||
print("Nostr DVM public key: " + str(pk.to_bech32()) + " Hex: " + str(pk.to_hex()) + " Supported DVM tasks: " +
|
||||
', '.join(p.NAME + ":" + p.TASK for p in self.dvm_config.SUPPORTED_DVMS) + "\n")
|
||||
print("Nostr DVM public key: " + str(pk.to_bech32()) + " Hex: " + str(pk.to_hex()) + "\n")
|
||||
|
||||
for relay in self.dvm_config.RELAY_LIST:
|
||||
self.client.add_relay(relay)
|
||||
@ -59,7 +60,8 @@ class DVM:
|
||||
if dvm.KIND not in kinds:
|
||||
kinds.append(dvm.KIND)
|
||||
dvm_filter = (Filter().kinds(kinds).since(Timestamp.now()))
|
||||
self.client.subscribe([dvm_filter, zap_filter])
|
||||
|
||||
self.client.subscribe([dvm_filter, zap_filter], None)
|
||||
|
||||
create_sql_table(self.dvm_config.DB)
|
||||
admin_make_database_updates(adminconfig=self.admin_config, dvmconfig=self.dvm_config, client=self.client)
|
||||
@ -69,48 +71,89 @@ class DVM:
|
||||
dvm_config = self.dvm_config
|
||||
keys = self.keys
|
||||
|
||||
def handle(self, relay_url, nostr_event):
|
||||
if EventDefinitions.KIND_NIP90_EXTRACT_TEXT <= nostr_event.kind() <= EventDefinitions.KIND_NIP90_GENERIC:
|
||||
def handle(self, relay_url, subscription_id, nostr_event: Event):
|
||||
|
||||
if EventDefinitions.KIND_NIP90_EXTRACT_TEXT.as_u64() <= nostr_event.kind().as_u64() <= EventDefinitions.KIND_NIP90_GENERIC.as_u64():
|
||||
handle_nip90_job_event(nostr_event)
|
||||
elif nostr_event.kind() == EventDefinitions.KIND_ZAP:
|
||||
elif nostr_event.kind().as_u64() == EventDefinitions.KIND_ZAP.as_u64():
|
||||
handle_zap(nostr_event)
|
||||
|
||||
def handle_msg(self, relay_url, msg):
|
||||
return
|
||||
|
||||
def handle_nip90_job_event(nip90_event):
|
||||
|
||||
# decrypted encrypted events
|
||||
nip90_event = check_and_decrypt_tags(nip90_event, self.dvm_config)
|
||||
# if event is encrypted, but we can't decrypt it (e.g. because its directed to someone else), return
|
||||
if nip90_event is None:
|
||||
return
|
||||
|
||||
user = get_or_add_user(self.dvm_config.DB, nip90_event.author().to_hex(), client=self.client,
|
||||
config=self.dvm_config)
|
||||
|
||||
task_is_free = False
|
||||
user_has_active_subscription = False
|
||||
cashu = ""
|
||||
p_tag_str = ""
|
||||
|
||||
for tag in nip90_event.tags():
|
||||
if tag.as_vec()[0] == "cashu":
|
||||
cashu = tag.as_vec()[1]
|
||||
elif tag.as_vec()[0] == "p":
|
||||
p_tag_str = tag.as_vec()[1]
|
||||
|
||||
if p_tag_str != "" and p_tag_str != self.dvm_config.PUBLIC_KEY:
|
||||
print("[" + self.dvm_config.NIP89.NAME + "] No public request, also not addressed to me.")
|
||||
return
|
||||
|
||||
|
||||
# check if task is supported by the current DVM
|
||||
task_supported, task = check_task_is_supported(nip90_event, client=self.client,
|
||||
config=self.dvm_config)
|
||||
# if task is supported, continue, else do nothing.
|
||||
if task_supported:
|
||||
# fetch or add user contacting the DVM from/to local database
|
||||
user = get_or_add_user(self.dvm_config.DB, nip90_event.author().to_hex(), client=self.client,
|
||||
config=self.dvm_config)
|
||||
# if user is blacklisted for some reason, send an error reaction and return
|
||||
if user.isblacklisted:
|
||||
send_job_status_reaction(nip90_event, "error", client=self.client, dvm_config=self.dvm_config)
|
||||
print("[" + self.dvm_config.NIP89.NAME + "] Request by blacklisted user, skipped")
|
||||
return
|
||||
|
||||
if user.isblacklisted:
|
||||
send_job_status_reaction(nip90_event, "error", client=self.client, dvm_config=self.dvm_config)
|
||||
print("[" + self.dvm_config.NIP89.NAME + "] Request by blacklisted user, skipped")
|
||||
|
||||
elif task_supported:
|
||||
print("[" + self.dvm_config.NIP89.NAME + "] Received new Request: " + task + " from " + user.name)
|
||||
duration = input_data_file_duration(nip90_event, dvm_config=self.dvm_config, client=self.client)
|
||||
amount = get_amount_per_task(task, self.dvm_config, duration)
|
||||
if amount is None:
|
||||
return
|
||||
|
||||
task_is_free = False
|
||||
|
||||
# If this is a subscription DVM and the Task is directed to us, check for active subscription
|
||||
if dvm_config.NIP88 is not None and p_tag_str == self.dvm_config.PUBLIC_KEY:
|
||||
|
||||
# if we stored in the database that the user has an active subscription, we don't need to check it
|
||||
print("User Subscription: " + str(user.subscribed) + " Current time: " + str(
|
||||
Timestamp.now().as_secs()))
|
||||
# if we have an entry in the db that user is subscribed, continue
|
||||
if int(user.subscribed) > int(Timestamp.now().as_secs()):
|
||||
print("User subscribed until: " + str(Timestamp.from_secs(user.subscribed).to_human_datetime()))
|
||||
user_has_active_subscription = True
|
||||
# otherwise we check for an active subscription by checking recipie events
|
||||
else:
|
||||
print("[" + self.dvm_config.NIP89.NAME + "] Checking Subscription status")
|
||||
subscription_status = nip88_has_active_subscription(PublicKey.parse(user.npub),
|
||||
self.dvm_config.NIP88.DTAG, self.client,
|
||||
self.dvm_config.PUBLIC_KEY)
|
||||
|
||||
if subscription_status["isActive"]:
|
||||
print("Checked Recipe: User subscribed until: " + str(
|
||||
Timestamp.from_secs(int(subscription_status["validUntil"])).to_human_datetime()))
|
||||
user_has_active_subscription = True
|
||||
update_user_subscription(user.npub,
|
||||
int(subscription_status["validUntil"]),
|
||||
self.client, self.dvm_config)
|
||||
else:
|
||||
print("No active subscription found")
|
||||
|
||||
for dvm in self.dvm_config.SUPPORTED_DVMS:
|
||||
if dvm.TASK == task and dvm.FIX_COST == 0 and dvm.PER_UNIT_COST == 0:
|
||||
if dvm.TASK == task and dvm.FIX_COST == 0 and dvm.PER_UNIT_COST == 0 and dvm_config.NIP88 is None:
|
||||
task_is_free = True
|
||||
|
||||
cashu_redeemed = False
|
||||
@ -124,31 +167,40 @@ class DVM:
|
||||
self.dvm_config)
|
||||
return
|
||||
# if user is whitelisted or task is free, just do the job
|
||||
if (user.iswhitelisted or task_is_free or cashu_redeemed) and (p_tag_str == "" or p_tag_str ==
|
||||
self.dvm_config.PUBLIC_KEY):
|
||||
if (user.iswhitelisted or task_is_free or cashu_redeemed) and (
|
||||
p_tag_str == "" or p_tag_str ==
|
||||
self.dvm_config.PUBLIC_KEY):
|
||||
print(
|
||||
"[" + self.dvm_config.NIP89.NAME + "] Free task or Whitelisted for task " + task +
|
||||
". Starting processing..")
|
||||
|
||||
if dvm_config.SEND_FEEDBACK_EVENTS:
|
||||
send_job_status_reaction(nip90_event, "processing", True, 0,
|
||||
client=self.client, dvm_config=self.dvm_config)
|
||||
client=self.client, dvm_config=self.dvm_config, user=user)
|
||||
|
||||
# when we reimburse users on error make sure to not send anything if it was free
|
||||
if user.iswhitelisted or task_is_free:
|
||||
amount = 0
|
||||
do_work(nip90_event, amount)
|
||||
# if task is directed to us via p tag and user has balance, do the job and update balance
|
||||
elif p_tag_str == self.dvm_config.PUBLIC_KEY and user.balance >= int(amount):
|
||||
balance = max(user.balance - int(amount), 0)
|
||||
update_sql_table(db=self.dvm_config.DB, npub=user.npub, balance=balance,
|
||||
iswhitelisted=user.iswhitelisted, isblacklisted=user.isblacklisted,
|
||||
nip05=user.nip05, lud16=user.lud16, name=user.name,
|
||||
lastactive=Timestamp.now().as_secs())
|
||||
# if task is directed to us via p tag and user has balance or is subscribed, do the job and update balance
|
||||
elif (p_tag_str == self.dvm_config.PUBLIC_KEY and (
|
||||
user.balance >= int(
|
||||
amount) and dvm_config.NIP88 is None) or (
|
||||
p_tag_str == self.dvm_config.PUBLIC_KEY and user_has_active_subscription)):
|
||||
|
||||
print(
|
||||
"[" + self.dvm_config.NIP89.NAME + "] Using user's balance for task: " + task +
|
||||
". Starting processing.. New balance is: " + str(balance))
|
||||
if not user_has_active_subscription:
|
||||
balance = max(user.balance - int(amount), 0)
|
||||
update_sql_table(db=self.dvm_config.DB, npub=user.npub, balance=balance,
|
||||
iswhitelisted=user.iswhitelisted, isblacklisted=user.isblacklisted,
|
||||
nip05=user.nip05, lud16=user.lud16, name=user.name,
|
||||
lastactive=Timestamp.now().as_secs(), subscribed=user.subscribed)
|
||||
|
||||
print(
|
||||
"[" + self.dvm_config.NIP89.NAME + "] Using user's balance for task: " + task +
|
||||
". Starting processing.. New balance is: " + str(balance))
|
||||
else:
|
||||
print("[" + self.dvm_config.NIP89.NAME + "] User has active subscription for task: " + task +
|
||||
". Starting processing.. Balance remains at: " + str(user.balance))
|
||||
|
||||
send_job_status_reaction(nip90_event, "processing", True, 0,
|
||||
client=self.client, dvm_config=self.dvm_config)
|
||||
@ -157,27 +209,40 @@ class DVM:
|
||||
|
||||
# else send a payment required event to user
|
||||
elif p_tag_str == "" or p_tag_str == self.dvm_config.PUBLIC_KEY:
|
||||
bid = 0
|
||||
for tag in nip90_event.tags():
|
||||
if tag.as_vec()[0] == 'bid':
|
||||
bid = int(tag.as_vec()[1])
|
||||
|
||||
print(
|
||||
"[" + self.dvm_config.NIP89.NAME + "] Payment required: New Nostr " + task + " Job event: "
|
||||
+ nip90_event.as_json())
|
||||
if bid > 0:
|
||||
bid_offer = int(bid / 1000)
|
||||
if bid_offer >= int(amount):
|
||||
send_job_status_reaction(nip90_event, "payment-required", False,
|
||||
int(amount), # bid_offer
|
||||
client=self.client, dvm_config=self.dvm_config)
|
||||
|
||||
else: # If there is no bid, just request server rate from user
|
||||
if dvm_config.NIP88 is not None:
|
||||
print(
|
||||
"[" + self.dvm_config.NIP89.NAME + "] Requesting payment for Event: " +
|
||||
"[" + self.dvm_config.NIP89.NAME + "] Hinting user for Subscription: " +
|
||||
nip90_event.id().to_hex())
|
||||
send_job_status_reaction(nip90_event, "payment-required",
|
||||
False, int(amount), client=self.client, dvm_config=self.dvm_config)
|
||||
send_job_status_reaction(nip90_event, "subscription-required",
|
||||
False, 0, client=self.client,
|
||||
dvm_config=self.dvm_config)
|
||||
else:
|
||||
bid = 0
|
||||
for tag in nip90_event.tags():
|
||||
if tag.as_vec()[0] == 'bid':
|
||||
bid = int(tag.as_vec()[1])
|
||||
|
||||
print(
|
||||
"[" + self.dvm_config.NIP89.NAME + "] Payment required: New Nostr " + task + " Job event: "
|
||||
+ nip90_event.as_json())
|
||||
if bid > 0:
|
||||
bid_offer = int(bid / 1000)
|
||||
if bid_offer >= int(amount):
|
||||
send_job_status_reaction(nip90_event, "payment-required", False,
|
||||
int(amount), # bid_offer
|
||||
client=self.client, dvm_config=self.dvm_config)
|
||||
|
||||
else: # If there is no bid, just request server rate from user
|
||||
print(
|
||||
"[" + self.dvm_config.NIP89.NAME + "] Requesting payment for Event: " +
|
||||
nip90_event.id().to_hex())
|
||||
send_job_status_reaction(nip90_event, "payment-required",
|
||||
False, int(amount), client=self.client, dvm_config=self.dvm_config)
|
||||
|
||||
|
||||
|
||||
|
||||
else:
|
||||
print("[" + self.dvm_config.NIP89.NAME + "] Job addressed to someone else, skipping..")
|
||||
# else:
|
||||
@ -192,11 +257,12 @@ class DVM:
|
||||
user = get_or_add_user(db=self.dvm_config.DB, npub=sender, client=self.client, config=self.dvm_config)
|
||||
|
||||
if zapped_event is not None:
|
||||
if zapped_event.kind() == EventDefinitions.KIND_FEEDBACK:
|
||||
if zapped_event.kind().as_u64() == EventDefinitions.KIND_FEEDBACK.as_u64():
|
||||
|
||||
amount = 0
|
||||
job_event = None
|
||||
p_tag_str = ""
|
||||
status = ""
|
||||
for tag in zapped_event.tags():
|
||||
if tag.as_vec()[0] == 'amount':
|
||||
amount = int(float(tag.as_vec()[1]) / 1000)
|
||||
@ -208,41 +274,53 @@ class DVM:
|
||||
return
|
||||
else:
|
||||
return
|
||||
elif tag.as_vec()[0] == 'status':
|
||||
status = tag.as_vec()[1]
|
||||
print(status)
|
||||
|
||||
# if a reaction by us got zapped
|
||||
print(status)
|
||||
if job_event.kind().as_u64() == EventDefinitions.KIND_NIP88_SUBSCRIBE_EVENT.as_u64():
|
||||
send_job_status_reaction(job_event, "subscription-success", client=self.client,
|
||||
dvm_config=self.dvm_config, user=user)
|
||||
|
||||
task_supported, task = check_task_is_supported(job_event, client=self.client,
|
||||
config=self.dvm_config)
|
||||
if job_event is not None and task_supported:
|
||||
print("Zap received for NIP90 task: " + str(invoice_amount) + " Sats from " + str(
|
||||
user.name))
|
||||
if amount <= invoice_amount:
|
||||
print("[" + self.dvm_config.NIP89.NAME + "] Payment-request fulfilled...")
|
||||
send_job_status_reaction(job_event, "processing", client=self.client,
|
||||
dvm_config=self.dvm_config)
|
||||
indices = [i for i, x in enumerate(self.job_list) if
|
||||
x.event == job_event]
|
||||
index = -1
|
||||
if len(indices) > 0:
|
||||
index = indices[0]
|
||||
if index > -1:
|
||||
if self.job_list[index].is_processed: # If payment-required appears a processing
|
||||
self.job_list[index].is_paid = True
|
||||
check_and_return_event(self.job_list[index].result, job_event)
|
||||
elif not (self.job_list[index]).is_processed:
|
||||
# If payment-required appears before processing
|
||||
self.job_list.pop(index)
|
||||
print("Starting work...")
|
||||
|
||||
|
||||
else:
|
||||
task_supported, task = check_task_is_supported(job_event, client=self.client,
|
||||
config=self.dvm_config)
|
||||
if job_event is not None and task_supported:
|
||||
print("Zap received for NIP90 task: " + str(invoice_amount) + " Sats from " + str(
|
||||
user.name))
|
||||
if amount <= invoice_amount:
|
||||
print("[" + self.dvm_config.NIP89.NAME + "] Payment-request fulfilled...")
|
||||
send_job_status_reaction(job_event, "processing", client=self.client,
|
||||
dvm_config=self.dvm_config, user=user)
|
||||
indices = [i for i, x in enumerate(self.job_list) if
|
||||
x.event == job_event]
|
||||
index = -1
|
||||
if len(indices) > 0:
|
||||
index = indices[0]
|
||||
if index > -1:
|
||||
if self.job_list[index].is_processed:
|
||||
self.job_list[index].is_paid = True
|
||||
check_and_return_event(self.job_list[index].result, job_event)
|
||||
elif not (self.job_list[index]).is_processed:
|
||||
# If payment-required appears before processing
|
||||
self.job_list.pop(index)
|
||||
print("Starting work...")
|
||||
do_work(job_event, invoice_amount)
|
||||
else:
|
||||
print("Job not in List, but starting work...")
|
||||
do_work(job_event, invoice_amount)
|
||||
else:
|
||||
print("Job not in List, but starting work...")
|
||||
do_work(job_event, invoice_amount)
|
||||
|
||||
else:
|
||||
send_job_status_reaction(job_event, "payment-rejected",
|
||||
False, invoice_amount, client=self.client,
|
||||
dvm_config=self.dvm_config)
|
||||
print("[" + self.dvm_config.NIP89.NAME + "] Invoice was not paid sufficiently")
|
||||
else:
|
||||
send_job_status_reaction(job_event, "payment-rejected",
|
||||
False, invoice_amount, client=self.client,
|
||||
dvm_config=self.dvm_config)
|
||||
print("[" + self.dvm_config.NIP89.NAME + "] Invoice was not paid sufficiently")
|
||||
elif zapped_event.kind().as_u64() == EventDefinitions.KIND_NIP88_SUBSCRIBE_EVENT.as_u64():
|
||||
print("new subscription, doing nothing")
|
||||
|
||||
elif zapped_event.kind() in EventDefinitions.ANY_RESULT:
|
||||
print("[" + self.dvm_config.NIP89.NAME + "] "
|
||||
@ -254,7 +332,7 @@ class DVM:
|
||||
config=self.dvm_config)
|
||||
|
||||
# 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: " +
|
||||
str(invoice_amount) + " Sats from " + str(user.name))
|
||||
update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client,
|
||||
@ -323,6 +401,7 @@ class DVM:
|
||||
post_processed = dvm.post_process(data, original_event)
|
||||
send_nostr_reply_event(post_processed, original_event.as_json())
|
||||
except Exception as e:
|
||||
print(e)
|
||||
# Zapping back by error in post-processing is a risk for the DVM because work has been done,
|
||||
# but maybe something with parsing/uploading failed. Try to avoid errors here as good as possible
|
||||
send_job_status_reaction(original_event, "error",
|
||||
@ -350,7 +429,7 @@ class DVM:
|
||||
e_tag = Tag.parse(["e", original_event.id().to_hex()])
|
||||
p_tag = Tag.parse(["p", original_event.author().to_hex()])
|
||||
alt_tag = Tag.parse(["alt", "This is the result of a NIP90 DVM AI task with kind " + str(
|
||||
original_event.kind()) + ". The task was: " + original_event.content()])
|
||||
original_event.kind().as_u64()) + ". The task was: " + original_event.content()])
|
||||
status_tag = Tag.parse(["status", "success"])
|
||||
reply_tags = [request_tag, e_tag, p_tag, alt_tag, status_tag]
|
||||
encrypted = False
|
||||
@ -371,15 +450,16 @@ class DVM:
|
||||
content = nip04_encrypt(self.keys.secret_key(), PublicKey.from_hex(original_event.author().to_hex()),
|
||||
content)
|
||||
|
||||
reply_event = EventBuilder(original_event.kind() + 1000, str(content), reply_tags).to_event(self.keys)
|
||||
reply_event = EventBuilder(Kind(original_event.kind().as_u64() + 1000), str(content), reply_tags).to_event(
|
||||
self.keys)
|
||||
|
||||
send_event(reply_event, client=self.client, dvm_config=self.dvm_config)
|
||||
print("[" + self.dvm_config.NIP89.NAME + "] " + str(
|
||||
original_event.kind() + 1000) + " Job Response event sent: " + reply_event.as_json())
|
||||
original_event.kind().as_u64() + 1000) + " Job Response event sent: " + reply_event.as_json())
|
||||
|
||||
def send_job_status_reaction(original_event, status, is_paid=True, amount=0, client=None,
|
||||
content=None,
|
||||
dvm_config=None):
|
||||
dvm_config=None, user=None):
|
||||
|
||||
task = get_task(original_event, client=client, dvm_config=dvm_config)
|
||||
alt_description, reaction = build_status_reaction(status, task, amount, content, dvm_config)
|
||||
@ -413,7 +493,8 @@ class DVM:
|
||||
bolt11 = ""
|
||||
payment_hash = ""
|
||||
expires = original_event.created_at().as_secs() + (60 * 60 * 24)
|
||||
if status == "payment-required" or (status == "processing" and not is_paid):
|
||||
if status == "payment-required" or (
|
||||
status == "processing" and not is_paid):
|
||||
if dvm_config.LNBITS_INVOICE_KEY != "":
|
||||
try:
|
||||
bolt11, payment_hash = create_bolt11_ln_bits(amount, dvm_config)
|
||||
@ -471,12 +552,13 @@ class DVM:
|
||||
reaction_event = EventBuilder(EventDefinitions.KIND_FEEDBACK, str(content), reply_tags).to_event(keys)
|
||||
send_event(reaction_event, client=self.client, dvm_config=self.dvm_config)
|
||||
print("[" + self.dvm_config.NIP89.NAME + "]" + ": Sent Kind " + str(
|
||||
EventDefinitions.KIND_FEEDBACK) + " Reaction: " + status + " " + reaction_event.as_json())
|
||||
EventDefinitions.KIND_FEEDBACK.as_u64()) + " Reaction: " + status + " " + reaction_event.as_json())
|
||||
return reaction_event.as_json()
|
||||
|
||||
def do_work(job_event, amount):
|
||||
if ((EventDefinitions.KIND_NIP90_EXTRACT_TEXT <= job_event.kind() <= EventDefinitions.KIND_NIP90_GENERIC)
|
||||
or job_event.kind() == EventDefinitions.KIND_DM):
|
||||
if ((
|
||||
EventDefinitions.KIND_NIP90_EXTRACT_TEXT.as_u64() <= job_event.kind().as_u64() <= EventDefinitions.KIND_NIP90_GENERIC.as_u64())
|
||||
or job_event.kind().as_u64() == EventDefinitions.KIND_DM.as_u64()):
|
||||
|
||||
task = get_task(job_event, client=self.client, dvm_config=self.dvm_config)
|
||||
|
||||
@ -515,9 +597,11 @@ class DVM:
|
||||
post_processed = dvm.post_process(result, job_event)
|
||||
send_nostr_reply_event(post_processed, job_event.as_json())
|
||||
except Exception as e:
|
||||
print(e)
|
||||
send_job_status_reaction(job_event, "error", content=str(e),
|
||||
dvm_config=self.dvm_config)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
# we could send the exception here to the user, but maybe that's not a good idea after all.
|
||||
send_job_status_reaction(job_event, "error", content=result,
|
||||
dvm_config=self.dvm_config)
|
||||
@ -527,7 +611,7 @@ class DVM:
|
||||
client=self.client, config=self.dvm_config)
|
||||
print(user.lud16 + " " + str(amount))
|
||||
bolt11 = zaprequest(user.lud16, amount, "Couldn't finish job, returning sats", job_event,
|
||||
user.npub,
|
||||
PublicKey.parse(user.npub),
|
||||
self.keys, self.dvm_config.RELAY_LIST, zaptype="private")
|
||||
if bolt11 is None:
|
||||
print("Receiver has no Lightning address, can't zap back.")
|
||||
@ -545,19 +629,19 @@ class DVM:
|
||||
for dvm in self.dvm_config.SUPPORTED_DVMS:
|
||||
scheduled_result = dvm.schedule(self.dvm_config)
|
||||
|
||||
|
||||
for job in self.job_list:
|
||||
if job.bolt11 != "" and job.payment_hash != "" and not job.payment_hash is None and not job.is_paid:
|
||||
ispaid = check_bolt11_ln_bits_is_paid(job.payment_hash, self.dvm_config)
|
||||
if ispaid and job.is_paid is False:
|
||||
print("is paid")
|
||||
job.is_paid = True
|
||||
amount = parse_amount_from_bolt11_invoice(job.bolt11)
|
||||
|
||||
job.is_paid = True
|
||||
send_job_status_reaction(job.event, "processing", True, 0,
|
||||
client=self.client,
|
||||
dvm_config=self.dvm_config)
|
||||
print("[" + self.dvm_config.NIP89.NAME + "] doing work from joblist")
|
||||
amount = parse_amount_from_bolt11_invoice(job.bolt11)
|
||||
do_work(job.event, amount)
|
||||
elif ispaid is None: # invoice expired
|
||||
self.job_list.remove(job)
|
||||
|
@ -7,17 +7,18 @@ import sys
|
||||
from sys import platform
|
||||
from threading import Thread
|
||||
from venv import create
|
||||
from nostr_sdk import Keys
|
||||
from nostr_sdk import Keys, Kind
|
||||
from nostr_dvm.dvm import DVM
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.output_utils import post_process_result
|
||||
|
||||
|
||||
class DVMTaskInterface:
|
||||
NAME: str
|
||||
KIND: int
|
||||
KIND: Kind
|
||||
TASK: str = ""
|
||||
FIX_COST: float = 0
|
||||
PER_UNIT_COST: float = 0
|
||||
@ -30,13 +31,14 @@ class DVMTaskInterface:
|
||||
admin_config: AdminConfig
|
||||
dependencies = []
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, admin_config: AdminConfig = None,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None,
|
||||
options=None, task=None):
|
||||
self.init(name, dvm_config, admin_config, nip89config, task)
|
||||
self.init(name, dvm_config, admin_config, nip88config, nip89config, task)
|
||||
self.options = options
|
||||
self.install_dependencies(dvm_config)
|
||||
|
||||
def init(self, name, dvm_config, admin_config=None, nip89config=None, task=None):
|
||||
def init(self, name, dvm_config, admin_config=None, nip88config=None, nip89config=None, task=None):
|
||||
self.NAME = name
|
||||
self.PRIVATE_KEY = dvm_config.PRIVATE_KEY
|
||||
if dvm_config.PUBLIC_KEY == "" or dvm_config.PUBLIC_KEY is None:
|
||||
@ -55,6 +57,12 @@ class DVMTaskInterface:
|
||||
self.KIND = nip89config.KIND
|
||||
|
||||
dvm_config.NIP89 = self.NIP89_announcement(nip89config)
|
||||
|
||||
if nip88config is None:
|
||||
dvm_config.NIP88 = None
|
||||
else:
|
||||
dvm_config.NIP88 = nip88config
|
||||
|
||||
self.dvm_config = dvm_config
|
||||
self.admin_config = admin_config
|
||||
|
||||
@ -150,4 +158,3 @@ def process_venv(identifier):
|
||||
DVMTaskInterface.write_output(result, args.output)
|
||||
except Exception as e:
|
||||
DVMTaskInterface.write_output("Error: " + str(e), args.output)
|
||||
|
||||
|
@ -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.definitions import EventDefinitions
|
||||
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.nwc_tools import nwc_zap
|
||||
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
|
||||
|
||||
|
||||
@ -24,7 +26,7 @@ class Subscription:
|
||||
# This is a simple list just to keep track which events we created and manage, so we don't pay for other requests
|
||||
def __init__(self, dvm_config, admin_config=None):
|
||||
self.NAME = "Subscription Handler"
|
||||
dvm_config.DB = "db/" + self.NAME + ".db"
|
||||
dvm_config.DB = "db/" + "subscriptions" + ".db"
|
||||
self.dvm_config = dvm_config
|
||||
nip89config = NIP89Config()
|
||||
nip89config.NAME = self.NAME
|
||||
@ -43,9 +45,7 @@ class Subscription:
|
||||
self.job_list = []
|
||||
|
||||
print("Nostr Subscription Handler public key: " + str(pk.to_bech32()) + " Hex: " + str(
|
||||
pk.to_hex()) + " Name: " + self.NAME +
|
||||
" Supported DVM tasks: " +
|
||||
', '.join(p.NAME + ":" + p.TASK for p in self.dvm_config.SUPPORTED_DVMS) + "\n")
|
||||
pk.to_hex()) + "\n")
|
||||
|
||||
for relay in self.dvm_config.RELAY_LIST:
|
||||
self.client.add_relay(relay)
|
||||
@ -61,7 +61,7 @@ class Subscription:
|
||||
|
||||
self.client.subscribe([zap_filter, dm_filter, cancel_subscription_filter], None)
|
||||
|
||||
create_subscription_sql_table("db/subscriptions")
|
||||
create_subscription_sql_table(dvm_config.DB)
|
||||
|
||||
# admin_make_database_updates(adminconfig=self.admin_config, dvmconfig=self.dvm_config, client=self.client)
|
||||
|
||||
@ -95,13 +95,85 @@ class Subscription:
|
||||
kind7001eventid = tag.as_vec()[1]
|
||||
|
||||
if kind7001eventid != "":
|
||||
subscription = get_from_subscription__sql_table("db/subscriptions", kind7001eventid)
|
||||
subscription = get_from_subscription_sql_table(dvm_config.DB, kind7001eventid)
|
||||
|
||||
if subscription is not None:
|
||||
update_subscription_sql_table("db/subscriptions", kind7001eventid, recipient,
|
||||
update_subscription_sql_table(dvm_config.DB, kind7001eventid, recipient,
|
||||
subscription.subscriber, subscription.nwc, subscription.cadence,
|
||||
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):
|
||||
|
||||
@ -123,105 +195,59 @@ class Subscription:
|
||||
tier_dtag = jsonevent['tier_dtag']
|
||||
|
||||
start = Timestamp.now().as_secs()
|
||||
end = Timestamp.now().as_secs()
|
||||
|
||||
isactivesubscription = False
|
||||
recipe = ""
|
||||
|
||||
subscription = get_from_subscription__sql_table("db/subscriptions", event7001)
|
||||
if subscription is not None and subscription.end > start:
|
||||
start = subscription.end
|
||||
isactivesubscription = True
|
||||
subscription = get_from_subscription_sql_table(dvm_config.DB, event7001)
|
||||
#if subscription is not None and subscription.end > start:
|
||||
# start = subscription.end
|
||||
# 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'])
|
||||
print(zapsstr)
|
||||
success = True
|
||||
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:
|
||||
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()
|
||||
recipe = make_subscription_zap_recipe(event7001, recipient, subscriber, start, end, tier_dtag)
|
||||
print("RECIPE " + recipe)
|
||||
isactivesubscription = True
|
||||
|
||||
if subscription is None:
|
||||
add_to_subscription_sql_table("db/subscriptions", event7001, recipient, subscriber, nwc,
|
||||
add_to_subscription_sql_table(dvm_config.DB, event7001, recipient, subscriber, nwc,
|
||||
cadence, overall_amount, start, end, tier_dtag,
|
||||
zapsstr, recipe, isactivesubscription)
|
||||
zapsstr, recipe, isactivesubscription, Timestamp.now().as_secs())
|
||||
print("new subscription entry")
|
||||
else:
|
||||
update_subscription_sql_table("db/subscriptions", event7001, recipient, subscriber, nwc,
|
||||
update_subscription_sql_table(dvm_config.DB, event7001, recipient, subscriber, nwc,
|
||||
cadence, overall_amount, start, end,
|
||||
tier_dtag, zapsstr, recipe, isactivesubscription)
|
||||
tier_dtag, zapsstr, recipe, isactivesubscription,
|
||||
Timestamp.now().as_secs())
|
||||
print("updated subscription entry")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print("Error in Subscriber " + str(e))
|
||||
|
||||
@ -230,7 +256,66 @@ class Subscription:
|
||||
try:
|
||||
while True:
|
||||
time.sleep(60.0)
|
||||
print("Checking Subscription")
|
||||
subscriptions = get_all_subscriptions_from_sql_table(dvm_config.DB)
|
||||
|
||||
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(dvm_config.DB, 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(dvm_config.DB, 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")
|
||||
|
||||
|
||||
else:
|
||||
delete_threshold = 60 * 60 * 24 * 365
|
||||
if subscription.cadence == "daily":
|
||||
delete_threshold = 60 * 60 * 24 * 3 # After 3 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(dvm_config.DB, subscription.id)
|
||||
print("Delete expired subscription")
|
||||
|
||||
print(str(Timestamp.now().as_secs()) + ": Checking " + str(len(subscriptions)) + " Subscription entries..")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print('Stay weird!')
|
||||
os.kill(os.getpid(), signal.SIGTERM)
|
||||
|
@ -1,12 +1,13 @@
|
||||
import json
|
||||
import os
|
||||
from datetime import timedelta
|
||||
from nostr_sdk import Client, Timestamp, PublicKey, Tag, Keys, Options, SecretKey, NostrSigner
|
||||
from nostr_sdk import Client, Timestamp, PublicKey, Tag, Keys, Options, SecretKey, NostrSigner, Kind, RelayOptions
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.output_utils import post_process_list_to_events
|
||||
|
||||
@ -19,15 +20,16 @@ Params: None
|
||||
|
||||
|
||||
class AdvancedSearch(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_CONTENT_SEARCH
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_CONTENT_SEARCH
|
||||
TASK: str = "search-content"
|
||||
FIX_COST: float = 0
|
||||
dvm_config: DVMConfig
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
@ -93,7 +95,9 @@ class AdvancedSearch(DVMTaskInterface):
|
||||
signer = NostrSigner.keys(keys)
|
||||
cli = Client.with_opts(signer, opts)
|
||||
|
||||
cli.add_relay(options["relay"])
|
||||
ropts = RelayOptions().ping(False)
|
||||
cli.add_relay_with_opts(options["relay"], ropts)
|
||||
|
||||
cli.connect()
|
||||
|
||||
#earch_since_seconds = int(options["since"]) * 24 * 60 * 60
|
||||
@ -121,12 +125,12 @@ class AdvancedSearch(DVMTaskInterface):
|
||||
userkeys.append(userkey)
|
||||
|
||||
if not options["users"]:
|
||||
notes_filter = Filter().kind(1).search(options["search"]).since(search_since).until(search_until).limit(options["max_results"])
|
||||
notes_filter = Filter().kind(Kind(1)).search(options["search"]).since(search_since).until(search_until).limit(options["max_results"])
|
||||
elif options["search"] == "":
|
||||
notes_filter = Filter().kind(1).authors(userkeys).since(search_since).until(
|
||||
notes_filter = Filter().kind(Kind(1)).authors(userkeys).since(search_since).until(
|
||||
search_until).limit(options["max_results"])
|
||||
else:
|
||||
notes_filter = Filter().kind(1).authors(userkeys).search(options["search"]).since(
|
||||
notes_filter = Filter().kind(Kind(1)).authors(userkeys).search(options["search"]).since(
|
||||
search_since).until(search_until).limit(options["max_results"])
|
||||
|
||||
|
||||
@ -163,7 +167,7 @@ def build_example(name, identifier, admin_config):
|
||||
# Add NIP89
|
||||
nip89info = {
|
||||
"name": name,
|
||||
"image": "https://image.nostr.build/a99ab925084029d9468fef8330ff3d9be2cf67da473b024f2a6d48b5cd77197f.jpg",
|
||||
"image": "https://nostr.band/android-chrome-192x192.png",
|
||||
"about": "I search notes on Nostr.band.",
|
||||
"encryptionSupported": True,
|
||||
"cashuAccepted": True,
|
||||
|
@ -3,12 +3,13 @@ import os
|
||||
from datetime import timedelta
|
||||
|
||||
import requests
|
||||
from nostr_sdk import Client, Timestamp, PublicKey, Tag, Keys, Options, SecretKey, NostrSigner, Event
|
||||
from nostr_sdk import Client, Timestamp, PublicKey, Tag, Keys, Options, SecretKey, NostrSigner, Event, Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.output_utils import post_process_list_to_events
|
||||
|
||||
@ -21,15 +22,16 @@ Params: None
|
||||
|
||||
|
||||
class AdvancedSearchWine(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_CONTENT_SEARCH
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_CONTENT_SEARCH
|
||||
TASK: str = "search-content"
|
||||
FIX_COST: float = 0
|
||||
dvm_config: DVMConfig
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
|
@ -2,12 +2,14 @@ import json
|
||||
import os
|
||||
from datetime import timedelta
|
||||
from nostr_sdk import Client, Timestamp, PublicKey, Tag, Keys, Options, SecretKey, NostrSigner, NostrDatabase, \
|
||||
ClientBuilder, Filter, NegentropyOptions, NegentropyDirection, init_logger, LogLevel, Event, EventId
|
||||
ClientBuilder, Filter, NegentropyOptions, NegentropyDirection, init_logger, LogLevel, Event, EventId, Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils import definitions
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config, check_and_set_d_tag_nip88, check_and_set_tiereventid_nip88
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.output_utils import post_process_list_to_events, post_process_list_to_users
|
||||
|
||||
@ -20,23 +22,24 @@ Params: None
|
||||
|
||||
|
||||
class DicoverContentCurrentlyPopular(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_CONTENT_DISCOVERY
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_CONTENT_DISCOVERY
|
||||
TASK: str = "discover-content"
|
||||
FIX_COST: float = 0
|
||||
dvm_config: DVMConfig
|
||||
last_schedule: int
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
self.last_schedule = Timestamp.now().as_secs()
|
||||
|
||||
use_logger = False
|
||||
if use_logger:
|
||||
init_logger(LogLevel.DEBUG)
|
||||
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
|
||||
self.sync_db()
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
@ -94,20 +97,21 @@ class DicoverContentCurrentlyPopular(DVMTaskInterface):
|
||||
# Query events from database
|
||||
timestamp_hour_ago = Timestamp.now().as_secs() - 3600
|
||||
lasthour = Timestamp.from_secs(timestamp_hour_ago)
|
||||
filter1 = Filter().kind(1).since(lasthour)
|
||||
|
||||
filter1 = Filter().kind(definitions.EventDefinitions.KIND_NOTE).since(lasthour)
|
||||
events = cli.database().query([filter1])
|
||||
ns.finallist = {}
|
||||
for event in events:
|
||||
if event.created_at().as_secs() > timestamp_hour_ago:
|
||||
ns.finallist[event.id().to_hex()] = 0
|
||||
filt = Filter().kinds([9735, 7, 1]).event(event.id()).since(lasthour)
|
||||
filt = Filter().kinds([definitions.EventDefinitions.KIND_ZAP, definitions.EventDefinitions.KIND_REACTION, definitions.EventDefinitions.KIND_NOTE]).event(event.id()).since(lasthour)
|
||||
reactions = cli.database().query([filt])
|
||||
ns.finallist[event.id().to_hex()] = len(reactions)
|
||||
|
||||
result_list = []
|
||||
finallist_sorted = sorted(ns.finallist.items(), key=lambda x: x[1], reverse=True)[:int(options["max_results"])]
|
||||
for entry in finallist_sorted:
|
||||
print(EventId.parse(entry[0]).to_bech32() + "/" + EventId.parse(entry[0]).to_hex() + ": " + str(entry[1]))
|
||||
#print(EventId.parse(entry[0]).to_bech32() + "/" + EventId.parse(entry[0]).to_hex() + ": " + str(entry[1]))
|
||||
e_tag = Tag.parse(["e", entry[0]])
|
||||
result_list.append(e_tag.as_vec())
|
||||
|
||||
@ -146,12 +150,16 @@ class DicoverContentCurrentlyPopular(DVMTaskInterface):
|
||||
|
||||
timestamp_hour_ago = Timestamp.now().as_secs() - 3600
|
||||
lasthour = Timestamp.from_secs(timestamp_hour_ago)
|
||||
filter1 = Filter().kinds([1, 7, 9735]).since(lasthour) # Notes, reactions, zaps
|
||||
|
||||
filter1 = Filter().kinds([definitions.EventDefinitions.KIND_NOTE, definitions.EventDefinitions.KIND_REACTION, definitions.EventDefinitions.KIND_ZAP]).since(lasthour) # Notes, reactions, zaps
|
||||
|
||||
# filter = Filter().author(keys.public_key())
|
||||
print("Syncing Notes of last hour.. this might take a while..")
|
||||
dbopts = NegentropyOptions().direction(NegentropyDirection.DOWN)
|
||||
cli.reconcile(filter1, dbopts)
|
||||
database.delete(Filter().until(Timestamp.from_secs(
|
||||
Timestamp.now().as_secs() - 3600))) # Clear old events so db doesnt get too full.
|
||||
|
||||
print("Done Syncing Notes of Last hour.")
|
||||
|
||||
|
||||
@ -163,13 +171,20 @@ def build_example(name, identifier, admin_config):
|
||||
dvm_config.USE_OWN_VENV = False
|
||||
dvm_config.SHOWLOG = True
|
||||
dvm_config.SCHEDULE_UPDATES_SECONDS = 600 # Every 10 minutes
|
||||
# Activate these to use a subscription based model instead
|
||||
# dvm_config.SUBSCRIPTION_REQUIRED = True
|
||||
# dvm_config.SUBSCRIPTION_DAILY_COST = 1
|
||||
dvm_config.FIX_COST = 0
|
||||
|
||||
# Add NIP89
|
||||
nip89info = {
|
||||
"name": name,
|
||||
"image": "https://image.nostr.build/f720192abfbfbcc21ce78281aca4bbd1ccf89ee7c90b54ae16b71ae9c1ad88e0.png",
|
||||
"about": "I show popular content",
|
||||
"image": "https://image.nostr.build/b29b6ec4bf9b6184f69d33cb44862db0d90a2dd9a506532e7ba5698af7d36210.jpg",
|
||||
"about": "I show notes that are currently popular",
|
||||
"lud16": dvm_config.LN_ADDRESS,
|
||||
"encryptionSupported": True,
|
||||
"cashuAccepted": True,
|
||||
"amount": "free",
|
||||
"nip90Params": {
|
||||
"max_results": {
|
||||
"required": False,
|
||||
@ -183,8 +198,68 @@ def build_example(name, identifier, admin_config):
|
||||
nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, nip89info["image"])
|
||||
nip89config.CONTENT = json.dumps(nip89info)
|
||||
|
||||
admin_config.UPDATE_PROFILE = False
|
||||
admin_config.REBROADCAST_NIP89 = True
|
||||
|
||||
return DicoverContentCurrentlyPopular(name=name, dvm_config=dvm_config, nip89config=nip89config,
|
||||
admin_config=admin_config)
|
||||
admin_config=admin_config)
|
||||
|
||||
|
||||
def build_example_subscription(name, identifier, admin_config):
|
||||
dvm_config = build_default_config(identifier)
|
||||
dvm_config.USE_OWN_VENV = False
|
||||
dvm_config.SHOWLOG = True
|
||||
dvm_config.SCHEDULE_UPDATES_SECONDS = 600 # Every 10 minutes
|
||||
# Activate these to use a subscription based model instead
|
||||
# dvm_config.SUBSCRIPTION_DAILY_COST = 1
|
||||
dvm_config.FIX_COST = 0
|
||||
|
||||
# Add NIP89
|
||||
nip89info = {
|
||||
"name": name,
|
||||
"image": "https://image.nostr.build/b29b6ec4bf9b6184f69d33cb44862db0d90a2dd9a506532e7ba5698af7d36210.jpg",
|
||||
"about": "I show notes that are currently popular, just like the free DVM, I'm also used for testing subscriptions. (beta, might break"
|
||||
")",
|
||||
"lud16": dvm_config.LN_ADDRESS,
|
||||
"encryptionSupported": True,
|
||||
"cashuAccepted": True,
|
||||
"subscription": True,
|
||||
"nip90Params": {
|
||||
"max_results": {
|
||||
"required": False,
|
||||
"values": [],
|
||||
"description": "The number of maximum results to return (default currently 100)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nip89config = NIP89Config()
|
||||
nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, nip89info["image"])
|
||||
nip89config.CONTENT = json.dumps(nip89info)
|
||||
|
||||
nip88config = NIP88Config()
|
||||
nip88config.DTAG = check_and_set_d_tag_nip88(identifier, name, dvm_config.PRIVATE_KEY, nip89info["image"])
|
||||
nip88config.TIER_EVENT = check_and_set_tiereventid_nip88(identifier, "1")
|
||||
nip89config.NAME = name
|
||||
nip88config.IMAGE = nip89info["image"]
|
||||
nip88config.TITLE = name
|
||||
nip88config.AMOUNT_DAILY = 100
|
||||
nip88config.AMOUNT_MONTHLY = 2000
|
||||
nip88config.CONTENT = "Subscribe to the DVM for unlimited use during your subscription"
|
||||
nip88config.PERK1DESC = "Unlimited requests"
|
||||
nip88config.PAYMENT_VERIFIER_PUBKEY = "5b5c045ecdf66fb540bdf2049fe0ef7f1a566fa427a4fe50d400a011b65a3a7e"
|
||||
|
||||
admin_config.UPDATE_PROFILE = False
|
||||
admin_config.REBROADCAST_NIP89 = True
|
||||
admin_config.REBROADCAST_NIP88 = False
|
||||
|
||||
# admin_config.FETCH_NIP88 = True
|
||||
# admin_config.EVENTID = "63a791cdc7bf78c14031616963105fce5793f532bb231687665b14fb6d805fdb"
|
||||
# admin_config.PRIVKEY = dvm_config.PRIVATE_KEY
|
||||
|
||||
return DicoverContentCurrentlyPopular(name=name, dvm_config=dvm_config, nip89config=nip89config,
|
||||
nip88config=nip88config,
|
||||
admin_config=admin_config)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -1,9 +1,13 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config
|
||||
from nostr_dvm.utils.mediasource_utils import organize_input_media_data
|
||||
from nostr_dvm.utils.output_utils import upload_media_to_hoster
|
||||
@ -18,15 +22,16 @@ Params: -language The target language
|
||||
|
||||
|
||||
class MediaConverter(DVMTaskInterface):
|
||||
KIND = EventDefinitions.KIND_NIP90_CONVERT_VIDEO
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_CONVERT_VIDEO
|
||||
TASK = "convert"
|
||||
FIX_COST = 20
|
||||
PER_UNIT_COST = 0.1
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
|
@ -3,12 +3,13 @@ import os
|
||||
from datetime import timedelta
|
||||
from threading import Thread
|
||||
|
||||
from nostr_sdk import Client, Timestamp, PublicKey, Tag, Keys, Options, SecretKey, NostrSigner
|
||||
from nostr_sdk import Client, Timestamp, PublicKey, Tag, Keys, Options, SecretKey, NostrSigner, Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.output_utils import post_process_list_to_users
|
||||
|
||||
@ -22,16 +23,17 @@ Params: None
|
||||
|
||||
|
||||
class DiscoverInactiveFollows(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_PEOPLE_DISCOVERY
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_PEOPLE_DISCOVERY
|
||||
TASK: str = "inactive-follows"
|
||||
FIX_COST: float = 50
|
||||
client: Client
|
||||
dvm_config: DVMConfig
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
# no input required
|
||||
@ -78,7 +80,7 @@ class DiscoverInactiveFollows(DVMTaskInterface):
|
||||
options = DVMTaskInterface.set_options(request_form)
|
||||
step = 20
|
||||
|
||||
followers_filter = Filter().author(PublicKey.from_hex(options["user"])).kind(3).limit(1)
|
||||
followers_filter = Filter().author(PublicKey.from_hex(options["user"])).kind(Kind(3)).limit(1)
|
||||
followers = cli.get_events_of([followers_filter], timedelta(seconds=self.dvm_config.RELAY_TIMEOUT))
|
||||
|
||||
if len(followers) > 0:
|
||||
|
@ -3,12 +3,13 @@ import os
|
||||
from datetime import timedelta
|
||||
from threading import Thread
|
||||
|
||||
from nostr_sdk import Client, Timestamp, PublicKey, Tag, Keys, Options, SecretKey, NostrSigner
|
||||
from nostr_sdk import Client, Timestamp, PublicKey, Tag, Keys, Options, SecretKey, NostrSigner, Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.output_utils import post_process_list_to_users
|
||||
|
||||
@ -22,16 +23,17 @@ Params: None
|
||||
|
||||
|
||||
class DiscoverNonFollowers(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_PEOPLE_DISCOVERY
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_PEOPLE_DISCOVERY
|
||||
TASK: str = "non-followers"
|
||||
FIX_COST: float = 50
|
||||
client: Client
|
||||
dvm_config: DVMConfig
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
# no input required
|
||||
@ -66,15 +68,15 @@ class DiscoverNonFollowers(DVMTaskInterface):
|
||||
keys = Keys.parse(sk.to_hex())
|
||||
signer = NostrSigner.keys(keys)
|
||||
cli = Client.with_opts(signer, opts)
|
||||
#cli.add_relay("wss://relay.nostr.band")
|
||||
# cli.add_relay("wss://relay.nostr.band")
|
||||
for relay in self.dvm_config.RELAY_LIST:
|
||||
cli.add_relay(relay)
|
||||
cli.add_relay(relay)
|
||||
cli.connect()
|
||||
|
||||
options = DVMTaskInterface.set_options(request_form)
|
||||
step = 20
|
||||
|
||||
followers_filter = Filter().author(PublicKey.from_hex(options["user"])).kind(3)
|
||||
followers_filter = Filter().author(PublicKey.from_hex(options["user"])).kind(Kind(3))
|
||||
followers = cli.get_events_of([followers_filter], timedelta(seconds=self.dvm_config.RELAY_TIMEOUT))
|
||||
|
||||
if len(followers) > 0:
|
||||
@ -96,7 +98,6 @@ class DiscoverNonFollowers(DVMTaskInterface):
|
||||
ns.dic[following] = "True"
|
||||
print("Followings: " + str(len(followings)))
|
||||
|
||||
|
||||
def scanList(users, instance, i, st):
|
||||
from nostr_sdk import Filter
|
||||
|
||||
@ -109,10 +110,9 @@ class DiscoverNonFollowers(DVMTaskInterface):
|
||||
cli.add_relay(relay)
|
||||
cli.connect()
|
||||
|
||||
|
||||
for i in range(i, i + st):
|
||||
filters = []
|
||||
filter1 = Filter().author(PublicKey.from_hex(users[i])).kind(3)
|
||||
filter1 = Filter().author(PublicKey.from_hex(users[i])).kind(Kind(3))
|
||||
filters.append(filter1)
|
||||
followers = cli.get_events_of(filters, timedelta(seconds=3))
|
||||
|
||||
@ -130,12 +130,12 @@ class DiscoverNonFollowers(DVMTaskInterface):
|
||||
if tag.as_vec()[0] == "p":
|
||||
if len(tag.as_vec()) > 1:
|
||||
if tag.as_vec()[1] == options["user"]:
|
||||
foundfollower = True
|
||||
break
|
||||
foundfollower = True
|
||||
break
|
||||
|
||||
if not foundfollower:
|
||||
instance.dic[best_entry.author().to_hex()] = "False"
|
||||
print( "DIDNT FIND " + best_entry.author().to_nostr_uri())
|
||||
print("DIDNT FIND " + best_entry.author().to_nostr_uri())
|
||||
|
||||
print(str(i) + "/" + str(len(users)))
|
||||
cli.disconnect()
|
||||
@ -147,7 +147,7 @@ class DiscoverNonFollowers(DVMTaskInterface):
|
||||
args = [followings, ns, begin, step]
|
||||
t = Thread(target=scanList, args=args)
|
||||
threads.append(t)
|
||||
begin = begin + step -1
|
||||
begin = begin + step - 1
|
||||
|
||||
# last to step size
|
||||
missing_scans = (len(followings) - begin)
|
||||
@ -216,7 +216,7 @@ def build_example(name, identifier, admin_config):
|
||||
nip89config.CONTENT = json.dumps(nip89info)
|
||||
|
||||
return DiscoverNonFollowers(name=name, dvm_config=dvm_config, nip89config=nip89config,
|
||||
admin_config=admin_config)
|
||||
admin_config=admin_config)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -1 +1,299 @@
|
||||
from nostr_sdk import Client, Timestamp, PublicKey, Tag, Keys, Options, SecretKey, NostrSigner, EventId
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
from datetime import timedelta
|
||||
from threading import Thread
|
||||
|
||||
from nostr_sdk import Client, Timestamp, PublicKey, Tag, Keys, Options, SecretKey, NostrSigner, EventId, Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.output_utils import post_process_list_to_users
|
||||
|
||||
"""
|
||||
This File contains a Module to find inactive follows for a user on nostr
|
||||
|
||||
Accepted Inputs: None needed
|
||||
Outputs: A list of users that have been inactive
|
||||
Params: None
|
||||
"""
|
||||
|
||||
|
||||
class DiscoverInactiveFollows(DVMTaskInterface):
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_PEOPLE_DISCOVERY
|
||||
TASK: str = "inactive-follows"
|
||||
FIX_COST: float = 50
|
||||
client: Client
|
||||
dvm_config: DVMConfig
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
# no input required
|
||||
return True
|
||||
|
||||
def create_request_from_nostr_event(self, event, client=None, dvm_config=None):
|
||||
self.dvm_config = dvm_config
|
||||
|
||||
request_form = {"jobID": event.id().to_hex()}
|
||||
|
||||
# default values
|
||||
user = event.author().to_hex()
|
||||
since_days = 90
|
||||
|
||||
for tag in event.tags():
|
||||
if tag.as_vec()[0] == 'param':
|
||||
param = tag.as_vec()[1]
|
||||
if param == "user": # check for param type
|
||||
user = tag.as_vec()[2]
|
||||
elif param == "since_days": # check for param type
|
||||
since_days = int(tag.as_vec()[2])
|
||||
|
||||
options = {
|
||||
"user": user,
|
||||
"since_days": since_days
|
||||
}
|
||||
request_form['options'] = json.dumps(options)
|
||||
return request_form
|
||||
|
||||
def process(self, request_form):
|
||||
from nostr_sdk import Filter
|
||||
from types import SimpleNamespace
|
||||
ns = SimpleNamespace()
|
||||
|
||||
opts = (Options().wait_for_send(False).send_timeout(timedelta(seconds=self.dvm_config.RELAY_TIMEOUT)))
|
||||
sk = SecretKey.from_hex(self.dvm_config.PRIVATE_KEY)
|
||||
keys = Keys.parse(sk.to_hex())
|
||||
signer = NostrSigner.keys(keys)
|
||||
cli = Client.with_opts(signer, opts)
|
||||
for relay in self.dvm_config.RELAY_LIST:
|
||||
cli.add_relay(relay)
|
||||
cli.connect()
|
||||
|
||||
options = DVMTaskInterface.set_options(request_form)
|
||||
|
||||
|
||||
|
||||
inactivefollowerslist = ""
|
||||
relay_list = ["wss://relay.damus.io", "wss://nostr-pub.wellorder.net", "wss://nos.lol", "wss://nostr.wine",
|
||||
"wss://relay.nostfiles.dev", "wss://nostr.mom", "wss://nostr.oxtr.dev",
|
||||
"wss://relay.nostr.bg", "wss://relay.f7z.io"]
|
||||
relaytimeout = 5
|
||||
step = 20
|
||||
keys = Keys.parse(os.getenv(env.NOSTR_PRIVATE_KEY))
|
||||
opts = Options().wait_for_send(False).send_timeout(timedelta(seconds=5)).skip_disconnected_relays(
|
||||
True)
|
||||
cl = Client.with_opts(keys, opts)
|
||||
for relay in relay_list:
|
||||
cl.add_relay(relay)
|
||||
cl.connect()
|
||||
|
||||
timeinseconds = 3 * 24 * 60 * 60
|
||||
dif = Timestamp.now().as_secs() - timeinseconds
|
||||
considernotessince = Timestamp.from_secs(dif)
|
||||
filt = Filter().author(user).kind(1).since(considernotessince)
|
||||
reactions = cl.get_events_of([filt], timedelta(seconds=relaytimeout))
|
||||
list = []
|
||||
random.shuffle(reactions)
|
||||
for reaction in reactions:
|
||||
if reaction.kind() == 1:
|
||||
list.append(reaction.content())
|
||||
all = json.dumps(list)
|
||||
all = all.replace("\n", " ").replace("\n\n", " ")
|
||||
cleared = ""
|
||||
tokens = all.split()
|
||||
for item in tokens:
|
||||
item = item.replace("\n", " ").lstrip("\"").rstrip(",").rstrip(("."))
|
||||
# print(item)
|
||||
if item.__contains__("http") or item.__contains__("\nhttp") or item.__contains__(
|
||||
"\n\nhttp") or item.lower().__contains__("nostr:") or item.lower().__contains__(
|
||||
"nevent") or item.__contains__("\\u"):
|
||||
cleareditem = ""
|
||||
else:
|
||||
cleareditem = item
|
||||
cleared = cleared + " " + cleareditem
|
||||
|
||||
cleared = cleared.replace("\n", " ")
|
||||
# res = re.sub(r"[^ a-zA-Z0-9.!?/\\:,]+", '', all)
|
||||
# print(cleared)
|
||||
try:
|
||||
answer = LLAMA2(
|
||||
"Give me the 15 most important substantives as keywords of the following input: " + cleared,
|
||||
"nostruser",
|
||||
"Reply only with a comma-seperated keywords. return topics starting with a *", clear=True)
|
||||
except:
|
||||
answer = ""
|
||||
|
||||
promptarr = answer.split(":")
|
||||
if len(promptarr) > 1:
|
||||
# print(promptarr[1])
|
||||
prompt = promptarr[1].lstrip("\n").replace("\n", ",").replace("*", ",").replace("•", ",")
|
||||
else:
|
||||
prompt = promptarr[0].replace("\n", ",").replace("*", "")
|
||||
|
||||
pattern = r"[^a-zA-Z,#'\s]"
|
||||
text = re.sub(pattern, "", prompt) + ","
|
||||
|
||||
# text = (text.replace("Let's,", "").replace("Why,", "").replace("GM,", "")
|
||||
# .replace("Remember,", "").replace("I,", "").replace("Think,", "")
|
||||
# .replace("Already,", ""))
|
||||
# print(text)
|
||||
keywords = text.split(', ')
|
||||
keywords = [x.lstrip().rstrip(',') for x in keywords if x]
|
||||
|
||||
print(keywords)
|
||||
|
||||
# answer = LLAMA2("Extent the given list with 5 synonyms per entry " + str(keywords), user,
|
||||
# "Reply only with a comma-seperated keywords. return topics starting with a *")
|
||||
# answer.replace(" - Alternatives:", ",")
|
||||
# print(answer)
|
||||
# promptarr = answer.split(":")
|
||||
# if len(promptarr) > 1:
|
||||
# # print(promptarr[1])
|
||||
# prompt = promptarr[1].lstrip("\n").replace("\n", ",").replace("*", "").replace("•", "")
|
||||
# else:
|
||||
# prompt = promptarr[0].replace("\n", ",").replace("*", "")
|
||||
|
||||
# pattern = r"[^a-zA-Z,'\s]"
|
||||
# text = re.sub(pattern, "", prompt) + ","
|
||||
# keywords = text.split(', ')
|
||||
|
||||
# print(keywords)
|
||||
|
||||
timeinseconds = 30 * 60 # last 30 min?
|
||||
dif = Timestamp.now().as_secs() - timeinseconds
|
||||
looksince = Timestamp.from_secs(dif)
|
||||
filt2 = Filter().kind(1).since(looksince)
|
||||
notes = cl.get_events_of([filt2], timedelta(seconds=6))
|
||||
|
||||
# finallist = []
|
||||
ns.finallist = {}
|
||||
|
||||
print("Notes found: " + str(len(notes)))
|
||||
|
||||
def scanList(noteid: EventId, instance, i, length):
|
||||
|
||||
relay_list = ["wss://relay.damus.io", "wss://nostr-pub.wellorder.net", "wss://nos.lol",
|
||||
"wss://nostr.wine",
|
||||
"wss://relay.nostfiles.dev", "wss://nostr.mom", "wss://nostr.oxtr.dev",
|
||||
"wss://relay.nostr.bg", "wss://relay.f7z.io"]
|
||||
keys = Keys.parse(os.getenv(env.NOSTR_PRIVATE_KEY))
|
||||
opts = Options().wait_for_send(wait_for_send).send_timeout(
|
||||
timedelta(seconds=5)).skip_disconnected_relays(True)
|
||||
cli = Client.with_opts(keys, opts)
|
||||
for relay in relay_list:
|
||||
cli.add_relay(relay)
|
||||
cli.connect()
|
||||
|
||||
filters = []
|
||||
instance.finallist[noteid.to_hex()] = 0
|
||||
filt = Filter().kinds([9735, 7, 1]).event(noteid)
|
||||
reactions = cl.get_events_of([filt], timedelta(seconds=5))
|
||||
print(str(len(reactions)) + " " + str(j) + "/" + str(len(notes)))
|
||||
instance.finallist[noteid.to_hex()] = len(reactions)
|
||||
|
||||
print(str(i) + "/" + str(length))
|
||||
cli.disconnect()
|
||||
|
||||
j = 0
|
||||
|
||||
threads = []
|
||||
|
||||
for note in notes:
|
||||
|
||||
j = j + 1
|
||||
res = [ele for ele in keywords if (ele.replace(',', "") in note.content())]
|
||||
if bool(res):
|
||||
if not note.id().to_hex() in ns.finallist and note.pubkey().to_hex() != user:
|
||||
args = [note.id(), ns, j, len(notes)]
|
||||
t = Thread(target=scanList, args=args)
|
||||
threads.append(t)
|
||||
|
||||
# Start all threads
|
||||
for x in threads:
|
||||
x.start()
|
||||
|
||||
# Wait for all of them to finish
|
||||
for x in threads:
|
||||
x.join()
|
||||
|
||||
finallist_sorted = sorted(ns.finallist.items(), key=lambda x: x[1], reverse=True)
|
||||
converted_dict = dict(finallist_sorted)
|
||||
print(json.dumps(converted_dict))
|
||||
|
||||
notelist = ""
|
||||
resultlist = []
|
||||
i = 0
|
||||
notelist = "Based on topics: " + json.dumps(keywords).lstrip("[").rstrip(("]")) + "\n\n"
|
||||
for k in converted_dict:
|
||||
# print(k)
|
||||
if is_bot:
|
||||
i = i + 1
|
||||
notelist = notelist + "nostr:" + EventId.from_hex(k).to_bech32() + "\n\n"
|
||||
if i == 25:
|
||||
break
|
||||
else:
|
||||
p_tag = Tag.parse(["p", k])
|
||||
resultlist.append(p_tag.as_vec())
|
||||
|
||||
else:
|
||||
return json.dumps(resultlist[:25])
|
||||
|
||||
def post_process(self, result, event):
|
||||
"""Overwrite the interface function to return a social client readable format, if requested"""
|
||||
for tag in event.tags():
|
||||
if tag.as_vec()[0] == 'output':
|
||||
format = tag.as_vec()[1]
|
||||
if format == "text/plain": # check for output type
|
||||
result = post_process_list_to_users(result)
|
||||
|
||||
# if not text/plain, don't post-process
|
||||
return result
|
||||
|
||||
|
||||
# We build an example here that we can call by either calling this file directly from the main directory,
|
||||
# or by adding it to our playground. You can call the example and adjust it to your needs or redefine it in the
|
||||
# playground or elsewhere
|
||||
def build_example(name, identifier, admin_config):
|
||||
dvm_config = build_default_config(identifier)
|
||||
admin_config.LUD16 = dvm_config.LN_ADDRESS
|
||||
# Add NIP89
|
||||
nip89info = {
|
||||
"name": name,
|
||||
"image": "https://image.nostr.build/b29b6ec4bf9b6184f69d33cb44862db0d90a2dd9a506532e7ba5698af7d36210.jpg",
|
||||
"about": "I discover users you follow, but that have been inactive on Nostr",
|
||||
"encryptionSupported": True,
|
||||
"cashuAccepted": True,
|
||||
"amount": "Free",
|
||||
"nip90Params": {
|
||||
"user": {
|
||||
"required": False,
|
||||
"values": [],
|
||||
"description": "Do the task for another user"
|
||||
},
|
||||
"since_days": {
|
||||
"required": False,
|
||||
"values": [],
|
||||
"description": "The number of days a user has not been active to be considered inactive"
|
||||
}
|
||||
}
|
||||
}
|
||||
nip89config = NIP89Config()
|
||||
nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, nip89info["image"])
|
||||
nip89config.CONTENT = json.dumps(nip89info)
|
||||
|
||||
return DiscoverInactiveFollows(name=name, dvm_config=dvm_config, nip89config=nip89config,
|
||||
admin_config=admin_config)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
process_venv(DiscoverInactiveFollows)
|
||||
|
@ -1,11 +1,12 @@
|
||||
import json
|
||||
import os
|
||||
from nostr_sdk import Tag
|
||||
from nostr_sdk import Tag, Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.output_utils import post_process_list_to_events
|
||||
|
||||
@ -18,15 +19,16 @@ Params: None
|
||||
|
||||
|
||||
class TrendingNotesNostrBand(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_CONTENT_DISCOVERY
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_CONTENT_DISCOVERY
|
||||
TASK: str = "trending-content"
|
||||
FIX_COST: float = 0
|
||||
dvm_config: DVMConfig
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
@ -76,8 +78,9 @@ class TrendingNotesNostrBand(DVMTaskInterface):
|
||||
|
||||
return json.dumps(result_list)
|
||||
|
||||
except:
|
||||
return "error"
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return json.dumps([])
|
||||
|
||||
def post_process(self, result, event):
|
||||
"""Overwrite the interface function to return a social client readable format, if requested"""
|
||||
@ -102,7 +105,7 @@ def build_example(name, identifier, admin_config):
|
||||
|
||||
nip89info = {
|
||||
"name": name,
|
||||
"image": "https://image.nostr.build/4dc758923c7bfc5ba92030e6419272ec7470c3809d36e88e99f3a9daece88bac.png",
|
||||
"image": "https://nostr.band/android-chrome-192x192.png",
|
||||
"about": "I show trending notes from nostr.band",
|
||||
"amount": "Free",
|
||||
"encryptionSupported": True,
|
||||
|
@ -5,11 +5,13 @@ from io import BytesIO
|
||||
|
||||
import requests
|
||||
from PIL import Image
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.output_utils import upload_media_to_hoster
|
||||
from nostr_dvm.utils.zap_utils import get_price_per_sat
|
||||
@ -23,16 +25,17 @@ Outputs: An url to an Image
|
||||
|
||||
|
||||
class ImageGenerationDALLE(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_GENERATE_IMAGE
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_GENERATE_IMAGE
|
||||
TASK: str = "text-to-image"
|
||||
FIX_COST: float = 120
|
||||
dependencies = [("nostr-dvm", "nostr-dvm"),
|
||||
("openai", "openai==1.3.5")]
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
|
@ -3,11 +3,13 @@ import os
|
||||
from io import BytesIO
|
||||
import requests
|
||||
from PIL import Image
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.output_utils import upload_media_to_hoster
|
||||
from nostr_dvm.utils.zap_utils import get_price_per_sat
|
||||
@ -22,16 +24,17 @@ Params:
|
||||
|
||||
|
||||
class ImageGenerationReplicateSDXL(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_GENERATE_IMAGE
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_GENERATE_IMAGE
|
||||
TASK: str = "text-to-image"
|
||||
FIX_COST: float = 120
|
||||
dependencies = [("nostr-dvm", "nostr-dvm"),
|
||||
("replicate", "replicate")]
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
|
@ -1,12 +1,14 @@
|
||||
import json
|
||||
import os
|
||||
from PIL import Image
|
||||
from nostr_sdk import Kind
|
||||
from tqdm import tqdm
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.output_utils import upload_media_to_hoster
|
||||
from nostr_dvm.utils.zap_utils import get_price_per_sat
|
||||
@ -21,7 +23,7 @@ Params:
|
||||
|
||||
|
||||
class ImageGenerationMLX(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_GENERATE_IMAGE
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_GENERATE_IMAGE
|
||||
TASK: str = "text-to-image"
|
||||
FIX_COST: float = 120
|
||||
dependencies = [("nostr-dvm", "nostr-dvm"),
|
||||
@ -32,10 +34,11 @@ class ImageGenerationMLX(DVMTaskInterface):
|
||||
("tqdm", "tqdm"),
|
||||
]
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
|
@ -1,10 +1,13 @@
|
||||
import json
|
||||
from multiprocessing.pool import ThreadPool
|
||||
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.backends.nova_server.utils import check_server_status, send_request_to_server
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
|
||||
@ -19,13 +22,14 @@ Params: -model # models: juggernaut, dynavision, colossusProject, newrea
|
||||
|
||||
|
||||
class ImageGenerationSDXL(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_GENERATE_IMAGE
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_GENERATE_IMAGE
|
||||
TASK: str = "text-to-image"
|
||||
FIX_COST: float = 50
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
|
@ -1,10 +1,13 @@
|
||||
import json
|
||||
from multiprocessing.pool import ThreadPool
|
||||
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.backends.nova_server.utils import check_server_status, send_request_to_server
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
|
||||
@ -19,13 +22,14 @@ Params: -model # models: juggernaut, dynavision, colossusProject, newrea
|
||||
|
||||
|
||||
class ImageGenerationSDXLIMG2IMG(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_GENERATE_IMAGE
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_GENERATE_IMAGE
|
||||
TASK: str = "image-to-image"
|
||||
FIX_COST: float = 70
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
hasurl = False
|
||||
|
@ -1,10 +1,13 @@
|
||||
import json
|
||||
from multiprocessing.pool import ThreadPool
|
||||
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.backends.nova_server.utils import check_server_status, send_request_to_server
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
|
||||
@ -18,13 +21,14 @@ Outputs: An textual description of the image
|
||||
|
||||
|
||||
class ImageInterrogator(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_EXTRACT_TEXT
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_EXTRACT_TEXT
|
||||
TASK: str = "image-to-text"
|
||||
FIX_COST: float = 80
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
hasurl = False
|
||||
|
@ -1,10 +1,13 @@
|
||||
import json
|
||||
from multiprocessing.pool import ThreadPool
|
||||
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.backends.nova_server.utils import check_server_status, send_request_to_server
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
|
||||
@ -18,13 +21,14 @@ Params: -upscale 2,3,4
|
||||
|
||||
|
||||
class ImageUpscale(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_GENERATE_IMAGE
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_GENERATE_IMAGE
|
||||
TASK: str = "image-to-image"
|
||||
FIX_COST: float = 20
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
hasurl = False
|
||||
|
@ -2,12 +2,13 @@ import json
|
||||
import os
|
||||
from datetime import timedelta
|
||||
from nostr_sdk import Client, Timestamp, PublicKey, Tag, Keys, Options, SecretKey, NostrSigner, NostrDatabase, \
|
||||
ClientBuilder, Filter, NegentropyOptions, NegentropyDirection, init_logger, LogLevel
|
||||
ClientBuilder, Filter, NegentropyOptions, NegentropyDirection, init_logger, LogLevel, Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.output_utils import post_process_list_to_events, post_process_list_to_users
|
||||
|
||||
@ -20,23 +21,22 @@ Params: None
|
||||
|
||||
|
||||
class SearchUser(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_USER_SEARCH
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_USER_SEARCH
|
||||
TASK: str = "search-user"
|
||||
FIX_COST: float = 0
|
||||
dvm_config: DVMConfig
|
||||
last_schedule: int
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
self.last_schedule = Timestamp.now().as_secs()
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
use_logger = False
|
||||
if use_logger:
|
||||
init_logger(LogLevel.DEBUG)
|
||||
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
|
||||
self.sync_db()
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
@ -95,7 +95,7 @@ class SearchUser(DVMTaskInterface):
|
||||
|
||||
# Query events from database
|
||||
|
||||
filter1 = Filter().kind(0)
|
||||
filter1 = Filter().kind(Kind(0))
|
||||
events = cli.database().query([filter1])
|
||||
# for event in events:
|
||||
# print(event.as_json())
|
||||
@ -153,7 +153,7 @@ class SearchUser(DVMTaskInterface):
|
||||
cli.add_relay("wss://relay.damus.io")
|
||||
cli.connect()
|
||||
|
||||
filter1 = Filter().kind(0)
|
||||
filter1 = Filter().kind(Kind(0))
|
||||
|
||||
# filter = Filter().author(keys.public_key())
|
||||
print("Syncing Profile Database.. this might take a while..")
|
||||
|
@ -5,9 +5,10 @@ from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.nostr_utils import get_referenced_event_by_id, get_event_by_id, get_events_by_ids
|
||||
from nostr_sdk import Tag
|
||||
from nostr_sdk import Tag, Kind
|
||||
|
||||
"""
|
||||
This File contains a Module to summarize Text, based on a prompt using a the HuggingChat LLM on Huggingface
|
||||
@ -18,16 +19,17 @@ Outputs: Generated text
|
||||
|
||||
|
||||
class TextSummarizationHuggingChat(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_SUMMARIZE_TEXT
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_SUMMARIZE_TEXT
|
||||
TASK: str = "summarization"
|
||||
FIX_COST: float = 0
|
||||
dependencies = [("nostr-dvm", "nostr-dvm"),
|
||||
("hugchat", "hugchat")]
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
|
@ -1,11 +1,12 @@
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from nostr_sdk import Tag
|
||||
from nostr_sdk import Tag, Kind
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.nostr_utils import get_referenced_event_by_id, get_events_by_ids, get_event_by_id
|
||||
|
||||
@ -18,16 +19,17 @@ Outputs: Generated text
|
||||
|
||||
|
||||
class SummarizationUnleashedChat(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_SUMMARIZE_TEXT
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_SUMMARIZE_TEXT
|
||||
TASK: str = "text-to-text"
|
||||
FIX_COST: float = 10
|
||||
dependencies = [("nostr-dvm", "nostr-dvm"),
|
||||
("openai", "openai")]
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
@ -107,7 +109,7 @@ class SummarizationUnleashedChat(DVMTaskInterface):
|
||||
for model in client.models.list():
|
||||
print('- ' + model.id)
|
||||
|
||||
content = "Summarize the following notes: " + str(options["prompt"])[:4000]
|
||||
content = "Summarize the following notes: " + str(options["prompt"])[:3500]
|
||||
normal_stream = client.chat.completions.create(
|
||||
messages=[
|
||||
{
|
||||
|
@ -2,10 +2,13 @@ import json
|
||||
import os
|
||||
import time
|
||||
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.mediasource_utils import organize_input_media_data
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
|
||||
@ -19,17 +22,18 @@ Outputs: Transcribed text
|
||||
|
||||
|
||||
class SpeechToTextGoogle(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_EXTRACT_TEXT
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_EXTRACT_TEXT
|
||||
TASK: str = "speech-to-text"
|
||||
FIX_COST: float = 10
|
||||
PER_UNIT_COST: float = 0.1
|
||||
dependencies = [("nostr-dvm", "nostr-dvm"),
|
||||
("speech_recognition", "SpeechRecognition==3.10.0")]
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
if options is None:
|
||||
self.options = {}
|
||||
|
||||
|
@ -2,10 +2,13 @@ import json
|
||||
import os
|
||||
import re
|
||||
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.nostr_utils import get_event_by_id
|
||||
|
||||
@ -19,16 +22,17 @@ Params: None
|
||||
|
||||
|
||||
class TextExtractionPDF(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_EXTRACT_TEXT
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_EXTRACT_TEXT
|
||||
TASK: str = "pdf-to-text"
|
||||
FIX_COST: float = 0
|
||||
dependencies = [("nostr-dvm", "nostr-dvm"),
|
||||
("pypdf", "pypdf==3.17.1")]
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
|
@ -2,11 +2,15 @@ import json
|
||||
import os
|
||||
import time
|
||||
from multiprocessing.pool import ThreadPool
|
||||
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.backends.nova_server.utils import check_server_status, send_request_to_server, send_file_to_server
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.mediasource_utils import organize_input_media_data
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
|
||||
@ -20,14 +24,16 @@ Outputs: Transcribed text
|
||||
|
||||
|
||||
class SpeechToTextWhisperX(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_EXTRACT_TEXT
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_EXTRACT_TEXT
|
||||
TASK: str = "speech-to-text"
|
||||
FIX_COST: float = 10
|
||||
PER_UNIT_COST: float = 0.1
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
|
@ -1,10 +1,13 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
|
||||
"""
|
||||
@ -16,16 +19,17 @@ Outputs: Generated text
|
||||
|
||||
|
||||
class TextGenerationHuggingChat(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_GENERATE_TEXT
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_GENERATE_TEXT
|
||||
TASK: str = "text-to-text"
|
||||
FIX_COST: float = 0
|
||||
dependencies = [("nostr-dvm", "nostr-dvm"),
|
||||
("hugchat", "hugchat")]
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
|
@ -1,10 +1,13 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
|
||||
"""
|
||||
@ -16,16 +19,17 @@ Outputs: Generated text
|
||||
|
||||
|
||||
class TextGenerationLLMLite(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_GENERATE_TEXT
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_GENERATE_TEXT
|
||||
TASK: str = "text-to-text"
|
||||
FIX_COST: float = 0
|
||||
dependencies = [("nostr-dvm", "nostr-dvm"),
|
||||
("litellm", "litellm==1.12.3")]
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
|
@ -1,10 +1,13 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
|
||||
"""
|
||||
@ -16,16 +19,17 @@ Outputs: Generated text
|
||||
|
||||
|
||||
class TextGenerationUnleashedChat(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_GENERATE_TEXT
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_GENERATE_TEXT
|
||||
TASK: str = "text-to-text"
|
||||
FIX_COST: float = 10
|
||||
dependencies = [("nostr-dvm", "nostr-dvm"),
|
||||
("openai", "openai")]
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
|
@ -1,6 +1,10 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
|
||||
os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1"
|
||||
from pathlib import Path
|
||||
import urllib.request
|
||||
@ -22,17 +26,18 @@ Outputs: Generated Audiofile
|
||||
|
||||
|
||||
class TextToSpeech(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_TEXT_TO_SPEECH
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_TEXT_TO_SPEECH
|
||||
TASK: str = "text-to-speech"
|
||||
FIX_COST: float = 50
|
||||
PER_UNIT_COST = 0.5
|
||||
dependencies = [("nostr-dvm", "nostr-dvm"),
|
||||
("TTS", "TTS==0.22.0")]
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
|
@ -1,9 +1,13 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.nostr_utils import get_referenced_event_by_id, get_event_by_id
|
||||
|
||||
@ -17,16 +21,17 @@ Params: -language The target language
|
||||
|
||||
|
||||
class TranslationGoogle(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_TRANSLATE_TEXT
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_TRANSLATE_TEXT
|
||||
TASK: str = "translation"
|
||||
FIX_COST: float = 0
|
||||
dependencies = [("nostr-dvm", "nostr-dvm"),
|
||||
("translatepy", "translatepy==2.3")]
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
|
@ -1,11 +1,13 @@
|
||||
import json
|
||||
import os
|
||||
import requests
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.nostr_utils import get_referenced_event_by_id, get_event_by_id
|
||||
|
||||
@ -21,14 +23,15 @@ Requires API key or self-hosted instance
|
||||
|
||||
|
||||
class TranslationLibre(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_TRANSLATE_TEXT
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_TRANSLATE_TEXT
|
||||
TASK: str = "translation"
|
||||
FIX_COST: float = 0
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
admin_config: AdminConfig = None, options=None, task=None):
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options, task)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
|
@ -4,11 +4,13 @@ from io import BytesIO
|
||||
import requests
|
||||
import urllib.request
|
||||
from PIL import Image
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.output_utils import upload_media_to_hoster
|
||||
from nostr_dvm.utils.zap_utils import get_price_per_sat
|
||||
@ -23,16 +25,17 @@ Params:
|
||||
|
||||
|
||||
class VideoGenerationReplicateSVD(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_GENERATE_VIDEO
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_GENERATE_VIDEO
|
||||
TASK: str = "image-to-video"
|
||||
FIX_COST: float = 120
|
||||
dependencies = [("nostr-dvm", "nostr-dvm"),
|
||||
("replicate", "replicate")]
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
dvm_config.SCRIPT = os.path.abspath(__file__)
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
|
@ -1,10 +1,14 @@
|
||||
import json
|
||||
import os
|
||||
from multiprocessing.pool import ThreadPool
|
||||
|
||||
from nostr_sdk import Kind
|
||||
|
||||
from nostr_dvm.backends.nova_server.utils import check_server_status, send_request_to_server
|
||||
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
|
||||
@ -18,13 +22,14 @@ Outputs: An url to a video
|
||||
|
||||
|
||||
class VideoGenerationSVD(DVMTaskInterface):
|
||||
KIND: int = EventDefinitions.KIND_NIP90_GENERATE_VIDEO
|
||||
KIND: Kind = EventDefinitions.KIND_NIP90_GENERATE_VIDEO
|
||||
TASK: str = "image-to-video"
|
||||
FIX_COST: float = 120
|
||||
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
|
||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
|
||||
admin_config: AdminConfig = None, options=None):
|
||||
super().__init__(name, dvm_config, nip89config, admin_config, options)
|
||||
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
|
||||
admin_config=admin_config, options=options)
|
||||
|
||||
def is_input_supported(self, tags, client=None, dvm_config=None):
|
||||
for tag in tags:
|
||||
|
@ -5,23 +5,28 @@ from nostr_sdk import Keys, PublicKey, Client
|
||||
from nostr_dvm.utils.database_utils import get_from_sql_table, list_db, delete_from_sql_table, update_sql_table, \
|
||||
get_or_add_user, clean_db
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig
|
||||
from nostr_dvm.utils.nip89_utils import nip89_announce_tasks, fetch_nip89_paramters_for_deletion
|
||||
from nostr_dvm.utils.nip88_utils import nip88_announce_tier, fetch_nip88_parameters_for_deletion, fetch_nip88_event, \
|
||||
check_and_set_tiereventid_nip88
|
||||
from nostr_dvm.utils.nip89_utils import nip89_announce_tasks, fetch_nip89_parameters_for_deletion
|
||||
from nostr_dvm.utils.nostr_utils import update_profile
|
||||
|
||||
|
||||
class AdminConfig:
|
||||
REBROADCAST_NIP89: bool = False
|
||||
REBROADCAST_NIP88: bool = False
|
||||
UPDATE_PROFILE: bool = False
|
||||
DELETE_NIP89: bool = False
|
||||
DELETE_NIP88: bool = False
|
||||
FETCH_NIP88: bool = False
|
||||
WHITELISTUSER: bool = False
|
||||
UNWHITELISTUSER: bool = False
|
||||
BLACKLISTUSER: bool = False
|
||||
DELETEUSER: bool = False
|
||||
LISTDATABASE: bool = False
|
||||
ClEANDB: bool = False
|
||||
INDEX: str = "1"
|
||||
|
||||
USERNPUBS: list = []
|
||||
LUD16: str = ""
|
||||
|
||||
EVENTID: str = ""
|
||||
PRIVKEY: str = ""
|
||||
@ -56,17 +61,17 @@ def admin_make_database_updates(adminconfig: AdminConfig = None, dvmconfig: DVMC
|
||||
|
||||
if adminconfig.WHITELISTUSER:
|
||||
user = get_or_add_user(db, publickey, client=client, config=dvmconfig)
|
||||
update_sql_table(db, user.npub, user.balance, True, False, user.nip05, user.lud16, user.name, user.lastactive)
|
||||
update_sql_table(db, user.npub, user.balance, True, False, user.nip05, user.lud16, user.name, user.lastactive, user.subscribed)
|
||||
user = get_from_sql_table(db, publickey)
|
||||
print(str(user.name) + " is whitelisted: " + str(user.iswhitelisted))
|
||||
|
||||
if adminconfig.UNWHITELISTUSER:
|
||||
user = get_from_sql_table(db, publickey)
|
||||
update_sql_table(db, user.npub, user.balance, False, False, user.nip05, user.lud16, user.name, user.lastactive)
|
||||
update_sql_table(db, user.npub, user.balance, False, False, user.nip05, user.lud16, user.name, user.lastactive, user.subscribed)
|
||||
|
||||
if adminconfig.BLACKLISTUSER:
|
||||
user = get_from_sql_table(db, publickey)
|
||||
update_sql_table(db, user.npub, user.balance, False, True, user.nip05, user.lud16, user.name, user.lastactive)
|
||||
update_sql_table(db, user.npub, user.balance, False, True, user.nip05, user.lud16, user.name, user.lastactive, user.subscribed)
|
||||
|
||||
if adminconfig.DELETEUSER:
|
||||
delete_from_sql_table(db, publickey)
|
||||
@ -80,11 +85,27 @@ def admin_make_database_updates(adminconfig: AdminConfig = None, dvmconfig: DVMC
|
||||
if adminconfig.REBROADCAST_NIP89:
|
||||
nip89_announce_tasks(dvmconfig, client=client)
|
||||
|
||||
if adminconfig.REBROADCAST_NIP88:
|
||||
annotier_id = nip88_announce_tier(dvmconfig, client=client)
|
||||
check_and_set_tiereventid_nip88(dvmconfig.IDENTIFIER, adminconfig.INDEX, annotier_id.to_hex())
|
||||
|
||||
if adminconfig.DELETE_NIP89:
|
||||
event_id = adminconfig.EVENTID
|
||||
keys = Keys.parse(
|
||||
adminconfig.PRIVKEY) # Private key from sender of Event (e.g. the key of an nip89 announcement you want to delete)
|
||||
fetch_nip89_paramters_for_deletion(keys, event_id, client, dvmconfig)
|
||||
fetch_nip89_parameters_for_deletion(keys, event_id, client, dvmconfig)
|
||||
|
||||
if adminconfig.DELETE_NIP88:
|
||||
event_id = adminconfig.EVENTID
|
||||
keys = Keys.parse(
|
||||
adminconfig.PRIVKEY) # Private key from sender of Event (e.g. the key of an nip89 announcement you want to delete)
|
||||
fetch_nip88_parameters_for_deletion(keys, event_id, client, dvmconfig)
|
||||
|
||||
if adminconfig.FETCH_NIP88:
|
||||
event_id = adminconfig.EVENTID
|
||||
keys = Keys.parse(
|
||||
adminconfig.PRIVKEY)
|
||||
fetch_nip88_event(keys, event_id, client, dvmconfig)
|
||||
|
||||
if adminconfig.UPDATE_PROFILE:
|
||||
update_profile(dvmconfig, client, lud16=adminconfig.LUD16)
|
||||
update_profile(dvmconfig, client, lud16=dvmconfig.LN_ADDRESS)
|
||||
|
@ -12,7 +12,7 @@ from nostr_dvm.utils.nostr_utils import get_event_by_id, get_referenced_event_by
|
||||
|
||||
def get_task(event, client, dvm_config):
|
||||
try:
|
||||
if event.kind() == EventDefinitions.KIND_NIP90_GENERIC: # use this for events that have no id yet, inclufr j tag
|
||||
if event.kind().as_u64() == EventDefinitions.KIND_NIP90_GENERIC.as_u64(): # use this for events that have no id yet, inclufr j tag
|
||||
for tag in event.tags():
|
||||
if tag.as_vec()[0] == 'j':
|
||||
return tag.as_vec()[1]
|
||||
@ -26,7 +26,7 @@ def get_task(event, client, dvm_config):
|
||||
return "unknown job: " + event.as_json()
|
||||
|
||||
# This looks a bit more complicated, but we do several tasks for text-extraction in the future
|
||||
elif event.kind() == EventDefinitions.KIND_NIP90_EXTRACT_TEXT:
|
||||
elif event.kind().as_u64() == EventDefinitions.KIND_NIP90_EXTRACT_TEXT.as_u64():
|
||||
for tag in event.tags():
|
||||
if tag.as_vec()[0] == "i":
|
||||
if tag.as_vec()[2] == "url":
|
||||
@ -57,7 +57,7 @@ def get_task(event, client, dvm_config):
|
||||
return "unknown type"
|
||||
else:
|
||||
return "unknown job"
|
||||
elif event.kind() == EventDefinitions.KIND_NIP90_GENERATE_IMAGE:
|
||||
elif event.kind().as_u64() == EventDefinitions.KIND_NIP90_GENERATE_IMAGE.as_u64():
|
||||
has_image_tag = False
|
||||
has_text_tag = False
|
||||
for tag in event.tags():
|
||||
@ -92,7 +92,7 @@ def get_task(event, client, dvm_config):
|
||||
else:
|
||||
|
||||
for dvm in dvm_config.SUPPORTED_DVMS:
|
||||
if dvm.KIND == event.kind():
|
||||
if dvm.KIND.as_u64() == event.kind().as_u64():
|
||||
return dvm.TASK
|
||||
except Exception as e:
|
||||
print("Get task: " + str(e))
|
||||
|
@ -7,7 +7,7 @@ from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
from logging import Filter
|
||||
|
||||
from nostr_sdk import Timestamp, Keys, PublicKey, EventBuilder, Filter
|
||||
from nostr_sdk import Timestamp, Keys, PublicKey, EventBuilder, Filter, Kind
|
||||
from nostr_dvm.utils.nostr_utils import send_event
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ class User:
|
||||
nip05: str
|
||||
lud16: str
|
||||
lastactive: int
|
||||
subscribed: int
|
||||
|
||||
|
||||
def create_sql_table(db):
|
||||
@ -40,7 +41,8 @@ def create_sql_table(db):
|
||||
nip05 text,
|
||||
lud16 text,
|
||||
name text,
|
||||
lastactive integer
|
||||
lastactive integer,
|
||||
subscribed integer
|
||||
); """)
|
||||
cur.execute("SELECT name FROM sqlite_master")
|
||||
con.close()
|
||||
@ -53,29 +55,29 @@ def add_sql_table_column(db):
|
||||
try:
|
||||
con = sqlite3.connect(db)
|
||||
cur = con.cursor()
|
||||
cur.execute(""" ALTER TABLE users ADD COLUMN lastactive 'integer' """)
|
||||
cur.execute(""" ALTER TABLE users ADD COLUMN subscribed 'integer' """)
|
||||
con.close()
|
||||
except Error as e:
|
||||
print(e)
|
||||
|
||||
|
||||
def add_to_sql_table(db, npub, sats, iswhitelisted, isblacklisted, nip05, lud16, name, lastactive):
|
||||
def add_to_sql_table(db, npub, sats, iswhitelisted, isblacklisted, nip05, lud16, name, lastactive, subscribed):
|
||||
try:
|
||||
con = sqlite3.connect(db)
|
||||
cur = con.cursor()
|
||||
data = (npub, sats, iswhitelisted, isblacklisted, nip05, lud16, name, lastactive)
|
||||
cur.execute("INSERT or IGNORE INTO users VALUES(?, ?, ?, ?, ?, ?, ?, ?)", data)
|
||||
data = (npub, sats, iswhitelisted, isblacklisted, nip05, lud16, name, lastactive, subscribed)
|
||||
cur.execute("INSERT or IGNORE INTO users VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)", data)
|
||||
con.commit()
|
||||
con.close()
|
||||
except Error as e:
|
||||
print("Error when Adding to DB: " + str(e))
|
||||
|
||||
|
||||
def update_sql_table(db, npub, balance, iswhitelisted, isblacklisted, nip05, lud16, name, lastactive):
|
||||
def update_sql_table(db, npub, balance, iswhitelisted, isblacklisted, nip05, lud16, name, lastactive, subscribed):
|
||||
try:
|
||||
con = sqlite3.connect(db)
|
||||
cur = con.cursor()
|
||||
data = (balance, iswhitelisted, isblacklisted, nip05, lud16, name, lastactive, npub)
|
||||
data = (balance, iswhitelisted, isblacklisted, nip05, lud16, name, lastactive, subscribed, npub)
|
||||
|
||||
cur.execute(""" UPDATE users
|
||||
SET sats = ? ,
|
||||
@ -84,7 +86,8 @@ def update_sql_table(db, npub, balance, iswhitelisted, isblacklisted, nip05, lud
|
||||
nip05 = ? ,
|
||||
lud16 = ? ,
|
||||
name = ? ,
|
||||
lastactive = ?
|
||||
lastactive = ?,
|
||||
subscribed = ?
|
||||
WHERE npub = ?""", data)
|
||||
con.commit()
|
||||
con.close()
|
||||
@ -102,6 +105,12 @@ def get_from_sql_table(db, npub):
|
||||
if row is None:
|
||||
return None
|
||||
else:
|
||||
|
||||
if len(row) < 9:
|
||||
add_sql_table_column(db)
|
||||
# Migrate
|
||||
|
||||
|
||||
user = User
|
||||
user.npub = row[0]
|
||||
user.balance = row[1]
|
||||
@ -111,6 +120,9 @@ def get_from_sql_table(db, npub):
|
||||
user.lud16 = row[5]
|
||||
user.name = row[6]
|
||||
user.lastactive = row[7]
|
||||
user.subscribed = row[8]
|
||||
if user.subscribed is None:
|
||||
user.subscribed = 0
|
||||
|
||||
return user
|
||||
|
||||
@ -162,28 +174,57 @@ def update_user_balance(db, npub, additional_sats, client, config):
|
||||
if user is None:
|
||||
name, nip05, lud16 = fetch_user_metadata(npub, client)
|
||||
add_to_sql_table(db, npub, (int(additional_sats) + config.NEW_USER_BALANCE), False, False,
|
||||
nip05, lud16, name, Timestamp.now().as_secs())
|
||||
nip05, lud16, name, Timestamp.now().as_secs(), 0)
|
||||
print("Adding User: " + npub + " (" + npub + ")")
|
||||
else:
|
||||
user = get_from_sql_table(db, npub)
|
||||
new_balance = int(user.balance) + int(additional_sats)
|
||||
update_sql_table(db, npub, new_balance, user.iswhitelisted, user.isblacklisted, user.nip05, user.lud16,
|
||||
user.name,
|
||||
Timestamp.now().as_secs())
|
||||
Timestamp.now().as_secs(), user.subscribed)
|
||||
print("Updated user balance for: " + str(user.name) +
|
||||
" Zap amount: " + str(additional_sats) + " Sats. New balance: " + str(new_balance) +" Sats")
|
||||
" Zap amount: " + str(additional_sats) + " Sats. New balance: " + str(new_balance) + " Sats")
|
||||
|
||||
if config is not None:
|
||||
keys = Keys.parse(config.PRIVATE_KEY)
|
||||
#time.sleep(1.0)
|
||||
# time.sleep(1.0)
|
||||
|
||||
message = ("Added " + str(additional_sats) + " Sats to balance. New balance is " + str(new_balance) + " Sats.")
|
||||
message = ("Added " + str(additional_sats) + " Sats to balance. New balance is " + str(
|
||||
new_balance) + " Sats.")
|
||||
|
||||
evt = EventBuilder.encrypted_direct_msg(keys, PublicKey.from_hex(npub), message,
|
||||
None).to_event(keys)
|
||||
None).to_event(keys)
|
||||
send_event(evt, client=client, dvm_config=config)
|
||||
|
||||
|
||||
def update_user_subscription(npub, subscribed_until, client, dvm_config):
|
||||
user = get_from_sql_table(dvm_config.DB, npub)
|
||||
if user is None:
|
||||
name, nip05, lud16 = fetch_user_metadata(npub, client)
|
||||
add_to_sql_table(dvm_config.DB, npub, dvm_config.NEW_USER_BALANCE, False, False,
|
||||
nip05, lud16, name, Timestamp.now().as_secs(), 0)
|
||||
print("Adding User: " + npub + " (" + npub + ")")
|
||||
else:
|
||||
user = get_from_sql_table(dvm_config.DB, npub)
|
||||
|
||||
update_sql_table(dvm_config.DB, npub, user.balance, user.iswhitelisted, user.isblacklisted, user.nip05,
|
||||
user.lud16,
|
||||
user.name,
|
||||
Timestamp.now().as_secs(), subscribed_until)
|
||||
print("Updated user subscription for: " + str(user.name))
|
||||
|
||||
if dvm_config is not None:
|
||||
keys = Keys.parse(dvm_config.PRIVATE_KEY)
|
||||
# time.sleep(1.0)
|
||||
|
||||
message = ("Subscribed to DVM " + dvm_config.NIP89.NAME + " until: " + str(
|
||||
Timestamp.from_secs(subscribed_until).to_human_datetime().replace("Z", " ").replace("T", " ")))
|
||||
|
||||
evt = EventBuilder.encrypted_direct_msg(keys, PublicKey.from_hex(npub), message,
|
||||
None).to_event(keys)
|
||||
send_event(evt, client=client, dvm_config=dvm_config)
|
||||
|
||||
|
||||
def get_or_add_user(db, npub, client, config, update=False):
|
||||
user = get_from_sql_table(db, npub)
|
||||
if user is None:
|
||||
@ -191,7 +232,7 @@ def get_or_add_user(db, npub, client, config, update=False):
|
||||
name, nip05, lud16 = fetch_user_metadata(npub, client)
|
||||
print("Adding User: " + npub + " (" + npub + ")")
|
||||
add_to_sql_table(db, npub, config.NEW_USER_BALANCE, False, False, nip05,
|
||||
lud16, name, Timestamp.now().as_secs())
|
||||
lud16, name, Timestamp.now().as_secs(), 0)
|
||||
user = get_from_sql_table(db, npub)
|
||||
return user
|
||||
except Exception as e:
|
||||
@ -201,7 +242,7 @@ def get_or_add_user(db, npub, client, config, update=False):
|
||||
name, nip05, lud16 = fetch_user_metadata(npub, client)
|
||||
print("Updating User: " + npub + " (" + npub + ")")
|
||||
update_sql_table(db, user.npub, user.balance, user.iswhitelisted, user.isblacklisted, nip05,
|
||||
lud16, name, Timestamp.now().as_secs())
|
||||
lud16, name, Timestamp.now().as_secs(), user.subscribed)
|
||||
user = get_from_sql_table(db, npub)
|
||||
return user
|
||||
except Exception as e:
|
||||
@ -214,9 +255,9 @@ def fetch_user_metadata(npub, client):
|
||||
name = ""
|
||||
nip05 = ""
|
||||
lud16 = ""
|
||||
pk = PublicKey.from_hex(npub)
|
||||
pk = PublicKey.parse(npub)
|
||||
print(f"\nGetting profile metadata for {pk.to_bech32()}...")
|
||||
profile_filter = Filter().kind(0).author(pk).limit(1)
|
||||
profile_filter = Filter().kind(Kind(0)).author(pk).limit(1)
|
||||
events = client.get_events_of([profile_filter], timedelta(seconds=1))
|
||||
if len(events) > 0:
|
||||
latest_entry = events[0]
|
||||
|
@ -1,41 +1,49 @@
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
|
||||
from nostr_sdk import Event
|
||||
from nostr_sdk import Event, Kind
|
||||
|
||||
|
||||
class EventDefinitions:
|
||||
KIND_DM = 4
|
||||
KIND_ZAP = 9735
|
||||
KIND_ANNOUNCEMENT = 31990
|
||||
KIND_NIP94_METADATA = 1063
|
||||
KIND_FEEDBACK = 7000
|
||||
KIND_NIP90_EXTRACT_TEXT = 5000
|
||||
KIND_NIP90_RESULT_EXTRACT_TEXT = KIND_NIP90_EXTRACT_TEXT + 1000
|
||||
KIND_NIP90_SUMMARIZE_TEXT = 5001
|
||||
KIND_NIP90_RESULT_SUMMARIZE_TEXT = KIND_NIP90_SUMMARIZE_TEXT + 1000
|
||||
KIND_NIP90_TRANSLATE_TEXT = 5002
|
||||
KIND_NIP90_RESULT_TRANSLATE_TEXT = KIND_NIP90_TRANSLATE_TEXT + 1000
|
||||
KIND_NIP90_GENERATE_TEXT = 5050
|
||||
KIND_NIP90_RESULT_GENERATE_TEXT = KIND_NIP90_GENERATE_TEXT + 1000
|
||||
KIND_NIP90_GENERATE_IMAGE = 5100
|
||||
KIND_NIP90_RESULT_GENERATE_IMAGE = KIND_NIP90_GENERATE_IMAGE + 1000
|
||||
KIND_NIP90_CONVERT_VIDEO = 5200
|
||||
KIND_NIP90_RESULT_CONVERT_VIDEO = KIND_NIP90_CONVERT_VIDEO + 1000
|
||||
KIND_NIP90_GENERATE_VIDEO = 5202
|
||||
KIND_NIP90_RESULT_GENERATE_VIDEO = KIND_NIP90_GENERATE_VIDEO + 1000
|
||||
KIND_NIP90_TEXT_TO_SPEECH = 5250
|
||||
KIND_NIP90_RESULT_TEXT_TO_SPEECH = KIND_NIP90_TEXT_TO_SPEECH + 1000
|
||||
KIND_NIP90_CONTENT_DISCOVERY = 5300
|
||||
KIND_NIP90_RESULT_CONTENT_DISCOVERY = KIND_NIP90_CONTENT_DISCOVERY + 1000
|
||||
KIND_NIP90_PEOPLE_DISCOVERY = 5301
|
||||
KIND_NIP90_RESULT_PEOPLE_DISCOVERY = KIND_NIP90_PEOPLE_DISCOVERY + 1000
|
||||
KIND_NIP90_CONTENT_SEARCH = 5302
|
||||
KIND_NIP90_RESULTS_CONTENT_SEARCH = KIND_NIP90_CONTENT_SEARCH + 1000
|
||||
KIND_NIP90_USER_SEARCH = 5303
|
||||
KIND_NIP90_RESULTS_USER_SEARCH = KIND_NIP90_USER_SEARCH + 1000
|
||||
KIND_NIP90_GENERIC = 5999
|
||||
KIND_NIP90_RESULT_GENERIC = KIND_NIP90_GENERIC + 1000
|
||||
KIND_NOTE = Kind(1)
|
||||
KIND_DM = Kind(4)
|
||||
KIND_REACTION = Kind(7)
|
||||
KIND_ZAP = Kind(9735)
|
||||
KIND_ANNOUNCEMENT = Kind(31990)
|
||||
KIND_NIP94_METADATA = Kind(1063)
|
||||
KIND_FEEDBACK = Kind(7000)
|
||||
KIND_NIP90_EXTRACT_TEXT = Kind(5000)
|
||||
KIND_NIP90_RESULT_EXTRACT_TEXT = Kind(6000)
|
||||
KIND_NIP90_SUMMARIZE_TEXT = Kind(5001)
|
||||
KIND_NIP90_RESULT_SUMMARIZE_TEXT = Kind(6001)
|
||||
KIND_NIP90_TRANSLATE_TEXT = Kind(5002)
|
||||
KIND_NIP90_RESULT_TRANSLATE_TEXT = Kind(6002)
|
||||
KIND_NIP90_GENERATE_TEXT = Kind(5050)
|
||||
KIND_NIP90_RESULT_GENERATE_TEXT = Kind(6050)
|
||||
KIND_NIP90_GENERATE_IMAGE = Kind(5100)
|
||||
KIND_NIP90_RESULT_GENERATE_IMAGE = Kind(6100)
|
||||
KIND_NIP90_CONVERT_VIDEO = Kind(5200)
|
||||
KIND_NIP90_RESULT_CONVERT_VIDEO = Kind(6200)
|
||||
KIND_NIP90_GENERATE_VIDEO = Kind(5202)
|
||||
KIND_NIP90_RESULT_GENERATE_VIDEO =Kind(6202)
|
||||
KIND_NIP90_TEXT_TO_SPEECH = Kind(5250)
|
||||
KIND_NIP90_RESULT_TEXT_TO_SPEECH = Kind(5650)
|
||||
KIND_NIP90_CONTENT_DISCOVERY = Kind(5300)
|
||||
KIND_NIP90_RESULT_CONTENT_DISCOVERY = Kind(6300)
|
||||
KIND_NIP90_PEOPLE_DISCOVERY = Kind(5301)
|
||||
KIND_NIP90_RESULT_PEOPLE_DISCOVERY = Kind(6301)
|
||||
KIND_NIP90_CONTENT_SEARCH = Kind(5302)
|
||||
KIND_NIP90_RESULTS_CONTENT_SEARCH = Kind(6302)
|
||||
KIND_NIP90_USER_SEARCH = Kind(5303)
|
||||
KIND_NIP90_RESULTS_USER_SEARCH = Kind(6303)
|
||||
KIND_NIP90_GENERIC = Kind(5999)
|
||||
KIND_NIP90_RESULT_GENERIC = Kind(6999)
|
||||
|
||||
KIND_NIP88_SUBSCRIBE_EVENT = Kind(7001)
|
||||
KIND_NIP88_STOP_SUBSCRIPTION_EVENT = Kind(7002)
|
||||
KIND_NIP88_PAYMENT_RECIPE = Kind(7003)
|
||||
KIND_NIP88_TIER_EVENT = Kind(37001)
|
||||
|
||||
ANY_RESULT = [KIND_NIP90_RESULT_EXTRACT_TEXT,
|
||||
KIND_NIP90_RESULT_SUMMARIZE_TEXT,
|
||||
KIND_NIP90_RESULT_TRANSLATE_TEXT,
|
||||
|
@ -2,6 +2,7 @@ import os
|
||||
|
||||
from nostr_sdk import Keys
|
||||
|
||||
from nostr_dvm.utils.nip88_utils import NIP88Config
|
||||
from nostr_dvm.utils.nip89_utils import NIP89Config
|
||||
from nostr_dvm.utils.nostr_utils import check_and_set_private_key
|
||||
from nostr_dvm.utils.output_utils import PostProcessFunctionType
|
||||
@ -32,12 +33,12 @@ class DVMConfig:
|
||||
USE_OWN_VENV = True # Make an own venv for each dvm's process function.Disable if you want to install packages into main venv. Only recommended if you dont want to run dvms with different dependency versions
|
||||
DB: str
|
||||
NEW_USER_BALANCE: int = 0 # Free credits for new users
|
||||
NIP88: NIP88Config
|
||||
NIP89: NIP89Config
|
||||
SEND_FEEDBACK_EVENTS = True
|
||||
SHOW_RESULT_BEFORE_PAYMENT: bool = False # if this is true show results even when not paid right after autoprocess
|
||||
SCHEDULE_UPDATES_SECONDS = 0
|
||||
|
||||
|
||||
def build_default_config(identifier):
|
||||
dvm_config = DVMConfig()
|
||||
dvm_config.PRIVATE_KEY = check_and_set_private_key(identifier)
|
||||
|
211
nostr_dvm/utils/nip88_utils.py
Normal file
211
nostr_dvm/utils/nip88_utils.py
Normal file
@ -0,0 +1,211 @@
|
||||
import os
|
||||
from datetime import timedelta
|
||||
from hashlib import sha256
|
||||
from pathlib import Path
|
||||
|
||||
import dotenv
|
||||
from nostr_sdk import Filter, Tag, Keys, EventBuilder, Client, EventId, PublicKey, Event, Timestamp, SingleLetterTag, \
|
||||
Alphabet
|
||||
from nostr_sdk.nostr_sdk import Duration
|
||||
|
||||
from nostr_dvm.utils import definitions
|
||||
from nostr_dvm.utils.definitions import EventDefinitions
|
||||
from nostr_dvm.utils.nostr_utils import send_event
|
||||
|
||||
|
||||
class NIP88Config:
|
||||
DTAG: str = ""
|
||||
TITLE: str = ""
|
||||
CONTENT: str = ""
|
||||
IMAGE: str = ""
|
||||
TIER_EVENT: str = ""
|
||||
PERK1DESC: str = ""
|
||||
PERK2DESC: str = ""
|
||||
PERK3DESC: str = ""
|
||||
PERK4DESC: str = ""
|
||||
PAYMENT_VERIFIER_PUBKEY: str = ""
|
||||
|
||||
AMOUNT_DAILY: int = None
|
||||
AMOUNT_MONTHLY: int = None
|
||||
AMOUNT_YEARLY: int = None
|
||||
|
||||
|
||||
def nip88_create_d_tag(name, pubkey, image):
|
||||
key_str = str(name + image + pubkey)
|
||||
d_tag = sha256(key_str.encode('utf-8')).hexdigest()[:16]
|
||||
return d_tag
|
||||
|
||||
|
||||
def fetch_nip88_parameters_for_deletion(keys, eventid, client, dvmconfig):
|
||||
idfilter = Filter().id(EventId.from_hex(eventid)).limit(1)
|
||||
nip88events = client.get_events_of([idfilter], timedelta(seconds=dvmconfig.RELAY_TIMEOUT))
|
||||
d_tag = ""
|
||||
if len(nip88events) == 0:
|
||||
print("Event not found. Potentially gone.")
|
||||
|
||||
for event in nip88events:
|
||||
print(event.as_json())
|
||||
for tag in event.tags():
|
||||
if tag.as_vec()[0] == "d":
|
||||
d_tag = tag.as_vec()[1]
|
||||
if d_tag == "":
|
||||
print("No dtag found")
|
||||
return
|
||||
|
||||
if event.author().to_hex() == keys.public_key().to_hex():
|
||||
nip88_delete_announcement(event.id().to_hex(), keys, d_tag, client, dvmconfig)
|
||||
print("NIP88 announcement deleted from known relays!")
|
||||
else:
|
||||
print("Privatekey does not belong to event")
|
||||
|
||||
|
||||
def fetch_nip88_event(keys, eventid, client, dvmconfig):
|
||||
idfilter = Filter().id(EventId.parse(eventid)).limit(1)
|
||||
nip88events = client.get_events_of([idfilter], timedelta(seconds=dvmconfig.RELAY_TIMEOUT))
|
||||
d_tag = ""
|
||||
if len(nip88events) == 0:
|
||||
print("Event not found. Potentially gone.")
|
||||
|
||||
for event in nip88events:
|
||||
|
||||
for tag in event.tags():
|
||||
if tag.as_vec()[0] == "d":
|
||||
d_tag = tag.as_vec()[1]
|
||||
if d_tag == "":
|
||||
print("No dtag found")
|
||||
return
|
||||
|
||||
if event.author().to_hex() == keys.public_key().to_hex():
|
||||
print(event.as_json())
|
||||
else:
|
||||
print("Privatekey does not belong to event")
|
||||
|
||||
|
||||
def nip88_delete_announcement(eid: str, keys: Keys, dtag: str, client: Client, config):
|
||||
e_tag = Tag.parse(["e", eid])
|
||||
a_tag = Tag.parse(
|
||||
["a", str(EventDefinitions.KIND_NIP88_TIER_EVENT) + ":" + keys.public_key().to_hex() + ":" + dtag])
|
||||
event = EventBuilder(5, "", [e_tag, a_tag]).to_event(keys)
|
||||
send_event(event, client, config)
|
||||
|
||||
|
||||
def nip88_has_active_subscription(user: PublicKey, tiereventdtag, client: Client, receiver_public_key_hex):
|
||||
subscription_status = {
|
||||
"isActive": False,
|
||||
"validUntil": 0,
|
||||
"subscriptionId": "",
|
||||
"expires": False,
|
||||
}
|
||||
|
||||
subscriptionfilter = Filter().kind(definitions.EventDefinitions.KIND_NIP88_PAYMENT_RECIPE).pubkey(
|
||||
PublicKey.parse(receiver_public_key_hex)).custom_tag(SingleLetterTag.uppercase(Alphabet.P),
|
||||
[user.to_hex()]).limit(1)
|
||||
evts = client.get_events_of([subscriptionfilter], timedelta(seconds=5))
|
||||
if len(evts) > 0:
|
||||
print(evts[0].as_json())
|
||||
matchesdtag = False
|
||||
for tag in evts[0].tags():
|
||||
if tag.as_vec()[0] == "valid":
|
||||
subscription_status["validUntil"] = int(tag.as_vec()[2])
|
||||
elif tag.as_vec()[0] == "e":
|
||||
subscription_status["subscriptionId"] = tag.as_vec()[1]
|
||||
elif tag.as_vec()[0] == "tier":
|
||||
if tag.as_vec()[1] == tiereventdtag:
|
||||
matchesdtag = True
|
||||
|
||||
if (subscription_status["validUntil"] > Timestamp.now().as_secs()) & matchesdtag:
|
||||
subscription_status["isActive"] = True
|
||||
|
||||
if subscription_status["isActive"]:
|
||||
# if subscription seems active, check if it has been canceled, and if so mark it as expiring.
|
||||
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
|
||||
|
||||
|
||||
def nip88_announce_tier(dvm_config, client):
|
||||
title_tag = Tag.parse(["title", str(dvm_config.NIP88.TITLE)])
|
||||
image_tag = Tag.parse(["image", str(dvm_config.NIP88.IMAGE)])
|
||||
d_tag = Tag.parse(["d", dvm_config.NIP88.DTAG])
|
||||
|
||||
# may todo
|
||||
zaptag1 = Tag.parse(["zap", dvm_config.PUBLIC_KEY, "wss://damus.io", "19"])
|
||||
zaptag2 = Tag.parse(["zap", "", "wss://damus.io", "1"])
|
||||
p_tag = Tag.parse(["p", dvm_config.NIP88.PAYMENT_VERIFIER_PUBKEY])
|
||||
|
||||
tags = [title_tag, image_tag, zaptag1, zaptag2, d_tag, p_tag]
|
||||
|
||||
if dvm_config.NIP88.AMOUNT_DAILY is not None:
|
||||
amount_tag = Tag.parse(["amount", str(dvm_config.NIP88.AMOUNT_DAILY * 1000), "msats", "daily"])
|
||||
tags.append(amount_tag)
|
||||
|
||||
if dvm_config.NIP88.AMOUNT_MONTHLY is not None:
|
||||
amount_tag = Tag.parse(["amount", str(dvm_config.NIP88.AMOUNT_MONTHLY * 1000), "msats", "monthly"])
|
||||
tags.append(amount_tag)
|
||||
|
||||
if dvm_config.NIP88.AMOUNT_YEARLY is not None:
|
||||
amount_tag = Tag.parse(["amount", str(dvm_config.NIP88.AMOUNT_YEARLY * 1000), "msats", "yearly"])
|
||||
tags.append(amount_tag)
|
||||
|
||||
if dvm_config.NIP88.PERK1DESC != "":
|
||||
perk_tag = Tag.parse(["perk", str(dvm_config.NIP88.PERK1DESC)])
|
||||
tags.append(perk_tag)
|
||||
if dvm_config.NIP88.PERK2DESC != "":
|
||||
perk_tag = Tag.parse(["perk", str(dvm_config.NIP88.PERK2DESC)])
|
||||
tags.append(perk_tag)
|
||||
if dvm_config.NIP88.PERK3DESC != "":
|
||||
perk_tag = Tag.parse(["perk", str(dvm_config.NIP88.PERK3DESC)])
|
||||
tags.append(perk_tag)
|
||||
if dvm_config.NIP88.PERK4DESC != "":
|
||||
perk_tag = Tag.parse(["perk", str(dvm_config.NIP88.PERK4DESC)])
|
||||
tags.append(perk_tag)
|
||||
|
||||
keys = Keys.parse(dvm_config.NIP89.PK)
|
||||
content = dvm_config.NIP88.CONTENT
|
||||
event = EventBuilder(EventDefinitions.KIND_NIP88_TIER_EVENT, content, tags).to_event(keys)
|
||||
annotier_id = send_event(event, client=client, dvm_config=dvm_config)
|
||||
|
||||
print("Announced NIP 88 Tier for " + dvm_config.NIP89.NAME)
|
||||
return annotier_id
|
||||
|
||||
# Relay and payment-verification
|
||||
|
||||
|
||||
# ["r", "wss://my-subscribers-only-relay.com"],
|
||||
# ["p", "<payment-verifier-pubkey>"],
|
||||
|
||||
def check_and_set_d_tag_nip88(identifier, name, pk, imageurl):
|
||||
if not os.getenv("NIP88_DTAG_" + identifier.upper()):
|
||||
new_dtag = nip88_create_d_tag(name, Keys.parse(pk).public_key().to_hex(),
|
||||
imageurl)
|
||||
nip88_add_dtag_to_env_file("NIP88_DTAG_" + identifier.upper(), new_dtag)
|
||||
print("Some new dtag:" + new_dtag)
|
||||
return new_dtag
|
||||
else:
|
||||
return os.getenv("NIP88_DTAG_" + identifier.upper())
|
||||
|
||||
|
||||
def check_and_set_tiereventid_nip88(identifier, index="1", eventid=None):
|
||||
if eventid is None:
|
||||
if not os.getenv("NIP88_TIEREVENT_" + index + identifier.upper()):
|
||||
print("No Tier Event ID set")
|
||||
return None
|
||||
else:
|
||||
return os.getenv("NIP88_TIEREVENT_" + index + identifier.upper())
|
||||
else:
|
||||
nip88_add_dtag_to_env_file("NIP88_TIEREVENT_" + index + identifier.upper(), eventid)
|
||||
return eventid
|
||||
|
||||
|
||||
def nip88_add_dtag_to_env_file(dtag, oskey):
|
||||
env_path = Path('.env')
|
||||
if env_path.is_file():
|
||||
print(f'loading environment from {env_path.resolve()}')
|
||||
dotenv.load_dotenv(env_path, verbose=True, override=True)
|
||||
dotenv.set_key(env_path, dtag, oskey)
|
@ -25,7 +25,7 @@ def nip89_create_d_tag(name, pubkey, image):
|
||||
|
||||
|
||||
def nip89_announce_tasks(dvm_config, client):
|
||||
k_tag = Tag.parse(["k", str(dvm_config.NIP89.KIND)])
|
||||
k_tag = Tag.parse(["k", str(dvm_config.NIP89.KIND.as_u64())])
|
||||
d_tag = Tag.parse(["d", dvm_config.NIP89.DTAG])
|
||||
keys = Keys.parse(dvm_config.NIP89.PK)
|
||||
content = dvm_config.NIP89.CONTENT
|
||||
@ -34,7 +34,7 @@ def nip89_announce_tasks(dvm_config, client):
|
||||
print("Announced NIP 89 for " + dvm_config.NIP89.NAME)
|
||||
|
||||
|
||||
def fetch_nip89_paramters_for_deletion(keys, eventid, client, dvmconfig):
|
||||
def fetch_nip89_parameters_for_deletion(keys, eventid, client, dvmconfig):
|
||||
idfilter = Filter().id(EventId.from_hex(eventid)).limit(1)
|
||||
nip89events = client.get_events_of([idfilter], timedelta(seconds=dvmconfig.RELAY_TIMEOUT))
|
||||
d_tag = ""
|
||||
|
@ -1,44 +1,84 @@
|
||||
import json
|
||||
import os
|
||||
from datetime import timedelta
|
||||
|
||||
import requests
|
||||
from nostr_sdk import Keys, PublicKey, Client, nip04_encrypt, EventBuilder, Tag, NostrSigner
|
||||
from nostr_sdk import Keys, PublicKey, Client, nip04_encrypt, EventBuilder, Tag, NostrSigner, Filter, Timestamp, \
|
||||
NostrWalletConnectUri, Nwc
|
||||
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig
|
||||
from nostr_dvm.utils.nostr_utils import check_and_set_private_key
|
||||
from nostr_dvm.utils.zap_utils import zaprequest
|
||||
|
||||
|
||||
def nwc_zap(connectionstr, bolt11, keys):
|
||||
target_pubkey, relay, secret = parse_connection_str(connectionstr)
|
||||
SecretSK = Keys.parse(secret)
|
||||
def nwc_zap(connectionstr, bolt11, keys, externalrelay=None):
|
||||
uri = NostrWalletConnectUri.parse(connectionstr)
|
||||
|
||||
content = {
|
||||
"method": "pay_invoice",
|
||||
"params": {
|
||||
"invoice": bolt11
|
||||
}
|
||||
}
|
||||
# Initialize NWC client
|
||||
nwc = Nwc(uri)
|
||||
|
||||
signer = NostrSigner.keys(keys)
|
||||
client = Client(signer)
|
||||
client.add_relay(relay)
|
||||
client.connect()
|
||||
info = nwc.get_info()
|
||||
print(info)
|
||||
|
||||
client_public_key = PublicKey.from_hex(target_pubkey)
|
||||
encrypted_content = nip04_encrypt(SecretSK.secret_key(), client_public_key, json.dumps(content))
|
||||
balance = nwc.get_balance()
|
||||
print(f"Balance: {balance} SAT")
|
||||
|
||||
pTag = Tag.parse(["p", client_public_key.to_hex()])
|
||||
event = EventBuilder(23194, encrypted_content,
|
||||
[pTag]).to_event(keys)
|
||||
event_id = nwc.pay_invoice(bolt11)
|
||||
print("NWC event: " + event_id)
|
||||
|
||||
event_id = client.send_event(event)
|
||||
print(event_id.to_hex())
|
||||
|
||||
#target_pubkey, relay, secret = parse_connection_str(connectionstr)
|
||||
#print(target_pubkey)
|
||||
#print(relay)
|
||||
#print(secret)
|
||||
#SecretSK = Keys.parse(secret)
|
||||
|
||||
#content = {
|
||||
# "method": "pay_invoice",
|
||||
# "params": {
|
||||
# "invoice": bolt11
|
||||
# }
|
||||
#}
|
||||
|
||||
#signer = NostrSigner.keys(keys)
|
||||
#client = Client(signer)
|
||||
#client.add_relay(relay)
|
||||
#if externalrelay is not None:
|
||||
# client.add_relay(externalrelay)
|
||||
|
||||
#client.connect()
|
||||
|
||||
#client_public_key = PublicKey.from_hex(target_pubkey)
|
||||
#encrypted_content = nip04_encrypt(SecretSK.secret_key(), client_public_key, json.dumps(content))
|
||||
|
||||
#pTag = Tag.parse(["p", client_public_key.to_hex()])
|
||||
|
||||
#event = EventBuilder(23194, encrypted_content,
|
||||
# [pTag]).to_event(keys)
|
||||
|
||||
#ts = Timestamp.now()
|
||||
#event_id = client.send_event(event)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#nwc_response_filter = Filter().kind(23195).since(ts)
|
||||
#events = client.get_events_of([nwc_response_filter], timedelta(seconds=5))
|
||||
|
||||
#if len(events) > 0:
|
||||
# for evt in events:
|
||||
# print(evt.as_json())
|
||||
#else:
|
||||
# print("No response found")
|
||||
|
||||
return event_id
|
||||
|
||||
|
||||
def parse_connection_str(connectionstring):
|
||||
split = connectionstring.split("?")
|
||||
targetpubkey = split[0].split(":")[1]
|
||||
targetpubkey = split[0].split(":")[1].replace("//", "")
|
||||
split2 = split[1].split("&")
|
||||
relay = split2[0].split("=")[1]
|
||||
relay = relay.replace("%3A%2F%2F", "://")
|
||||
|
@ -206,6 +206,12 @@ def build_status_reaction(status, task, amount, content, dvm_config):
|
||||
amount) + " Sats. "
|
||||
reaction = alt_description + emoji.emojize(":orange_heart:")
|
||||
|
||||
elif status == "subscription-required":
|
||||
alt_description = "NIP90 DVM AI task " + task + " requires payment for subscription"
|
||||
reaction = alt_description + emoji.emojize(":orange_heart:")
|
||||
|
||||
|
||||
|
||||
elif status == "payment-rejected":
|
||||
alt_description = "NIP90 DVM AI task " + task + " payment is below required amount of " + str(
|
||||
amount) + " Sats. "
|
||||
|
181
nostr_dvm/utils/subscription_utils.py
Normal file
181
nostr_dvm/utils/subscription_utils.py
Normal file
@ -0,0 +1,181 @@
|
||||
import sqlite3
|
||||
from dataclasses import dataclass
|
||||
from sqlite3 import Error
|
||||
|
||||
|
||||
@dataclass
|
||||
class Subscription:
|
||||
id: str
|
||||
recipent: str
|
||||
subscriber: str
|
||||
nwc: str
|
||||
cadence: str
|
||||
amount: int
|
||||
begin: int
|
||||
end: int
|
||||
tier_dtag: str
|
||||
zaps: str
|
||||
recipe: str
|
||||
active: bool
|
||||
lastupdate: int
|
||||
|
||||
|
||||
def create_subscription_sql_table(db):
|
||||
try:
|
||||
import os
|
||||
if not os.path.exists(r'db'):
|
||||
os.makedirs(r'db')
|
||||
if not os.path.exists(r'outputs'):
|
||||
os.makedirs(r'outputs')
|
||||
con = sqlite3.connect(db)
|
||||
cur = con.cursor()
|
||||
cur.execute(""" CREATE TABLE IF NOT EXISTS subscriptions (
|
||||
id text PRIMARY KEY,
|
||||
recipient text,
|
||||
subscriber text,
|
||||
nwc text NOT NULL,
|
||||
cadence text,
|
||||
amount int,
|
||||
begin int,
|
||||
end int,
|
||||
tier_dtag text,
|
||||
zaps text,
|
||||
recipe text,
|
||||
active boolean,
|
||||
lastupdate int
|
||||
|
||||
|
||||
); """)
|
||||
cur.execute("SELECT name FROM sqlite_master")
|
||||
con.close()
|
||||
|
||||
except Error as e:
|
||||
print(e)
|
||||
|
||||
|
||||
def add_to_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 = (id, recipient, subscriber, nwc, cadence, amount, begin, end, tier_dtag, zaps, recipe, active, lastupdate)
|
||||
print(id)
|
||||
print(recipient)
|
||||
print(subscriber)
|
||||
print(nwc)
|
||||
cur.execute("INSERT or IGNORE INTO subscriptions VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", data)
|
||||
con.commit()
|
||||
con.close()
|
||||
except Error as e:
|
||||
print("Error when Adding to DB: " + str(e))
|
||||
|
||||
|
||||
def get_from_subscription_sql_table(db, id):
|
||||
try:
|
||||
con = sqlite3.connect(db)
|
||||
cur = con.cursor()
|
||||
cur.execute("SELECT * FROM subscriptions WHERE id=?", (id,))
|
||||
row = cur.fetchone()
|
||||
con.close()
|
||||
if row is None:
|
||||
return None
|
||||
else:
|
||||
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]
|
||||
|
||||
return subscription
|
||||
|
||||
except Error as e:
|
||||
print("Error Getting from DB: " + str(e))
|
||||
return None
|
||||
|
||||
|
||||
def get_all_subscriptions_from_sql_table(db):
|
||||
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:
|
||||
con = sqlite3.connect(db)
|
||||
cur = con.cursor()
|
||||
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
|
||||
SET recipient = ? ,
|
||||
subscriber = ? ,
|
||||
nwc = ? ,
|
||||
cadence = ? ,
|
||||
amount = ? ,
|
||||
begin = ? ,
|
||||
end = ?,
|
||||
tier_dtag = ?,
|
||||
zaps = ?,
|
||||
recipe = ?,
|
||||
active = ?,
|
||||
lastupdate = ?
|
||||
|
||||
WHERE id = ?""", data)
|
||||
con.commit()
|
||||
con.close()
|
||||
except Error as e:
|
||||
print("Error Updating DB: " + str(e))
|
||||
|
||||
|
||||
|
||||
|
@ -8,7 +8,7 @@ import requests
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util.Padding import pad
|
||||
from bech32 import bech32_decode, convertbits, bech32_encode
|
||||
from nostr_sdk import nostr_sdk, PublicKey, SecretKey, Event, EventBuilder, Tag, Keys, generate_shared_key
|
||||
from nostr_sdk import nostr_sdk, PublicKey, SecretKey, Event, EventBuilder, Tag, Keys, generate_shared_key, Kind
|
||||
|
||||
from nostr_dvm.utils.nostr_utils import get_event_by_id, check_and_decrypt_own_tags
|
||||
import lnurl
|
||||
@ -50,7 +50,7 @@ def parse_zap_event_tags(zap_event, keys, name, client, config):
|
||||
keys.secret_key(),
|
||||
zap_request_event.author())
|
||||
decrypted_private_event = Event.from_json(decrypted_content)
|
||||
if decrypted_private_event.kind() == 9733:
|
||||
if decrypted_private_event.kind().as_u64() == 9733:
|
||||
sender = decrypted_private_event.author().to_hex()
|
||||
message = decrypted_private_event.content()
|
||||
# if message != "":
|
||||
@ -132,7 +132,7 @@ def create_bolt11_lud16(lud16, amount):
|
||||
def create_lnbits_account(name):
|
||||
if os.getenv("LNBITS_ADMIN_ID") is None or os.getenv("LNBITS_ADMIN_ID") == "":
|
||||
print("No admin id set, no wallet created.")
|
||||
return "","","","", "failed"
|
||||
return "", "", "", "", "failed"
|
||||
data = {
|
||||
'admin_id': os.getenv("LNBITS_ADMIN_ID"),
|
||||
'wallet_name': name,
|
||||
@ -240,6 +240,10 @@ def decrypt_private_zap_message(msg: str, privkey: SecretKey, pubkey: PublicKey)
|
||||
|
||||
|
||||
def zaprequest(lud16: str, amount: int, content, zapped_event, zapped_user, keys, relay_list, zaptype="public"):
|
||||
print(lud16)
|
||||
print(str(amount))
|
||||
print(content)
|
||||
print(zapped_user.to_hex())
|
||||
if lud16.startswith("LNURL") or lud16.startswith("lnurl"):
|
||||
url = lnurl.decode(lud16)
|
||||
elif '@' in lud16: # LNaddress
|
||||
@ -250,6 +254,7 @@ def zaprequest(lud16: str, amount: int, content, zapped_event, zapped_user, keys
|
||||
response = requests.get(url)
|
||||
ob = json.loads(response.content)
|
||||
callback = ob["callback"]
|
||||
print(ob["callback"])
|
||||
encoded_lnurl = lnurl.encode(url)
|
||||
amount_tag = Tag.parse(['amount', str(amount * 1000)])
|
||||
relays_tag = Tag.parse(['relays', str(relay_list)])
|
||||
@ -262,12 +267,11 @@ def zaprequest(lud16: str, amount: int, content, zapped_event, zapped_user, keys
|
||||
p_tag = Tag.parse(['p', zapped_user.to_hex()])
|
||||
tags = [amount_tag, relays_tag, p_tag, lnurl_tag]
|
||||
|
||||
|
||||
if zaptype == "private":
|
||||
key_str = keys.secret_key().to_hex() + zapped_event.id().to_hex() + str(zapped_event.created_at().as_secs())
|
||||
encryption_key = sha256(key_str.encode('utf-8')).hexdigest()
|
||||
|
||||
zap_request = EventBuilder(9733, content,
|
||||
zap_request = EventBuilder(Kind(9733), content,
|
||||
[p_tag, e_tag]).to_event(keys).as_json()
|
||||
keys = Keys.parse(encryption_key)
|
||||
encrypted_content = enrypt_private_zap_message(zap_request, keys.secret_key(), zapped_event.author())
|
||||
@ -275,7 +279,7 @@ def zaprequest(lud16: str, amount: int, content, zapped_event, zapped_user, keys
|
||||
tags.append(anon_tag)
|
||||
content = ""
|
||||
|
||||
zap_request = EventBuilder(9734, content,
|
||||
zap_request = EventBuilder(Kind(9734), content,
|
||||
tags).to_event(keys).as_json()
|
||||
|
||||
response = requests.get(callback + "?amount=" + str(int(amount) * 1000) + "&nostr=" + urllib.parse.quote_plus(
|
||||
@ -287,6 +291,7 @@ def zaprequest(lud16: str, amount: int, content, zapped_event, zapped_user, keys
|
||||
print("ZAP REQUEST: " + e)
|
||||
return None
|
||||
|
||||
|
||||
def get_price_per_sat(currency):
|
||||
import requests
|
||||
|
||||
@ -334,8 +339,6 @@ def make_ln_address_nostdress(identifier, npub, pin, nostdressdomain):
|
||||
return "", ""
|
||||
|
||||
|
||||
|
||||
|
||||
def check_and_set_ln_bits_keys(identifier, npub):
|
||||
if not os.getenv("LNBITS_INVOICE_KEY_" + identifier.upper()):
|
||||
invoicekey, adminkey, walletid, userid, success = create_lnbits_account(identifier)
|
||||
@ -365,4 +368,4 @@ def add_key_to_env_file(value, oskey):
|
||||
env_path = Path('.env')
|
||||
if env_path.is_file():
|
||||
dotenv.load_dotenv(env_path, verbose=True, override=True)
|
||||
dotenv.set_key(env_path, value, oskey)
|
||||
dotenv.set_key(env_path, value, oskey)
|
||||
|
4
setup.py
4
setup.py
@ -1,6 +1,6 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
VERSION = '0.2.6'
|
||||
VERSION = '0.3.0'
|
||||
DESCRIPTION = 'A framework to build and run Nostr NIP90 Data Vending Machines'
|
||||
LONG_DESCRIPTION = ('A framework to build and run Nostr NIP90 Data Vending Machines. '
|
||||
'This is an early stage release. Interfaces might change/brick')
|
||||
@ -15,7 +15,7 @@ setup(
|
||||
long_description=LONG_DESCRIPTION,
|
||||
packages=find_packages(include=['nostr_dvm', 'nostr_dvm.*']),
|
||||
|
||||
install_requires=["nostr-sdk==0.9.1",
|
||||
install_requires=["nostr-sdk=>0.10.0",
|
||||
"bech32",
|
||||
"pycryptodome==3.20.0",
|
||||
"python-dotenv==1.0.0",
|
||||
|
@ -1,12 +1,17 @@
|
||||
import os
|
||||
import threading
|
||||
from pathlib import Path
|
||||
|
||||
import dotenv
|
||||
from nostr_sdk import Keys
|
||||
|
||||
from nostr_dvm.subscription import Subscription
|
||||
from nostr_dvm.tasks import content_discovery_currently_popular
|
||||
from nostr_dvm.utils.admin_utils import AdminConfig
|
||||
from nostr_dvm.utils.backend_utils import keep_alive
|
||||
from nostr_dvm.utils.dvmconfig import DVMConfig
|
||||
from nostr_dvm.utils.nostr_utils import check_and_set_private_key
|
||||
from nostr_dvm.utils.zap_utils import check_and_set_ln_bits_keys
|
||||
|
||||
|
||||
def playground():
|
||||
@ -19,10 +24,24 @@ def playground():
|
||||
admin_config.REBROADCAST_NIP89 = False
|
||||
admin_config.UPDATE_PROFILE = False
|
||||
|
||||
discovery_test = content_discovery_currently_popular.build_example("Currently Popular Notes DVM", "discovery_content_test", admin_config)
|
||||
discovery_test.run()
|
||||
discovery_test_sub = content_discovery_currently_popular.build_example_subscription("Currently Popular Notes DVM (with Subscriptions)", "discovery_content_test", admin_config)
|
||||
discovery_test_sub.run()
|
||||
|
||||
keep_alive()
|
||||
#discovery_test = content_discovery_currently_popular.build_example("Currently Popular Notes DVM",
|
||||
# "discovery_content_test", admin_config)
|
||||
#discovery_test.run()
|
||||
|
||||
subscription_config = DVMConfig()
|
||||
subscription_config.PRIVATE_KEY = check_and_set_private_key("dvm_subscription")
|
||||
npub = Keys.parse(subscription_config.PRIVATE_KEY).public_key().to_bech32()
|
||||
invoice_key, admin_key, wallet_id, user_id, lnaddress = check_and_set_ln_bits_keys("dvm_subscription", npub)
|
||||
subscription_config.LNBITS_INVOICE_KEY = invoice_key
|
||||
subscription_config.LNBITS_ADMIN_KEY = admin_key # The dvm might pay failed jobs back
|
||||
subscription_config.LNBITS_URL = os.getenv("LNBITS_HOST")
|
||||
x = threading.Thread(target=Subscription, args=(Subscription(subscription_config),))
|
||||
x.start()
|
||||
|
||||
#keep_alive()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -757,7 +757,7 @@ export default {
|
||||
if(!jsonentry.amount){
|
||||
jsonentry.amount = ""
|
||||
}
|
||||
if(jsonentry.amount === "subscription"){
|
||||
if(jsonentry.subscription === true){
|
||||
// if(susbcrition_tier) {
|
||||
const filter = new Filter().kind(37001).author(entry.author)
|
||||
let tiers = await client.getEventsOf([filter], Duration.fromSecs(5))
|
||||
@ -817,11 +817,13 @@ export default {
|
||||
|
||||
|
||||
}
|
||||
console.log("hello")
|
||||
let subscription_status = await hasActiveSubscription(store.state.pubkey.toHex(), nip88.d, evt.author.toHex(), nip88.amounts)
|
||||
nip88.hasActiveSubscription = subscription_status.isActive
|
||||
nip88.subscribedUntil = subscription_status.validUntil
|
||||
nip88.subscriptionId = subscription_status.subscriptionId
|
||||
nip88.expires = subscription_status.expires
|
||||
console.log(subscription_status)
|
||||
|
||||
|
||||
jsonentry.nip88 = nip88
|
||||
|
@ -90,8 +90,8 @@
|
||||
<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()==='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="!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>
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,574 +0,0 @@
|
||||
<script setup>
|
||||
|
||||
|
||||
import {
|
||||
Client,
|
||||
Filter,
|
||||
Timestamp,
|
||||
Event,
|
||||
Metadata,
|
||||
PublicKey,
|
||||
EventBuilder,
|
||||
Tag,
|
||||
EventId,
|
||||
Nip19Event, Alphabet, Keys, nip04_decrypt, SecretKey, Duration
|
||||
} from "@rust-nostr/nostr-sdk";
|
||||
import store from '../store';
|
||||
import miniToastr from "mini-toastr";
|
||||
import VueNotifications from "vue-notifications";
|
||||
import {computed, watch} from "vue";
|
||||
import deadnip89s from "@/components/data/deadnip89s.json";
|
||||
import {data} from "autoprefixer";
|
||||
import {requestProvider} from "webln";
|
||||
import Newnote from "@/components/Newnote.vue";
|
||||
import SummarizationGeneration from "@/components/SummarizationGeneration.vue"
|
||||
import {post_note, schedule, copyurl, copyinvoice, sleep, getEvents, get_user_infos, nextInput, createBolt11Lud16, getEventsOriginalOrder} from "../components/helper/Helper.vue"
|
||||
import amberSignerService from "./android-signer/AndroidSigner";
|
||||
import StringUtil from "@/components/helper/string.ts";
|
||||
|
||||
|
||||
let dvms =[]
|
||||
|
||||
async function generate_feed() {
|
||||
|
||||
try {
|
||||
if(store.state.pubkey === undefined || localStorage.getItem('nostr-key-method') === "anon"){
|
||||
miniToastr.showMessage("Some algorithms may need your profile to give personalized recommendations. Sign-in for a better experience.", "Not signed in.", VueNotifications.types.warn)
|
||||
|
||||
}
|
||||
|
||||
dvms = []
|
||||
store.commit('set_recommendation_dvms', dvms)
|
||||
|
||||
let client = store.state.client
|
||||
|
||||
let content = "NIP 90 Content Discovery request"
|
||||
let kind = 5300
|
||||
let tags = []
|
||||
let res;
|
||||
let requestid;
|
||||
|
||||
if (localStorage.getItem('nostr-key-method') === 'android-signer') {
|
||||
let draft = {
|
||||
content: content,
|
||||
kind: kind,
|
||||
pubkey: store.state.pubkey.toHex(),
|
||||
tags: tags,
|
||||
createdAt: Date.now()
|
||||
};
|
||||
|
||||
res = await amberSignerService.signEvent(draft)
|
||||
await client.sendEvent(Event.fromJson(JSON.stringify(res)))
|
||||
requestid = res.id;
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
let tags_t = []
|
||||
for (let tag of tags){
|
||||
tags_t.push(Tag.parse(tag))
|
||||
}
|
||||
let evt = new EventBuilder(kind, content, tags_t)
|
||||
res = await client.sendEventBuilder(evt);
|
||||
|
||||
|
||||
requestid = res.toHex();
|
||||
}
|
||||
|
||||
store.commit('set_current_request_id_recommendation', requestid)
|
||||
if (!store.state.recommendationehasEventListener){
|
||||
store.commit('set_recommendationEventListener', true)
|
||||
listen()
|
||||
|
||||
}
|
||||
else{
|
||||
console.log("Already has event listener")
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function listen() {
|
||||
let client = store.state.client
|
||||
let pubkey = store.state.pubkey
|
||||
|
||||
const filter = new Filter().kinds([7000, 6300]).pubkey(pubkey).since(Timestamp.now());
|
||||
await client.subscribe([filter]);
|
||||
|
||||
const handle = {
|
||||
// Handle event
|
||||
handleEvent: async (relayUrl, event) => {
|
||||
/* if (store.state.recommendationehasEventListener === false){
|
||||
return true
|
||||
}*/
|
||||
//const dvmname = getNamefromId(event.author.toHex())
|
||||
console.log("Received new event from", relayUrl);
|
||||
//console.log(event.asJson())
|
||||
let resonsetorequest = false
|
||||
sleep(1200).then(async () => {
|
||||
for (let tag in event.tags) {
|
||||
if (event.tags[tag].asVec()[0] === "e") {
|
||||
//console.log(event.tags[tag].asVec()[1])
|
||||
//console.log(test)
|
||||
if (event.tags[tag].asVec()[1] === store.state.requestidRecommendation) {
|
||||
resonsetorequest = true
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (resonsetorequest === true) {
|
||||
if (event.kind === 7000) {
|
||||
try {
|
||||
console.log("7000: ", event.content);
|
||||
// console.log("DVM: " + event.author.toHex())
|
||||
//miniToastr.showMessage("DVM: " + dvmname, event.content, VueNotifications.types.info)
|
||||
|
||||
await addDVM(event)
|
||||
|
||||
} catch (error) {
|
||||
console.log("Error: ", error);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
else if (event.kind === 6300) {
|
||||
let entries = []
|
||||
//console.log("6300:", event.content);
|
||||
|
||||
let event_etags = JSON.parse(event.content)
|
||||
if (event_etags.length > 0) {
|
||||
for (let etag of event_etags) {
|
||||
const eventid = EventId.fromHex(etag[1]).toHex()
|
||||
entries.push(eventid)
|
||||
}
|
||||
const events = await getEventsOriginalOrder(entries)
|
||||
let authors = []
|
||||
for (const evt of events) {
|
||||
authors.push(evt.author)
|
||||
}
|
||||
|
||||
if (authors.length > 0) {
|
||||
let profiles = await get_user_infos(authors)
|
||||
let items = []
|
||||
let index = 0
|
||||
for (const evt of events) {
|
||||
let p = profiles.find(record => record.author === evt.author.toHex())
|
||||
let bech32id = evt.id.toBech32()
|
||||
let nip19 = new Nip19Event(evt.id, evt.author, store.state.relays)
|
||||
let nip19bech32 = nip19.toBech32()
|
||||
let picture = p === undefined ? "../assets/nostr-purple.svg" : p["profile"]["picture"]
|
||||
let name = p === undefined ? bech32id : p["profile"]["name"]
|
||||
let highlighterurl = "https://highlighter.com/e/" + nip19bech32
|
||||
let njumpurl = "https://njump.me/" + nip19bech32
|
||||
let nostrudelurl = "https://nostrudel.ninja/#/n/" + bech32id
|
||||
let uri = "nostr:" + bech32id // nip19.toNostrUri()
|
||||
|
||||
|
||||
if (items.find(e => e.id.toHex() === evt.id.toHex()) === undefined) {
|
||||
items.push({
|
||||
id: evt.id,
|
||||
content: evt.content,
|
||||
author: name,
|
||||
authorurl: "https://njump.me/" + evt.author.toBech32(),
|
||||
links: {
|
||||
"uri": uri,
|
||||
"highlighter": highlighterurl,
|
||||
"njump": njumpurl,
|
||||
"nostrudel": nostrudelurl
|
||||
},
|
||||
avatar: picture,
|
||||
index: index,
|
||||
indicator: {"time": evt.createdAt.toHumanDatetime(), "index": index}
|
||||
})
|
||||
index = index+1
|
||||
}
|
||||
|
||||
}
|
||||
if (dvms.find(i => i.id === event.author.toHex()) === undefined){
|
||||
await addDVM(event)
|
||||
console.log("add dvm because of bug")
|
||||
}
|
||||
|
||||
|
||||
dvms.find(i => i.id === event.author.toHex()).result.length = 0
|
||||
dvms.find(i => i.id === event.author.toHex()).result.push.apply(dvms.find(i => i.id === event.author.toHex()).result, items)
|
||||
dvms.find(i => i.id === event.author.toHex()).status = "finished"
|
||||
|
||||
}
|
||||
}
|
||||
store.commit('set_recommendation_dvms', dvms)
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
// Handle relay message
|
||||
handleMsg: async (relayUrl, message) => {
|
||||
//console.log("Received message from", relayUrl, message.asJson());
|
||||
}
|
||||
};
|
||||
|
||||
client.handleNotifications(handle);
|
||||
}
|
||||
|
||||
|
||||
const urlinput = ref("");
|
||||
|
||||
async function addDVM(event){
|
||||
let status = "unknown"
|
||||
let jsonentry = {
|
||||
id: event.author.toHex(),
|
||||
kind: "",
|
||||
status: status,
|
||||
result: [],
|
||||
name: event.author.toBech32(),
|
||||
about: "",
|
||||
image: "",
|
||||
amount: 0,
|
||||
bolt11: ""
|
||||
}
|
||||
|
||||
for (const tag in event.tags) {
|
||||
if (event.tags[tag].asVec()[0] === "status") {
|
||||
status = event.tags[tag].asVec()[1]
|
||||
}
|
||||
|
||||
if (event.tags[tag].asVec()[0] === "amount") {
|
||||
jsonentry.amount = event.tags[tag].asVec()[1]
|
||||
if (event.tags[tag].asVec().length > 2) {
|
||||
jsonentry.bolt11 = event.tags[tag].asVec()[2]
|
||||
}
|
||||
else{
|
||||
let profiles = await get_user_infos([event.author])
|
||||
let created = 0
|
||||
if (profiles.length > 0){
|
||||
// for (const profile of profiles){
|
||||
console.log(profiles[0].profile)
|
||||
let current = profiles[0]
|
||||
// if (profiles[0].profile.createdAt > created){
|
||||
// created = profile.profile.createdAt
|
||||
// current = profile
|
||||
// }
|
||||
|
||||
|
||||
let lud16 = current.profile.lud16
|
||||
if (lud16 !== null && lud16 !== ""){
|
||||
console.log("LUD16: " + lud16)
|
||||
jsonentry.bolt11 = await createBolt11Lud16(lud16, jsonentry.amount)
|
||||
console.log(jsonentry.bolt11)
|
||||
if(jsonentry.bolt11 === ""){
|
||||
status = "error"
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log("NO LNURL")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
console.log("PROFILE NOT FOUND")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//let dvm = store.state.nip89dvms.find(x => JSON.parse(x.event).pubkey === event.author.toHex())
|
||||
for (const el of store.state.nip89dvms) {
|
||||
if (JSON.parse(el.event).pubkey === event.author.toHex().toString()) {
|
||||
jsonentry.name = el.name
|
||||
jsonentry.about = el.about
|
||||
jsonentry.image = el.image
|
||||
|
||||
console.log(jsonentry)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (event.content !== "" && status !== "payment-required" && status !== "error" && status !== "finished" && status !== "paid"){
|
||||
status = event.content
|
||||
}
|
||||
jsonentry.status = status
|
||||
console.log(dvms)
|
||||
if (dvms.filter(i => i.id === jsonentry.id).length === 0) {
|
||||
dvms.push(jsonentry)
|
||||
}
|
||||
|
||||
|
||||
//dvms.find(i => i.id === jsonentry.id).status = status
|
||||
store.commit('set_recommendation_dvms', dvms)
|
||||
|
||||
}
|
||||
|
||||
async function zap_local(invoice) {
|
||||
|
||||
let success = await zap(invoice)
|
||||
if (success){
|
||||
dvms.find(i => i.bolt11 === invoice).status = "paid"
|
||||
store.commit('set_recommendation_results', dvms)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
defineProps({
|
||||
msg: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
})
|
||||
|
||||
import { ref } from "vue";
|
||||
import ModalComponent from "../components/Newnote.vue";
|
||||
import VueDatePicker from "@vuepic/vue-datepicker";
|
||||
import {timestamp} from "@vueuse/core";
|
||||
import NoteTable from "@/components/NoteTable.vue";
|
||||
import zap from "@/components/helper/Zap.vue";
|
||||
|
||||
const isModalOpened = ref(false);
|
||||
const modalcontent = ref("");
|
||||
const datetopost = ref(Date.now());
|
||||
|
||||
|
||||
const openModal = result => {
|
||||
datetopost.value = Date.now();
|
||||
isModalOpened.value = true;
|
||||
|
||||
|
||||
|
||||
//let resevents = ""
|
||||
//for (let evt of result){
|
||||
// resevents = resevents + "nostr:" + (evt.id.toBech32()) + "\n"
|
||||
//}
|
||||
modalcontent.value = result
|
||||
};
|
||||
const closeModal = () => {
|
||||
isModalOpened.value = false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const submitHandler = async () => {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<!-- font-thin bg-gradient-to-r from-white to-nostr bg-clip-text text-transparent -->
|
||||
|
||||
<template>
|
||||
|
||||
<div class="greetings">
|
||||
<br>
|
||||
<br>
|
||||
<h1 class="text-7xl font-black tracking-wide">Noogle</h1>
|
||||
<h1 class="text-7xl font-black tracking-wide">Content</h1>
|
||||
<h1 class="text-7xl font-black tracking-wide">Discovery</h1>
|
||||
|
||||
<h2 class="text-base-200-content text-center tracking-wide text-2xl font-thin ">
|
||||
Algorithms, but you are the one in control.</h2>
|
||||
<h3>
|
||||
<br>
|
||||
<button class="v-Button" @click="generate_feed()">Recommend me Notes</button>
|
||||
</h3>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<ModalComponent :isOpen="isModalOpened" @modal-close="closeModal" @submit="submitHandler" name="first-modal">
|
||||
<template #header>Summarize Results <br></template>
|
||||
<template #content>
|
||||
|
||||
<SummarizationGeneration :events="modalcontent"></SummarizationGeneration>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<!-- <div>
|
||||
<VueDatePicker :min-date="new Date()" :teleport="false" :dark="true" position="right" className="bg-base-200 inline-flex flex-none" style="width: 220px;" v-model="datetopost"></VueDatePicker>
|
||||
<button className="v-Button" @click="schedule(modalcontent, datetopost)" @click.stop="closeModal"><img width="25px" style="margin-right: 5px" src="../../public/shipyard.ico"/>Schedule Note with Shipyard DVM</button>
|
||||
<br>
|
||||
or
|
||||
<br>
|
||||
<button className="v-Button" style="margin-bottom: 0px" @click="post_note(modalcontent)" @click.stop="closeModal"><img width="25px" style="margin-right: 5px;" src="../../public/favicon.ico"/>Post Note now</button>
|
||||
</div> -->
|
||||
</template>
|
||||
</ModalComponent>
|
||||
|
||||
<div class=" relative space-y-3">
|
||||
<div class="grid grid-cols-1 gap-6">
|
||||
|
||||
<div className="card w-70 bg-base-100 shadow-xl flex flex-col" v-for="dvm in store.state.recommendationdvms"
|
||||
:key="dvm.id">
|
||||
|
||||
|
||||
|
||||
|
||||
<div className="card-body">
|
||||
|
||||
<div className="playeauthor-wrapper">
|
||||
<figure className="w-20">
|
||||
<img className="avatar" v-if="dvm.image" :src="dvm.image" alt="DVM Picture" />
|
||||
<img class="avatar" v-else src="@/assets/nostr-purple.svg" />
|
||||
</figure>
|
||||
|
||||
|
||||
<h2 className="card-title">{{ dvm.name }}</h2>
|
||||
</div>
|
||||
<h3 class="fa-cut" v-html="StringUtil.parseHyperlinks(dvm.about)"></h3>
|
||||
|
||||
|
||||
|
||||
<div className="card-actions justify-end mt-auto" >
|
||||
|
||||
<div className="tooltip mt-auto">
|
||||
|
||||
|
||||
<button v-if="dvm.status !== 'finished' && dvm.status !== 'paid' && dvm.status !== 'payment-required' && dvm.status !== 'error'" className="btn">{{dvm.status}}</button>
|
||||
<button v-if="dvm.status === 'finished'" className="btn">Done</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 === 'payment-required'" className="zap-Button" @click="zap_local(dvm.bolt11);">{{ dvm.amount/1000 }} Sats</button>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- <div v-if="dvm.result.length > 0" class="collapse bg-base-200">
|
||||
<input type="checkbox" class="peer" />
|
||||
<div class="collapse-title bg-primary text-primary-content peer-checked:bg-secondary peer-checked:text-secondary-content">
|
||||
Click me to show/hide content
|
||||
</div>
|
||||
<div class="collapse-content bg-primary text-primary-content peer-checked:bg-base-200 peer-checked:text-accent">
|
||||
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- <details open ></details> -->
|
||||
<details v-if="dvm.status === 'finished'" class="collapse bg-base">
|
||||
<summary class="collapse-title "><div class="btn">Show/Hide Results</div></summary>
|
||||
<div class="collapse-content font-size-0" className="z-10" id="collapse">
|
||||
|
||||
<NoteTable :data="dvm.result" ></NoteTable>
|
||||
|
||||
|
||||
</div>
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
<div data-tip="Make Summarization" v-if="dvm.result.length > 0 && store.state.pubkey.toHex() !== Keys.parse(store.state.nooglekey).publicKey.toHex()" >
|
||||
<button @click="openModal(dvm.result)" class="w-8 h-8 rounded-full bg-nostr border-white border-1 text-white flex items-center justify-center focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black tooltip" data-top='Share' aria-label="make note" role="button">
|
||||
<svg class="w-4 h-4 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M9 19V.352A3.451 3.451 0 0 0 7.5 0a3.5 3.5 0 0 0-3.261 2.238A3.5 3.5 0 0 0 2.04 6.015a3.518 3.518 0 0 0-.766 1.128c-.042.1-.064.209-.1.313a3.34 3.34 0 0 0-.106.344 3.463 3.463 0 0 0 .02 1.468A4.016 4.016 0 0 0 .3 10.5l-.015.036a3.861 3.861 0 0 0-.216.779A3.968 3.968 0 0 0 0 12a4.032 4.032 0 0 0 .107.889 4 4 0 0 0 .2.659c.006.014.015.027.021.041a3.85 3.85 0 0 0 .417.727c.105.146.219.284.342.415.072.076.148.146.225.216.1.091.205.179.315.26.11.081.2.14.308.2.02.013.039.028.059.04v.053a3.506 3.506 0 0 0 3.03 3.469 3.426 3.426 0 0 0 4.154.577A.972.972 0 0 1 9 19Zm10.934-7.68a3.956 3.956 0 0 0-.215-.779l-.017-.038a4.016 4.016 0 0 0-.79-1.235 3.417 3.417 0 0 0 .017-1.468 3.387 3.387 0 0 0-.1-.333c-.034-.108-.057-.22-.1-.324a3.517 3.517 0 0 0-.766-1.128 3.5 3.5 0 0 0-2.202-3.777A3.5 3.5 0 0 0 12.5 0a3.451 3.451 0 0 0-1.5.352V19a.972.972 0 0 1-.184.546 3.426 3.426 0 0 0 4.154-.577A3.506 3.506 0 0 0 18 15.5v-.049c.02-.012.039-.027.059-.04.106-.064.208-.13.308-.2s.214-.169.315-.26c.077-.07.153-.14.225-.216a4.007 4.007 0 0 0 .459-.588c.115-.176.215-.361.3-.554.006-.014.015-.027.021-.041.087-.213.156-.434.205-.659.013-.057.024-.115.035-.173.046-.237.07-.478.073-.72a3.948 3.948 0 0 0-.066-.68Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.zap-Button{
|
||||
@apply btn hover:bg-amber-400 border-amber-400 text-base;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.v-Button {
|
||||
@apply bg-nostr hover:bg-nostr2 focus:ring-white mb-2 inline-flex flex-none items-center rounded-lg border border-black px-3 py-1.5 text-sm leading-4 text-white transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-white dark:focus:ring-offset-gray-900;
|
||||
height: 48px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.c-Input {
|
||||
@apply bg-base-200 text-accent dark:bg-black dark:text-white focus:ring-white mb-2 inline-flex flex-none items-center rounded-lg border border-transparent px-3 py-1.5 text-sm leading-4 text-accent-content transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-white dark:focus:ring-offset-gray-900;
|
||||
|
||||
width: 350px;
|
||||
height: 48px;
|
||||
|
||||
}
|
||||
|
||||
.d-Input {
|
||||
@apply bg-black hover:bg-gray-900 focus:ring-white mb-2 inline-flex flex-none items-center rounded-lg border border-transparent px-3 py-1.5 text-sm leading-4 text-white transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-white dark:focus:ring-offset-gray-900;
|
||||
width: 300px;
|
||||
|
||||
color: white;
|
||||
background: black;
|
||||
}
|
||||
|
||||
.playeauthor-wrapper {
|
||||
padding: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
width:100%;
|
||||
height:125px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.0rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
||||
.avatar {
|
||||
margin-right: 10px;
|
||||
margin-left: 0px;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
box-shadow: inset 0 4px 4px 0 rgb(0 0 0 / 10%);
|
||||
}
|
||||
|
||||
.greetings h1,
|
||||
.greetings h3 {
|
||||
text-align: left;
|
||||
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
|
||||
.greetings h1,
|
||||
.greetings h3 {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -382,7 +382,7 @@ export async function hasActiveSubscription(pubkeystring, tiereventdtag, tieraut
|
||||
|
||||
let subscriptionfilter = new Filter().kind(7003).pubkey(PublicKey.parse(tierauthorid)).customTag(SingleLetterTag.uppercase(Alphabet.P), [pubkeystring]).limit(1)
|
||||
let evts = await client.getEventsOf([subscriptionfilter], Duration.fromSecs(5))
|
||||
console.log(evts)
|
||||
|
||||
if (evts.length > 0){
|
||||
console.log(evts[0].asJson())
|
||||
let matchesdtag = false
|
||||
@ -419,7 +419,7 @@ export async function hasActiveSubscription(pubkeystring, tiereventdtag, tieraut
|
||||
return subscriptionstatus
|
||||
|
||||
}
|
||||
|
||||
return subscriptionstatus
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user