diff --git a/bot/bot.py b/bot/bot.py index f5820e3..f8f8e30 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -159,7 +159,8 @@ class Bot: elif decrypted_text.startswith("cashuA"): print("Received Cashu token:" + decrypted_text) - cashu_redeemed, cashu_message, total_amount, fees = redeem_cashu(decrypted_text, self.dvm_config, self.client) + cashu_redeemed, cashu_message, total_amount, fees = redeem_cashu(decrypted_text, self.dvm_config, + self.client) print(cashu_message) if cashu_message == "success": update_user_balance(self.dvm_config.DB, sender, total_amount, client=self.client, @@ -173,7 +174,8 @@ class Bot: elif decrypted_text.lower().startswith("what's the second best"): time.sleep(3.0) evt = EventBuilder.new_encrypted_direct_msg(self.keys, nostr_event.pubkey(), - "No, there is no second best.\n\nhttps://cdn.nostr.build/p/mYLv.mp4",nostr_event.id()).to_event(self.keys) + "No, there is no second best.\n\nhttps://cdn.nostr.build/p/mYLv.mp4", + nostr_event.id()).to_event(self.keys) send_event(evt, client=self.client, dvm_config=self.dvm_config) else: @@ -230,7 +232,7 @@ class Bot: if status == "success" or status == "error" or status == "processing" or status == "partial" and content != "": entry = next((x for x in self.job_list if x['event_id'] == etag), None) - if entry is not None: + if entry is not None and entry['dvm_key'] == nostr_event.pubkey().to_hex(): user = get_or_add_user(db=self.dvm_config.DB, npub=entry['npub'], client=self.client, config=self.dvm_config) time.sleep(2.0) @@ -324,7 +326,8 @@ class Bot: is_encrypted = True entry = next((x for x in self.job_list if x['event_id'] == etag), None) - if entry is not None: + if entry is not None and entry[ + 'dvm_key'] == nostr_event.pubkey().to_hex(): print(entry) user = get_or_add_user(db=self.dvm_config.DB, npub=entry['npub'], client=self.client, config=self.dvm_config) @@ -340,14 +343,12 @@ class Bot: dvms = [x for x in self.dvm_config.SUPPORTED_DVMS if x.PUBLIC_KEY == nostr_event.pubkey().to_hex() and x.KIND == nostr_event.kind() - 1000] if len(dvms) > 0: - dvm = dvms[0] - if dvm.dvm_config.EXTERNAL_POST_PROCESS_TYPE != PostProcessFunctionType.NONE: - if dvm.dvm_config.EXTERNAL_POST_PROCESS_TYPE == PostProcessFunctionType.LIST_TO_EVENTS: - content = post_process_list_to_events(content) - elif dvm.dvm_config.EXTERNAL_POST_PROCESS_TYPE == PostProcessFunctionType.LIST_TO_USERS: - content = post_process_list_to_users(content) - - + dvm = dvms[0] + if dvm.dvm_config.EXTERNAL_POST_PROCESS_TYPE != PostProcessFunctionType.NONE: + if dvm.dvm_config.EXTERNAL_POST_PROCESS_TYPE == PostProcessFunctionType.LIST_TO_EVENTS: + content = post_process_list_to_events(content) + elif dvm.dvm_config.EXTERNAL_POST_PROCESS_TYPE == PostProcessFunctionType.LIST_TO_USERS: + content = post_process_list_to_users(content) print("[" + self.NAME + "] Received results, message to orignal sender " + user.name) time.sleep(1.0) @@ -372,13 +373,11 @@ class Bot: if tag.as_vec()[0] == "e": etag = tag.as_vec()[1] - user = get_or_add_user(self.dvm_config.DB, sender, client=self.client, config=self.dvm_config) - entry = next((x for x in self.job_list if x['event_id'] == etag), None) print(entry) - #print(entry['dvm_key']) + # print(entry['dvm_key']) # print(str(zapped_event.pubkey().to_hex())) # print(str(zap_event.pubkey().to_hex())) print(sender) @@ -387,19 +386,17 @@ class Bot: user = get_or_add_user(db=self.dvm_config.DB, npub=entry["npub"], client=self.client, config=self.dvm_config) - print("HELLO: " + user.name) sender = user.npub - #print(zap_event.as_json()) if zapped_event is not None: - if not anon: - print("[" + self.NAME + "] Note Zap received for Bot balance: " + str( - invoice_amount) + " Sats from " + str( - user.name)) - update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client, - config=self.dvm_config) + if not anon: + print("[" + self.NAME + "] Note Zap received for Bot balance: " + str( + invoice_amount) + " Sats from " + str( + user.name)) + update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client, + config=self.dvm_config) - # a regular note + # a regular note elif not anon: print("[" + self.NAME + "] Profile Zap received for Bot balance: " + str( invoice_amount) + " Sats from " + str( @@ -470,7 +467,6 @@ class Bot: tags.append(relays) return tags - tags = [] command = decrypted_text.replace(split[0] + " ", "") split = command.split(" -") @@ -506,7 +502,6 @@ class Bot: remaining_text = command.replace(input, "") print(remaining_text) - params = remaining_text.rstrip().split(" -") for i in params: @@ -524,7 +519,8 @@ class Bot: else: if param == "user": if value.startswith("@") or value.startswith("nostr:") or value.startswith("npub"): - value = PublicKey.from_bech32(value.replace("@","").replace("nostr:","")).to_hex() + value = PublicKey.from_bech32( + value.replace("@", "").replace("nostr:", "")).to_hex() tag = Tag.parse(["param", param, value]) tags.append(tag) print("Added params: " + str(tag.as_vec())) diff --git a/main.py b/main.py index 86c1580..1c525ce 100644 --- a/main.py +++ b/main.py @@ -10,7 +10,7 @@ import tasks.textextraction_pdf as textextraction_pdf import tasks.textextraction_google as textextraction_google import tasks.translation_google as translation_google import tasks.translation_libretranslate as translation_libretranslate -from tasks import imagegeneration_replicate_sdxl, videogeneration_replicate_svd, imagegeneration_sdxl +from tasks import imagegeneration_replicate_sdxl, videogeneration_replicate_svd, imagegeneration_sdxl, trending_notes_nostrband from utils.admin_utils import AdminConfig from utils.backend_utils import keep_alive @@ -137,6 +137,10 @@ def playground(): bot_config.SUPPORTED_DVMS.append(discover_inactive) discover_inactive.run() + trending = trending_notes_nostrband.build_example("Trending Notes on nostr.band", "trending_notes_nostrband", admin_config) + bot_config.SUPPORTED_DVMS.append(trending) + trending.run() + # Run the bot Bot(bot_config) # Keep the main function alive for libraries that require it, like openai diff --git a/tasks/advanced_search.py b/tasks/advanced_search.py index 42b78e1..09a399d 100644 --- a/tasks/advanced_search.py +++ b/tasks/advanced_search.py @@ -44,6 +44,7 @@ class AdvancedSearch(DVMTaskInterface): 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 diff --git a/tasks/trending_notes_nostrband.py b/tasks/trending_notes_nostrband.py new file mode 100644 index 0000000..115b95e --- /dev/null +++ b/tasks/trending_notes_nostrband.py @@ -0,0 +1,157 @@ +import json +import os +import re +from datetime import timedelta +from pathlib import Path +from threading import Thread + +import dotenv +from nostr_sdk import Client, Timestamp, PublicKey, Tag, Keys, Options, Alphabet, SecretKey, Event + +from interfaces.dvmtaskinterface import DVMTaskInterface + +from utils.admin_utils import AdminConfig +from utils.backend_utils import keep_alive +from utils.definitions import EventDefinitions +from utils.dvmconfig import DVMConfig +from utils.nip89_utils import NIP89Config, check_and_set_d_tag +from utils.nostr_utils import get_event_by_id, check_and_set_private_key +from utils.output_utils import post_process_list_to_users, post_process_list_to_events +from utils.zap_utils import check_and_set_ln_bits_keys + +""" +This File contains a Module to search for notes +Accepted Inputs: a search query +Outputs: A list of events +Params: None +""" + + +class TrendingNotesNostrBand(DVMTaskInterface): + KIND: int = EventDefinitions.KIND_NIP90_CONTENT_DISCOVERY + TASK: str = "trending-content" + FIX_COST: float = 0 + dvm_config: DVMConfig + + 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): + 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 + print(self.dvm_config.PRIVATE_KEY) + + request_form = {"jobID": event.id().to_hex()} + + for tag in event.tags(): + if tag.as_vec()[0] == 'i': + input_type = tag.as_vec()[2] + + options = { + + } + request_form['options'] = json.dumps(options) + return request_form + + def process(self, request_form): + from nostr_sdk import Filter + options = DVMTaskInterface.set_options(request_form) + + import requests + + url = "https://api.nostr.band/v0/trending/notes" + try: + response = requests.get(url) + response_json = response.json() + result_list = [] + i = 0 + if len(response_json["notes"]) > 0: + for note in response_json["notes"]: + i += 1 + if i < 20: + e_tag = Tag.parse(["e", note["id"]]) + print(e_tag.as_vec()) + result_list.append(e_tag.as_vec()) + else: + break + + return json.dumps(result_list) + + except: + return "error" + + + + + + 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 + + +# 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): + dvm_config = DVMConfig() + dvm_config.PRIVATE_KEY = check_and_set_private_key(identifier) + npub = Keys.from_sk_str(dvm_config.PRIVATE_KEY).public_key().to_bech32() + invoice_key, admin_key, wallet_id, user_id, lnaddress = check_and_set_ln_bits_keys(identifier, npub) + dvm_config.LNBITS_INVOICE_KEY = invoice_key + dvm_config.LNBITS_ADMIN_KEY = admin_key # The dvm might pay failed jobs back + dvm_config.LNBITS_URL = os.getenv("LNBITS_HOST") + admin_config.LUD16 = lnaddress + # Add NIP89 + nip90params = { + } + nip89info = { + "name": name, + "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", + "about": "I show trending notes from nostr.band", + "encryptionSupported": True, + "cashuAccepted": True, + "nip90Params": nip90params + } + + nip89config = NIP89Config() + nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, + nip89info["image"]) + + nip89config.CONTENT = json.dumps(nip89info) + return TrendingNotesNostrBand(name=name, dvm_config=dvm_config, nip89config=nip89config, + admin_config=admin_config) + + +if __name__ == '__main__': + 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) + else: + raise FileNotFoundError(f'.env file not found at {env_path} ') + + admin_config = AdminConfig() + admin_config.REBROADCAST_NIP89 = False + admin_config.UPDATE_PROFILE = False + admin_config.LUD16 = "" + + dvm = build_example("Trending Notes on Nostr.band", "trending_notes_nostrband", admin_config) + dvm.run() + + keep_alive()