mirror of
https://github.com/believethehype/nostrdvm.git
synced 2025-03-17 21:31:52 +01:00
auto generate keys/dtagsl auto update user profiles, sdimg2img
This commit is contained in:
parent
023ba6d55f
commit
028c32f42d
38
.env_example
38
.env_example
@ -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
1
.gitignore
vendored
@ -162,3 +162,4 @@ cython_debug/
|
||||
*.db
|
||||
|
||||
outputs
|
||||
.env2
|
||||
|
47
main.py
47
main.py
@ -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
|
||||
|
157
playground.py
157
playground.py
@ -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
|
||||
|
@ -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 |
|
@ -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])
|
||||
|
186
tasks/imagegeneration_sdxlimg2img.py
Normal file
186
tasks/imagegeneration_sdxlimg2img.py
Normal 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)
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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)
|
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user