diff --git a/nostr_dvm/tasks/content_discovery_currently_popular.py b/nostr_dvm/tasks/content_discovery_currently_popular.py index e306667..1ff1df7 100644 --- a/nostr_dvm/tasks/content_discovery_currently_popular.py +++ b/nostr_dvm/tasks/content_discovery_currently_popular.py @@ -138,7 +138,6 @@ class DicoverContentCurrentlyPopular(DVMTaskInterface): if len(reactions) >= self.min_reactions: ns.finallist[event.id().to_hex()] = len(reactions) if len(ns.finallist) == 0: - cli.disconnect() cli.shutdown() 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])) e_tag = Tag.parse(["e", entry[0]]) result_list.append(e_tag.as_vec()) - cli.disconnect() cli.shutdown() - print("[" + self.dvm_config.IDENTIFIER + "] Filtered " + str( + print("[" + self.dvm_config.NIP89.NAME + "] Filtered " + str( len(result_list)) + " fitting events.") return json.dumps(result_list) @@ -196,15 +194,15 @@ class DicoverContentCurrentlyPopular(DVMTaskInterface): 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( + 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) 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. cli.shutdown() 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, diff --git a/nostr_dvm/tasks/content_discovery_currently_popular_by_top_zaps.py b/nostr_dvm/tasks/content_discovery_currently_popular_by_top_zaps.py index bf4eb62..5bf1622 100644 --- a/nostr_dvm/tasks/content_discovery_currently_popular_by_top_zaps.py +++ b/nostr_dvm/tasks/content_discovery_currently_popular_by_top_zaps.py @@ -162,7 +162,7 @@ class DicoverContentCurrentlyPopularZaps(DVMTaskInterface): e_tag = Tag.parse(["e", entry[0]]) 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.") cli.disconnect() @@ -216,15 +216,15 @@ class DicoverContentCurrentlyPopularZaps(DVMTaskInterface): 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( + 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) 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. cli.shutdown() 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, diff --git a/nostr_dvm/tasks/content_discovery_currently_popular_followers.py b/nostr_dvm/tasks/content_discovery_currently_popular_followers.py index a09fcdc..1f0f874 100644 --- a/nostr_dvm/tasks/content_discovery_currently_popular_followers.py +++ b/nostr_dvm/tasks/content_discovery_currently_popular_followers.py @@ -164,7 +164,7 @@ class DicoverContentCurrentlyPopularFollowers(DVMTaskInterface): result_list.append(e_tag.as_vec()) cli.connect() cli.shutdown() - print("[" + self.dvm_config.IDENTIFIER + "] Filtered " + str( + print("[" + self.dvm_config.NIP89.NAME + "] Filtered " + str( len(result_list)) + " fitting events.") return json.dumps(result_list) @@ -214,14 +214,14 @@ class DicoverContentCurrentlyPopularFollowers(DVMTaskInterface): 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( + 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) 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. 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..") diff --git a/nostr_dvm/tasks/content_discovery_currently_popular_topic.py b/nostr_dvm/tasks/content_discovery_currently_popular_topic.py index 7d65da6..480c124 100644 --- a/nostr_dvm/tasks/content_discovery_currently_popular_topic.py +++ b/nostr_dvm/tasks/content_discovery_currently_popular_topic.py @@ -177,10 +177,8 @@ class DicoverContentCurrentlyPopularbyTopic(DVMTaskInterface): e_tag = Tag.parse(["e", entry[0]]) 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.") - - cli.disconnect() cli.shutdown() 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 # 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) 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. 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, diff --git a/nostr_dvm/tasks/content_discovery_update_db_only.py b/nostr_dvm/tasks/content_discovery_update_db_only.py new file mode 100644 index 0000000..a267abf --- /dev/null +++ b/nostr_dvm/tasks/content_discovery_update_db_only.py @@ -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) diff --git a/nostr_dvm/utils/dvmconfig.py b/nostr_dvm/utils/dvmconfig.py index d4a9844..fd98db1 100644 --- a/nostr_dvm/utils/dvmconfig.py +++ b/nostr_dvm/utils/dvmconfig.py @@ -25,10 +25,16 @@ class DVMConfig: "wss://nostr.oxtr.dev", "wss://relay.nostr.bg", "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_LONG_TIMEOUT = 30 diff --git a/nostr_dvm/utils/nostr_utils.py b/nostr_dvm/utils/nostr_utils.py index c29aec0..12a24cf 100644 --- a/nostr_dvm/utils/nostr_utils.py +++ b/nostr_dvm/utils/nostr_utils.py @@ -123,11 +123,13 @@ def get_inbox_relays(event_to_send: Event, client: Client, dvm_config): for tag in nip65event.tags(): if tag.as_vec()[0] == 'r' and len(tag.as_vec()) == 2: 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: if tag.as_vec()[2] == "read": 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 @@ -140,7 +142,8 @@ def send_event_outbox(event: Event, client, dvm_config) -> EventId: if tag.as_vec()[0] == 'relays': for index, param in enumerate(tag.as_vec()): 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 diff --git a/setup.py b/setup.py index e9f1f72..a4e8768 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ 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' LONG_DESCRIPTION = ('A framework to build and run Nostr NIP90 Data Vending Machines. See the github repository for more information') diff --git a/tests/discovery.py b/tests/discovery.py index 4ad585d..b897086 100644 --- a/tests/discovery.py +++ b/tests/discovery.py @@ -6,6 +6,9 @@ from pathlib import Path import dotenv 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.tasks.content_discovery_currently_popular import DicoverContentCurrentlyPopular 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 rebroadcast_NIP89 = False # Announce NIP89 on startup -rebroadcast_NIP65_Relay_List = True +rebroadcast_NIP65_Relay_List = False update_profile = False 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: 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): dvm_config: DVMConfig = build_default_config(identifier) dvm_config.USE_OWN_VENV = False dvm_config.CUSTOM_PROCESSING_MESSAGE = custom_processing_msg - dvm_config.RELAY_LIST = ["wss://dvms.f7z.io", - "wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg" - ] + #dvm_config.RELAY_LIST = ["wss://dvms.f7z.io", + # "wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg" + # ] admin_config.LUD16 = dvm_config.LN_ADDRESS # 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.FIX_COST = cost dvm_config.CUSTOM_PROCESSING_MESSAGE = processing_msg - dvm_config.RELAY_LIST = ["wss://dvms.f7z.io", - "wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg" - ] + #dvm_config.RELAY_LIST = ["wss://dvms.f7z.io", + # "wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg" + # ] admin_config.LUD16 = dvm_config.LN_ADDRESS # 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", #"wss://relay.nostr.net"] dvm_config.CUSTOM_PROCESSING_MESSAGE = processing_msg - dvm_config.RELAY_LIST = ["wss://dvms.f7z.io", - "wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg" - ] + #dvm_config.RELAY_LIST = ["wss://dvms.f7z.io", + # "wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg" + # ] admin_config.LUD16 = dvm_config.LN_ADDRESS # 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.FIX_COST = cost dvm_config.CUSTOM_PROCESSING_MESSAGE = processing_msg - dvm_config.RELAY_LIST = ["wss://dvms.f7z.io", - "wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg" - ] + #dvm_config.RELAY_LIST = ["wss://dvms.f7z.io", + # "wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg" + # ] admin_config.LUD16 = dvm_config.LN_ADDRESS # 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.FIX_COST = cost dvm_config.CUSTOM_PROCESSING_MESSAGE = processing_msg - dvm_config.RELAY_LIST = ["wss://dvms.f7z.io", - "wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg" - ] + #dvm_config.RELAY_LIST = ["wss://dvms.f7z.io", + # "wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg" + # ] admin_config.LUD16 = dvm_config.LN_ADDRESS # Add NIP89 @@ -233,6 +280,26 @@ def build_example_top_zapped(name, identifier, admin_config, options, image, cos 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 admin_config_top_zaps = AdminConfig() admin_config_top_zaps.REBROADCAST_NIP89 = rebroadcast_NIP89 @@ -264,6 +331,7 @@ def playground(): discovery_topzaps.run() + # Popular NOSTR.band admin_config_trending_nostr_band = AdminConfig() 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..", "Looking for the goodest bois and girls.."] 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_content_fluffy", admin_config_animals, options_animal, @@ -348,10 +416,10 @@ def playground(): admin_config_plants.REBROADCAST_NIP89 = rebroadcast_NIP89 admin_config_plants.REBROADCAST_NIP65_RELAY_LIST = rebroadcast_NIP65_Relay_List admin_config_plants.UPDATE_PROFILE = update_profile - #admin_config_plants.DELETE_NIP89 = True - #admin_config_plants.PRIVKEY = "" - #admin_config_plants.EVENTID = "ff28be59708ee597c7010fd43a7e649e1ab51da491266ca82a84177e0007e4d6" - #admin_config_plants.POW = True + # admin_config_plants.DELETE_NIP89 = True + # admin_config_plants.PRIVKEY = "" + # admin_config_plants.EVENTID = "ff28be59708ee597c7010fd43a7e649e1ab51da491266ca82a84177e0007e4d6" + # admin_config_plants.POW = True options_plants = { "search_list": ["garden", "gardening", "nature", " plants ", " plant ", " herb ", " herbs " " pine ", "homesteading", "rosemary", "chicken", "🪻", "🌿", "☘️", "🌲", "flower", "forest", "watering", @@ -360,7 +428,8 @@ def playground(): "avoid_list": ["porn", "smoke", "nsfw", "bitcoin", "bolt12", "bolt11", "github", "currency", "utxo", "encryption", "government", "airpod", "ipad", "iphone", "android", "warren", "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", "white house", "blocks", "streaming", "summary", "wealth", "beef", "cunt", "nigger", "business", "retail", "bakery", "synth", "slaughterhouse", "hamas", "dog days", "ww3", "socialmedia", @@ -379,7 +448,7 @@ def playground(): "Looking for #goodvibes..", "All I do is #blooming.."] update_db = False 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, image=image, description=description, @@ -387,18 +456,17 @@ def playground(): cost=cost, processing_msg=custom_processing_msg, update_db=update_db) - discovery_test_sub.run() - + discovery_garden.run() # Popular Followers admin_config_followers = AdminConfig() admin_config_followers.REBROADCAST_NIP89 = rebroadcast_NIP89 admin_config_followers.REBROADCAST_NIP65_RELAY_LIST = rebroadcast_NIP65_Relay_List admin_config_followers.UPDATE_PROFILE = update_profile - #admin_config_followers.DELETE_NIP89 = True - #admin_config_followers.PRIVKEY = "" - #admin_config_followers.EVENTID = "590cd7b2902224f740acbd6845023a5ab4a959386184f3360c2859019cfd48fa" - #admin_config_followers.POW = True + # admin_config_followers.DELETE_NIP89 = True + # admin_config_followers.PRIVKEY = "" + # admin_config_followers.EVENTID = "590cd7b2902224f740acbd6845023a5ab4a959386184f3360c2859019cfd48fa" + # admin_config_followers.POW = True custom_processing_msg = ["Processing popular notes from npubs you follow..", "Let's see what npubs you follow have been up to..", "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_NIP65_RELAY_LIST = rebroadcast_NIP65_Relay_List admin_config_global_popular.UPDATE_PROFILE = update_profile - #admin_config_global_popular.DELETE_NIP89 = True - #admin_config_global_popular.PRIVKEY = "" - #admin_config_global_popular.EVENTID = "2fea4ee2ccf0fa11db171113ffd7a676f800f34121478b7c9a4e73c2f1990028" - #admin_config_global_popular.POW = True + # admin_config_global_popular.DELETE_NIP89 = True + # admin_config_global_popular.PRIVKEY = "" + # admin_config_global_popular.EVENTID = "2fea4ee2ccf0fa11db171113ffd7a676f800f34121478b7c9a4e73c2f1990028" + # admin_config_global_popular.POW = True 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.."] update_db = False @@ -452,25 +520,6 @@ def playground(): update_db=update_db) 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__':