diff --git a/nostr_dvm/bot.py b/nostr_dvm/bot.py index 518da3f..ce26a3e 100644 --- a/nostr_dvm/bot.py +++ b/nostr_dvm/bot.py @@ -5,7 +5,7 @@ import signal from multiprocessing.connection import Connection from nostr_sdk import (Keys, Timestamp, Filter, nip04_decrypt, nip44_decrypt, HandleNotification, EventBuilder, PublicKey, - Options, Tag, Event, EventId, Nip19Event, Kind, KindEnum, NostrSigner, nip44_encrypt, + Options, Tag, Event, EventId, Nip19Event, Kind, KindEnum, NostrSigner, nip44_encrypt, Nip44Version, UnsignedEvent, UnwrappedGift, uniffi_set_event_loop, ClientBuilder, make_private_msg) from nostr_dvm.utils.admin_utils import admin_make_database_updates @@ -192,7 +192,7 @@ class Bot: PublicKey.from_hex( self.dvm_config.SUPPORTED_DVMS[ index].PUBLIC_KEY), - params_as_str) + params_as_str, Nip44Version.V2) # add encrypted and p tag on the outside encrypted_tag = Tag.parse(['encrypted']) # add the encrypted params to the content @@ -391,7 +391,7 @@ class Bot: client=self.client, config=self.dvm_config) await asyncio.sleep(2.0) if entry["giftwrap"]: - event = await make_private_msg(self.signer, PublicKey.parse(PublicKey.parse(entry["npub"])), content) + event = await make_private_msg(self.signer, PublicKey.parse(entry["npub"]), content) await self.client.send_event(event) else: await send_nip04_dm(self.client, content, PublicKey.parse(entry['npub']), self.dvm_config) @@ -421,7 +421,7 @@ class Bot: amount) + " Sats from balance to DVM. New balance is " + str( balance) + " Sats.\n" if entry["giftwrap"]: - event = await make_private_msg(self.signer, PublicKey.parse(PublicKey.parse(entry["npub"])), message) + event = await make_private_msg(self.signer, PublicKey.parse(entry["npub"]), message) await self.client.send_event(event) else: await send_nip04_dm(self.client, content, PublicKey.parse(entry['npub']), @@ -437,7 +437,7 @@ class Bot: int(amount - user.balance)) + " Sats, then try again." if entry["giftwrap"]: - event = await make_private_msg(self.signer, PublicKey.parse(PublicKey.parse((entry["npub"]))), message) + event = await make_private_msg(self.signer, PublicKey.parse(entry["npub"]), message) await self.client.send_event(event) else: await send_nip04_dm(self.client, message, PublicKey.parse(entry['npub']), diff --git a/nostr_dvm/subscription.py b/nostr_dvm/subscription.py index 8a4e4bf..646a5de 100644 --- a/nostr_dvm/subscription.py +++ b/nostr_dvm/subscription.py @@ -5,8 +5,8 @@ import os import signal from datetime import timedelta -from nostr_sdk import (Keys, Client, Timestamp, Filter, nip04_decrypt, nip44_decrypt, HandleNotification, EventBuilder, PublicKey, - Options, Tag, Event, nip44_encrypt, NostrSigner, EventId, uniffi_set_event_loop, make_private_msg) +from nostr_sdk import (Keys, Client, Timestamp, Filter, nip04_decrypt, nip44_decrypt, HandleNotification, EventBuilder, PublicKey, + Options, Tag, Event, nip44_encrypt, NostrSigner, EventId, uniffi_set_event_loop, make_private_msg, Nip44Version) from nostr_dvm.utils.database_utils import fetch_user_metadata from nostr_dvm.utils.definitions import EventDefinitions, relay_timeout @@ -148,7 +148,7 @@ class Subscription: content = json.dumps(str_tags) content = nip44_encrypt(self.keys.secret_key(), PublicKey.from_hex(original_event.author().to_hex()), - content) + content, Nip44Version.V2) reply_tags = encryption_tags keys = Keys.parse(dvm_config.PRIVATE_KEY) diff --git a/nostr_dvm/utils/gallery_utils.py b/nostr_dvm/utils/gallery_utils.py index 6bb25eb..4b0bd68 100644 --- a/nostr_dvm/utils/gallery_utils.py +++ b/nostr_dvm/utils/gallery_utils.py @@ -1,4 +1,20 @@ -from nostr_sdk import Keys, EventBuilder, Kind +import asyncio +import hashlib +from datetime import timedelta +from io import BytesIO +from urllib.request import urlopen +import re +import mimetypes +import os + + +from PIL import Image +import numpy +from nostr_sdk import Keys, EventBuilder, Kind, Client, NostrSigner, Filter, EventId, Tag, PublicKey +from blurhash import encode +import requests +from urllib.parse import urlparse + from nostr_dvm.utils.nostr_utils import send_event from nostr_dvm.utils.print_utils import bcolors @@ -13,3 +29,133 @@ async def gallery_announce_list(tags, dvm_config, client): print( bcolors.BLUE + "[" + dvm_config.NIP89.NAME + "] Announced Gallery for " + dvm_config.NIP89.NAME + " (EventID: " + str( eventid.to_hex()) + ")" + bcolors.ENDC) + + +async def convert_nip93_to_nip68(private_key, relay_list, user_to_import_npub=None, startindex=0): + keys = Keys.parse(private_key) + if user_to_import_npub is None: + user_to_import_npub = keys.public_key().to_hex() + + client = Client(NostrSigner.keys(keys)) + for relay in relay_list: + await client.add_relay(relay) + await client.connect() + + nip93_filter = Filter().kind(Kind(1163)).author(PublicKey.parse(user_to_import_npub)) + + events = await client.fetch_events([nip93_filter], timedelta(5)) + + events_vec = events.to_vec() + reversed_events_vec = reversed(events_vec) + counter = -1 + for event in reversed_events_vec: + counter += 1 + if counter < startindex: + continue + + image_url = "" + content = "" + m = "" + x = "" + dim = "" + blurhash = "" + size = "" + for tag in event.tags().to_vec(): + if tag.as_vec()[0] == "url": + image_url = tag.as_vec()[1] + elif tag.as_vec()[0] == "m": + m = tag.as_vec()[1] + elif tag.as_vec()[0] == "x": + x = tag.as_vec()[1] + elif tag.as_vec()[0] == "dim": + dim = tag.as_vec()[1] + elif tag.as_vec()[0] == "blurhash": + blurhash = tag.as_vec()[1] + elif tag.as_vec()[0] == "e": + eventid = tag.as_vec()[1] + if len(tag.as_vec()) == 3: + relay_hint = tag.as_vec()[2] + try: + await client.connect_relay(relay_hint) + except Exception as e: + print(relay_hint) + e_filter = Filter().id(EventId.parse(eventid)).limit(1) + content_events = await client.fetch_events([e_filter], timedelta(5)) + if len(content_events.to_vec()) > 0: + content_event = content_events.to_vec()[0] + content = re.sub(r'^https?:\/\/.*[\r\n]*', '', content_event.content(), flags=re.MULTILINE).rstrip() + + var = input("Convert and post this image? (y(es)/n(o)/d(elete): " + image_url + " Content: " + content + "\n") + if var == "d" or var == "delete": + delete_event = EventBuilder.delete([event.id()]).sign_with_keys(keys) + result = await client.send_event(delete_event) + print(result.output) + + + elif var == "y" or var == "yes": + try: + response = requests.get(image_url) + img = Image.open(BytesIO(response.content)).convert("RGB") + + if blurhash == "": + blurhash = encode(numpy.array(img)) + if dim == "": + width, height = img.size + dim = str(width) + "x" + str(height) + if m == "": + m, _ = mimetypes.guess_type(image_url) + if size == "": + try: + d = urlopen(image_url) + size = str(d.info()['Content-Length']) + except Exception: + size = "" + if x == "": + e = BytesIO(response.content) + + a = urlparse(image_url) + print(os.path.basename(a.path)) + with open(os.path.basename(a.path), "wb") as f: + f.write(e.getbuffer()) + h = hashlib.sha256() + with open(os.path.basename(a.path), 'rb') as file: + while True: + # Reading is buffered, so we can read smaller chunks. + chunk = file.read(h.block_size) + if not chunk: + break + h.update(chunk) + x_old = x + x = h.hexdigest() + + except Exception as e: + print(e) + + + print("Image: " + image_url + " Content: " + content + " m: " + m + " x: " + x + " dim: " + dim + " size: " + size + " blurhash: " + blurhash) + + imeta_tag = Tag.parse(["imeta", "url " + image_url, "m " + m, "alt " + content, "x " + x, "size " + size, "blurhash " + blurhash ]) + x_tag = Tag.parse(["x", x]) + alt_tag = Tag.parse(["alt", "List of pictures"]) + m_tag = Tag.parse(["m", m]) + tags = [alt_tag, imeta_tag, x_tag, m_tag] + nip68event = EventBuilder(Kind(20), content).tags(tags).sign_with_keys(keys) + print(nip68event) + + await client.send_event(nip68event) + + + + +if __name__ == '__main__': + private_key = "nsec..." + + RELAY_LIST = ["wss://relay.primal.net", + "wss://relay.damus.io", + "wss://relay.nostrplebs.com", + "wss://nostr.mom", + "wss://nostr.oxtr.dev", + "wss://relay.nostr.band" + ] + startindex = 0 + asyncio.run(convert_nip93_to_nip68(private_key, RELAY_LIST, startindex)) diff --git a/tutorials/10_delete_nip89.py b/tutorials/10_delete_nip89.py index 11993ee..8fa8724 100644 --- a/tutorials/10_delete_nip89.py +++ b/tutorials/10_delete_nip89.py @@ -7,55 +7,62 @@ import asyncio -from nostr_sdk import EventId, Keys, Client, NostrSigner +from datetime import timedelta +from nostr_sdk import Keys, Client, NostrSigner, Filter + +from nostr_dvm.utils.definitions import EventDefinitions from nostr_dvm.utils.dvmconfig import DVMConfig from nostr_dvm.utils.nip89_utils import fetch_nip89_parameters_for_deletion # Method to delete the NIP89, don't worry, you don't have to touch this, take a look in the main function. -async def delete_nip_89(event_id, private_key, relay_list, pow=True): - event_id = EventId.parse(event_id).to_hex() +async def delete_nip_89(private_key, relay_list, pow=True): keys = Keys.parse(private_key) dvm_config = DVMConfig() dvm_config.RELAY_LIST = relay_list - - client = Client(NostrSigner.keys(keys)) for relay in dvm_config.RELAY_LIST: await client.add_relay(relay) await client.connect() + filter = Filter().kind(EventDefinitions.KIND_ANNOUNCEMENT).author(keys.public_key()) + events = await client.fetch_events([filter], timedelta(seconds=5)) - await fetch_nip89_parameters_for_deletion(keys, event_id, client, dvm_config, pow) + if len(events.to_vec()) == 0: + print("Couldn't find note on relays. Seems they are gone.") + return + + for event in events.to_vec(): + print(event) + await fetch_nip89_parameters_for_deletion(keys, event.id().to_hex(), client, dvm_config, pow) if __name__ == '__main__': - + # What do you need to delete an event? - - # First, you need the event id of the announcement. You can copy the json of the announcement from various clients, - # like Amethyst or you should also see it at noogle.lol/nip89. Copy the json to a text editor and in the first line - # you should see an entry "id":"someid". Copy the id and enter it in the event_id field below: - evend_id = "6f91dce309e....." - - # The second thing you need is the private key of the DVM the announcement was made for. - # NostrDVM stores all your keys in the .env file. Open the .env file and search for + + # The first thing you need is the private key of the DVM the announcement was made for. + # NostrDVM stores all your keys in the .env file. Open the .env file and search for # DVM_PRIVATE_KEY_{IDENTIFIER_OF_YOUR_DVM}. Enter it below in the field private_key: - - private_key = "6221e3181....." - + + private_key = "83274234123..." + # You can either use Proof of Work to send the delete event or not. Some relays require you to use POW in order to write. # Sending a POW event might take up to a couple of minutes, so if you decide to use it, have some patience in the progress. # If you know the relays you published your announcements to do not require POW, you can also set it to False which speeds up the progress. - pow = True - - # And finally set the relay list you want to send the deletion request to. Ideally, you use the same relays that you use + pow = False + + # And finally set the relay list you want to send the deletion request to. Ideally, you use the same relays that you use # in your DVM's config. Maybe announcements also got propagated to other relays, so you might need to play around a bit until it's gone everywhere. RELAY_LIST = ["wss://relay.primal.net", + "wss://relay.damus.io", + "wss://relay.nostrplebs.com", + "wss://promenade.fiatjaf.com", "wss://nostr.mom", "wss://nostr.oxtr.dev", + "wss://relay.nostr.band" ] - - # That's it. Once you entered the info, run the script and if your private key matches the ID and the event can be found it should be deleted. + + # That's it. Once you entered the info, run the script and if your private key matches the ID and the event can be found it should be deleted. # Otherwise you'll get a warning - asyncio.run(delete_nip_89(evend_id, private_key, RELAY_LIST, pow)) \ No newline at end of file + asyncio.run(delete_nip_89(private_key, RELAY_LIST, pow)) \ No newline at end of file