From 42b3d763f12daea8cdb15711da270595298c3ba9 Mon Sep 17 00:00:00 2001 From: Believethehype Date: Wed, 29 Nov 2023 15:09:35 +0100 Subject: [PATCH] allow external dvms to be added to the bot --- bot/bot.py | 80 +++++++++++++++++------------ core/dvm.py | 37 +++++++------ interfaces/dvmtaskinterface.py | 28 ++++++---- main.py | 18 +++++-- playground.py | 22 ++++---- tasks/textextraction_whisperx.py | 4 +- tasks/translation_libretranslate.py | 12 ++--- utils/definitions.py | 1 + utils/dvmconfig.py | 4 +- utils/mediasource_utils.py | 17 +++--- utils/nip89_utils.py | 57 +++++++++++++++----- utils/nostr_utils.py | 4 +- utils/output_utils.py | 1 - utils/zap_utils.py | 4 +- 14 files changed, 178 insertions(+), 111 deletions(-) diff --git a/bot/bot.py b/bot/bot.py index 2a31c4c..59009fd 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -9,15 +9,16 @@ from nostr_sdk import (Keys, Client, Timestamp, Filter, nip04_decrypt, HandleNot Options, Tag, Event, nip04_encrypt) from utils.admin_utils import admin_make_database_updates -from utils.backend_utils import get_amount_per_task from utils.database_utils import get_or_add_user, update_user_balance, create_sql_table, update_sql_table, User from utils.definitions import EventDefinitions +from utils.nip89_utils import nip89_fetch_events_pubkey from utils.nostr_utils import send_event from utils.zap_utils import parse_zap_event_tags, pay_bolt11_ln_bits, zap class Bot: job_list: list + # This is a simple list just to keep track which events we created and manage, so we don't pay for other requests def __init__(self, dvm_config, admin_config=None): self.NAME = "Bot" @@ -85,33 +86,25 @@ class Bot: if decrypted_text[0].isdigit(): index = int(decrypted_text.split(' ')[0]) - 1 - task = self.dvm_config.SUPPORTED_DVMS[index].TASK - if decrypted_text.split(" ")[1].lower() == "info": - nip89 = self.dvm_config.SUPPORTED_DVMS[index].dvm_config.NIP89 - - - nip89content = json.loads(nip89.content) - info = "" - if nip89content.get("name"): - info += "Name: " + nip89content.get("name") + "\n" - info += nip89content.get("image")+ "\n" - info += "About:\n" + nip89content.get("about") + "\n" - params = nip89content["nip90Params"] - print(params) - info += "\nParameters:\n" - for param in params: - info += "-" + param + '\n' - info += "Required: " + str(params[param]['required']) + '\n' - info += "Possible Values: " + json.dumps(params[param]['values']) + '\n\n' + info = print_dvm_info(self.client, index) + time.sleep(2.0) + if info is not None: evt = EventBuilder.new_encrypted_direct_msg(self.keys, nostr_event.pubkey(), info, None).to_event(self.keys) - send_event(evt, client=self.client, dvm_config=dvm_config) + else: + evt = EventBuilder.new_encrypted_direct_msg(self.keys, nostr_event.pubkey(), + "No NIP89 Info found for " + + self.dvm_config.SUPPORTED_DVMS[index].NAME, + None).to_event(self.keys) + + send_event(evt, client=self.client, dvm_config=dvm_config) - print(nip89content) else: - print("[" + self.NAME + "] Request from " + str(user.name) + " (" + str(user.nip05) + ", Balance: " + task = self.dvm_config.SUPPORTED_DVMS[index].TASK + print("[" + self.NAME + "] Request from " + str(user.name) + " (" + str( + user.nip05) + ", Balance: " + str(user.balance) + " Sats) Task: " + str(task)) if user.isblacklisted: @@ -129,13 +122,10 @@ class Bot: input_type = "url" i_tag = Tag.parse(["i", input, input_type]) - #bid = str(self.dvm_config.SUPPORTED_DVMS[index].FIX_COST * 1000) - #bid_tag = Tag.parse(['bid', bid, bid]) relays_tag = Tag.parse(["relays", json.dumps(self.dvm_config.RELAY_LIST)]) alt_tag = Tag.parse(["alt", self.dvm_config.SUPPORTED_DVMS[index].TASK]) tags = [i_tag.as_vec(), relays_tag.as_vec(), alt_tag.as_vec()] - remaining_text = command.replace(input, "") print(remaining_text) params = remaining_text.rstrip().split(" -") @@ -269,7 +259,8 @@ class Bot: amount_msats = int(tag.as_vec()[1]) amount = int(amount_msats / 1000) entry = next((x for x in self.job_list if x['event_id'] == etag), None) - if entry is not None and entry['is_paid'] is False and entry['dvm_key'] == nostr_event.pubkey().to_hex(): + if entry is not None and entry['is_paid'] is False and entry[ + 'dvm_key'] == nostr_event.pubkey().to_hex(): # if we get a bolt11, we pay and move on user = get_or_add_user(db=self.dvm_config.DB, npub=entry["npub"], client=self.client, config=self.dvm_config) @@ -287,19 +278,22 @@ class Bot: + " Sats.\n", None).to_event(self.keys) - print("[" + self.NAME + "] Replying " + user.name + " with \"scheduled\" confirmation") + print( + "[" + self.NAME + "] Replying " + user.name + " with \"scheduled\" confirmation") send_event(evt, client=self.client, dvm_config=dvm_config) else: print("Bot payment-required") - evt = EventBuilder.new_encrypted_direct_msg(self.keys, PublicKey.from_hex(entry["npub"]), - "Current balance: " + str(user.balance) + " Sats. Balance of "+ str(amount) +" Sats required. Please zap me with at least " + + evt = EventBuilder.new_encrypted_direct_msg(self.keys, + PublicKey.from_hex(entry["npub"]), + "Current balance: " + str( + user.balance) + " Sats. Balance of " + str( + amount) + " Sats required. Please zap me with at least " + str(int(amount - user.balance)) + " Sats, then try again.", None).to_event(self.keys) send_event(evt, client=self.client, dvm_config=dvm_config) return - if len(tag.as_vec()) > 2: bolt11 = tag.as_vec()[2] # else we create a zap @@ -390,6 +384,28 @@ class Bot: self.client.handle_notifications(NotificationHandler()) + def print_dvm_info(client, index): + pubkey = self.dvm_config.SUPPORTED_DVMS[index].dvm_config.PUBLIC_KEY + kind = self.dvm_config.SUPPORTED_DVMS[index].KIND + nip89content_str = nip89_fetch_events_pubkey(client, pubkey, kind) + print(nip89content_str) + if nip89content_str is not None: + nip89content = json.loads(nip89content_str) + info = "" + if nip89content.get("name"): + info += "Name: " + nip89content.get("name") + "\n" + info += nip89content.get("image") + "\n" + info += "About:\n" + nip89content.get("about") + "\n" + params = nip89content["nip90Params"] + info += "\nParameters:\n" + for param in params: + info += "-" + param + '\n' + info += "Required: " + str(params[param]['required']) + '\n' + info += "Possible Values: " + json.dumps(params[param]['values']) + '\n\n' + return info + + return None + try: while True: time.sleep(1.0) @@ -398,7 +414,3 @@ class Bot: os.kill(os.getpid(), signal.SIGTERM) - def run(self): - bot = Bot - nostr_dvm_thread = Thread(target=bot, args=[self.dvm_config]) - nostr_dvm_thread.start() diff --git a/core/dvm.py b/core/dvm.py index fc9f6af..87a1613 100644 --- a/core/dvm.py +++ b/core/dvm.py @@ -98,12 +98,11 @@ class DVM: 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") + print("[" + self.dvm_config.NIP89.NAME + "] Request by blacklisted user, skipped") elif task_supported: - print("[" + self.dvm_config.NIP89.name + "] Received new Request: " + task + " from " + user.name) + print("[" + self.dvm_config.NIP89.NAME + "] Received new Request: " + task + " from " + user.name) duration = input_data_file_duration(nip90_event, dvm_config=self.dvm_config, client=self.client) - print("File Duration: " + str(duration)) amount = get_amount_per_task(task, self.dvm_config, duration) if amount is None: return @@ -125,7 +124,7 @@ class DVM: # if user is whitelisted or task is free, just do the job if user.iswhitelisted or task_is_free or cashu_redeemed: print( - "[" + self.dvm_config.NIP89.name + "] Free task or Whitelisted for task " + task + + "[" + self.dvm_config.NIP89.NAME + "] Free task or Whitelisted for task " + task + ". Starting processing..") send_job_status_reaction(nip90_event, "processing", True, 0, @@ -142,7 +141,7 @@ class DVM: lastactive=Timestamp.now().as_secs()) print( - "[" + self.dvm_config.NIP89.name + "] Using user's balance for task: " + task + + "[" + self.dvm_config.NIP89.NAME + "] Using user's balance for task: " + task + ". Starting processing.. New balance is: " + str(balance)) send_job_status_reaction(nip90_event, "processing", True, 0, @@ -158,7 +157,7 @@ class DVM: bid = int(tag.as_vec()[1]) print( - "[" + self.dvm_config.NIP89.name + "] Payment required: New Nostr " + task + " Job event: " + "[" + self.dvm_config.NIP89.NAME + "] Payment required: New Nostr " + task + " Job event: " + nip90_event.as_json()) if bid > 0: bid_offer = int(bid / 1000) @@ -169,19 +168,19 @@ class DVM: else: # If there is no bid, just request server rate from user print( - "[" + self.dvm_config.NIP89.name + "] Requesting payment for Event: " + + "[" + 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 + "] Task " + task + " not supported on this DVM, skipping..") + # print("[" + self.dvm_config.NIP89.NAME + "] Task " + task + " not supported on this DVM, skipping..") def handle_zap(zap_event): try: invoice_amount, zapped_event, sender, message, anon = parse_zap_event_tags(zap_event, self.keys, - self.dvm_config.NIP89.name, + self.dvm_config.NIP89.NAME, self.client, self.dvm_config) user = get_or_add_user(db=self.dvm_config.DB, npub=sender, client=self.client, config=self.dvm_config) @@ -211,7 +210,7 @@ class DVM: 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...") + 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 @@ -237,26 +236,26 @@ class DVM: 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") + print("[" + self.dvm_config.NIP89.NAME + "] Invoice was not paid sufficiently") elif zapped_event.kind() in EventDefinitions.ANY_RESULT: - print("[" + self.dvm_config.NIP89.name + "] " + print("[" + self.dvm_config.NIP89.NAME + "] " "Someone zapped the result of an exisiting Task. Nice") elif not anon: - print("[" + self.dvm_config.NIP89.name + "] Note Zap received for DVM balance: " + + print("[" + self.dvm_config.NIP89.NAME + "] Note Zap received for DVM balance: " + str(invoice_amount) + " Sats from " + str(user.name)) update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client, config=self.dvm_config) # a regular note elif not anon: - print("[" + self.dvm_config.NIP89.name + "] Profile Zap received for DVM balance: " + + print("[" + self.dvm_config.NIP89.NAME + "] Profile Zap received for DVM balance: " + str(invoice_amount) + " Sats from " + str(user.name)) update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client, config=self.dvm_config) except Exception as e: - print("[" + self.dvm_config.NIP89.name + "] Error during content decryption: " + str(e)) + print("[" + self.dvm_config.NIP89.NAME + "] Error during content decryption: " + str(e)) def check_event_has_not_unfinished_job_input(nevent, append, client, dvmconfig): task_supported, task = check_task_is_supported(nevent, client, config=dvmconfig) @@ -349,7 +348,7 @@ class DVM: reply_event = EventBuilder(original_event.kind() + 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( + print("[" + self.dvm_config.NIP89.NAME + "] " + str( original_event.kind() + 1000) + " Job Response event sent: " + reply_event.as_json()) def send_job_status_reaction(original_event, status, is_paid=True, amount=0, client=None, @@ -433,7 +432,7 @@ class DVM: keys = Keys.from_sk_str(dvm_config.PRIVATE_KEY) 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( + print("[" + self.dvm_config.NIP89.NAME + "]" + ": Sent Kind " + str( EventDefinitions.KIND_FEEDBACK) + " Reaction: " + status + " " + reaction_event.as_json()) return reaction_event.as_json() @@ -468,7 +467,7 @@ class DVM: 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") + print("[" + self.dvm_config.NIP89.NAME + "] doing work from joblist") do_work(job.event) elif ispaid is None: # invoice expired self.job_list.remove(job) @@ -483,7 +482,7 @@ class DVM: try: self.jobs_on_hold_list.remove(job) except: - print("[" + self.dvm_config.NIP89.name + "] Error removing Job on Hold from List after expiry") + print("[" + self.dvm_config.NIP89.NAME + "] Error removing Job on Hold from List after expiry") if Timestamp.now().as_secs() > job.timestamp + 60 * 20: # remove jobs to look for after 20 minutes.. self.jobs_on_hold_list.remove(job) diff --git a/interfaces/dvmtaskinterface.py b/interfaces/dvmtaskinterface.py index 8aea1a9..db54d32 100644 --- a/interfaces/dvmtaskinterface.py +++ b/interfaces/dvmtaskinterface.py @@ -5,14 +5,14 @@ from nostr_sdk import Keys from utils.admin_utils import AdminConfig from utils.dvmconfig import DVMConfig -from utils.nip89_utils import NIP89Announcement, NIP89Config +from utils.nip89_utils import NIP89Config from core.dvm import DVM class DVMTaskInterface: NAME: str KIND: int - TASK: str + TASK: str = "" FIX_COST: float = 0 PER_UNIT_COST: float = 0 PRIVATE_KEY: str @@ -22,11 +22,11 @@ class DVMTaskInterface: admin_config: AdminConfig def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, admin_config: AdminConfig = None, - options=None): - self.init(name, dvm_config, admin_config, nip89config) + options=None, task=None): + self.init(name, dvm_config, admin_config, nip89config, task) self.options = options - def init(self, name, dvm_config, admin_config=None, nip89config=None): + def init(self, name, dvm_config, admin_config=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: @@ -36,9 +36,15 @@ class DVMTaskInterface: self.FIX_COST = dvm_config.FIX_COST if dvm_config.PER_UNIT_COST is not None: self.PER_UNIT_COST = dvm_config.PER_UNIT_COST + if task is not None: + self.TASK = task + dvm_config.SUPPORTED_DVMS = [self] dvm_config.DB = "db/" + self.NAME + ".db" + if nip89config.KIND is not None: + self.KIND = nip89config.KIND + dvm_config.NIP89 = self.NIP89_announcement(nip89config) self.dvm_config = dvm_config self.admin_config = admin_config @@ -49,12 +55,12 @@ class DVMTaskInterface: nostr_dvm_thread.start() def NIP89_announcement(self, nip89config: NIP89Config): - nip89 = NIP89Announcement() - nip89.name = self.NAME - nip89.kind = self.KIND - nip89.pk = self.PRIVATE_KEY - nip89.dtag = nip89config.DTAG - nip89.content = nip89config.CONTENT + nip89 = NIP89Config() + nip89.NAME = self.NAME + nip89.KIND = self.KIND + nip89.PK = self.PRIVATE_KEY + nip89.DTAG = nip89config.DTAG + nip89.CONTENT = nip89config.CONTENT return nip89 def is_input_supported(self, tags) -> bool: diff --git a/main.py b/main.py index 78c70a2..90f7350 100644 --- a/main.py +++ b/main.py @@ -11,7 +11,8 @@ from nostr_sdk import Keys from bot.bot import Bot from playground import build_pdf_extractor, build_googletranslator, build_unstable_diffusion, build_sketcher, \ build_dalle, \ - build_whisperx, build_libretranslator + build_whisperx, build_libretranslator, build_external_dvm +from utils.definitions import EventDefinitions from utils.dvmconfig import DVMConfig @@ -73,8 +74,19 @@ def run_nostr_dvm_with_local_config(): bot_config.SUPPORTED_DVMS.append(dalle) dalle.run() - bot = Bot(bot_config) - bot.run() + # Spawn DVM7.. oh wait, actually we don't spawn a new DVM, we use the dvmtaskinterface to define an external dvm by providing some info about it, such as + # their pubkey, a name, task, kind etc. + + libretranslate_external = build_external_dvm(name="External DVM test", + pubkey="08fd6bdb17cb2c8a87f8d50653238cb46e26cd44948c474f51dae5f138609da6", + task="translation", + kind=EventDefinitions.KIND_NIP90_TRANSLATE_TEXT, + fix_cost=0, per_unit_cost=0) + bot_config.SUPPORTED_DVMS.append(libretranslate_external) + #Don't run it, it's on someone else's machine and we simply make the bot aware of it. + + Bot(bot_config) + # Keep the main function alive for libraries that require it, like openai try: diff --git a/playground.py b/playground.py index a72eee3..54ed81c 100644 --- a/playground.py +++ b/playground.py @@ -11,6 +11,7 @@ from tasks.textextractionpdf import TextExtractionPDF from tasks.translation_google import TranslationGoogle from tasks.translation_libretranslate import TranslationLibre from utils.admin_utils import AdminConfig +from utils.definitions import EventDefinitions from utils.dvmconfig import DVMConfig from utils.nip89_utils import NIP89Config @@ -93,8 +94,7 @@ def build_googletranslator(name): nip89config = NIP89Config() nip89config.DTAG = os.getenv("TASK_TRANSLATION_NIP89_DTAG") nip89config.CONTENT = json.dumps(nip89info) - return TranslationGoogle(name=name, dvm_config=dvm_config, nip89config=nip89config, - admin_config=admin_config) + return TranslationGoogle(name=name, dvm_config=dvm_config, nip89config=nip89config, admin_config=admin_config) def build_libretranslator(name): @@ -160,7 +160,7 @@ def build_unstable_diffusion(name): nip89config = NIP89Config() nip89config.DTAG = os.getenv("TASK_IMAGE_GENERATION_NIP89_DTAG") nip89config.CONTENT = json.dumps(nip89info) - return ImageGenerationSDXL(name=name, dvm_config=dvm_config, nip89config=nip89config, + return ImageGenerationSDXL(name=name, dvm_config=dvm_config, nip89config=nip89config, admin_config=admin_config, options=options) @@ -194,7 +194,7 @@ def build_whisperx(name): nip89config = NIP89Config() nip89config.DTAG = os.getenv("TASK_SPEECH_TO_TEXT_NIP89") nip89config.CONTENT = json.dumps(nip89info) - return SpeechToTextWhisperX(name=name, dvm_config=dvm_config, nip89config=nip89config, + return SpeechToTextWhisperX(name=name, dvm_config=dvm_config, nip89config=nip89config, admin_config=admin_config, options=options) @@ -229,7 +229,7 @@ def build_sketcher(name): nip89config.DTAG = os.getenv("TASK_IMAGE_GENERATION_NIP89_DTAG2") nip89config.CONTENT = json.dumps(nip89info) # We add an optional AdminConfig for this one, and tell the dvm to rebroadcast its NIP89 - return ImageGenerationSDXL(name=name, dvm_config=dvm_config, nip89config=nip89config, + return ImageGenerationSDXL(name=name, dvm_config=dvm_config, nip89config=nip89config, admin_config=admin_config, options=options) @@ -261,20 +261,22 @@ def build_dalle(name): nip89config.DTAG = os.getenv("TASK_IMAGE_GENERATION_NIP89_DTAG3") nip89config.CONTENT = json.dumps(nip89info) # We add an optional AdminConfig for this one, and tell the dvm to rebroadcast its NIP89 - return ImageGenerationDALLE(name=name, dvm_config=dvm_config, nip89config=nip89config, - admin_config=admin_config) + return ImageGenerationDALLE(name=name, dvm_config=dvm_config, nip89config=nip89config, admin_config=admin_config) -def external_dvm(name, pubkey): +def build_external_dvm(name, pubkey, task, kind, fix_cost, per_unit_cost): dvm_config = DVMConfig() - dvm_config.PUBLIC_KEY = Keys.from_public_key(pubkey).public_key().to_hex() + dvm_config.PUBLIC_KEY = PublicKey.from_hex(pubkey).to_hex() + dvm_config.FIX_COST = fix_cost + dvm_config.PER_UNIT_COST = per_unit_cost nip89info = { "name": name, } nip89config = NIP89Config() + nip89config.KIND = kind nip89config.CONTENT = json.dumps(nip89info) - return DVMTaskInterface(name=name, dvm_config=dvm_config, nip89config=nip89config) + return DVMTaskInterface(name=name, dvm_config=dvm_config, nip89config=nip89config, task=task) # Little Gimmick: diff --git a/tasks/textextraction_whisperx.py b/tasks/textextraction_whisperx.py index adfe096..53b4447 100644 --- a/tasks/textextraction_whisperx.py +++ b/tasks/textextraction_whisperx.py @@ -8,7 +8,7 @@ from backends.nova_server import check_nova_server_status, send_request_to_nova_ from interfaces.dvmtaskinterface import DVMTaskInterface from utils.admin_utils import AdminConfig from utils.dvmconfig import DVMConfig -from utils.mediasource_utils import organize_input_data +from utils.mediasource_utils import organize_input_data_to_audio from utils.nip89_utils import NIP89Config from utils.definitions import EventDefinitions @@ -102,7 +102,7 @@ class SpeechToTextWhisperX(DVMTaskInterface): except: end_time = float(tag.as_vec()[3]) - filepath = organize_input_data(url, input_type, start_time, end_time, dvm_config, client) + filepath = organize_input_data_to_audio(url, input_type, start_time, end_time, dvm_config, client) pathonserver = send_file_to_nova_server(filepath, self.options['nova_server']) io_input = { diff --git a/tasks/translation_libretranslate.py b/tasks/translation_libretranslate.py index d44737e..903f029 100644 --- a/tasks/translation_libretranslate.py +++ b/tasks/translation_libretranslate.py @@ -24,8 +24,8 @@ class TranslationLibre(DVMTaskInterface): FIX_COST: float = 0 def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, - admin_config: AdminConfig = None, options=None): - super().__init__(name, dvm_config, nip89config, admin_config, options) + admin_config: AdminConfig = None, options=None, task=None): + super().__init__(name, dvm_config, nip89config, admin_config, options, task) def is_input_supported(self, tags): for tag in tags: @@ -88,10 +88,10 @@ class TranslationLibre(DVMTaskInterface): reply = json.loads(response.text) if reply.get("translatedText"): translated_text = reply['translatedText'] - - confidence = reply["detectedLanguage"]['confidence'] - language = reply["detectedLanguage"]['language'] - print(translated_text + "language: " + language + "conf: " + confidence) + #untested + #confidence = reply["detectedLanguage"]['confidence'] + #language = reply["detectedLanguage"]['language'] + #print(translated_text + "language: " + language + "conf: " + confidence) else: return response.text diff --git a/utils/definitions.py b/utils/definitions.py index cc7140f..373cd01 100644 --- a/utils/definitions.py +++ b/utils/definitions.py @@ -5,6 +5,7 @@ from nostr_sdk import Event class EventDefinitions: KIND_DM: int = 4 KIND_ZAP: int = 9735 + KIND_ANNOUNCEMENT: int = 31990 KIND_NIP94_METADATA: int = 1063 KIND_FEEDBACK: int = 7000 KIND_NIP90_EXTRACT_TEXT = 5000 diff --git a/utils/dvmconfig.py b/utils/dvmconfig.py index a6c1e38..39aaa09 100644 --- a/utils/dvmconfig.py +++ b/utils/dvmconfig.py @@ -2,7 +2,7 @@ import os from nostr_sdk import Keys -from utils.nip89_utils import NIP89Announcement +from utils.nip89_utils import NIP89Config class DVMConfig: @@ -22,7 +22,7 @@ class DVMConfig: LNBITS_URL = 'https://lnbits.com' DB: str NEW_USER_BALANCE: int = 250 # Free credits for new users - NIP89: NIP89Announcement + NIP89: NIP89Config SHOW_RESULT_BEFORE_PAYMENT: bool = False # if this is true show results even when not paid right after autoprocess diff --git a/utils/mediasource_utils.py b/utils/mediasource_utils.py index b574058..c7ed688 100644 --- a/utils/mediasource_utils.py +++ b/utils/mediasource_utils.py @@ -9,7 +9,7 @@ from utils.nostr_utils import get_event_by_id def input_data_file_duration(event, dvm_config, client, start=0, end=0): - print("[" + dvm_config.NIP89.name + "] Getting Duration of the Media file..") + print("[" + dvm_config.NIP89.NAME + "] Getting Duration of the Media file..") input_value = "" input_type = "url" for tag in event.tags(): @@ -17,11 +17,17 @@ def input_data_file_duration(event, dvm_config, client, start=0, end=0): input_value = tag.as_vec()[1] input_type = tag.as_vec()[2] + if input_type == "text": + #For now, ingore length of any text, just return 1. + return 1 + if input_type == "event": # NIP94 event evt = get_event_by_id(input_value, client=client, config=dvm_config) if evt is not None: input_value, input_type = check_nip94_event_for_media(evt, input_value, input_type) - + if input_type == "text": + # For now, ingore length of any text, just return 1. + return 1 if input_type == "url": source_type = check_source_type(input_value) @@ -46,9 +52,7 @@ def input_data_file_duration(event, dvm_config, client, start=0, end=0): return 1 - - -def organize_input_data(input_value, input_type, start, end, dvm_config, client, process=True) -> str: +def organize_input_data_to_audio(input_value, input_type, start, end, dvm_config, client) -> str: if input_type == "event": # NIP94 event evt = get_event_by_id(input_value, client=client, config=dvm_config) if evt is not None: @@ -71,7 +75,6 @@ def organize_input_data(input_value, input_type, start, end, dvm_config, client, convert_media_length(start, end, duration)) print("New Duration of the Media file: " + str(new_duration)) - # TODO if already in a working format and time is 0 0, dont convert print("Converting from " + str(start_time) + " until " + str(end_time)) # for now, we cut and convert all files to mp3 @@ -81,6 +84,7 @@ def organize_input_data(input_value, input_type, start, end, dvm_config, client, ffmpegio.audio.write(final_filename, fs, x, overwrite=True) return final_filename + def check_nip94_event_for_media(evt, input_value, input_type): # Parse NIP94 event for url, if found, use it. if evt.kind() == 1063: @@ -92,6 +96,7 @@ def check_nip94_event_for_media(evt, input_value, input_type): return input_value, input_type + def convert_media_length(start: float, end: float, duration: float): if end == 0.0: end_time = duration diff --git a/utils/nip89_utils.py b/utils/nip89_utils.py index cefca0e..e67e536 100644 --- a/utils/nip89_utils.py +++ b/utils/nip89_utils.py @@ -1,18 +1,17 @@ -from nostr_sdk import Tag, Keys, EventBuilder +from datetime import timedelta + +from nostr_sdk import Tag, Keys, EventBuilder, Filter, Alphabet, PublicKey, Event + +from utils.definitions import EventDefinitions from utils.nostr_utils import send_event -class NIP89Announcement: - name: str - kind: int - dtag: str - pk: str - content: str - - class NIP89Config: - DTAG: str - CONTENT: str + DTAG: str = "" + NAME: str = "" + KIND: int = None + PK: str = "" + CONTENT: str = "" def nip89_announce_tasks(dvm_config, client): @@ -20,6 +19,38 @@ def nip89_announce_tasks(dvm_config, client): d_tag = Tag.parse(["d", dvm_config.NIP89.dtag]) keys = Keys.from_sk_str(dvm_config.NIP89.pk) content = dvm_config.NIP89.content - event = EventBuilder(31990, content, [k_tag, d_tag]).to_event(keys) + event = EventBuilder(EventDefinitions.KIND_ANNOUNCEMENT, content, [k_tag, d_tag]).to_event(keys) send_event(event, client=client, dvm_config=dvm_config) - print("Announced NIP 89 for " + dvm_config.NIP89.name) + print("Announced NIP 89 for " + dvm_config.NIP89.NAME) + + +def nip89_fetch_all_dvms(client): + ktags = [] + for i in range(5000, 5999): + ktags.append(str(i)) + + filter = Filter().kind(EventDefinitions.KIND_ANNOUNCEMENT).custom_tag(Alphabet.K, ktags) + events = client.get_events_of([filter], timedelta(seconds=5)) + for event in events: + print(event.as_json()) + + +def nip89_fetch_events_pubkey(client, pubkey, kind): + ktags = [str(kind)] + # for i in range(5000, 5999): + # ktags.append(str(i)) + nip89filter = (Filter().kind(EventDefinitions.KIND_ANNOUNCEMENT).author(PublicKey.from_hex(pubkey)). + custom_tag(Alphabet.K, ktags)) + events = client.get_events_of([nip89filter], timedelta(seconds=2)) + + dvms = {} + for event in events: + if dvms.get(event.pubkey().to_hex()): + if dvms.get(event.pubkey().to_hex()).created_at().as_secs() < event.created_at().as_secs(): + dvms[event.pubkey().to_hex()] = event + else: + dvms[event.pubkey().to_hex()] = event + + # should be one element of the kind now + for dvm in dvms: + return dvms[dvm].content() diff --git a/utils/nostr_utils.py b/utils/nostr_utils.py index 23a45d9..f989a90 100644 --- a/utils/nostr_utils.py +++ b/utils/nostr_utils.py @@ -78,7 +78,7 @@ def check_and_decrypt_tags(event, dvm_config): if is_encrypted: if p != dvm_config.PUBLIC_KEY: - print("[" + dvm_config.NIP89.name + "] Task encrypted and not addressed to this DVM, " + print("[" + dvm_config.NIP89.NAME + "] Task encrypted and not addressed to this DVM, " "skipping..") return None @@ -111,7 +111,7 @@ def check_and_decrypt_own_tags(event, dvm_config): if is_encrypted: if dvm_config.PUBLIC_KEY != event.pubkey().to_hex(): - print("[" + dvm_config.NIP89.name + "] Task encrypted and not addressed to this DVM, " + print("[" + dvm_config.NIP89.NAME + "] Task encrypted and not addressed to this DVM, " "skipping..") return None diff --git a/utils/output_utils.py b/utils/output_utils.py index daf50e8..68eefe7 100644 --- a/utils/output_utils.py +++ b/utils/output_utils.py @@ -83,7 +83,6 @@ def post_process_result(anno, original_event): elif isinstance(anno, NoneType): return "An error occurred" else: - print("Nonetype") result = replace_broken_words(anno) # TODO return result diff --git a/utils/zap_utils.py b/utils/zap_utils.py index 0f8b007..40c937e 100644 --- a/utils/zap_utils.py +++ b/utils/zap_utils.py @@ -86,7 +86,7 @@ def create_bolt11_ln_bits(sats: int, config: DVMConfig) -> (str, str): if config.LNBITS_URL == "": return None url = config.LNBITS_URL + "/api/v1/payments" - data = {'out': False, 'amount': sats, 'memo': "Nostr-DVM " + config.NIP89.name} + data = {'out': False, 'amount': sats, 'memo': "Nostr-DVM " + config.NIP89.NAME} headers = {'X-API-Key': config.LNBITS_INVOICE_KEY, 'Content-Type': 'application/json', 'charset': 'UTF-8'} try: res = requests.post(url, json=data, headers=headers) @@ -285,7 +285,7 @@ def redeem_cashu(cashu, required_amount, config, client) -> (bool, str): err = ("Token value (Payment: " + str(total_amount) + " Sats. Fees: " + str(fees) + " Sats) below required amount of " + str(required_amount) + " Sats. Cashu token has not been claimed.") - print("[" + config.NIP89.name + "] " + err) + print("[" + config.NIP89.NAME + "] " + err) return False, err if config.LNBITS_INVOICE_KEY != "":