auto generate keys/dtagsl auto update user profiles, sdimg2img

This commit is contained in:
Believethehype 2023-11-30 15:30:49 +01:00
parent 023ba6d55f
commit 028c32f42d
12 changed files with 493 additions and 155 deletions

View File

@ -1,38 +1,16 @@
#This is needed for the test_client
NOSTR_TEST_CLIENT_PRIVATE_KEY = "a secret hex key for the test dvm client"
#This is needed for the (optional) bot
BOT_PRIVATE_KEY = "The private key for a test bot that communicates with dvms"
#These are all for the playground and can be replaced and adjusted however needed
NOSTR_PRIVATE_KEY = "a secret hexkey for some demo dvms"
NOSTR_PRIVATE_KEY2 = "another secret hexkey for demo dvm with another key"
NOSTR_PRIVATE_KEY3 = "another secret hexkey for demo dvm with another key"
NOSTR_PRIVATE_KEY4 = "another secret hexkey for demo dvm with another key"
NOSTR_PRIVATE_KEY5 = "another secret hexkey for demo dvm with another key"
BOT_PRIVATE_KEY = "The private key for a test bot that communicates with dvms"
NOSTR_TEST_CLIENT_PRIVATE_KEY = "a secret hex key for the test dvm client"
# Optional LNBITS options to create invoices (if empty, it will use the lud16 from profile, make sure to set one)
# Optional LNBITS options to create invoices (if empty, it will use the lud16 from profile)
# Admin Key is (only) required for bot or if any payments should be made
LNBITS_INVOICE_KEY = ""
LNBITS_ADMIN_KEY = "" # In order to pay invoices, e.g. from the bot to DVMs, or reimburse users. Keep this secret and use responsibly.
LNBITS_HOST = "https://lnbits.com"
#Backend Specific Options for tasks that require them. A DVM needing these should only be started if these are set.
# Some d tags we use in the testfile to announce or dvms. Create one at vendata.io)
TASK_TEXT_EXTRACTION_NIP89_DTAG = "asdd"
TASK_TRANSLATION_NIP89_DTAG = "abcded"
TASK_IMAGE_GENERATION_NIP89_DTAG = "fgdfgdf"
TASK_IMAGE_GENERATION_NIP89_DTAG2 = "fdgdfg"
TASK_IMAGE_GENERATION_NIP89_DTAG3 = "asdasd"
TASK_SPEECH_TO_TEXT_NIP89 = "asdasdas"
TASK_MEDIA_CONVERTER_NIP89_DTAG = "asdasdasd"
TASK_DISCOVER_INACTIVE_NIP89_DTAG = "sdfdfsdf12312"
#Backend Specific Options for tasks that require inputs, such as Endpoints or API Keys
OPENAI_API_KEY = "" # Enter your OpenAI API Key to use DVMs with OpenAI services
LIBRE_TRANSLATE_ENDPOINT = "" # Url to LibreTranslate Endpoint e.g. https://libretranslate.com
LIBRE_TRANSLATE_API_KEY = "" # API Key, if required (You can host your own instance where you don't need it)
NOVA_SERVER = "" # Enter the address of a nova-server instance, locally or on a machine in your network host:port
NOVA_SERVER = "" # Enter the address of a nova-server instance, locally or on a machine in your network host:port
# We will automatically create dtags and private keys based on the identifier variable in main.
# If your DVM already has a dtag and private key you can replace it here before publishing the DTAG to not create a new one.
# The name and NIP90 info of the DVM can be changed but the identifier must stay the same in order to not create a different dtag.

1
.gitignore vendored
View File

@ -162,3 +162,4 @@ cython_debug/
*.db
outputs
.env2

47
main.py
View File

@ -1,26 +1,24 @@
import os
import signal
import sys
import time
from pathlib import Path
from threading import Thread
import dotenv
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_external_dvm, build_media_converter, build_inactive_follows_finder
build_whisperx, build_libretranslator, build_external_dvm, build_media_converter, build_inactive_follows_finder, \
build_image_converter
from utils.definitions import EventDefinitions
from utils.dvmconfig import DVMConfig
from utils.nostr_utils import check_and_set_private_key
def run_nostr_dvm_with_local_config():
# We will run an optional bot that can communicate with the DVMs
# Note this is very basic for now and still under development
bot_config = DVMConfig()
bot_config.PRIVATE_KEY = os.getenv("BOT_PRIVATE_KEY")
bot_config.PRIVATE_KEY = check_and_set_private_key("bot")
bot_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY")
bot_config.LNBITS_ADMIN_KEY = os.getenv("LNBITS_ADMIN_KEY") # The bot will forward zaps for us, use responsibly
bot_config.LNBITS_URL = os.getenv("LNBITS_HOST")
@ -29,38 +27,46 @@ def run_nostr_dvm_with_local_config():
# You can add arbitrary DVMs there and instantiate them here
# Spawn DVM1 Kind 5000: A local Text Extractor from PDFs
pdfextractor = build_pdf_extractor("PDF Extractor")
pdfextractor = build_pdf_extractor("PDF Extractor", "pdf_extractor")
# If we don't add it to the bot, the bot will not provide access to the DVM
pdfextractor.run()
# Spawn DVM2 Kind 5002 Local Text TranslationGoogle, calling the free Google API.
translator = build_googletranslator("Google Translator")
translator = build_googletranslator("Google Translator", "google_translator")
bot_config.SUPPORTED_DVMS.append(translator) # We add translator to the bot
translator.run()
# Spawn DVM3 Kind 5002 Local Text TranslationLibre, calling the free LibreTranslateApi, as an alternative.
# This will only run and appear on the bot if an endpoint is set in the .env
if os.getenv("LIBRE_TRANSLATE_ENDPOINT") is not None and os.getenv("LIBRE_TRANSLATE_ENDPOINT") != "":
libre_translator = build_libretranslator("Libre Translator")
libre_translator = build_libretranslator("Libre Translator", "google_translator")
bot_config.SUPPORTED_DVMS.append(libre_translator) # We add translator to the bot
libre_translator.run()
# Spawn DVM3 Kind 5100 Image Generation This one uses a specific backend called nova-server.
# If you want to use it, see the instructions in backends/nova_server
if os.getenv("NOVA_SERVER") is not None and os.getenv("NOVA_SERVER") != "":
unstable_artist = build_unstable_diffusion("Unstable Diffusion")
unstable_artist = build_unstable_diffusion("Unstable Diffusion", "unstable_diffusion")
bot_config.SUPPORTED_DVMS.append(unstable_artist) # We add unstable Diffusion to the bot
unstable_artist.run()
# Spawn DVM4, another Instance of text-to-image, as before but use a different privatekey, model and lora this time.
if os.getenv("NOVA_SERVER") is not None and os.getenv("NOVA_SERVER") != "":
sketcher = build_sketcher("Sketcher")
sketcher = build_sketcher("Sketcher", "sketcher")
bot_config.SUPPORTED_DVMS.append(sketcher) # We also add Sketcher to the bot
sketcher.run()
# Spawn DVM5, image-to-image, .
if os.getenv("NOVA_SERVER") is not None and os.getenv("NOVA_SERVER") != "":
imageconverter = build_image_converter("Image Converter Inkpunk", "image_converter_inkpunk")
bot_config.SUPPORTED_DVMS.append(imageconverter) # We also add Sketcher to the bot
imageconverter.run()
# Spawn DVM5, Another script on nova-server calling WhisperX to transcribe media files
if os.getenv("NOVA_SERVER") is not None and os.getenv("NOVA_SERVER") != "":
whisperer = build_whisperx("Whisperer")
whisperer = build_whisperx("Whisperer", "whisperx")
bot_config.SUPPORTED_DVMS.append(whisperer) # We also add Sketcher to the bot
whisperer.run()
@ -68,7 +74,7 @@ def run_nostr_dvm_with_local_config():
# per call. Make sure you have enough balance and the DVM's cost is set higher than what you pay yourself, except, you know,
# you're being generous.
if os.getenv("OPENAI_API_KEY") is not None and os.getenv("OPENAI_API_KEY") != "":
dalle = build_dalle("Dall-E 3")
dalle = build_dalle("Dall-E 3", "dalle3")
bot_config.SUPPORTED_DVMS.append(dalle)
dalle.run()
@ -82,7 +88,8 @@ def run_nostr_dvm_with_local_config():
tasktiger_external.SUPPORTS_ENCRYPTION = False # if the dvm does not support encrypted events, just send a regular event and mark it with p tag. Other dvms might initial answer
bot_config.SUPPORTED_DVMS.append(tasktiger_external)
# Don't run it, it's on someone else's machine and we simply make the bot aware of it.
# Don't run it, it's on someone else's machine, and we simply make the bot aware of it.
# DVM: 8 Another external dvm for recommendations:
ymhm_external = build_external_dvm(name="External DVM: You might have missed",
@ -95,16 +102,18 @@ def run_nostr_dvm_with_local_config():
bot_config.SUPPORTED_DVMS.append(ymhm_external)
# Spawn DVM9.. A Media Grabber/Converter
media_bringer = build_media_converter("Media Bringer")
bot_config.SUPPORTED_DVMS.append(media_bringer) # We also add Sketcher to the bot
media_bringer = build_media_converter("Media Bringer", "media_converter")
bot_config.SUPPORTED_DVMS.append(media_bringer)
media_bringer.run()
#Spawn DVM10 Discover inactive followers
discover_inactive = build_inactive_follows_finder("Bygones")
bot_config.SUPPORTED_DVMS.append(discover_inactive) # We also add Sketcher to the bot
# Spawn DVM10 Discover inactive followers
discover_inactive = build_inactive_follows_finder("Bygones", "discovery_inactive_follows")
bot_config.SUPPORTED_DVMS.append(discover_inactive)
discover_inactive.run()
Bot(bot_config)
# Keep the main function alive for libraries that require it, like openai

View File

@ -1,37 +1,36 @@
import json
import os
from nostr_sdk import PublicKey, Keys
from nostr_sdk import PublicKey
from interfaces.dvmtaskinterface import DVMTaskInterface
from tasks.convert_media import MediaConverter
from tasks.discovery_inactive_follows import DiscoverInactiveFollows
from tasks.imagegeneration_openai_dalle import ImageGenerationDALLE
from tasks.imagegeneration_sdxl import ImageGenerationSDXL
from tasks.imagegeneration_sdxlimg2img import ImageGenerationSDXLIMG2IMG
from tasks.textextraction_whisperx import SpeechToTextWhisperX
from tasks.textextraction_pdf 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, nip89_create_d_tag
from utils.nip89_utils import NIP89Config, check_and_set_d_tag
from utils.nostr_utils import check_and_set_private_key
"""
This File is a playground to create DVMs. It shows some examples of DVMs that make use of the modules in the tasks folder
These DVMs should be considered examples and will be extended in the future. env variables are used to not commit keys,
but if used privatley, these can also be directly filled in this file. The main.py function calls some of the functions
defined here and starts the DVMs.
These DVMs should be considered examples and will be extended in the future.
Keys and dtags will be automatically generated and stored in the .env file.
If you already have a pk and dtag you can replace them there before publishing the nip89
Note that the admin_config is optional, and if given commands as defined in admin_utils will be called at start of the
DVM. For example the NIP89 event can be rebroadcasted (store the d_tag somewhere).
DVM. For example the NIP89 event can be rebroadcasted.
DM_ALLOWED is used to tell the DVM to which npubs it should listen to. We use this here to listen to our bot,
as defined in main.py to perform jobs on it's behalf and reply.
If LNBITS_INVOICE_KEY is not set (=""), the DVM is still zappable but a lud16 address in required in the profile.
if LNBITS_INVOICE_KEY is not set (=""), the DVM is still zappable but a lud16 address in required in the profile.
additional options can be set, for example to preinitalize vaiables or give parameters that are required to perform a
Additional options can be set, for example to preinitalize vaiables or give parameters that are required to perform a
task, for example an address or an API key.
@ -39,17 +38,28 @@ task, for example an address or an API key.
# Generate an optional Admin Config, in this case, whenever we give our DVMs this config, they will (re)broadcast
# their NIP89 announcement
# You can create individual admins configs and hand them over when initializing the dvm,
# for example to whilelist users or add to their balance.
# If you use this global config, options will be set for all dvms that use it.
admin_config = AdminConfig()
admin_config.REBROADCAST_NIP89 = False
# Set rebroadcast to true once you have set your NIP89 descriptions and d tags. You only need to rebroadcast once you
# want to update your NIP89 descriptions
admin_config.UPDATE_PROFILE = False
admin_config.LUD16 = ""
def build_pdf_extractor(name):
# Auto update the profiles of your privkeys based on the nip89 information.
# Ideally set a lightning address in the LUD16 field above so our DVMs can get zapped from everywhere
# We build a couple of example dvms, create privatekeys and dtags and set their NIP89 descriptions
# We currently call these from the main function and start the dvms there.
def build_pdf_extractor(name, identifier):
dvm_config = DVMConfig()
dvm_config.PRIVATE_KEY = os.getenv("NOSTR_PRIVATE_KEY")
dvm_config.PRIVATE_KEY = check_and_set_private_key(identifier)
dvm_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY")
dvm_config.LNBITS_URL = os.getenv("LNBITS_HOST")
# Add NIP89
@ -62,15 +72,15 @@ def build_pdf_extractor(name):
}
nip89config = NIP89Config()
nip89config.DTAG = os.getenv("TASK_TEXT_EXTRACTION_NIP89_DTAG")
nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, nip89info["image"])
nip89config.CONTENT = json.dumps(nip89info)
return TextExtractionPDF(name=name, dvm_config=dvm_config, nip89config=nip89config,
admin_config=admin_config)
def build_googletranslator(name):
def build_googletranslator(name, identifier):
dvm_config = DVMConfig()
dvm_config.PRIVATE_KEY = os.getenv("NOSTR_PRIVATE_KEY")
dvm_config.PRIVATE_KEY = check_and_set_private_key(identifier)
dvm_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY")
dvm_config.LNBITS_URL = os.getenv("LNBITS_HOST")
@ -94,14 +104,15 @@ def build_googletranslator(name):
"nip90Params": nip90params
}
nip89config = NIP89Config()
nip89config.DTAG = os.getenv("TASK_TRANSLATION_NIP89_DTAG")
nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY,
nip89info["image"])
nip89config.CONTENT = json.dumps(nip89info)
return TranslationGoogle(name=name, dvm_config=dvm_config, nip89config=nip89config, admin_config=admin_config)
def build_libretranslator(name):
def build_libretranslator(name, identifier):
dvm_config = DVMConfig()
dvm_config.PRIVATE_KEY = os.getenv("NOSTR_PRIVATE_KEY5")
dvm_config.PRIVATE_KEY = check_and_set_private_key(identifier)
dvm_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY")
dvm_config.LNBITS_URL = os.getenv("LNBITS_HOST")
@ -127,15 +138,16 @@ def build_libretranslator(name):
"nip90Params": nip90params
}
nip89config = NIP89Config()
nip89config.DTAG = os.getenv("TASK_TRANSLATION_NIP89_DTAG6")
nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY,
nip89info["image"])
nip89config.CONTENT = json.dumps(nip89info)
return TranslationLibre(name=name, dvm_config=dvm_config, nip89config=nip89config,
admin_config=admin_config, options=options)
def build_unstable_diffusion(name):
def build_unstable_diffusion(name, identifier):
dvm_config = DVMConfig()
dvm_config.PRIVATE_KEY = os.getenv("NOSTR_PRIVATE_KEY")
dvm_config.PRIVATE_KEY = check_and_set_private_key(identifier)
dvm_config.LNBITS_INVOICE_KEY = "" # This one will not use Lnbits to create invoices, but rely on zaps
dvm_config.LNBITS_URL = ""
@ -160,15 +172,16 @@ def build_unstable_diffusion(name):
"nip90Params": nip90params
}
nip89config = NIP89Config()
nip89config.DTAG = os.getenv("TASK_IMAGE_GENERATION_NIP89_DTAG")
nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY,
nip89info["image"])
nip89config.CONTENT = json.dumps(nip89info)
return ImageGenerationSDXL(name=name, dvm_config=dvm_config, nip89config=nip89config,
admin_config=admin_config, options=options)
def build_whisperx(name):
def build_whisperx(name, identifier):
dvm_config = DVMConfig()
dvm_config.PRIVATE_KEY = os.getenv("NOSTR_PRIVATE_KEY4")
dvm_config.PRIVATE_KEY = check_and_set_private_key(identifier)
dvm_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY")
dvm_config.LNBITS_URL = os.getenv("LNBITS_HOST")
@ -194,15 +207,16 @@ def build_whisperx(name):
"nip90Params": nip90params
}
nip89config = NIP89Config()
nip89config.DTAG = os.getenv("TASK_SPEECH_TO_TEXT_NIP89")
nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY,
nip89info["image"])
nip89config.CONTENT = json.dumps(nip89info)
return SpeechToTextWhisperX(name=name, dvm_config=dvm_config, nip89config=nip89config,
admin_config=admin_config, options=options)
def build_sketcher(name):
def build_sketcher(name, identifier):
dvm_config = DVMConfig()
dvm_config.PRIVATE_KEY = os.getenv("NOSTR_PRIVATE_KEY2")
dvm_config.PRIVATE_KEY = check_and_set_private_key(identifier)
dvm_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY")
dvm_config.LNBITS_URL = os.getenv("LNBITS_HOST")
@ -228,16 +242,58 @@ def build_sketcher(name):
options = {'default_model': "mohawk", 'default_lora': "timburton", 'nova_server': os.getenv("NOVA_SERVER")}
nip89config = NIP89Config()
nip89config.DTAG = os.getenv("TASK_IMAGE_GENERATION_NIP89_DTAG2")
nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY,
nip89info["image"])
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,
admin_config=admin_config, options=options)
def build_dalle(name):
def build_image_converter(name, identifier):
dvm_config = DVMConfig()
dvm_config.PRIVATE_KEY = os.getenv("NOSTR_PRIVATE_KEY3")
dvm_config.PRIVATE_KEY = check_and_set_private_key(identifier)
dvm_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY")
dvm_config.LNBITS_URL = os.getenv("LNBITS_HOST")
nip90params = {
"negative_prompt": {
"required": False,
"values": []
},
"lora": {
"required": False,
"values": ["inkpunk", "timburton", "voxel"]
},
"strength": {
"required": False,
"values": []
}
}
nip89info = {
"name": name,
"image": "https://image.nostr.build/229c14e440895da30de77b3ca145d66d4b04efb4027ba3c44ca147eecde891f1.jpg",
"about": "I convert an image to another image, kinda random for now. ",
"nip90Params": nip90params
}
# A module might have options it can be initialized with, here we set a default model, lora and the nova-server
options = {'default_lora': "inkpunk", 'strength': 0.5, 'nova_server': os.getenv("NOVA_SERVER")}
nip89config = NIP89Config()
nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY,
nip89info["image"])
nip89config.CONTENT = json.dumps(nip89info)
# We add an optional AdminConfig for this one, and tell the dvm to rebroadcast its NIP89
return ImageGenerationSDXLIMG2IMG(name=name, dvm_config=dvm_config, nip89config=nip89config,
admin_config=admin_config, options=options)
def build_dalle(name, identifier):
dvm_config = DVMConfig()
dvm_config.PRIVATE_KEY = check_and_set_private_key(identifier)
dvm_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY")
dvm_config.LNBITS_URL = os.getenv("LNBITS_HOST")
profit_in_sats = 10
@ -260,15 +316,16 @@ def build_dalle(name):
# address it should use. These parameters can be freely defined in the task component
nip89config = NIP89Config()
nip89config.DTAG = os.getenv("TASK_IMAGE_GENERATION_NIP89_DTAG3")
nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY,
nip89info["image"])
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)
def build_media_converter(name):
def build_media_converter(name, identifier):
dvm_config = DVMConfig()
dvm_config.PRIVATE_KEY = os.getenv("NOSTR_PRIVATE_KEY6")
dvm_config.PRIVATE_KEY = check_and_set_private_key(identifier)
dvm_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY")
dvm_config.LNBITS_URL = os.getenv("LNBITS_HOST")
# Add NIP89
@ -286,19 +343,15 @@ def build_media_converter(name):
}
nip89config = NIP89Config()
new_dtag = nip89_create_d_tag(name, Keys.from_sk_str(dvm_config.PRIVATE_KEY).public_key().to_hex(),
nip89info["image"])
print("Some new dtag:" + new_dtag)
nip89config.DTAG = os.getenv("TASK_MEDIA_CONVERTER_NIP89_DTAG")
nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, nip89info["image"])
nip89config.CONTENT = json.dumps(nip89info)
return MediaConverter(name=name, dvm_config=dvm_config, nip89config=nip89config,
admin_config=admin_config)
def build_inactive_follows_finder(name):
def build_inactive_follows_finder(name, identifier):
dvm_config = DVMConfig()
dvm_config.PRIVATE_KEY = os.getenv("NOSTR_PRIVATE_KEY7")
dvm_config.PRIVATE_KEY = check_and_set_private_key(identifier)
dvm_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY")
dvm_config.LNBITS_URL = os.getenv("LNBITS_HOST")
# Add NIP89
@ -323,14 +376,16 @@ def build_inactive_follows_finder(name):
}
nip89config = NIP89Config()
new_dtag = nip89_create_d_tag(name, Keys.from_sk_str(dvm_config.PRIVATE_KEY).public_key().to_hex(),
nip89info["image"])
print("Some new dtag:" + new_dtag)
nip89config.DTAG = os.getenv("TASK_DISCOVER_INACTIVE_NIP89_DTAG")
nip89config.DTAG = 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)
admin_config=admin_config)
# This function can be used to build a DVM object for a DVM we don't control, but we want the bot to be aware of.
# See main.py for examples.
def build_external_dvm(name, pubkey, task, kind, fix_cost, per_unit_cost):
dvm_config = DVMConfig()
dvm_config.PUBLIC_KEY = PublicKey.from_hex(pubkey).to_hex()
@ -346,8 +401,8 @@ def build_external_dvm(name, pubkey, task, kind, fix_cost, per_unit_cost):
return DVMTaskInterface(name=name, dvm_config=dvm_config, nip89config=nip89config, task=task)
# Little Gimmick:
# For Dalle where we have to pay 4cent per image, we fetch current sat price in fiat
# Little optional Gimmick:
# For Dalle where we have to pay 4cent per image to openai, we fetch current sat price in fiat from coinstats api
# and update cost at each start
def get_price_per_sat(currency):
import requests

View File

@ -6,12 +6,13 @@ Reusable backend functions can be defined in backends (e.g. API calls)
Current List of Tasks:
| Module | Kind | Description | Backend |
|----------------------|------|------------------------------------------------|-------------|
| TextExtractionPDF | 5000 | Extracts Text from a PDF file | local |
| SpeechToTextWhisperX | 5000 | Extracts Speech from Media files | nova-server |
| TranslationGoogle | 5002 | Translates Inputs to another language | google API |
| TranslationLibre | 5002 | Translates Inputs to another language | libre API |
| ImageGenerationSDXL | 5100 | Generates an Image with StableDiffusionXL | nova-server |
| ImageGenerationDALLE | 5100 | Generates an Image with Dall-E | openAI |
| MediaConverter | 5300 | Converts a link of a media file and uploads it | openAI |
| Module | Kind | Description | Backend |
|--------------------------|------|------------------------------------------------|-------------|
| TextExtractionPDF | 5000 | Extracts Text from a PDF file | local |
| SpeechToTextWhisperX | 5000 | Extracts Speech from Media files | nova-server |
| TranslationGoogle | 5002 | Translates Inputs to another language | googleAPI |
| TranslationLibre | 5002 | Translates Inputs to another language | libreAPI |
| ImageGenerationSDXL | 5100 | Generates an Image with StableDiffusionXL | nova-server |
| ImageGenerationDALLE | 5100 | Generates an Image with Dall-E | openAI |
| MediaConverter | 5200 | Converts a link of a media file and uploads it | openAI |
| DiscoverInactiveFollows | 5301 | Find inactive Nostr users | local |

View File

@ -82,9 +82,9 @@ class ImageGenerationSDXL(DVMTaskInterface):
elif tag.as_vec()[1] == "lora_weight":
lora_weight = tag.as_vec()[2]
elif tag.as_vec()[1] == "strength":
strength = tag.as_vec()[2]
strength = float(tag.as_vec()[2])
elif tag.as_vec()[1] == "guidance_scale":
guidance_scale = tag.as_vec()[2]
guidance_scale = float(tag.as_vec()[2])
elif tag.as_vec()[1] == "ratio":
if len(tag.as_vec()) > 3:
ratio_width = (tag.as_vec()[2])

View File

@ -0,0 +1,186 @@
import json
from multiprocessing.pool import ThreadPool
from backends.nova_server import check_nova_server_status, send_request_to_nova_server
from interfaces.dvmtaskinterface import DVMTaskInterface
from utils.admin_utils import AdminConfig
from utils.dvmconfig import DVMConfig
from utils.nip89_utils import NIP89Config
from utils.definitions import EventDefinitions
"""
This File contains a Module to transform Text input on NOVA-Server and receive results back.
Accepted Inputs: Prompt (text)
Outputs: An url to an Image
Params: -model # models: juggernaut, dynavision, colossusProject, newreality, unstable
-lora # loras (weights on top of models) voxel,
"""
class ImageGenerationSDXLIMG2IMG(DVMTaskInterface):
KIND: int = EventDefinitions.KIND_NIP90_GENERATE_IMAGE
TASK: str = "image-to-image"
FIX_COST: float = 50
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config,
admin_config: AdminConfig = None, options=None):
super().__init__(name, dvm_config, nip89config, admin_config, options)
def is_input_supported(self, tags):
hasurl = False
hasprompt = False
for tag in tags:
if tag.as_vec()[0] == 'i':
input_value = tag.as_vec()[1]
input_type = tag.as_vec()[2]
if input_type == "url":
hasurl = True
elif input_type == "text":
hasprompt = True #Little optional when lora is set
elif tag.as_vec()[0] == 'output':
output = tag.as_vec()[1]
if (output == "" or
not (output == "image/png" or "image/jpg"
or output == "image/png;format=url" or output == "image/jpg;format=url")):
print("Output format not supported, skipping..")
return False
if not hasurl:
return False
return True
def create_request_form_from_nostr_event(self, event, client=None, dvm_config=None):
request_form = {"jobID": event.id().to_hex() + "_" + self.NAME.replace(" ", "")}
request_form["trainerFilePath"] = r'modules\stablediffusionxl\stablediffusionxl-img2img.trainer'
prompt = ""
negative_prompt = ""
url = ""
if self.options.get("default_model"):
model = self.options['default_model']
else:
model = "stabilityai/stable-diffusion-xl-refiner-1.0"
ratio_width = "1"
ratio_height = "1"
width = ""
height = ""
if self.options.get("default_lora"):
lora = self.options['default_lora']
else:
lora = ""
lora_weight = ""
if self.options.get("strength"):
strength = float(self.options['strength'])
else:
strength = 0.8
if self.options.get("guidance_scale"):
guidance_scale = float(self.options['guidance_scale'])
else:
guidance_scale = 11.0
for tag in event.tags():
if tag.as_vec()[0] == 'i':
input_type = tag.as_vec()[2]
if input_type == "text":
prompt = tag.as_vec()[1]
elif input_type == "url":
url = tag.as_vec()[1]
elif tag.as_vec()[0] == 'param':
print("Param: " + tag.as_vec()[1] + ": " + tag.as_vec()[2])
if tag.as_vec()[1] == "negative_prompt":
negative_prompt = tag.as_vec()[2]
elif tag.as_vec()[1] == "lora":
lora = tag.as_vec()[2]
elif tag.as_vec()[1] == "lora_weight":
lora_weight = tag.as_vec()[2]
elif tag.as_vec()[1] == "strength":
strength = float(tag.as_vec()[2])
elif tag.as_vec()[1] == "guidance_scale":
guidance_scale = float(tag.as_vec()[2])
elif tag.as_vec()[1] == "ratio":
if len(tag.as_vec()) > 3:
ratio_width = (tag.as_vec()[2])
ratio_height = (tag.as_vec()[3])
elif len(tag.as_vec()) == 3:
split = tag.as_vec()[2].split(":")
ratio_width = split[0]
ratio_height = split[1]
# if size is set it will overwrite ratio.
elif tag.as_vec()[1] == "size":
if len(tag.as_vec()) > 3:
width = (tag.as_vec()[2])
height = (tag.as_vec()[3])
elif len(tag.as_vec()) == 3:
split = tag.as_vec()[2].split("x")
if len(split) > 1:
width = split[0]
height = split[1]
elif tag.as_vec()[1] == "model":
model = tag.as_vec()[2]
io_input_image = {
"id": "input_image",
"type": "input",
"src": "url:Image",
"uri": url
}
io_input = {
"id": "input_prompt",
"type": "input",
"src": "request:text",
"data": prompt
}
io_negative = {
"id": "negative_prompt",
"type": "input",
"src": "request:text",
"data": negative_prompt
}
io_output = {
"id": "output_image",
"type": "output",
"src": "request:image"
}
request_form['data'] = json.dumps([io_input_image, io_input, io_negative, io_output])
options = {
"model": model,
"ratio": ratio_width + '-' + ratio_height,
"width": width,
"height": height,
"strength": strength,
"guidance_scale": guidance_scale,
"lora": lora,
"lora_weight": lora_weight,
"n_steps": 30
}
request_form['options'] = json.dumps(options)
return request_form
def process(self, request_form):
try:
# Call the process route of NOVA-Server with our request form.
response = send_request_to_nova_server(request_form, self.options['nova_server'])
if bool(json.loads(response)['success']):
print("Job " + request_form['jobID'] + " sent to NOVA-server")
pool = ThreadPool(processes=1)
thread = pool.apply_async(check_nova_server_status, (request_form['jobID'], self.options['nova_server']))
print("Wait for results of NOVA-Server...")
result = thread.get()
return result
except Exception as e:
raise Exception(e)

View File

@ -10,13 +10,13 @@ from nostr_sdk import Keys, Client, Tag, EventBuilder, Filter, HandleNotificatio
nip04_encrypt
from utils.dvmconfig import DVMConfig
from utils.nostr_utils import send_event
from utils.nostr_utils import send_event, check_and_set_private_key
from utils.definitions import EventDefinitions
# TODO HINT: Best use this path with a previously whitelisted privkey, as zapping events is not implemented in the lib/code
def nostr_client_test_translation(input, kind, lang, sats, satsmax):
keys = Keys.from_sk_str(os.getenv("NOSTR_TEST_CLIENT_PRIVATE_KEY"))
keys = Keys.from_sk_str(check_and_set_private_key("test_client"))
if kind == "text":
iTag = Tag.parse(["i", input, "text"])
elif kind == "event":
@ -43,7 +43,7 @@ def nostr_client_test_translation(input, kind, lang, sats, satsmax):
def nostr_client_test_image(prompt):
keys = Keys.from_sk_str(os.getenv("NOSTR_TEST_CLIENT_PRIVATE_KEY"))
keys = Keys.from_sk_str(check_and_set_private_key("test_client"))
iTag = Tag.parse(["i", prompt, "text"])
outTag = Tag.parse(["output", "image/png;format=url"])
@ -70,8 +70,8 @@ def nostr_client_test_image(prompt):
def nostr_client_test_image_private(prompt, cashutoken):
keys = Keys.from_sk_str(os.getenv("NOSTR_TEST_CLIENT_PRIVATE_KEY"))
receiver_keys = Keys.from_sk_str(os.getenv("NOSTR_PRIVATE_KEY"))
keys = Keys.from_sk_str(check_and_set_private_key("test_client"))
receiver_keys = Keys.from_sk_str(check_and_set_private_key("sketcher"))
# TODO more advanced logic, more parsing, params etc, just very basic test functions for now
@ -109,7 +109,7 @@ def nostr_client_test_image_private(prompt, cashutoken):
return nip90request.as_json()
def nostr_client():
keys = Keys.from_sk_str(os.getenv("NOSTR_TEST_CLIENT_PRIVATE_KEY"))
keys = Keys.from_sk_str(check_and_set_private_key("test_client"))
sk = keys.secret_key()
pk = keys.public_key()
print(f"Nostr Client public key: {pk.to_bech32()}, Hex: {pk.to_hex()} ")
@ -127,13 +127,13 @@ def nostr_client():
client.subscribe([dm_zap_filter, dvm_filter])
#nostr_client_test_translation("This is the result of the DVM in spanish", "text", "es", 20, 20)
#nostr_client_test_translation("note1p8cx2dz5ss5gnk7c59zjydcncx6a754c0hsyakjvnw8xwlm5hymsnc23rs", "event", "es", 20,20)
nostr_client_test_translation("note1p8cx2dz5ss5gnk7c59zjydcncx6a754c0hsyakjvnw8xwlm5hymsnc23rs", "event", "es", 20,20)
#nostr_client_test_translation("44a0a8b395ade39d46b9d20038b3f0c8a11168e67c442e3ece95e4a1703e2beb", "event", "zh", 20, 20)
#nostr_client_test_image("a beautiful purple ostrich watching the sunset")
cashutoken = "cashuAeyJ0b2tlbiI6W3sicHJvb2ZzIjpbeyJpZCI6InZxc1VRSVorb0sxOSIsImFtb3VudCI6MSwiQyI6IjAyNWU3ODZhOGFkMmExYTg0N2YxMzNiNGRhM2VhMGIyYWRhZGFkOTRiYzA4M2E2NWJjYjFlOTgwYTE1NGIyMDA2NCIsInNlY3JldCI6InQ1WnphMTZKMGY4UElQZ2FKTEg4V3pPck5rUjhESWhGa291LzVzZFd4S0U9In0seyJpZCI6InZxc1VRSVorb0sxOSIsImFtb3VudCI6NCwiQyI6IjAyOTQxNmZmMTY2MzU5ZWY5ZDc3MDc2MGNjZmY0YzliNTMzMzVmZTA2ZGI5YjBiZDg2Njg5Y2ZiZTIzMjVhYWUwYiIsInNlY3JldCI6IlRPNHB5WE43WlZqaFRQbnBkQ1BldWhncm44UHdUdE5WRUNYWk9MTzZtQXM9In0seyJpZCI6InZxc1VRSVorb0sxOSIsImFtb3VudCI6MTYsIkMiOiIwMmRiZTA3ZjgwYmMzNzE0N2YyMDJkNTZiMGI3ZTIzZTdiNWNkYTBhNmI3Yjg3NDExZWYyOGRiZDg2NjAzNzBlMWIiLCJzZWNyZXQiOiJHYUNIdHhzeG9HM3J2WWNCc0N3V0YxbU1NVXczK0dDN1RKRnVwOHg1cURzPSJ9XSwibWludCI6Imh0dHBzOi8vbG5iaXRzLmJpdGNvaW5maXhlc3RoaXMub3JnL2Nhc2h1L2FwaS92MS9ScDlXZGdKZjlxck51a3M1eVQ2SG5rIn1dfQ=="
nostr_client_test_image_private("a beautiful ostrich watching the sunset", cashutoken )
#cashutoken = "cashuAeyJ0b2tlbiI6W3sicHJvb2ZzIjpbeyJpZCI6InZxc1VRSVorb0sxOSIsImFtb3VudCI6MSwiQyI6IjAyNWU3ODZhOGFkMmExYTg0N2YxMzNiNGRhM2VhMGIyYWRhZGFkOTRiYzA4M2E2NWJjYjFlOTgwYTE1NGIyMDA2NCIsInNlY3JldCI6InQ1WnphMTZKMGY4UElQZ2FKTEg4V3pPck5rUjhESWhGa291LzVzZFd4S0U9In0seyJpZCI6InZxc1VRSVorb0sxOSIsImFtb3VudCI6NCwiQyI6IjAyOTQxNmZmMTY2MzU5ZWY5ZDc3MDc2MGNjZmY0YzliNTMzMzVmZTA2ZGI5YjBiZDg2Njg5Y2ZiZTIzMjVhYWUwYiIsInNlY3JldCI6IlRPNHB5WE43WlZqaFRQbnBkQ1BldWhncm44UHdUdE5WRUNYWk9MTzZtQXM9In0seyJpZCI6InZxc1VRSVorb0sxOSIsImFtb3VudCI6MTYsIkMiOiIwMmRiZTA3ZjgwYmMzNzE0N2YyMDJkNTZiMGI3ZTIzZTdiNWNkYTBhNmI3Yjg3NDExZWYyOGRiZDg2NjAzNzBlMWIiLCJzZWNyZXQiOiJHYUNIdHhzeG9HM3J2WWNCc0N3V0YxbU1NVXczK0dDN1RKRnVwOHg1cURzPSJ9XSwibWludCI6Imh0dHBzOi8vbG5iaXRzLmJpdGNvaW5maXhlc3RoaXMub3JnL2Nhc2h1L2FwaS92MS9ScDlXZGdKZjlxck51a3M1eVQ2SG5rIn1dfQ=="
#nostr_client_test_image_private("a beautiful ostrich watching the sunset", cashutoken )
class NotificationHandler(HandleNotification):
def handle(self, relay_url, event):
print(f"Received new event from {relay_url}: {event.as_json()}")
@ -161,7 +161,7 @@ def nostr_client():
if __name__ == '__main__':
env_path = Path('../.env')
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)

View File

@ -6,18 +6,23 @@ from nostr_sdk import Keys, EventBuilder, PublicKey
from utils.database_utils import get_from_sql_table, list_db, delete_from_sql_table, update_sql_table, \
get_or_add_user, clean_db
from utils.dvmconfig import DVMConfig
from utils.nip89_utils import nip89_announce_tasks
from utils.nostr_utils import send_event
from utils.nip89_utils import nip89_announce_tasks, NIP89Config
from utils.nostr_utils import send_event, update_profile
class AdminConfig:
REBROADCAST_NIP89: bool = False
UPDATE_PROFILE: bool = False
WHITELISTUSER: bool = False
UNWHITELISTUSER: bool = False
BLACKLISTUSER: bool = False
DELETEUSER: bool = False
LISTDATABASE: bool = False
ClEANDB: bool = False
USERNPUB: str = ""
LUD16: str = ""
def admin_make_database_updates(adminconfig: AdminConfig = None, dvmconfig: DVMConfig = None, client=None):
# This is called on start of Server, Admin function to manually whitelist/blacklist/add balance/delete users
@ -31,46 +36,43 @@ def admin_make_database_updates(adminconfig: AdminConfig = None, dvmconfig: DVMC
and adminconfig.USERNPUB == ""):
return
if adminconfig.UPDATE_PROFILE and (dvmconfig.NIP89 is None):
return
db = dvmconfig.DB
rebroadcast_nip89 = adminconfig.REBROADCAST_NIP89
cleandb = adminconfig.ClEANDB
listdatabase = adminconfig.LISTDATABASE
deleteuser = adminconfig.DELETEUSER
whitelistuser = adminconfig.WHITELISTUSER
unwhitelistuser = adminconfig.UNWHITELISTUSER
blacklistuser = adminconfig.BLACKLISTUSER
if str(adminconfig.USERNPUB).startswith("npub"):
publickey = PublicKey.from_bech32(adminconfig.USERNPUB).to_hex()
else:
publickey = adminconfig.USERNPUB
if adminconfig.USERNPUB != "":
if str(adminconfig.USERNPUB).startswith("npub"):
publickey = PublicKey.from_bech32(adminconfig.USERNPUB).to_hex()
else:
publickey = adminconfig.USERNPUB
if whitelistuser:
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)
user = get_from_sql_table(db, publickey)
print(str(user.name) + " is whitelisted: " + str(user.iswhitelisted))
if unwhitelistuser:
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)
if blacklistuser:
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)
if deleteuser:
if adminconfig.DELETEUSER:
delete_from_sql_table(db, publickey)
if cleandb:
if adminconfig.ClEANDB:
clean_db(db)
if listdatabase:
if adminconfig.LISTDATABASE:
list_db(db)
if rebroadcast_nip89:
if adminconfig.REBROADCAST_NIP89:
nip89_announce_tasks(dvmconfig, client=client)
if adminconfig.UPDATE_PROFILE:
update_profile(dvmconfig, lud16=adminconfig.LUD16)

View File

@ -5,7 +5,7 @@ from nostr_sdk import Event, Tag
from utils.definitions import EventDefinitions
from utils.mediasource_utils import check_source_type, media_source
from utils.nostr_utils import get_event_by_id
from utils.nostr_utils import get_event_by_id, get_referenced_event_by_id
def get_task(event, client, dvm_config):
@ -53,8 +53,38 @@ def get_task(event, client, dvm_config):
return "unknown type"
else:
return "unknown job"
elif event.kind() == EventDefinitions.KIND_NIP90_GENERATE_IMAGE:
has_image_tag = False
has_text_tag = False
for tag in event.tags():
if tag.as_vec()[0] == "i":
if tag.as_vec()[2] == "url":
file_type = check_url_is_readable(tag.as_vec()[1])
if file_type == "image":
has_image_tag = True
print("found image tag")
elif tag.as_vec()[2] == "job":
evt = get_referenced_event_by_id(event_id=tag.as_vec()[1], kinds=
[EventDefinitions.KIND_NIP90_RESULT_EXTRACT_TEXT,
EventDefinitions.KIND_NIP90_RESULT_TRANSLATE_TEXT,
EventDefinitions.KIND_NIP90_RESULT_SUMMARIZE_TEXT],
client=client,
dvm_config=dvm_config)
if evt is not None:
file_type = check_url_is_readable(evt.content())
if file_type == "image":
has_image_tag = True
elif tag.as_vec()[2] == "text":
has_text_tag = True
if has_image_tag:
return "image-to-image"
elif has_text_tag and not has_image_tag:
return "text-to-image"
# TODO if a task can consist of multiple inputs add them here
# This is not ideal. Maybe such events should have their own kind
# else if kind is supported, simply return task
else:

View File

@ -1,6 +1,9 @@
import os
from datetime import timedelta
from hashlib import sha256
from pathlib import Path
import dotenv
from nostr_sdk import Tag, Keys, EventBuilder, Filter, Alphabet, PublicKey, Event
from utils.definitions import EventDefinitions
@ -16,21 +19,17 @@ class NIP89Config:
def nip89_create_d_tag(name, pubkey, image):
#import hashlib
#m = hashlib.md5()
#m.update(str(name + image + pubkey).encode("utf-8"))
#d_tag = m.hexdigest()[0:16]
key_str = str(name + image + pubkey)
d_tag = sha256(key_str.encode('utf-8')).hexdigest()[:16]
return d_tag
def nip89_announce_tasks(dvm_config, client):
k_tag = Tag.parse(["k", str(dvm_config.NIP89.kind)])
d_tag = Tag.parse(["d", dvm_config.NIP89.dtag])
keys = Keys.from_sk_str(dvm_config.NIP89.pk)
content = dvm_config.NIP89.content
k_tag = Tag.parse(["k", str(dvm_config.NIP89.KIND)])
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(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)
@ -66,3 +65,21 @@ def nip89_fetch_events_pubkey(client, pubkey, kind):
# should be one element of the kind now
for dvm in dvms:
return dvms[dvm].content()
def check_and_set_d_tag(identifier, name, pk, imageurl):
if not os.getenv("NIP89_DTAG_" + identifier.upper()):
new_dtag = nip89_create_d_tag(name, Keys.from_sk_str(pk).public_key().to_hex(),
imageurl)
nip89_add_dtag_to_env_file("NIP89_DTAG_" + identifier.upper(), new_dtag)
print("Some new dtag:" + new_dtag)
return new_dtag
else:
return os.getenv("NIP89_DTAG_" + identifier.upper())
def nip89_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)

View File

@ -1,6 +1,10 @@
import json
import os
from datetime import timedelta
from nostr_sdk import Filter, Client, Alphabet, EventId, Event, PublicKey, Tag, Keys, nip04_decrypt
from pathlib import Path
import dotenv
from nostr_sdk import Filter, Client, Alphabet, EventId, Event, PublicKey, Tag, Keys, nip04_decrypt, Metadata, Options
def get_event_by_id(event_id: str, client: Client, config=None) -> Event | None:
@ -99,6 +103,7 @@ def check_and_decrypt_tags(event, dvm_config):
return event
def check_and_decrypt_own_tags(event, dvm_config):
try:
tags = []
@ -131,3 +136,57 @@ def check_and_decrypt_own_tags(event, dvm_config):
print(e)
return event
def update_profile(dvm_config, lud16=""):
keys = Keys.from_sk_str(dvm_config.PRIVATE_KEY)
opts = (Options().wait_for_send(True).send_timeout(timedelta(seconds=dvm_config.RELAY_TIMEOUT))
.skip_disconnected_relays(True))
client = Client.with_opts(keys, opts)
for relay in dvm_config.RELAY_LIST:
client.add_relay(relay)
client.connect()
nip89content = json.loads(dvm_config.NIP89.CONTENT)
if nip89content.get("name"):
name = nip89content.get("name")
about = nip89content.get("about")
image = nip89content.get("image")
# Set metadata
metadata = Metadata() \
.set_name(name) \
.set_display_name(name) \
.set_about(about) \
.set_picture(image) \
.set_lud16(lud16)
# .set_banner("https://example.com/banner.png") \
# .set_nip05("username@example.com") \
print(f"Setting profile metadata for {keys.public_key().to_bech32()}...")
print(metadata.as_json())
client.set_metadata(metadata)
client.disconnect()
def check_and_set_private_key(identifier):
if not os.getenv("DVM_PRIVATE_KEY_" + identifier.upper()):
pk = Keys.generate().secret_key().to_hex()
add_pk_to_env_file("DVM_PRIVATE_KEY_" + identifier.upper(), pk)
return pk
else:
return os.getenv("DVM_PRIVATE_KEY_" + identifier.upper())
def add_pk_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)
def generate_private_key():
return Keys.generate()