mirror of
https://github.com/aljazceru/nostrdvm.git
synced 2025-12-19 15:14:18 +01:00
subscriptions / sdk update work in progress
This commit is contained in:
@@ -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,10 +56,10 @@ 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)
|
||||
@@ -68,17 +69,34 @@ class Bot:
|
||||
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
|
||||
|
||||
@@ -265,7 +283,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(
|
||||
|
||||
228
nostr_dvm/dvm.py
228
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,10 +71,11 @@ 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):
|
||||
@@ -81,6 +84,7 @@ class DVM:
|
||||
def handle_nip90_job_event(nip90_event):
|
||||
|
||||
nip90_event = check_and_decrypt_tags(nip90_event, self.dvm_config)
|
||||
|
||||
if nip90_event is None:
|
||||
return
|
||||
|
||||
@@ -96,7 +100,7 @@ class DVM:
|
||||
|
||||
task_supported, task = check_task_is_supported(nip90_event, client=self.client,
|
||||
config=self.dvm_config)
|
||||
|
||||
print(task_supported)
|
||||
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")
|
||||
@@ -109,8 +113,33 @@ class DVM:
|
||||
return
|
||||
|
||||
task_is_free = False
|
||||
user_has_active_subscription = False
|
||||
|
||||
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
|
||||
if user.subscribed > Timestamp.now().as_secs():
|
||||
print("User subscribed until: " + str(Timestamp.from_secs(user.subscribed).to_human_datetime()))
|
||||
user_has_active_subscription = True
|
||||
else:
|
||||
# otherwise we check for an active subscription
|
||||
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)
|
||||
|
||||
if subscription_status["isActive"]:
|
||||
print("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 +153,39 @@ 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 +194,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 +242,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 +259,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 + "] "
|
||||
@@ -350,7 +413,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 +434,15 @@ 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 +476,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 +535,12 @@ 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)
|
||||
|
||||
@@ -545,19 +609,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,
|
||||
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 = NIP88Config()
|
||||
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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
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:
|
||||
|
||||
@@ -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,20 @@ 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,7 +149,8 @@ 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..")
|
||||
@@ -163,13 +167,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 +194,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,
|
||||
"amount": "subscription",
|
||||
"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
|
||||
|
||||
@@ -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,9 +68,9 @@ 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)
|
||||
@@ -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,7 +110,6 @@ 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)
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
@@ -111,6 +114,7 @@ def get_from_sql_table(db, npub):
|
||||
user.lud16 = row[5]
|
||||
user.name = row[6]
|
||||
user.lastactive = row[7]
|
||||
user.subscribed = row[8]
|
||||
|
||||
return user
|
||||
|
||||
@@ -162,14 +166,14 @@ 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")
|
||||
|
||||
@@ -184,6 +188,33 @@ def update_user_balance(db, npub, additional_sats, client, config):
|
||||
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 +222,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 +232,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 +245,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)
|
||||
|
||||
201
nostr_dvm/utils/nip88_utils.py
Normal file
201
nostr_dvm/utils/nip88_utils.py
Normal file
@@ -0,0 +1,201 @@
|
||||
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, dvm_config):
|
||||
subscription_status = {
|
||||
"isActive": False,
|
||||
"validUntil": 0,
|
||||
"subscriptionId": "",
|
||||
}
|
||||
|
||||
subscriptionfilter = Filter().kind(definitions.EventDefinitions.KIND_NIP88_PAYMENT_RECIPE).pubkey(
|
||||
PublicKey.parse(dvm_config.PUBLIC_KEY)).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
|
||||
|
||||
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. "
|
||||
|
||||
130
nostr_dvm/utils/subscription_utils.py
Normal file
130
nostr_dvm/utils/subscription_utils.py
Normal file
@@ -0,0 +1,130 @@
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
); """)
|
||||
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):
|
||||
try:
|
||||
con = sqlite3.connect(db)
|
||||
cur = con.cursor()
|
||||
data = (id, recipient, subscriber, nwc, cadence, amount, begin, end, tier_dtag, zaps, recipe, active)
|
||||
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]
|
||||
|
||||
return subscription
|
||||
|
||||
except Error as e:
|
||||
print("Error Getting from DB: " + str(e))
|
||||
return None
|
||||
|
||||
|
||||
def update_subscription_sql_table(db, id, recipient, subscriber, nwc, cadence, amount, begin, end, tier_dtag, zaps,
|
||||
recipe, active):
|
||||
try:
|
||||
con = sqlite3.connect(db)
|
||||
cur = con.cursor()
|
||||
data = (recipient, subscriber, nwc, cadence, amount, begin, end, tier_dtag, zaps, recipe, active, id)
|
||||
|
||||
cur.execute(""" UPDATE subscriptions
|
||||
SET recipient = ? ,
|
||||
subscriber = ? ,
|
||||
nwc = ? ,
|
||||
cadence = ? ,
|
||||
amount = ? ,
|
||||
begin = ? ,
|
||||
end = ?,
|
||||
tier_dtag = ?,
|
||||
zaps = ?,
|
||||
recipe = ?,
|
||||
active = ?
|
||||
|
||||
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
|
||||
@@ -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)
|
||||
|
||||
2
setup.py
2
setup.py
@@ -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",
|
||||
|
||||
@@ -4,9 +4,13 @@ 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,8 +23,21 @@ 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()
|
||||
|
||||
#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")
|
||||
Subscription(subscription_config)
|
||||
|
||||
keep_alive()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user