From cb7802c509b8b8f8dc350d099d2ed40af31a37ff Mon Sep 17 00:00:00 2001 From: Believethehype <1097224+believethehype@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:06:46 +0200 Subject: [PATCH 1/5] adaptions to sdk 0.11.0 --- nostr_dvm/bot.py | 10 +++++----- nostr_dvm/dvm.py | 6 +++--- nostr_dvm/subscription.py | 4 ++-- nostr_dvm/tasks/summarization_huggingchat.py | 2 +- nostr_dvm/tasks/summarization_unleashed_chat.py | 2 +- nostr_dvm/utils/backend_utils.py | 6 +++--- setup.py | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/nostr_dvm/bot.py b/nostr_dvm/bot.py index c9d330d..44b5446 100644 --- a/nostr_dvm/bot.py +++ b/nostr_dvm/bot.py @@ -74,22 +74,22 @@ class Bot: if (EventDefinitions.KIND_NIP90_EXTRACT_TEXT.as_u64() + 1000 <= nostr_event.kind().as_u64() <= EventDefinitions.KIND_NIP90_GENERIC.as_u64() + 1000): handle_nip90_response_event(nostr_event) - elif nostr_event.kind().as_u64() == EventDefinitions.KIND_FEEDBACK.as_u64(): + elif nostr_event.kind() == EventDefinitions.KIND_FEEDBACK: handle_nip90_feedback(nostr_event) - elif nostr_event.kind().as_u64() == EventDefinitions.KIND_ZAP.as_u64(): + elif nostr_event.kind() == EventDefinitions.KIND_ZAP: handle_zap(nostr_event) - elif nostr_event.kind().match_enum(KindEnum.ENCRYPTED_DIRECT_MESSAGE()): + elif nostr_event.kind() == EventDefinitions.KIND_DM: try: handle_dm(nostr_event) except Exception as e: print(f"Error during content NIP04 decryption: {e}") - elif nostr_event.kind().match_enum(KindEnum.GIFT_WRAP()): + elif nostr_event.kind() == KindEnum.GIFT_WRAP(): print("Decrypting NIP59 event") try: rumor: UnsignedEvent = nip59_extract_rumor(self.keys, nostr_event) - if rumor.kind().match_enum(KindEnum.SEALED_DIRECT()): + if rumor.kind() == KindEnum.SEALED_DIRECT(): msg = rumor.content() print(f"Received new msg [sealed]: {msg}") self.client.send_sealed_msg(rumor.author(), "Nip44 is not supported yet, but coming soon", None) diff --git a/nostr_dvm/dvm.py b/nostr_dvm/dvm.py index b8ab756..88ed935 100644 --- a/nostr_dvm/dvm.py +++ b/nostr_dvm/dvm.py @@ -272,7 +272,7 @@ class DVM: user = get_or_add_user(db=self.dvm_config.DB, npub=sender, client=self.client, config=self.dvm_config) if zapped_event is not None: - if zapped_event.kind().as_u64() == EventDefinitions.KIND_FEEDBACK.as_u64(): + if zapped_event.kind() == EventDefinitions.KIND_FEEDBACK: amount = 0 job_event = None @@ -295,7 +295,7 @@ class DVM: # if a reaction by us got zapped print(status) - if job_event.kind().as_u64() == EventDefinitions.KIND_NIP88_SUBSCRIBE_EVENT.as_u64(): + if job_event.kind() == EventDefinitions.KIND_NIP88_SUBSCRIBE_EVENT: send_job_status_reaction(job_event, "subscription-success", client=self.client, dvm_config=self.dvm_config, user=user) @@ -334,7 +334,7 @@ class DVM: False, invoice_amount, client=self.client, dvm_config=self.dvm_config) print("[" + self.dvm_config.NIP89.NAME + "] Invoice was not paid sufficiently") - elif zapped_event.kind().as_u64() == EventDefinitions.KIND_NIP88_SUBSCRIBE_EVENT.as_u64(): + elif zapped_event.kind() == EventDefinitions.KIND_NIP88_SUBSCRIBE_EVENT: print("new subscription, doing nothing") elif zapped_event.kind() in EventDefinitions.ANY_RESULT: diff --git a/nostr_dvm/subscription.py b/nostr_dvm/subscription.py index 6fcded6..a593060 100644 --- a/nostr_dvm/subscription.py +++ b/nostr_dvm/subscription.py @@ -78,9 +78,9 @@ class Subscription: keys = self.keys def handle(self, relay_url, subscription_id, nostr_event: Event): - if nostr_event.kind().as_u64() == EventDefinitions.KIND_NIP90_DVM_SUBSCRIPTION.as_u64(): + if nostr_event.kind() == EventDefinitions.KIND_NIP90_DVM_SUBSCRIPTION: handle_nwc_request(nostr_event) - elif nostr_event.kind().as_u64() == EventDefinitions.KIND_NIP88_STOP_SUBSCRIPTION_EVENT.as_u64(): + elif nostr_event.kind() == EventDefinitions.KIND_NIP88_STOP_SUBSCRIPTION_EVENT: handle_cancel(nostr_event) def handle_msg(self, relay_url, msg): diff --git a/nostr_dvm/tasks/summarization_huggingchat.py b/nostr_dvm/tasks/summarization_huggingchat.py index 3a81c47..45c8dd0 100644 --- a/nostr_dvm/tasks/summarization_huggingchat.py +++ b/nostr_dvm/tasks/summarization_huggingchat.py @@ -67,7 +67,7 @@ class TextSummarizationHuggingChat(DVMTaskInterface): print("Event not found") raise Exception - if evt.kind().as_u64() == EventDefinitions.KIND_NIP90_RESULT_CONTENT_DISCOVERY: + if evt.kind() == EventDefinitions.KIND_NIP90_RESULT_CONTENT_DISCOVERY: result_list = json.loads(evt.content()) prompt = "" for tag in result_list: diff --git a/nostr_dvm/tasks/summarization_unleashed_chat.py b/nostr_dvm/tasks/summarization_unleashed_chat.py index 66247f5..2b56330 100644 --- a/nostr_dvm/tasks/summarization_unleashed_chat.py +++ b/nostr_dvm/tasks/summarization_unleashed_chat.py @@ -68,7 +68,7 @@ class SummarizationUnleashedChat(DVMTaskInterface): print("Event not found") raise Exception - if evt.kind().as_u64() == EventDefinitions.KIND_NIP90_RESULT_CONTENT_DISCOVERY: + if evt.kind() == EventDefinitions.KIND_NIP90_RESULT_CONTENT_DISCOVERY: result_list = json.loads(evt.content()) prompt = "" for tag in result_list: diff --git a/nostr_dvm/utils/backend_utils.py b/nostr_dvm/utils/backend_utils.py index 2398453..ac28697 100644 --- a/nostr_dvm/utils/backend_utils.py +++ b/nostr_dvm/utils/backend_utils.py @@ -12,7 +12,7 @@ from nostr_dvm.utils.nostr_utils import get_event_by_id, get_referenced_event_by def get_task(event, client, dvm_config): try: - if event.kind().as_u64() == EventDefinitions.KIND_NIP90_GENERIC.as_u64(): # use this for events that have no id yet, inclufr j tag + if event.kind() == EventDefinitions.KIND_NIP90_GENERIC: # use this for events that have no id yet, inclufr j tag for tag in event.tags(): if tag.as_vec()[0] == 'j': return tag.as_vec()[1] @@ -26,7 +26,7 @@ def get_task(event, client, dvm_config): return "unknown job: " + event.as_json() # This looks a bit more complicated, but we do several tasks for text-extraction in the future - elif event.kind().as_u64() == EventDefinitions.KIND_NIP90_EXTRACT_TEXT.as_u64(): + elif event.kind() == EventDefinitions.KIND_NIP90_EXTRACT_TEXT: for tag in event.tags(): if tag.as_vec()[0] == "i": if tag.as_vec()[2] == "url": @@ -57,7 +57,7 @@ def get_task(event, client, dvm_config): return "unknown type" else: return "unknown job" - elif event.kind().as_u64() == EventDefinitions.KIND_NIP90_GENERATE_IMAGE.as_u64(): + elif event.kind() == EventDefinitions.KIND_NIP90_GENERATE_IMAGE: has_image_tag = False has_text_tag = False for tag in event.tags(): diff --git a/setup.py b/setup.py index 5c87d61..b4ab367 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( long_description=LONG_DESCRIPTION, packages=find_packages(include=['nostr_dvm', 'nostr_dvm.*']), - install_requires=["nostr-sdk==0.10.0", + install_requires=["nostr-sdk==0.11.0", "bech32", "pycryptodome==3.20.0", "python-dotenv==1.0.0", From c60e8af3afb7b8b35e81ce15aa92a3b1088ae3b5 Mon Sep 17 00:00:00 2001 From: Believethehype <1097224+believethehype@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:54:59 +0200 Subject: [PATCH 2/5] fixes for summarizers --- nostr_dvm/tasks/summarization_huggingchat.py | 18 ++++++++---------- .../tasks/summarization_unleashed_chat.py | 16 +++++++--------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/nostr_dvm/tasks/summarization_huggingchat.py b/nostr_dvm/tasks/summarization_huggingchat.py index 45c8dd0..00585e2 100644 --- a/nostr_dvm/tasks/summarization_huggingchat.py +++ b/nostr_dvm/tasks/summarization_huggingchat.py @@ -54,8 +54,8 @@ class TextSummarizationHuggingChat(DVMTaskInterface): prompt += tag.as_vec()[1] + "\n" elif input_type == "event": collect_events.append(tag.as_vec()[1]) - #evt = get_event_by_id(tag.as_vec()[1], client=client, config=dvm_config) - #prompt += evt.content() + "\n" + # evt = get_event_by_id(tag.as_vec()[1], client=client, config=dvm_config) + # prompt += evt.content() + "\n" elif input_type == "job": evt = get_referenced_event_by_id(event_id=tag.as_vec()[1], client=client, kinds=[EventDefinitions.KIND_NIP90_RESULT_EXTRACT_TEXT, @@ -66,7 +66,7 @@ class TextSummarizationHuggingChat(DVMTaskInterface): if evt is None: print("Event not found") raise Exception - + if evt.kind() == EventDefinitions.KIND_NIP90_RESULT_CONTENT_DISCOVERY: result_list = json.loads(evt.content()) prompt = "" @@ -83,9 +83,11 @@ class TextSummarizationHuggingChat(DVMTaskInterface): for evt in evts: prompt += evt.content() + "\n" + clean_prompt = re.sub(r'^https?:\/\/.*[\r\n]*', '', prompt, flags=re.MULTILINE) options = { - "prompt": prompt, + "prompt": clean_prompt[:4000], } + request_form['options'] = json.dumps(options) return request_form @@ -102,14 +104,11 @@ class TextSummarizationHuggingChat(DVMTaskInterface): cookies = sign.login() sign.saveCookiesToDir(cookie_path_dir) - options = DVMTaskInterface.set_options(request_form) try: chatbot = hugchat.ChatBot(cookies=cookies.get_dict()) # or cookie_path="usercookies/.json" - text = re.sub(r'^https?:\/\/.*[\r\n]*', '', str(options["prompt"]), flags=re.MULTILINE) - - query_result = chatbot.query("Summarize the following notes: " + text[:3500]) + query_result = chatbot.query("Summarize the following notes: " + str(options["prompt"])) print(query_result["text"]) # or query_result.text or query_result["text"] return str(query_result["text"]).lstrip() @@ -118,7 +117,6 @@ class TextSummarizationHuggingChat(DVMTaskInterface): raise Exception(e) - # 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 @@ -140,7 +138,7 @@ def build_example(name, identifier, admin_config): nip89config.CONTENT = json.dumps(nip89info) return TextSummarizationHuggingChat(name=name, dvm_config=dvm_config, nip89config=nip89config, - admin_config=admin_config) + admin_config=admin_config) if __name__ == '__main__': diff --git a/nostr_dvm/tasks/summarization_unleashed_chat.py b/nostr_dvm/tasks/summarization_unleashed_chat.py index 2b56330..e9e5345 100644 --- a/nostr_dvm/tasks/summarization_unleashed_chat.py +++ b/nostr_dvm/tasks/summarization_unleashed_chat.py @@ -4,7 +4,7 @@ import re from nostr_sdk import Tag, Kind from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv from nostr_dvm.utils.admin_utils import AdminConfig -from nostr_dvm.utils.definitions import EventDefinitions +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 from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag @@ -84,9 +84,9 @@ class SummarizationUnleashedChat(DVMTaskInterface): for evt in evts: prompt += evt.content() + "\n" - prompt = re.sub(r'http\S+', '', prompt) + clean_prompt = re.sub(r'^https?:\/\/.*[\r\n]*', '', prompt, flags=re.MULTILINE) options = { - "prompt": prompt, + "prompt": clean_prompt[:4000], "nostr": nostr_mode, } request_form['options'] = json.dumps(options) @@ -108,14 +108,13 @@ class SummarizationUnleashedChat(DVMTaskInterface): for model in client.models.list(): print('- ' + model.id) - text = re.sub(r'^https?:\/\/.*[\r\n]*', '', str(options["prompt"]), flags=re.MULTILINE) - content = "Summarize the following notes: " + text[:3500] + content = "Summarize the following notes: " + str(options["prompt"]) normal_stream = client.chat.completions.create( messages=[ { 'role': 'user', - 'content':content, + 'content': content, } ], model='dolphin-2.2.1-mistral-7b', @@ -148,7 +147,6 @@ def build_example(name, identifier, admin_config): dvm_config.SEND_FEEDBACK_EVENTS = True admin_config.LUD16 = dvm_config.LN_ADDRESS - nip89info = { "name": name, "image": "https://unleashed.chat/_app/immutable/assets/hero.pehsu4x_.jpeg", @@ -164,9 +162,9 @@ def build_example(name, identifier, admin_config): admin_config2 = AdminConfig() admin_config2.REBROADCAST_NIP89 = False - return SummarizationUnleashedChat(name=name, dvm_config=dvm_config, nip89config=nip89config, admin_config=admin_config2) + return SummarizationUnleashedChat(name=name, dvm_config=dvm_config, nip89config=nip89config, + admin_config=admin_config2) if __name__ == '__main__': process_venv(SummarizationUnleashedChat) - From 6935284f79664604fc6e21336c2b4de78d9e6fa1 Mon Sep 17 00:00:00 2001 From: Believethehype <1097224+believethehype@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:39:22 +0200 Subject: [PATCH 3/5] Update bot.py --- nostr_dvm/bot.py | 50 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/nostr_dvm/bot.py b/nostr_dvm/bot.py index 44b5446..3ddbcdd 100644 --- a/nostr_dvm/bot.py +++ b/nostr_dvm/bot.py @@ -6,7 +6,7 @@ from datetime import timedelta from nostr_sdk import (Keys, Client, Timestamp, Filter, nip04_decrypt, HandleNotification, EventBuilder, PublicKey, Options, Tag, Event, nip04_encrypt, NostrSigner, EventId, Nip19Event, Kind, KindEnum, - UnsignedEvent, nip59_extract_rumor) + UnsignedEvent, UnwrappedGift) from nostr_dvm.utils.admin_utils import admin_make_database_updates from nostr_dvm.utils.database_utils import get_or_add_user, update_user_balance, create_sql_table, update_sql_table @@ -53,13 +53,15 @@ class Bot: zap_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_ZAP]).since(Timestamp.now()) dm_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_DM]).since(Timestamp.now()) + nip59_filter = Filter().pubkey(pk).kind(Kind.from_enum(KindEnum.GIFT_WRAP())).since( + Timestamp.from_secs(Timestamp.now().as_secs() - 60 * 60 * 24 * 7)) kinds = [EventDefinitions.KIND_NIP90_GENERIC, EventDefinitions.KIND_FEEDBACK] for dvm in self.dvm_config.SUPPORTED_DVMS: if dvm.KIND not in kinds: kinds.append(Kind(dvm.KIND.as_u64() + 1000)) dvm_filter = (Filter().kinds(kinds).since(Timestamp.now())) - self.client.subscribe([zap_filter, dm_filter, dvm_filter], None) + self.client.subscribe([zap_filter, dm_filter, nip59_filter, dvm_filter], None) create_sql_table(self.dvm_config.DB) admin_make_database_updates(adminconfig=self.admin_config, dvmconfig=self.dvm_config, client=self.client) @@ -82,34 +84,54 @@ class Bot: elif nostr_event.kind() == EventDefinitions.KIND_DM: try: - handle_dm(nostr_event) + handle_dm(nostr_event, False) except Exception as e: print(f"Error during content NIP04 decryption: {e}") elif nostr_event.kind() == KindEnum.GIFT_WRAP(): - print("Decrypting NIP59 event") try: - rumor: UnsignedEvent = nip59_extract_rumor(self.keys, nostr_event) - if rumor.kind() == KindEnum.SEALED_DIRECT(): - msg = rumor.content() - print(f"Received new msg [sealed]: {msg}") - self.client.send_sealed_msg(rumor.author(), "Nip44 is not supported yet, but coming soon", None) - else: - print(f"{rumor.as_json()}") + handle_dm(nostr_event, True) except Exception as e: print(f"Error during content NIP59 decryption: {e}") + + def handle_msg(self, relay_url, msg): return - def handle_dm(nostr_event): + def handle_dm(nostr_event, giftwrap): sender = nostr_event.author().to_hex() if sender == self.keys.public_key().to_hex(): return try: - decrypted_text = nip04_decrypt(self.keys.secret_key(), nostr_event.author(), nostr_event.content()) + sealed = " " + if giftwrap: + print("Decrypting NIP59 event") + try: + # Extract rumor + unwrapped_gift = UnwrappedGift.from_gift_wrap(self.keys, nostr_event) + sender = unwrapped_gift.sender() + rumor: UnsignedEvent = unwrapped_gift.rumor() + + # Check timestamp of rumor + if rumor.created_at().as_secs() >= Timestamp.now().as_secs(): + if rumor.kind().match_enum(KindEnum.SEALED_DIRECT()): + decrypted_text = rumor.content() + print(f"Received new msg [sealed]: {decrypted_text}") + sealed = " [sealed] " + # client.send_sealed_msg(sender, f"Echo: {msg}", None) + else: + print(f"{rumor.as_json()}") + except Exception as e: + print(f"Error during content NIP59 decryption: {e}") + + else: + decrypted_text = nip04_decrypt(self.keys.secret_key(), nostr_event.author(), nostr_event.content()) + + + user = get_or_add_user(db=self.dvm_config.DB, npub=sender, client=self.client, config=self.dvm_config) - print("[" + self.NAME + "] Message from " + user.name + ": " + decrypted_text) + print("[" + self.NAME + "]" + sealed + "Message from " + user.name + ": " + decrypted_text) # if user selects an index from the overview list... if decrypted_text[0].isdigit(): From e7d9c217f1e3656d7540f23ba68245d8abc98885 Mon Sep 17 00:00:00 2001 From: Believethehype <1097224+believethehype@users.noreply.github.com> Date: Mon, 15 Apr 2024 17:31:51 +0200 Subject: [PATCH 4/5] giftwrap for bot --- nostr_dvm/bot.py | 312 +++++++++++++++++------------- nostr_dvm/dvm.py | 2 +- nostr_dvm/utils/database_utils.py | 13 +- 3 files changed, 185 insertions(+), 142 deletions(-) diff --git a/nostr_dvm/bot.py b/nostr_dvm/bot.py index 3ddbcdd..7c73cdd 100644 --- a/nostr_dvm/bot.py +++ b/nostr_dvm/bot.py @@ -65,6 +65,7 @@ class Bot: create_sql_table(self.dvm_config.DB) admin_make_database_updates(adminconfig=self.admin_config, dvmconfig=self.dvm_config, client=self.client) + # add_sql_table_column(dvm_config.DB) class NotificationHandler(HandleNotification): @@ -87,14 +88,12 @@ class Bot: handle_dm(nostr_event, False) except Exception as e: print(f"Error during content NIP04 decryption: {e}") - elif nostr_event.kind() == KindEnum.GIFT_WRAP(): + elif nostr_event.kind().match_enum(KindEnum.GIFT_WRAP()): try: - handle_dm(nostr_event, True) + handle_dm(nostr_event, True) except Exception as e: print(f"Error during content NIP59 decryption: {e}") - - def handle_msg(self, relay_url, msg): return @@ -102,15 +101,14 @@ class Bot: sender = nostr_event.author().to_hex() if sender == self.keys.public_key().to_hex(): return - + decrypted_text = "" try: sealed = " " if giftwrap: - print("Decrypting NIP59 event") try: # Extract rumor unwrapped_gift = UnwrappedGift.from_gift_wrap(self.keys, nostr_event) - sender = unwrapped_gift.sender() + sender = unwrapped_gift.sender().to_hex() rumor: UnsignedEvent = unwrapped_gift.rumor() # Check timestamp of rumor @@ -126,112 +124,139 @@ class Bot: print(f"Error during content NIP59 decryption: {e}") else: - decrypted_text = nip04_decrypt(self.keys.secret_key(), nostr_event.author(), nostr_event.content()) + try: + decrypted_text = nip04_decrypt(self.keys.secret_key(), nostr_event.author(), + nostr_event.content()) + except Exception as e: + print(f"Error during content NIP04 decryption: {e}") + if decrypted_text != "": + user = get_or_add_user(db=self.dvm_config.DB, npub=sender, client=self.client, + config=self.dvm_config) + print("[" + self.NAME + "]" + sealed + "Message from " + user.name + ": " + decrypted_text) - user = get_or_add_user(db=self.dvm_config.DB, npub=sender, client=self.client, config=self.dvm_config) - print("[" + self.NAME + "]" + sealed + "Message from " + user.name + ": " + decrypted_text) - - # if user selects an index from the overview list... - if decrypted_text[0].isdigit(): - split = decrypted_text.split(' ') - index = int(split[0]) - 1 - # if user sends index info, e.g. 1 info, we fetch the nip89 information and reply with it. - if len(split) > 1 and split[1].lower() == "info": - answer_nip89(nostr_event, index) - # otherwise we probably have to do some work, so build an event from input and send it to the DVM - else: - task = self.dvm_config.SUPPORTED_DVMS[index].TASK - print("[" + self.NAME + "] Request from " + str(user.name) + " (" + str(user.nip05) + - ", Balance: " + str(user.balance) + " Sats) Task: " + str(task)) - - if user.isblacklisted: - # If users are blacklisted for some reason, tell them. - answer_blacklisted(nostr_event) + # if user selects an index from the overview list... + if decrypted_text != "" and decrypted_text[0].isdigit(): + split = decrypted_text.split(' ') + index = int(split[0]) - 1 + # if user sends index info, e.g. 1 info, we fetch the nip89 information and reply with it. + if len(split) > 1 and split[1].lower() == "info": + answer_nip89(nostr_event, index) + # otherwise we probably have to do some work, so build an event from input and send it to the DVM else: - # Parse inputs to params - tags = build_params(decrypted_text, nostr_event, index) - p_tag = Tag.parse(['p', self.dvm_config.SUPPORTED_DVMS[index].PUBLIC_KEY]) + task = self.dvm_config.SUPPORTED_DVMS[index].TASK + print("[" + self.NAME + "] Request from " + str(user.name) + " (" + str(user.nip05) + + ", Balance: " + str(user.balance) + " Sats) Task: " + str(task)) + + if user.isblacklisted: + # If users are blacklisted for some reason, tell them. + answer_blacklisted(nostr_event, giftwrap, sender) - if self.dvm_config.SUPPORTED_DVMS[index].SUPPORTS_ENCRYPTION: - tags_str = [] - for tag in tags: - tags_str.append(tag.as_vec()) - params_as_str = json.dumps(tags_str) - print(params_as_str) - # and encrypt them - encrypted_params = nip04_encrypt(self.keys.secret_key(), - PublicKey.from_hex( - self.dvm_config.SUPPORTED_DVMS[index].PUBLIC_KEY), - params_as_str) - # add encrypted and p tag on the outside - encrypted_tag = Tag.parse(['encrypted']) - # add the encrypted params to the content - nip90request = (EventBuilder(self.dvm_config.SUPPORTED_DVMS[index].KIND, - encrypted_params, [p_tag, encrypted_tag]). - to_event(self.keys)) else: - tags.append(p_tag) + # Parse inputs to params + tags = build_params(decrypted_text, nostr_event, index) + p_tag = Tag.parse(['p', self.dvm_config.SUPPORTED_DVMS[index].PUBLIC_KEY]) - nip90request = (EventBuilder(self.dvm_config.SUPPORTED_DVMS[index].KIND, - "", tags). - to_event(self.keys)) + if self.dvm_config.SUPPORTED_DVMS[index].SUPPORTS_ENCRYPTION: + tags_str = [] + for tag in tags: + tags_str.append(tag.as_vec()) + params_as_str = json.dumps(tags_str) + print(params_as_str) + # and encrypt them + encrypted_params = nip04_encrypt(self.keys.secret_key(), + PublicKey.from_hex( + self.dvm_config.SUPPORTED_DVMS[ + index].PUBLIC_KEY), + params_as_str) + # add encrypted and p tag on the outside + encrypted_tag = Tag.parse(['encrypted']) + # add the encrypted params to the content + nip90request = (EventBuilder(self.dvm_config.SUPPORTED_DVMS[index].KIND, + encrypted_params, [p_tag, encrypted_tag]). + to_event(self.keys)) + else: + tags.append(p_tag) - # remember in the job_list that we have made an event, if anybody asks for payment, - # we know we actually sent the request - entry = {"npub": user.npub, "event_id": nip90request.id().to_hex(), - "dvm_key": self.dvm_config.SUPPORTED_DVMS[index].PUBLIC_KEY, "is_paid": False} - self.job_list.append(entry) + nip90request = (EventBuilder(self.dvm_config.SUPPORTED_DVMS[index].KIND, + "", tags). + to_event(self.keys)) - # send the event to the DVM - send_event(nip90request, client=self.client, dvm_config=self.dvm_config) - # print(nip90request.as_json()) + # remember in the job_list that we have made an event, if anybody asks for payment, + # we know we actually sent the request + entry = {"npub": user.npub, "event_id": nip90request.id().to_hex(), + "dvm_key": self.dvm_config.SUPPORTED_DVMS[index].PUBLIC_KEY, "is_paid": False, "giftwrap": giftwrap} + self.job_list.append(entry) + + # send the event to the DVM + send_event(nip90request, client=self.client, dvm_config=self.dvm_config) + # print(nip90request.as_json()) - elif decrypted_text.lower().startswith("balance"): - time.sleep(3.0) - evt = EventBuilder.encrypted_direct_msg(self.keys, nostr_event.author(), - "Your current balance is " + str( - user.balance) + " Sats. Zap me to add to your balance. I will use your balance interact with the DVMs for you.\n" - "I support both public and private Zaps, as well as Zapplepay.\n" - "Alternativly you can add a #cashu token with \"-cashu cashuASomeToken\" to your command.\n Make sure the token is worth the requested amount + " - "mint fees (at least 3 sat).\n Not all DVMs might accept Cashu tokens." - , None).to_event(self.keys) - send_event(evt, client=self.client, dvm_config=dvm_config) + elif decrypted_text.lower().startswith("balance"): + time.sleep(3.0) + message = "Your current balance is " + str(user.balance) + ("Sats. Zap me to add to your " + "balance. I will use your " + "balance interact with the DVMs " + "for you.\n I support both " + "public and private Zaps, " + "as well as " + "Zapplepay.\nAlternativly you " + "can add a #cashu token with " + "\"-cashu cashuASomeToken\" to " + "your command.\n Make sure the " + "token is worth the requested " + "amount mint fees (at least 3 " + "sat).\n Not all DVMs might " + "accept Cashu tokens.") + if giftwrap: + self.client.send_sealed_msg(PublicKey.parse(sender), message, None) + else: + evt = EventBuilder.encrypted_direct_msg(self.keys, PublicKey.parse(sender), + message,None).to_event(self.keys) + send_event(evt, client=self.client, dvm_config=dvm_config) + 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) + print(cashu_message) + if cashu_message == "success": + update_user_balance(self.dvm_config.DB, sender, total_amount, client=self.client, + config=self.dvm_config) + else: + time.sleep(2.0) + message = "Error: " + cashu_message + ". Token has not been redeemed." + + if giftwrap: + self.client.send_sealed_msg(PublicKey.parse(sender), message, None) + else: + evt = EventBuilder.encrypted_direct_msg(self.keys, PublicKey.from_hex(sender), message, + None).to_event(self.keys) + send_event(evt, client=self.client, dvm_config=self.dvm_config) + elif decrypted_text.lower().startswith("what's the second best"): + time.sleep(3.0) + message = "No, there is no second best.\n\nhttps://cdn.nostr.build/p/mYLv.mp4" + if giftwrap: + self.client.send_sealed_msg(PublicKey.parse(sender), message, None) + else: + evt = EventBuilder.encrypted_direct_msg(self.keys, PublicKey.parse(sender), + message, + nostr_event.id()).to_event(self.keys) + send_event(evt, client=self.client, dvm_config=self.dvm_config) - 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) - print(cashu_message) - if cashu_message == "success": - update_user_balance(self.dvm_config.DB, sender, total_amount, client=self.client, - config=self.dvm_config) else: - time.sleep(2.0) - message = "Error: " + cashu_message + ". Token has not been redeemed." - evt = EventBuilder.encrypted_direct_msg(self.keys, PublicKey.from_hex(sender), message, - None).to_event(self.keys) - send_event(evt, client=self.client, dvm_config=self.dvm_config) - elif decrypted_text.lower().startswith("what's the second best"): - time.sleep(3.0) - evt = EventBuilder.encrypted_direct_msg(self.keys, nostr_event.author(), - "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: - # Build an overview of known DVMs and send it to the user - answer_overview(nostr_event) + # Build an overview of known DVMs and send it to the user + answer_overview(nostr_event, giftwrap, sender) except Exception as e: print("Error in bot " + str(e)) def handle_nip90_feedback(nostr_event): - #print(nostr_event.as_json()) + # print(nostr_event.as_json()) try: is_encrypted = False status = "" @@ -281,14 +306,18 @@ class Bot: user = get_or_add_user(db=self.dvm_config.DB, npub=entry['npub'], client=self.client, config=self.dvm_config) time.sleep(2.0) - reply_event = EventBuilder.encrypted_direct_msg(self.keys, + if entry["giftwrap"]: + self.client.send_sealed_msg(PublicKey.parse(entry["npub"]), content, None) + else: + reply_event = EventBuilder.encrypted_direct_msg(self.keys, PublicKey.from_hex(entry['npub']), content, None).to_event(self.keys) + + send_event(reply_event, client=self.client, dvm_config=dvm_config) print(status + ": " + content) print( "[" + self.NAME + "] Received reaction from " + nostr_event.author().to_hex() + " message to orignal sender " + user.name) - send_event(reply_event, client=self.client, dvm_config=dvm_config) elif status == "payment-required" or status == "partial": for tag in nostr_event.tags(): @@ -307,28 +336,30 @@ class Bot: iswhitelisted=user.iswhitelisted, isblacklisted=user.isblacklisted, nip05=user.nip05, lud16=user.lud16, name=user.name, lastactive=Timestamp.now().as_secs(), subscribed=user.subscribed) - evt = EventBuilder.encrypted_direct_msg(self.keys, - PublicKey.from_hex(entry["npub"]), - "Paid " + str( - amount) + " Sats from balance to DVM. New balance is " + - str(balance) - + " Sats.\n", - None).to_event(self.keys) + message = "Paid " + str(amount) + " Sats from balance to DVM. New balance is " + str(balance) + " Sats.\n" + if entry["giftwrap"]: + self.client.send_sealed_msg(PublicKey.parse(entry["npub"]), message, None) + else: + evt = EventBuilder.encrypted_direct_msg(self.keys, + PublicKey.parse(entry["npub"]), + message, + None).to_event(self.keys) + send_event(evt, client=self.client, dvm_config=dvm_config) print( "[" + self.NAME + "] Replying " + user.name + " with \"scheduled\" confirmation") - send_event(evt, client=self.client, dvm_config=dvm_config) + else: print("Bot payment-required") time.sleep(2.0) evt = EventBuilder.encrypted_direct_msg(self.keys, - PublicKey.from_hex(entry["npub"]), - "Current balance: " + str( - user.balance) + " Sats. Balance of " + str( - amount) + " Sats required. Please zap me with at least " + - str(int(amount - user.balance)) - + " Sats, then try again.", - None).to_event(self.keys) + PublicKey.parse(entry["npub"]), + "Current balance: " + str( + user.balance) + " Sats. Balance of " + str( + amount) + " Sats required. Please zap me with at least " + + str(int(amount - user.balance)) + + " Sats, then try again.", + None).to_event(self.keys) send_event(evt, client=self.client, dvm_config=dvm_config) return @@ -398,11 +429,14 @@ class Bot: print("[" + self.NAME + "] Received results, message to orignal sender " + user.name) time.sleep(1.0) - reply_event = EventBuilder.encrypted_direct_msg(self.keys, - PublicKey.from_hex(user.npub), + if entry["giftwrap"]: + self.client.send_sealed_msg(PublicKey.parse(user.npub), content, None) + else: + reply_event = EventBuilder.encrypted_direct_msg(self.keys, + PublicKey.parse(user.npub), content, None).to_event(self.keys) - send_event(reply_event, client=self.client, dvm_config=dvm_config) + send_event(reply_event, client=self.client, dvm_config=dvm_config) except Exception as e: print(e) @@ -453,7 +487,7 @@ class Bot: except Exception as e: print("[" + self.NAME + "] Error during content decryption:" + str(e)) - def answer_overview(nostr_event): + def answer_overview(nostr_event, giftwrap, sender): message = "DVMs that I support:\n\n" index = 1 for p in self.dvm_config.SUPPORTED_DVMS: @@ -466,35 +500,39 @@ class Bot: index += 1 time.sleep(3.0) - evt = EventBuilder.encrypted_direct_msg(self.keys, nostr_event.author(), - message + "\nSelect an Index and provide an input (" - "e.g. \"2 A purple ostrich\")\nType \"index info\" to learn " - "more about each DVM. (e.g. \"2 info\")\n\n" - "Type \"balance\" to see your current balance", + + text = message + "\nSelect an Index and provide an input (e.g. \"2 A purple ostrich\")\nType \"index info\" to learn more about each DVM. (e.g. \"2 info\")\n\n Type \"balance\" to see your current balance" + if giftwrap: + self.client.send_sealed_msg(PublicKey.parse(sender), text, None) + else: + evt = EventBuilder.encrypted_direct_msg(self.keys, PublicKey.parse(sender), + text, nostr_event.id()).to_event(self.keys) - send_event(evt, client=self.client, dvm_config=dvm_config) + send_event(evt, client=self.client, dvm_config=dvm_config) - def answer_blacklisted(nostr_event): - # For some reason an admin might blacklist npubs, e.g. for abusing the service - evt = EventBuilder.encrypted_direct_msg(self.keys, nostr_event.author(), - "Your are currently blocked from all " - "services.", None).to_event(self.keys) - send_event(evt, client=self.client, dvm_config=dvm_config) - - def answer_nip89(nostr_event, index): - info = print_dvm_info(self.client, index) - time.sleep(2.0) - if info is not None: - evt = EventBuilder.encrypted_direct_msg(self.keys, nostr_event.author(), - info, None).to_event(self.keys) + def answer_blacklisted(nostr_event, giftwrap, sender): + message = "Your are currently blocked from this service." + if giftwrap: + self.client.send_sealed_msg(PublicKey.parse(sender), message, None) else: + # For some reason an admin might blacklist npubs, e.g. for abusing the service evt = EventBuilder.encrypted_direct_msg(self.keys, nostr_event.author(), - "No NIP89 Info found for " + - self.dvm_config.SUPPORTED_DVMS[index].NAME, - None).to_event(self.keys) + message, None).to_event(self.keys) + send_event(evt, client=self.client, dvm_config=dvm_config) - send_event(evt, client=self.client, dvm_config=dvm_config) + def answer_nip89(nostr_event, index, giftwrap, sender): + info = print_dvm_info(self.client, index) + if info is None: + info = "No NIP89 Info found for " + self.dvm_config.SUPPORTED_DVMS[index].NAME + time.sleep(2.0) + + if giftwrap: + self.client.send_sealed_msg(PublicKey.parse(sender), info, None) + else: + evt = EventBuilder.encrypted_direct_msg(self.keys, nostr_event.author(), + info, None).to_event(self.keys) + send_event(evt, client=self.client, dvm_config=dvm_config) def build_params(decrypted_text, nostr_event, index): tags = [] diff --git a/nostr_dvm/dvm.py b/nostr_dvm/dvm.py index 88ed935..405a626 100644 --- a/nostr_dvm/dvm.py +++ b/nostr_dvm/dvm.py @@ -5,7 +5,7 @@ from datetime import timedelta from sys import platform from nostr_sdk import PublicKey, Keys, Client, Tag, Event, EventBuilder, Filter, HandleNotification, Timestamp, \ - init_logger, LogLevel, Options, nip04_encrypt, NostrSigner, Kind, SubscribeAutoCloseOptions + init_logger, LogLevel, Options, nip04_encrypt, NostrSigner, Kind import time diff --git a/nostr_dvm/utils/database_utils.py b/nostr_dvm/utils/database_utils.py index feddbe3..4662ddd 100644 --- a/nostr_dvm/utils/database_utils.py +++ b/nostr_dvm/utils/database_utils.py @@ -169,7 +169,7 @@ def list_db(db): print(e) -def update_user_balance(db, npub, additional_sats, client, config): +def update_user_balance(db, npub, additional_sats, client, config, giftwrap=False): user = get_from_sql_table(db, npub) if user is None: name, nip05, lud16 = fetch_user_metadata(npub, client) @@ -192,9 +192,14 @@ def update_user_balance(db, npub, additional_sats, client, config): message = ("Added " + str(additional_sats) + " Sats to balance. New balance is " + str( new_balance) + " Sats.") - evt = EventBuilder.encrypted_direct_msg(keys, PublicKey.from_hex(npub), message, - None).to_event(keys) - send_event(evt, client=client, dvm_config=config) + if giftwrap: + client.send_sealed_msg(PublicKey.parse(npub), message, None) + else: + + + evt = EventBuilder.encrypted_direct_msg(keys, PublicKey.parse(npub), message, + None).to_event(keys) + send_event(evt, client=client, dvm_config=config) def update_user_subscription(npub, subscribed_until, client, dvm_config): From 3c5477d64064b23386016daf0098e7282a27bc73 Mon Sep 17 00:00:00 2001 From: Believethehype <1097224+believethehype@users.noreply.github.com> Date: Tue, 16 Apr 2024 14:33:11 +0200 Subject: [PATCH 5/5] bump version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b4ab367..9f6107e 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -VERSION = '0.3.2' +VERSION = '0.3.3' 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. ' 'This is an early stage release. Interfaces might change/brick')