diff --git a/bot/bot.py b/bot/bot.py index 317091e..6b1a39f 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -3,7 +3,6 @@ import os import signal import time from datetime import timedelta -from threading import Thread from nostr_sdk import (Keys, Client, Timestamp, Filter, nip04_decrypt, HandleNotification, EventBuilder, PublicKey, Options, Tag, Event, nip04_encrypt) @@ -13,6 +12,7 @@ from utils.database_utils import get_or_add_user, update_user_balance, create_sq from utils.definitions import EventDefinitions from utils.nip89_utils import nip89_fetch_events_pubkey from utils.nostr_utils import send_event +from utils.output_utils import PostProcessFunctionType, post_process_list_to_users, post_process_list_to_events from utils.zap_utils import parse_zap_event_tags, pay_bolt11_ln_bits, zap @@ -138,7 +138,7 @@ class Bot: # send the event to the DVM send_event(nip90request, client=self.client, dvm_config=self.dvm_config) - #print(nip90request.as_json()) + # print(nip90request.as_json()) @@ -234,7 +234,7 @@ class Bot: # if we get a bolt11, we pay and move on user = get_or_add_user(db=self.dvm_config.DB, npub=entry["npub"], client=self.client, config=self.dvm_config) - if user.balance > amount: + if user.balance >= amount: balance = max(user.balance - amount, 0) update_sql_table(db=self.dvm_config.DB, npub=user.npub, balance=balance, iswhitelisted=user.iswhitelisted, isblacklisted=user.isblacklisted, @@ -315,6 +315,20 @@ class Bot: else: return + 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] + print(dvms[0]) + + 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) + + + print("[" + self.NAME + "] Received results, message to orignal sender " + user.name) time.sleep(1.0) reply_event = EventBuilder.new_encrypted_direct_msg(self.keys, diff --git a/main.py b/main.py index cca72e9..984d6ff 100644 --- a/main.py +++ b/main.py @@ -12,6 +12,7 @@ from playground import build_pdf_extractor, build_googletranslator, build_unstab from utils.definitions import EventDefinitions from utils.dvmconfig import DVMConfig from utils.nostr_utils import check_and_set_private_key +from utils.output_utils import PostProcessFunctionType def run_nostr_dvm_with_local_config(): @@ -88,7 +89,7 @@ def run_nostr_dvm_with_local_config(): pubkey="d483935d6bfcef3645195c04c97bbb70aedb6e65665c5ea83e562ca3c7acb978", task="text-to-image", kind=EventDefinitions.KIND_NIP90_GENERATE_IMAGE, - fix_cost=100, per_unit_cost=0) + fix_cost=80, per_unit_cost=0) tasktiger_external.SUPPORTS_ENCRYPTION = False # if the dvm does not support encrypted events, just send a regular event and mark it with p tag. Other dvms might initial answer bot_config.SUPPORTED_DVMS.append(tasktiger_external) @@ -97,10 +98,11 @@ def run_nostr_dvm_with_local_config(): # DVM: 8 Another external dvm for recommendations: ymhm_external = build_external_dvm(name="External DVM: You might have missed", - pubkey="6b37d5dc88c1cbd32d75b713f6d4c2f7766276f51c9337af9d32c8d715cc1b93", - task="content-discovery", - kind=EventDefinitions.KIND_NIP90_CONTENT_DISCOVERY, - fix_cost=0, per_unit_cost=0) + pubkey="6b37d5dc88c1cbd32d75b713f6d4c2f7766276f51c9337af9d32c8d715cc1b93", + task="content-discovery", + kind=EventDefinitions.KIND_NIP90_CONTENT_DISCOVERY, + fix_cost=0, per_unit_cost=0, + external_post_process=PostProcessFunctionType.LIST_TO_EVENTS) ymhm_external.SUPPORTS_ENCRYPTION = False # if the dvm does not support encrypted events, just send a regular event and mark it with p tag. Other dvms might initial answer bot_config.SUPPORTED_DVMS.append(ymhm_external) diff --git a/playground.py b/playground.py index 36938a8..c8544d7 100644 --- a/playground.py +++ b/playground.py @@ -18,6 +18,7 @@ from utils.admin_utils import AdminConfig from utils.dvmconfig import DVMConfig from utils.nip89_utils import NIP89Config, check_and_set_d_tag from utils.nostr_utils import check_and_set_private_key +from utils.output_utils import PostProcessFunctionType """ This File is a playground to create DVMs. It shows some examples of DVMs that make use of the modules in the tasks folder @@ -241,7 +242,8 @@ def build_googletranscribe(name, identifier): nip89info["image"]) nip89config.CONTENT = json.dumps(nip89info) return SpeechToTextGoogle(name=name, dvm_config=dvm_config, nip89config=nip89config, - admin_config=admin_config, options=options) + admin_config=admin_config, options=options) + def build_sketcher(name, identifier): dvm_config = DVMConfig() @@ -415,11 +417,12 @@ def build_inactive_follows_finder(name, identifier): # This function can be used to build a DVM object for a DVM we don't control, but we want the bot to be aware of. # See main.py for examples. -def build_external_dvm(name, pubkey, task, kind, fix_cost, per_unit_cost): +def build_external_dvm(name, pubkey, task, kind, fix_cost, per_unit_cost, external_post_process=PostProcessFunctionType.NONE): dvm_config = DVMConfig() dvm_config.PUBLIC_KEY = PublicKey.from_hex(pubkey).to_hex() dvm_config.FIX_COST = fix_cost dvm_config.PER_UNIT_COST = per_unit_cost + dvm_config.EXTERNAL_POST_PROCESS_TYPE = external_post_process nip89info = { "name": name, } @@ -427,7 +430,9 @@ def build_external_dvm(name, pubkey, task, kind, fix_cost, per_unit_cost): nip89config.KIND = kind nip89config.CONTENT = json.dumps(nip89info) - return DVMTaskInterface(name=name, dvm_config=dvm_config, nip89config=nip89config, task=task) + interface = DVMTaskInterface(name=name, dvm_config=dvm_config, nip89config=nip89config, task=task) + + return interface # Little optional Gimmick: diff --git a/tasks/discovery_inactive_follows.py b/tasks/discovery_inactive_follows.py index 3563ecb..6f5229c 100644 --- a/tasks/discovery_inactive_follows.py +++ b/tasks/discovery_inactive_follows.py @@ -12,6 +12,7 @@ from utils.definitions import EventDefinitions from utils.dvmconfig import DVMConfig from utils.nip89_utils import NIP89Config from utils.nostr_utils import get_event_by_id +from utils.output_utils import post_process_list_to_users """ This File contains a Module to find inactive follows for a user on nostr @@ -158,13 +159,7 @@ class DiscoverInactiveFollows(DVMTaskInterface): if tag.as_vec()[0] == 'output': format = tag.as_vec()[1] if format == "text/plain": # check for output type - result_list = json.loads(result) - inactive_follows_list = "" - for tag in result_list: - p_tag = Tag.parse(tag) - inactive_follows_list = inactive_follows_list + "nostr:" + PublicKey.from_hex( - p_tag.as_vec()[1]).to_bech32() + "\n" - return inactive_follows_list + result = post_process_list_to_users(result) # if not text/plain, don't post-process return result diff --git a/utils/dvmconfig.py b/utils/dvmconfig.py index 8cfedad..ea595f5 100644 --- a/utils/dvmconfig.py +++ b/utils/dvmconfig.py @@ -1,8 +1,5 @@ -import os - -from nostr_sdk import Keys - from utils.nip89_utils import NIP89Config +from utils.output_utils import PostProcessFunctionType class DVMConfig: @@ -19,6 +16,7 @@ class DVMConfig: "wss://nostr-pub.wellorder.net"] RELAY_TIMEOUT = 3 + EXTERNAL_POST_PROCESS_TYPE = PostProcessFunctionType.NONE # Leave this on None, except the DVM is external LNBITS_INVOICE_KEY = '' LNBITS_ADMIN_KEY = '' # In order to pay invoices, e.g. from the bot to DVMs, or reimburse users. LNBITS_URL = 'https://lnbits.com' diff --git a/utils/output_utils.py b/utils/output_utils.py index 68eefe7..515cff5 100644 --- a/utils/output_utils.py +++ b/utils/output_utils.py @@ -5,6 +5,7 @@ from types import NoneType import emoji import requests +from nostr_sdk import Tag, PublicKey, EventId from pyupload.uploader import CatboxUploader import pandas @@ -14,6 +15,12 @@ Post process results to either given output format or a Nostr readable plain tex ''' +class PostProcessFunctionType: + NONE = 0 + LIST_TO_USERS = 1 + LIST_TO_EVENTS = 2 + + def post_process_result(anno, original_event): print("Post-processing...") if isinstance(anno, pandas.DataFrame): # if input is an anno we parse it to required output format @@ -33,7 +40,7 @@ def post_process_result(anno, original_event): if output_format == "text/plain": result = pandas_to_plaintext(anno) result = replace_broken_words( - str(result).replace("\"", "").replace('[', "").replace(']', + str(result).replace("\"", "").replace('[', "").replace(']', "").lstrip(None)) return result @@ -78,7 +85,7 @@ def post_process_result(anno, original_event): result = pandas_to_plaintext(anno) print(result) result = str(result).replace("\"", "").replace('[', "").replace(']', - "").lstrip(None) + "").lstrip(None) return result elif isinstance(anno, NoneType): return "An error occurred" @@ -87,6 +94,26 @@ def post_process_result(anno, original_event): return result +def post_process_list_to_events(result): + result_list = json.loads(result) + result_str = "" + for tag in result_list: + p_tag = Tag.parse(tag) + result_str = result_str + "nostr:" + EventId.from_hex( + p_tag.as_vec()[1]).to_bech32() + "\n" + return result_str + + +def post_process_list_to_users(result): + result_list = json.loads(result) + result_str = "" + for tag in result_list: + p_tag = Tag.parse(tag) + result_str = result_str + "nostr:" + PublicKey.from_hex( + p_tag.as_vec()[1]).to_bech32() + "\n" + return result_str + + def pandas_to_plaintext(anno): result = "" for each_row in anno['name']: @@ -96,7 +123,6 @@ def pandas_to_plaintext(anno): return result - ''' Convenience function to replace words like Noster with Nostr '''