add paid outbox relays to avoid (to be continued..), fixes, better logging, add standalone updatedb dvm

This commit is contained in:
Believethehype
2024-06-04 20:15:20 +02:00
parent ba5377714e
commit 6210465df4
9 changed files with 401 additions and 78 deletions

View File

@ -138,7 +138,6 @@ class DicoverContentCurrentlyPopular(DVMTaskInterface):
if len(reactions) >= self.min_reactions: if len(reactions) >= self.min_reactions:
ns.finallist[event.id().to_hex()] = len(reactions) ns.finallist[event.id().to_hex()] = len(reactions)
if len(ns.finallist) == 0: if len(ns.finallist) == 0:
cli.disconnect()
cli.shutdown() cli.shutdown()
return self.result return self.result
@ -148,9 +147,8 @@ class DicoverContentCurrentlyPopular(DVMTaskInterface):
# print(EventId.parse(entry[0]).to_bech32() + "/" + EventId.parse(entry[0]).to_hex() + ": " + str(entry[1])) # print(EventId.parse(entry[0]).to_bech32() + "/" + EventId.parse(entry[0]).to_hex() + ": " + str(entry[1]))
e_tag = Tag.parse(["e", entry[0]]) e_tag = Tag.parse(["e", entry[0]])
result_list.append(e_tag.as_vec()) result_list.append(e_tag.as_vec())
cli.disconnect()
cli.shutdown() cli.shutdown()
print("[" + self.dvm_config.IDENTIFIER + "] Filtered " + str( print("[" + self.dvm_config.NIP89.NAME + "] Filtered " + str(
len(result_list)) + " fitting events.") len(result_list)) + " fitting events.")
return json.dumps(result_list) return json.dumps(result_list)
@ -196,15 +194,15 @@ class DicoverContentCurrentlyPopular(DVMTaskInterface):
definitions.EventDefinitions.KIND_ZAP]).since(since) # Notes, reactions, zaps definitions.EventDefinitions.KIND_ZAP]).since(since) # Notes, reactions, zaps
# filter = Filter().author(keys.public_key()) # filter = Filter().author(keys.public_key())
print("[" + self.dvm_config.IDENTIFIER + "] Syncing notes of the last " + str( print("[" + self.dvm_config.NIP89.NAME + "] Syncing notes of the last " + str(
self.db_since) + " seconds.. this might take a while..") self.db_since) + " seconds.. this might take a while..")
dbopts = NegentropyOptions().direction(NegentropyDirection.DOWN) dbopts = NegentropyOptions().direction(NegentropyDirection.DOWN)
cli.reconcile(filter1, dbopts) cli.reconcile(filter1, dbopts)
database.delete(Filter().until(Timestamp.from_secs( cli.database().delete(Filter().until(Timestamp.from_secs(
Timestamp.now().as_secs() - self.db_since))) # Clear old events so db doesn't get too full. Timestamp.now().as_secs() - self.db_since))) # Clear old events so db doesn't get too full.
cli.shutdown() cli.shutdown()
print( print(
"[" + self.dvm_config.IDENTIFIER + "] Done Syncing Notes of the last " + str(self.db_since) + " seconds..") "[" + self.dvm_config.NIP89.NAME + "] Done Syncing Notes of the last " + str(self.db_since) + " seconds..")
# We build an example here that we can call by either calling this file directly from the main directory, # We build an example here that we can call by either calling this file directly from the main directory,

View File

@ -162,7 +162,7 @@ class DicoverContentCurrentlyPopularZaps(DVMTaskInterface):
e_tag = Tag.parse(["e", entry[0]]) e_tag = Tag.parse(["e", entry[0]])
result_list.append(e_tag.as_vec()) result_list.append(e_tag.as_vec())
print("[" + self.dvm_config.IDENTIFIER + "] Filtered " + str( print("[" + self.dvm_config.NIP89.NAME+ "] Filtered " + str(
len(result_list)) + " fitting events.") len(result_list)) + " fitting events.")
cli.disconnect() cli.disconnect()
@ -216,15 +216,15 @@ class DicoverContentCurrentlyPopularZaps(DVMTaskInterface):
definitions.EventDefinitions.KIND_ZAP]).since(since) # Notes, reactions, zaps definitions.EventDefinitions.KIND_ZAP]).since(since) # Notes, reactions, zaps
# filter = Filter().author(keys.public_key()) # filter = Filter().author(keys.public_key())
print("[" + self.dvm_config.IDENTIFIER + "] Syncing notes of the last " + str( print("[" + self.dvm_config.NIP89.NAME + "] Syncing notes of the last " + str(
self.db_since) + " seconds.. this might take a while..") self.db_since) + " seconds.. this might take a while..")
dbopts = NegentropyOptions().direction(NegentropyDirection.DOWN) dbopts = NegentropyOptions().direction(NegentropyDirection.DOWN)
cli.reconcile(filter1, dbopts) cli.reconcile(filter1, dbopts)
database.delete(Filter().until(Timestamp.from_secs( cli.database().delete(Filter().until(Timestamp.from_secs(
Timestamp.now().as_secs() - self.db_since))) # Clear old events so db doesn't get too full. Timestamp.now().as_secs() - self.db_since))) # Clear old events so db doesn't get too full.
cli.shutdown() cli.shutdown()
print( print(
"[" + self.dvm_config.IDENTIFIER + "] Done Syncing Notes of the last " + str(self.db_since) + " seconds..") "[" + self.dvm_config.NIP89.NAME + "] Done Syncing Notes of the last " + str(self.db_since) + " seconds..")
# We build an example here that we can call by either calling this file directly from the main directory, # We build an example here that we can call by either calling this file directly from the main directory,

View File

@ -164,7 +164,7 @@ class DicoverContentCurrentlyPopularFollowers(DVMTaskInterface):
result_list.append(e_tag.as_vec()) result_list.append(e_tag.as_vec())
cli.connect() cli.connect()
cli.shutdown() cli.shutdown()
print("[" + self.dvm_config.IDENTIFIER + "] Filtered " + str( print("[" + self.dvm_config.NIP89.NAME + "] Filtered " + str(
len(result_list)) + " fitting events.") len(result_list)) + " fitting events.")
return json.dumps(result_list) return json.dumps(result_list)
@ -214,14 +214,14 @@ class DicoverContentCurrentlyPopularFollowers(DVMTaskInterface):
definitions.EventDefinitions.KIND_ZAP]).since(since) # Notes, reactions, zaps definitions.EventDefinitions.KIND_ZAP]).since(since) # Notes, reactions, zaps
# filter = Filter().author(keys.public_key()) # filter = Filter().author(keys.public_key())
print("[" + self.dvm_config.IDENTIFIER + "] Syncing notes of the last " + str( print("[" + self.dvm_config.NIP89.NAME + "] Syncing notes of the last " + str(
self.db_since) + " seconds.. this might take a while..") self.db_since) + " seconds.. this might take a while..")
dbopts = NegentropyOptions().direction(NegentropyDirection.DOWN) dbopts = NegentropyOptions().direction(NegentropyDirection.DOWN)
cli.reconcile(filter1, dbopts) cli.reconcile(filter1, dbopts)
database.delete(Filter().until(Timestamp.from_secs( cli.database().delete(Filter().until(Timestamp.from_secs(
Timestamp.now().as_secs() - self.db_since))) # Clear old events so db doesn't get too full. Timestamp.now().as_secs() - self.db_since))) # Clear old events so db doesn't get too full.
cli.shutdown() cli.shutdown()
print("[" + self.dvm_config.IDENTIFIER + "] Done Syncing Notes of the last " + str( print("[" + self.dvm_config.NIP89.NAME + "] Done Syncing Notes of the last " + str(
self.db_since) + " seconds..") self.db_since) + " seconds..")

View File

@ -177,10 +177,8 @@ class DicoverContentCurrentlyPopularbyTopic(DVMTaskInterface):
e_tag = Tag.parse(["e", entry[0]]) e_tag = Tag.parse(["e", entry[0]])
result_list.append(e_tag.as_vec()) result_list.append(e_tag.as_vec())
print("[" + self.dvm_config.IDENTIFIER + "] Filtered " + str( print("[" + self.dvm_config.NIP89.NAME + "] Filtered " + str(
len(result_list)) + " fitting events.") len(result_list)) + " fitting events.")
cli.disconnect()
cli.shutdown() cli.shutdown()
return json.dumps(result_list) return json.dumps(result_list)
@ -218,13 +216,13 @@ class DicoverContentCurrentlyPopularbyTopic(DVMTaskInterface):
filter1 = Filter().kinds([definitions.EventDefinitions.KIND_NOTE, definitions.EventDefinitions.KIND_REACTION, definitions.EventDefinitions.KIND_ZAP]).since(since) # Notes, reactions, zaps filter1 = Filter().kinds([definitions.EventDefinitions.KIND_NOTE, definitions.EventDefinitions.KIND_REACTION, definitions.EventDefinitions.KIND_ZAP]).since(since) # Notes, reactions, zaps
# filter = Filter().author(keys.public_key()) # filter = Filter().author(keys.public_key())
print("[" + self.dvm_config.IDENTIFIER + "] Syncing notes of the last " + str(self.db_since) + " seconds.. this might take a while..") print("[" + self.dvm_config.NIP89.NAME + "] Syncing notes of the last " + str(self.db_since) + " seconds.. this might take a while..")
dbopts = NegentropyOptions().direction(NegentropyDirection.DOWN) dbopts = NegentropyOptions().direction(NegentropyDirection.DOWN)
cli.reconcile(filter1, dbopts) cli.reconcile(filter1, dbopts)
database.delete(Filter().until(Timestamp.from_secs( cli.database().delete(Filter().until(Timestamp.from_secs(
Timestamp.now().as_secs() - self.db_since))) # Clear old events so db doesn't get too full. Timestamp.now().as_secs() - self.db_since))) # Clear old events so db doesn't get too full.
cli.shutdown() cli.shutdown()
print("[" + self.dvm_config.IDENTIFIER + "] Done Syncing Notes of the last " + str(self.db_since) + " seconds..") print("[" + self.dvm_config.NIP89.NAME + "] Done Syncing Notes of the last " + str(self.db_since) + " seconds..")
# We build an example here that we can call by either calling this file directly from the main directory, # We build an example here that we can call by either calling this file directly from the main directory,

View File

@ -0,0 +1,269 @@
import json
import os
from datetime import timedelta
from nostr_sdk import Client, Timestamp, PublicKey, Tag, Keys, Options, SecretKey, NostrSigner, NostrDatabase, \
ClientBuilder, Filter, NegentropyOptions, NegentropyDirection, init_logger, LogLevel, Event, EventId, Kind
from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv
from nostr_dvm.utils import definitions
from nostr_dvm.utils.admin_utils import AdminConfig
from nostr_dvm.utils.definitions import EventDefinitions
from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config
from nostr_dvm.utils.nip88_utils import NIP88Config, check_and_set_d_tag_nip88, check_and_set_tiereventid_nip88
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag, create_amount_tag
from nostr_dvm.utils.output_utils import post_process_list_to_events
"""
This File contains a Module to update the database for content discovery dvms
Accepted Inputs: none
Outputs: A list of events
Params: None
"""
class DicoverContentDBUpdateScheduler(DVMTaskInterface):
KIND: Kind = EventDefinitions.KIND_NIP90_CONTENT_DISCOVERY
TASK: str = "update-db-on-schedule"
FIX_COST: float = 0
dvm_config: DVMConfig
last_schedule: int
min_reactions = 2
db_since = 10 * 3600
db_name = "db/nostr_default_recent_notes.db"
search_list = []
avoid_list = []
must_list = []
personalized = False
result = ""
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, nip88config: NIP88Config = None,
admin_config: AdminConfig = None, options=None):
super().__init__(name=name, dvm_config=dvm_config, nip89config=nip89config, nip88config=nip88config,
admin_config=admin_config, options=options)
# Generate Generic request form for dvms that provide generic results (e.g only a calculation per update,
# not per call)
self.request_form = {"jobID": "generic"}
opts = {
"max_results": 200,
}
self.request_form['options'] = json.dumps(opts)
dvm_config.SCRIPT = os.path.abspath(__file__)
if self.options.get("personalized"):
self.personalized = bool(self.options.get("personalized"))
self.last_schedule = Timestamp.now().as_secs()
if self.options.get("search_list"):
self.search_list = self.options.get("search_list")
# print(self.search_list)
if self.options.get("avoid_list"):
self.avoid_list = self.options.get("avoid_list")
if self.options.get("must_list"):
self.must_list = self.options.get("must_list")
if self.options.get("db_name"):
self.db_name = self.options.get("db_name")
if self.options.get("db_since"):
self.db_since = int(self.options.get("db_since"))
use_logger = False
if use_logger:
init_logger(LogLevel.DEBUG)
if self.dvm_config.UPDATE_DATABASE:
self.sync_db()
def is_input_supported(self, tags, client=None, dvm_config=None):
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 != "text":
return False
return True
def create_request_from_nostr_event(self, event, client=None, dvm_config=None):
self.dvm_config = dvm_config
request_form = {"jobID": event.id().to_hex()}
# default values
max_results = 200
for tag in event.tags():
if tag.as_vec()[0] == 'i':
input_type = tag.as_vec()[2]
elif tag.as_vec()[0] == 'param':
param = tag.as_vec()[1]
if param == "max_results": # check for param type
max_results = int(tag.as_vec()[2])
options = {
"max_results": max_results,
}
request_form['options'] = json.dumps(options)
self.request_form = request_form
return request_form
def process(self, request_form):
return "I don't return results, I just update the DB."
def post_process(self, result, event):
"""Overwrite the interface function to return a social client readable format, if requested"""
for tag in event.tags():
if tag.as_vec()[0] == 'output':
format = tag.as_vec()[1]
if format == "text/plain": # check for output type
result = post_process_list_to_events(result)
# if not text/plain, don't post-process
return result
def schedule(self, dvm_config):
if dvm_config.SCHEDULE_UPDATES_SECONDS == 0:
return 0
else:
if Timestamp.now().as_secs() >= self.last_schedule + dvm_config.SCHEDULE_UPDATES_SECONDS:
if self.dvm_config.UPDATE_DATABASE:
self.sync_db()
self.last_schedule = Timestamp.now().as_secs()
return 1
def sync_db(self):
opts = (Options().wait_for_send(False).send_timeout(timedelta(seconds=self.dvm_config.RELAY_LONG_TIMEOUT)))
sk = SecretKey.from_hex(self.dvm_config.PRIVATE_KEY)
keys = Keys.parse(sk.to_hex())
signer = NostrSigner.keys(keys)
database = NostrDatabase.sqlite(self.db_name)
cli = ClientBuilder().signer(signer).database(database).opts(opts).build()
for relay in self.dvm_config.RECONCILE_DB_RELAY_LIST:
cli.add_relay(relay)
cli.connect()
timestamp_since = Timestamp.now().as_secs() - self.db_since
since = Timestamp.from_secs(timestamp_since)
filter1 = Filter().kinds([definitions.EventDefinitions.KIND_NOTE, definitions.EventDefinitions.KIND_REACTION,
definitions.EventDefinitions.KIND_ZAP]).since(since) # Notes, reactions, zaps
# filter = Filter().author(keys.public_key())
print("[" + self.dvm_config.IDENTIFIER + "] Syncing notes of the last " + str(
self.db_since) + " seconds.. this might take a while..")
dbopts = NegentropyOptions().direction(NegentropyDirection.DOWN)
cli.reconcile(filter1, dbopts)
cli.database().delete(Filter().until(Timestamp.from_secs(
Timestamp.now().as_secs() - self.db_since))) # Clear old events so db doesn't get too full.
cli.shutdown()
print(
"[" + self.dvm_config.IDENTIFIER + "] Done Syncing Notes of the last " + str(self.db_since) + " seconds..")
# We build an example here that we can call by either calling this file directly from the main directory,
# or by adding it to our playground. You can call the example and adjust it to your needs or redefine it in the
# playground or elsewhere
def build_example(name, identifier, admin_config, options, image, description, update_rate=600, cost=0,
processing_msg=None, update_db=True):
dvm_config = build_default_config(identifier)
dvm_config.USE_OWN_VENV = False
dvm_config.SHOWLOG = True
dvm_config.SCHEDULE_UPDATES_SECONDS = update_rate # Every 10 minutes
dvm_config.UPDATE_DATABASE = update_db
# Activate these to use a subscription based model instead
# dvm_config.SUBSCRIPTION_REQUIRED = True
# dvm_config.SUBSCRIPTION_DAILY_COST = 1
dvm_config.FIX_COST = cost
dvm_config.CUSTOM_PROCESSING_MESSAGE = processing_msg
admin_config.LUD16 = dvm_config.LN_ADDRESS
# Add NIP89
nip89info = {
"name": name,
"image": image,
"picture": image,
"about": description,
"lud16": dvm_config.LN_ADDRESS,
"encryptionSupported": True,
"cashuAccepted": True,
"personalized": False,
"amount": create_amount_tag(cost),
"nip90Params": {
"max_results": {
"required": False,
"values": [],
"description": "The number of maximum results to return (default currently 100)"
}
}
}
nip89config = NIP89Config()
nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, nip89info["image"])
nip89config.CONTENT = json.dumps(nip89info)
return DicoverContentDBUpdateScheduler(name=name, dvm_config=dvm_config, nip89config=nip89config,
admin_config=admin_config, options=options)
def build_example_subscription(name, identifier, admin_config, options, image, description, processing_msg=None,
update_db=True):
dvm_config = build_default_config(identifier)
dvm_config.USE_OWN_VENV = False
dvm_config.SHOWLOG = True
dvm_config.SCHEDULE_UPDATES_SECONDS = 600 # Every 10 minutes
dvm_config.UPDATE_DATABASE = update_db
# Activate these to use a subscription based model instead
dvm_config.FIX_COST = 0
dvm_config.CUSTOM_PROCESSING_MESSAGE = processing_msg
admin_config.LUD16 = dvm_config.LN_ADDRESS
# Add NIP89
nip89info = {
"name": name,
"image": image,
"picture": image,
"about": description,
"lud16": dvm_config.LN_ADDRESS,
"encryptionSupported": True,
"cashuAccepted": True,
"subscription": True,
"personalized": False,
"nip90Params": {
"max_results": {
"required": False,
"values": [],
"description": "The number of maximum results to return (default currently 100)"
}
}
}
nip89config = NIP89Config()
nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, nip89info["image"])
nip89config.CONTENT = json.dumps(nip89info)
nip88config = NIP88Config()
nip88config.DTAG = check_and_set_d_tag_nip88(identifier, name, dvm_config.PRIVATE_KEY, nip89info["image"])
nip88config.TIER_EVENT = check_and_set_tiereventid_nip88(identifier, "1")
nip89config.NAME = name
nip88config.IMAGE = nip89info["image"]
nip88config.TITLE = name
nip88config.AMOUNT_DAILY = 100
nip88config.AMOUNT_MONTHLY = 2000
nip88config.CONTENT = "Subscribe to the DVM for unlimited use during your subscription"
nip88config.PERK1DESC = "Unlimited requests"
nip88config.PERK2DESC = "Support NostrDVM & NostrSDK development"
nip88config.PAYMENT_VERIFIER_PUBKEY = "5b5c045ecdf66fb540bdf2049fe0ef7f1a566fa427a4fe50d400a011b65a3a7e"
# admin_config.FETCH_NIP88 = True
# admin_config.EVENTID = "63a791cdc7bf78c14031616963105fce5793f532bb231687665b14fb6d805fdb"
# admin_config.PRIVKEY = dvm_config.PRIVATE_KEY
return DicoverContentDBUpdateScheduler(name=name, dvm_config=dvm_config, nip89config=nip89config,
nip88config=nip88config,
admin_config=admin_config,
options=options)
if __name__ == '__main__':
process_venv(DicoverContentDBUpdateScheduler)

View File

@ -25,10 +25,16 @@ class DVMConfig:
"wss://nostr.oxtr.dev", "wss://relay.nostr.bg", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg",
"wss://relay.nostr.net" , "wss://relay.primal.net"] #, "wss://relay.snort.social"] "wss://relay.nostr.net" , "wss://relay.primal.net"] #, "wss://relay.snort.social"]
AVOID_PAID_OUTBOX_RELAY_LIST = ["wss://nostrelay.yeghro.site", "wss://nostr.wine",
"wss://nostr21.com", "wss://nostr.bitcoiner.social", "wss://nostr.orangepill.dev",
"wss://relay.lnpay.me", "wss://relay.snort.social", "wss://relay.minds.com/nostr/v1/ws",
"wss://nostr-pub.semisol.dev", "wss://mostr.pub", "wss://minds.com",
"wss://yabu.me", "wss://relay.yozora.world", "wss://filter.nostr.wine/?global=all", "wss://eden.nostr.land",
"wss://relay.orangepill.ovh", "wss://nostr.jcloud.es", "wss://af.purplerelay.com", "wss://za.purplerelay.com",
# cli.add_relay("wss://relay.primal.net") ]
#If a DVM has a paid subscription, overwrite list without the paid one.
#"wss://relay.damus.io"
RELAY_TIMEOUT = 5 RELAY_TIMEOUT = 5
RELAY_LONG_TIMEOUT = 30 RELAY_LONG_TIMEOUT = 30

View File

@ -123,11 +123,13 @@ def get_inbox_relays(event_to_send: Event, client: Client, dvm_config):
for tag in nip65event.tags(): for tag in nip65event.tags():
if tag.as_vec()[0] == 'r' and len(tag.as_vec()) == 2: if tag.as_vec()[0] == 'r' and len(tag.as_vec()) == 2:
rtag = tag.as_vec()[1] rtag = tag.as_vec()[1]
relays.append(rtag) if rtag.rstrip("/") not in dvm_config.AVOID_PAID_OUTBOX_RELAY_LIST:
relays.append(rtag)
elif tag.as_vec()[0] == 'r' and len(tag.as_vec()) == 3: elif tag.as_vec()[0] == 'r' and len(tag.as_vec()) == 3:
if tag.as_vec()[2] == "read": if tag.as_vec()[2] == "read":
rtag = tag.as_vec()[1] rtag = tag.as_vec()[1]
relays.append(rtag) if rtag.rstrip("/") not in dvm_config.AVOID_PAID_OUTBOX_RELAY_LIST:
relays.append(rtag)
return relays return relays
@ -140,7 +142,8 @@ def send_event_outbox(event: Event, client, dvm_config) -> EventId:
if tag.as_vec()[0] == 'relays': if tag.as_vec()[0] == 'relays':
for index, param in enumerate(tag.as_vec()): for index, param in enumerate(tag.as_vec()):
if index != 0: if index != 0:
relays.append(tag.as_vec()[index]) if tag.as_vec()[index].rstrip("/") not in dvm_config.AVOID_PAID_OUTBOX_RELAY_LIST:
relays.append(tag.as_vec()[index])
break break

View File

@ -1,6 +1,6 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
VERSION = '0.5.8' VERSION = '0.5.9'
DESCRIPTION = 'A framework to build and run Nostr NIP90 Data Vending Machines' DESCRIPTION = 'A framework to build and run Nostr NIP90 Data Vending Machines'
LONG_DESCRIPTION = ('A framework to build and run Nostr NIP90 Data Vending Machines. See the github repository for more information') LONG_DESCRIPTION = ('A framework to build and run Nostr NIP90 Data Vending Machines. See the github repository for more information')

View File

@ -6,6 +6,9 @@ from pathlib import Path
import dotenv import dotenv
from nostr_sdk import init_logger, LogLevel, Keys, NostrLibrary from nostr_sdk import init_logger, LogLevel, Keys, NostrLibrary
from nostr_dvm.tasks.content_discovery_update_db_only import DicoverContentDBUpdateScheduler
#os.environ["RUST_BACKTRACE"] = "full"
from nostr_dvm.subscription import Subscription from nostr_dvm.subscription import Subscription
from nostr_dvm.tasks.content_discovery_currently_popular import DicoverContentCurrentlyPopular from nostr_dvm.tasks.content_discovery_currently_popular import DicoverContentCurrentlyPopular
from nostr_dvm.tasks.content_discovery_currently_popular_by_top_zaps import DicoverContentCurrentlyPopularZaps from nostr_dvm.tasks.content_discovery_currently_popular_by_top_zaps import DicoverContentCurrentlyPopularZaps
@ -19,7 +22,7 @@ from nostr_dvm.utils.nostr_utils import check_and_set_private_key
from nostr_dvm.utils.zap_utils import check_and_set_ln_bits_keys from nostr_dvm.utils.zap_utils import check_and_set_ln_bits_keys
rebroadcast_NIP89 = False # Announce NIP89 on startup rebroadcast_NIP89 = False # Announce NIP89 on startup
rebroadcast_NIP65_Relay_List = True rebroadcast_NIP65_Relay_List = False
update_profile = False update_profile = False
global_update_rate = 120 # set this high on first sync so db can fully sync before another process trys to. global_update_rate = 120 # set this high on first sync so db can fully sync before another process trys to.
@ -30,13 +33,57 @@ use_logger = True
if use_logger: if use_logger:
init_logger(LogLevel.INFO) init_logger(LogLevel.INFO)
def build_db_scheduler(name, identifier, admin_config, options, image, description, update_rate=600, cost=0,
processing_msg=None, update_db=True):
dvm_config = build_default_config(identifier)
dvm_config.USE_OWN_VENV = False
dvm_config.SHOWLOG = True
dvm_config.SCHEDULE_UPDATES_SECONDS = update_rate # Every 10 minutes
dvm_config.UPDATE_DATABASE = update_db
# Activate these to use a subscription based model instead
# dvm_config.SUBSCRIPTION_REQUIRED = True
# dvm_config.SUBSCRIPTION_DAILY_COST = 1
dvm_config.FIX_COST = cost
dvm_config.CUSTOM_PROCESSING_MESSAGE = processing_msg
admin_config.LUD16 = dvm_config.LN_ADDRESS
# Add NIP89
nip89info = {
"name": name,
"image": image,
"picture": image,
"about": description,
"lud16": dvm_config.LN_ADDRESS,
"encryptionSupported": True,
"cashuAccepted": True,
"personalized": False,
"amount": create_amount_tag(cost),
"nip90Params": {
"max_results": {
"required": False,
"values": [],
"description": "The number of maximum results to return (default currently 100)"
}
}
}
nip89config = NIP89Config()
nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, nip89info["image"])
nip89config.CONTENT = json.dumps(nip89info)
return DicoverContentDBUpdateScheduler(name=name, dvm_config=dvm_config, nip89config=nip89config,
admin_config=admin_config, options=options)
def build_example_nostrband(name, identifier, admin_config, image, about, custom_processing_msg): def build_example_nostrband(name, identifier, admin_config, image, about, custom_processing_msg):
dvm_config: DVMConfig = build_default_config(identifier) dvm_config: DVMConfig = build_default_config(identifier)
dvm_config.USE_OWN_VENV = False dvm_config.USE_OWN_VENV = False
dvm_config.CUSTOM_PROCESSING_MESSAGE = custom_processing_msg dvm_config.CUSTOM_PROCESSING_MESSAGE = custom_processing_msg
dvm_config.RELAY_LIST = ["wss://dvms.f7z.io", #dvm_config.RELAY_LIST = ["wss://dvms.f7z.io",
"wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg" # "wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg"
] # ]
admin_config.LUD16 = dvm_config.LN_ADDRESS admin_config.LUD16 = dvm_config.LN_ADDRESS
# Add NIP89 # Add NIP89
@ -67,9 +114,9 @@ def build_example_topic(name, identifier, admin_config, options, image, descript
dvm_config.UPDATE_DATABASE = update_db dvm_config.UPDATE_DATABASE = update_db
dvm_config.FIX_COST = cost dvm_config.FIX_COST = cost
dvm_config.CUSTOM_PROCESSING_MESSAGE = processing_msg dvm_config.CUSTOM_PROCESSING_MESSAGE = processing_msg
dvm_config.RELAY_LIST = ["wss://dvms.f7z.io", #dvm_config.RELAY_LIST = ["wss://dvms.f7z.io",
"wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg" # "wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg"
] # ]
admin_config.LUD16 = dvm_config.LN_ADDRESS admin_config.LUD16 = dvm_config.LN_ADDRESS
# Add NIP89 # Add NIP89
@ -111,9 +158,9 @@ def build_example_popular(name, identifier, admin_config, options, image, cost=0
#dvm_config.RELAY_LIST = ["wss://dvms.f7z.io", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg", #dvm_config.RELAY_LIST = ["wss://dvms.f7z.io", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg",
#"wss://relay.nostr.net"] #"wss://relay.nostr.net"]
dvm_config.CUSTOM_PROCESSING_MESSAGE = processing_msg dvm_config.CUSTOM_PROCESSING_MESSAGE = processing_msg
dvm_config.RELAY_LIST = ["wss://dvms.f7z.io", #dvm_config.RELAY_LIST = ["wss://dvms.f7z.io",
"wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg" # "wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg"
] # ]
admin_config.LUD16 = dvm_config.LN_ADDRESS admin_config.LUD16 = dvm_config.LN_ADDRESS
# Add NIP89 # Add NIP89
@ -152,9 +199,9 @@ def build_example_popular_followers(name, identifier, admin_config, options, ima
dvm_config.UPDATE_DATABASE = update_db dvm_config.UPDATE_DATABASE = update_db
dvm_config.FIX_COST = cost dvm_config.FIX_COST = cost
dvm_config.CUSTOM_PROCESSING_MESSAGE = processing_msg dvm_config.CUSTOM_PROCESSING_MESSAGE = processing_msg
dvm_config.RELAY_LIST = ["wss://dvms.f7z.io", #dvm_config.RELAY_LIST = ["wss://dvms.f7z.io",
"wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg" # "wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg"
] # ]
admin_config.LUD16 = dvm_config.LN_ADDRESS admin_config.LUD16 = dvm_config.LN_ADDRESS
# Add NIP89 # Add NIP89
@ -196,9 +243,9 @@ def build_example_top_zapped(name, identifier, admin_config, options, image, cos
dvm_config.UPDATE_DATABASE = update_db dvm_config.UPDATE_DATABASE = update_db
dvm_config.FIX_COST = cost dvm_config.FIX_COST = cost
dvm_config.CUSTOM_PROCESSING_MESSAGE = processing_msg dvm_config.CUSTOM_PROCESSING_MESSAGE = processing_msg
dvm_config.RELAY_LIST = ["wss://dvms.f7z.io", #dvm_config.RELAY_LIST = ["wss://dvms.f7z.io",
"wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg" # "wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg"
] # ]
admin_config.LUD16 = dvm_config.LN_ADDRESS admin_config.LUD16 = dvm_config.LN_ADDRESS
# Add NIP89 # Add NIP89
@ -233,6 +280,26 @@ def build_example_top_zapped(name, identifier, admin_config, options, image, cos
def playground(): def playground():
#DB Scheduler, do not announce, just use it to update the DB for the other DVMs.
admin_config_db_scheduler= AdminConfig()
options_animal = {
"db_name": "db/nostr_recent_notes.db",
"db_since": 48 * 60 * 60, # 48h since gmt,
"personalized": False,
"logger": False}
image = ""
about = "I just update the Database based on my schedule"
db_scheduler = build_db_scheduler("DB Scheduler",
"db_scheduler",
admin_config_db_scheduler, options_animal,
image=image,
description=about,
update_rate=global_update_rate,
cost=0,
update_db=True)
db_scheduler.run()
# Popular top zapped # Popular top zapped
admin_config_top_zaps = AdminConfig() admin_config_top_zaps = AdminConfig()
admin_config_top_zaps.REBROADCAST_NIP89 = rebroadcast_NIP89 admin_config_top_zaps.REBROADCAST_NIP89 = rebroadcast_NIP89
@ -264,6 +331,7 @@ def playground():
discovery_topzaps.run() discovery_topzaps.run()
# Popular NOSTR.band # Popular NOSTR.band
admin_config_trending_nostr_band = AdminConfig() admin_config_trending_nostr_band = AdminConfig()
admin_config_trending_nostr_band.REBROADCAST_NIP89 = rebroadcast_NIP89 admin_config_trending_nostr_band.REBROADCAST_NIP89 = rebroadcast_NIP89
@ -330,7 +398,7 @@ def playground():
custom_processing_msg = ["Looking for fluffy frens...", "Let's see if we find some animals for you..", custom_processing_msg = ["Looking for fluffy frens...", "Let's see if we find some animals for you..",
"Looking for the goodest bois and girls.."] "Looking for the goodest bois and girls.."]
cost = 0 cost = 0
update_db = True # As this is our largerst DB we update it here, and the other dvms use it. TODO make an own scheduler that only updates the db update_db = False # As this is our largerst DB we update it here, and the other dvms use it. TODO make an own scheduler that only updates the db
discovery_animals = build_example_topic("Fluffy Frens", discovery_animals = build_example_topic("Fluffy Frens",
"discovery_content_fluffy", "discovery_content_fluffy",
admin_config_animals, options_animal, admin_config_animals, options_animal,
@ -348,10 +416,10 @@ def playground():
admin_config_plants.REBROADCAST_NIP89 = rebroadcast_NIP89 admin_config_plants.REBROADCAST_NIP89 = rebroadcast_NIP89
admin_config_plants.REBROADCAST_NIP65_RELAY_LIST = rebroadcast_NIP65_Relay_List admin_config_plants.REBROADCAST_NIP65_RELAY_LIST = rebroadcast_NIP65_Relay_List
admin_config_plants.UPDATE_PROFILE = update_profile admin_config_plants.UPDATE_PROFILE = update_profile
#admin_config_plants.DELETE_NIP89 = True # admin_config_plants.DELETE_NIP89 = True
#admin_config_plants.PRIVKEY = "" # admin_config_plants.PRIVKEY = ""
#admin_config_plants.EVENTID = "ff28be59708ee597c7010fd43a7e649e1ab51da491266ca82a84177e0007e4d6" # admin_config_plants.EVENTID = "ff28be59708ee597c7010fd43a7e649e1ab51da491266ca82a84177e0007e4d6"
#admin_config_plants.POW = True # admin_config_plants.POW = True
options_plants = { options_plants = {
"search_list": ["garden", "gardening", "nature", " plants ", " plant ", " herb ", " herbs " " pine ", "search_list": ["garden", "gardening", "nature", " plants ", " plant ", " herb ", " herbs " " pine ",
"homesteading", "rosemary", "chicken", "🪻", "🌿", "☘️", "🌲", "flower", "forest", "watering", "homesteading", "rosemary", "chicken", "🪻", "🌿", "☘️", "🌲", "flower", "forest", "watering",
@ -360,7 +428,8 @@ def playground():
"avoid_list": ["porn", "smoke", "nsfw", "bitcoin", "bolt12", "bolt11", "github", "currency", "utxo", "avoid_list": ["porn", "smoke", "nsfw", "bitcoin", "bolt12", "bolt11", "github", "currency", "utxo",
"encryption", "government", "airpod", "ipad", "iphone", "android", "warren", "encryption", "government", "airpod", "ipad", "iphone", "android", "warren",
"moderna", "pfizer", "corona", "socialism", "critical theory", "law of nature" "moderna", "pfizer", "corona", "socialism", "critical theory", "law of nature"
"murder", "tax", "engagement", "hodlers", "hodl", "gdp", "global markets", "crypto", "wherostr", "murder", "tax", "engagement",
"hodlers", "hodl", "gdp", "global markets", "crypto", "wherostr",
"presidency", "dollar", "asset", "microsoft", "amazon", "billionaire", "ceo", "industry", "presidency", "dollar", "asset", "microsoft", "amazon", "billionaire", "ceo", "industry",
"white house", "blocks", "streaming", "summary", "wealth", "beef", "cunt", "nigger", "business", "white house", "blocks", "streaming", "summary", "wealth", "beef", "cunt", "nigger", "business",
"retail", "bakery", "synth", "slaughterhouse", "hamas", "dog days", "ww3", "socialmedia", "retail", "bakery", "synth", "slaughterhouse", "hamas", "dog days", "ww3", "socialmedia",
@ -379,7 +448,7 @@ def playground():
"Looking for #goodvibes..", "All I do is #blooming.."] "Looking for #goodvibes..", "All I do is #blooming.."]
update_db = False update_db = False
cost = 0 cost = 0
discovery_test_sub = build_example_topic("Garden & Growth", "discovery_content_garden", discovery_garden = build_example_topic("Garden & Growth", "discovery_content_garden",
admin_config_plants, options_plants, admin_config_plants, options_plants,
image=image, image=image,
description=description, description=description,
@ -387,18 +456,17 @@ def playground():
cost=cost, cost=cost,
processing_msg=custom_processing_msg, processing_msg=custom_processing_msg,
update_db=update_db) update_db=update_db)
discovery_test_sub.run() discovery_garden.run()
# Popular Followers # Popular Followers
admin_config_followers = AdminConfig() admin_config_followers = AdminConfig()
admin_config_followers.REBROADCAST_NIP89 = rebroadcast_NIP89 admin_config_followers.REBROADCAST_NIP89 = rebroadcast_NIP89
admin_config_followers.REBROADCAST_NIP65_RELAY_LIST = rebroadcast_NIP65_Relay_List admin_config_followers.REBROADCAST_NIP65_RELAY_LIST = rebroadcast_NIP65_Relay_List
admin_config_followers.UPDATE_PROFILE = update_profile admin_config_followers.UPDATE_PROFILE = update_profile
#admin_config_followers.DELETE_NIP89 = True # admin_config_followers.DELETE_NIP89 = True
#admin_config_followers.PRIVKEY = "" # admin_config_followers.PRIVKEY = ""
#admin_config_followers.EVENTID = "590cd7b2902224f740acbd6845023a5ab4a959386184f3360c2859019cfd48fa" # admin_config_followers.EVENTID = "590cd7b2902224f740acbd6845023a5ab4a959386184f3360c2859019cfd48fa"
#admin_config_followers.POW = True # admin_config_followers.POW = True
custom_processing_msg = ["Processing popular notes from npubs you follow..", custom_processing_msg = ["Processing popular notes from npubs you follow..",
"Let's see what npubs you follow have been up to..", "Let's see what npubs you follow have been up to..",
"Processing a personalized feed, just for you.."] "Processing a personalized feed, just for you.."]
@ -427,10 +495,10 @@ def playground():
admin_config_global_popular.REBROADCAST_NIP89 = rebroadcast_NIP89 admin_config_global_popular.REBROADCAST_NIP89 = rebroadcast_NIP89
admin_config_global_popular.REBROADCAST_NIP65_RELAY_LIST = rebroadcast_NIP65_Relay_List admin_config_global_popular.REBROADCAST_NIP65_RELAY_LIST = rebroadcast_NIP65_Relay_List
admin_config_global_popular.UPDATE_PROFILE = update_profile admin_config_global_popular.UPDATE_PROFILE = update_profile
#admin_config_global_popular.DELETE_NIP89 = True # admin_config_global_popular.DELETE_NIP89 = True
#admin_config_global_popular.PRIVKEY = "" # admin_config_global_popular.PRIVKEY = ""
#admin_config_global_popular.EVENTID = "2fea4ee2ccf0fa11db171113ffd7a676f800f34121478b7c9a4e73c2f1990028" # admin_config_global_popular.EVENTID = "2fea4ee2ccf0fa11db171113ffd7a676f800f34121478b7c9a4e73c2f1990028"
#admin_config_global_popular.POW = True # admin_config_global_popular.POW = True
custom_processing_msg = ["Looking for popular notes on the Nostr..", "Let's see what's trending on Nostr..", custom_processing_msg = ["Looking for popular notes on the Nostr..", "Let's see what's trending on Nostr..",
"Finding the best notes on the Nostr.."] "Finding the best notes on the Nostr.."]
update_db = False update_db = False
@ -452,25 +520,6 @@ def playground():
update_db=update_db) update_db=update_db)
discovery_global.run() discovery_global.run()
# discovery_test_sub = content_discovery_currently_popular.build_example_subscription("Currently Popular Notes DVM (with Subscriptions)", "discovery_content_test", admin_config)
# discovery_test_sub.run()
# Subscription Manager DVM
# subscription_config = DVMConfig()
# subscription_config.PRIVATE_KEY = check_and_set_private_key("dvm_subscription")
# npub = Keys.parse(subscription_config.PRIVATE_KEY).public_key().to_bech32()
# invoice_key, admin_key, wallet_id, user_id, lnaddress = check_and_set_ln_bits_keys("dvm_subscription", npub)
# subscription_config.LNBITS_INVOICE_KEY = invoice_key
# subscription_config.LNBITS_ADMIN_KEY = admin_key # The dvm might pay failed jobs back
# subscription_config.LNBITS_URL = os.getenv("LNBITS_HOST")
# sub_admin_config = AdminConfig()
# sub_admin_config.USERNPUBS = ["7782f93c5762538e1f7ccc5af83cd8018a528b9cd965048386ca1b75335f24c6"] #Add npubs of services that can contact the subscription handler
# currently there is none, but add this once subscriptions are live.
# x = threading.Thread(target=Subscription, args=(Subscription(subscription_config, sub_admin_config),))
# x.start()
# keep_alive()
if __name__ == '__main__': if __name__ == '__main__':