πββ¬ @πββ¬ - 3mo
Yo, my favorite new blossom+deno to nostr shit is to scrape tenor #catstr gifs and πΈ them up now that nostrcheck is in as well, thanks to nostr:nprofile1qqszv6q4uryjzr06xfxxew34wwc5hmjfmfpqn229d72gfegsdn2q3fgpz9mhxue69uhkummnw3ezuamfdejj7qg6waehxw309ahx7um5wgh8g6r9wdsk6etrv96zu6t09uqsuamnwvaz7tmwdaejumr0dshs9h69jx nostr:nprofile1qqsqgc0uhmxycvm5gwvn944c7yfxnnxm0nyh8tt62zhrvtd3xkj8fhgprdmhxue69uhkwmr9v9ek7mnpw3hhytnyv4mz7un9d3shjqgcwaehxw309ahx7umywf5hvefwv9c8qtmjv4kxz7gpzemhxue69uhhyetvv9ujumt0wd68ytnsw43z7s3al0v and the heads up of nostr:nprofile1qqsgdp0taan9xwxadyc79nxl8svanu895yr8eyv0ytnss8p9tru047qpp4mhxue69uhkummn9ekx7mqpz3mhxue69uhhyetvv9ujuerpd46hxtnfduq3qamnwvaz7tmwdaehgu3wwa5kuegzls3js instagram script. nostr:nevent1qqsq5t4scgw95pma9w0gk8tk0j5t07mu9lel3qv4ssa7p40a95gg5fgpzfmhxue69uhkummnw3ezumr49e4k2tczyz24gvcfdl9zy0ef4h749gxzmcr683fwk6q0ndxeadkydkged32quqcyqqqqqqg3phnsz ``` import { NSecSigner, NRelay1, NSchema as n } from '@nostrify/nostrify'; import { BlossomUploader } from '@nostrify/nostrify/uploaders'; import * as nip19 from 'nostr-tools/nip19'; // Helper function to convert a hex string to Uint8Array function hexToUint8Array(hex: string): Uint8Array { if (hex.length % 2 !== 0) { throw new Error("Hex string must have an even length"); } const array = new Uint8Array(hex.length / 2); for (let i = 0; i < hex.length; i += 2) { array[i / 2] = parseInt(hex.substr(i, 2), 16); } return array; } // Retrieve the secret key from an environment variable const hexSecretKey = Deno.env.get('SECRET_KEY_HEX'); if (!hexSecretKey) { throw new Error('Environment variable "SECRET_KEY_HEX" is not set.'); } const secretKey: Uint8Array = hexToUint8Array(hexSecretKey); // Initialize the signer with your secret key const signer = new NSecSigner(secretKey); // Define the relay URLs const relayUrls = [ 'wss://nostr.mom', 'wss://nos.lol', 'wss://relay.primal.net', 'wss://e.nos.lol', 'wss://relay.nostr.band' ]; // Create an array of NRelay1 instances const relays = relayUrls.map(url => new NRelay1(url)); // Path to the JSON file that stores uploaded files const uploadedFilesPath = './uploaded_files.json'; // Function to read uploaded files from JSON async function readUploadedFiles(): Promise<Set<string>> { try { const data = await Deno.readTextFile(uploadedFilesPath); return new Set(JSON.parse(data)); } catch { return new Set(); } } // Function to write uploaded files to JSON async function writeUploadedFiles(uploadedFiles: Set<string>) { await Deno.writeTextFile(uploadedFilesPath, JSON.stringify(Array.from(uploadedFiles))); } // Function to sign, parse, and upload a media file async function signAndUploadMedia(filePath: string, uploadedFiles: Set<string>) { try { // Check if the file has already been uploaded if (uploadedFiles.has(filePath)) { console.log(`File ${filePath} has already been uploaded. Skipping.`); return; } // Get the public key from the signer const pubkey = await signer.getPublicKey(); // Initialize the uploader const uploader = new BlossomUploader({ servers: [ 'https://cdn.satellite.earth', 'https://nstore.nostrver.se', 'https://blossom.puhcho.me', 'https://blossom.primal.net', 'https://files.v0l.io/', 'https://cdn.nostrcheck.me' ], signer: signer, // Use the signer for authentication }); // Read the file const fileBuffer = await Deno.readFile(filePath); const file = new File([fileBuffer], filePath.split('/').pop()!); // Upload the file and get the tags const tags = await uploader.upload(file); // Find the URL in the tags let fileUrl = 'Unknown URL'; for (const tag of tags) { if (tag[0] === "url" && tag[1]) { fileUrl = tag[1]; break; } } // Create event data const eventData = { kind: 1, content: `${fileUrl}`, tags: tags, created_at: Math.floor(Date.now() / 1000), }; // Sign the event to get id and sig const signedEvent = await signer.signEvent(eventData); const completeEventData = { ...eventData, id: signedEvent.id, pubkey: pubkey, sig: signedEvent.sig, }; // Parse and validate the complete event data using NSchema const event = n.event().parse(completeEventData); console.log('Parsed and validated event:', event); // Send the event to each relay for (const relay of relays) { console.log('Sending event to relay:', relay); console.log(await relay.event(event)); await relay.close(); } // Add the file to the uploaded files set and update the JSON file uploadedFiles.add(filePath); await writeUploadedFiles(uploadedFiles); console.log("Done!"); } catch (error) { console.error('Error signing and uploading media:', error); } } // Function to select a random valid file from a folder async function getRandomValidFileFromFolder(folderPath: string, uploadedFiles: Set<string>): Promise<string | null> { const validExtensions = ['jpg', 'mp4', 'webp', 'gif']; const files: string[] = []; for await (const dirEntry of Deno.readDir(folderPath)) { if (dirEntry.isFile) { const extension = dirEntry.name.split('.').pop()?.toLowerCase(); if (extension && validExtensions.includes(extension)) { files.push(dirEntry.name); } } } // Filter out files that have already been uploaded const unuploadedFiles = files.filter(file => !uploadedFiles.has(`${folderPath}/${file}`)); if (unuploadedFiles.length === 0) { console.error('No unuploaded valid files found. Please add new JPG, MP4, or WEBP files.'); return null; } const randomIndex = Math.floor(Math.random() * unuploadedFiles.length); return `${folderPath}/${unuploadedFiles[randomIndex]}`; } // Example usage const folderPath = Deno.env.get('MEDIA_FOLDER_PATH'); if (folderPath) { readUploadedFiles().then((uploadedFiles) => { getRandomValidFileFromFolder(folderPath, uploadedFiles).then((randomFilePath) => { if (randomFilePath) { signAndUploadMedia(randomFilePath, uploadedFiles); } }); }); } else { console.error('Environment variable "MEDIA_FOLDER_PATH" is not set.'); } ``` Also puts amethyst gallery to good use: https://v.nostr.build/ICPPrle8VODjFOPV.mp4
``` import { NSecSigner, NRelay1, NSchema as n } from '@nostrify/nostrify'; import { BlossomUploader } from '@nostrify/nostrify/uploaders'; import * as nip19 from 'nostr-tools/nip19'; // Helper function to convert a hex string to Uint8Array function hexToUint8Array(hex: string): Uint8Array { if (hex.length % 2 !== 0) { throw new Error("Hex string must have an even length"); } const array = new Uint8Array(hex.length / 2); for (let i = 0; i < hex.length; i += 2) { array[i / 2] = parseInt(hex.substr(i, 2), 16); } return array; } // Main function to execute the script async function main() { // Retrieve the secret key from an environment variable const hexSecretKey = Deno.env.get('SECRET_KEY_HEX'); if (!hexSecretKey) { console.error('Environment variable "SECRET_KEY_HEX" is not set.'); Deno.exit(1); return; } const secretKey: Uint8Array = hexToUint8Array(hexSecretKey); // Initialize the signer with your secret key const signer = new NSecSigner(secretKey); // Define the relay URLs const relayUrls = [ 'wss://nostr.mom', 'wss://nos.lol', 'wss://relay.primal.net', 'wss://e.nos.lol', 'wss://relay.nostr.band' ]; // Create an array of NRelay1 instances with their URLs const relays = relayUrls.map(url => ({ relay: new NRelay1(url), url })); // Path to the JSON file that stores uploaded files const uploadedFilesPath = './uploaded_files.json'; // Path to the text file that stores uploaded URLs const urlFilePath = './uploaded_urls.txt'; // Function to read uploaded files from JSON async function readUploadedFiles(): Promise<Set<string>> { try { const data = await Deno.readTextFile(uploadedFilesPath); return new Set(JSON.parse(data)); } catch { return new Set(); } } // Function to write uploaded files to JSON async function writeUploadedFiles(uploadedFiles: Set<string>) { await Deno.writeTextFile(uploadedFilesPath, JSON.stringify(Array.from(uploadedFiles))); } // Function to append a URL to a text file async function appendUrlToFile(fileUrl: string, urlFilePath: string) { try { await Deno.writeTextFile(urlFilePath, fileUrl + '\n', { append: true }); console.log(`Appended URL to file: ${fileUrl}`); } catch (error) { console.error('Error appending URL to file:', error); } } // Function to sign, parse, and upload a media file async function signAndUploadMedia(filePath: string, uploadedFiles: Set<string>, urlFilePath: string) { try { // Check if the file has already been uploaded if (uploadedFiles.has(filePath)) { console.log(`File ${filePath} has already been uploaded. Skipping.`); return; } // Get the public key from the signer const pubkey = await signer.getPublicKey(); // Initialize the uploader const uploader = new BlossomUploader({ servers: [ 'https://cdn.satellite.earth', 'https://nstore.nostrver.se', 'https://blossom.puhcho.me', 'https://blossom.primal.net', 'https://cdn.nostrcheck.me' ], signer: signer, }); // Read the file const fileBuffer = await Deno.readFile(filePath); const file = new File([fileBuffer], filePath.split('/').pop()!); // Upload the file and get the tags const tags = await uploader.upload(file); // Find the URL in the tags let fileUrl = 'Unknown URL'; for (const tag of tags) { if (tag[0] === "url" && tag[1]) { fileUrl = tag[1]; break; } } // Append the URL to the text file await appendUrlToFile(fileUrl, urlFilePath); // Create event data const eventData = { kind: 1, content: `${fileUrl}`, tags: tags, created_at: Math.floor(Date.now() / 1000), }; // Sign the event to get id and sig const signedEvent = await signer.signEvent(eventData); const completeEventData = { ...eventData, id: signedEvent.id, pubkey: pubkey, sig: signedEvent.sig, }; // Parse and validate the complete event data using NSchema const event = n.event().parse(completeEventData); console.log('Parsed and validated event:', event); // Send the event to each relay for (const { relay, url } of relays) { try { console.log(`Sending event to relay ${url}`); await relay.event(event); console.log(`Event sent successfully to ${url}`); } catch (error) { console.error(`Error sending event to relay ${url}:`, error); } finally { try { await relay.close(); console.log(`Relay ${url} closed`); } catch (closeError) { console.error(`Error closing relay ${url}:`, closeError); } } } // Add the file to the uploaded files set and update the JSON file uploadedFiles.add(filePath); await writeUploadedFiles(uploadedFiles); console.log("Done!"); } catch (error) { console.error('Error signing and uploading media:', error); } } // Function to select a random valid file from a folder async function getRandomValidFileFromFolder(folderPath: string, uploadedFiles: Set<string>): Promise<string | null> { const validExtensions = ['jpg', 'mp4', 'webp', 'gif']; const files: string[] = []; for await (const dirEntry of Deno.readDir(folderPath)) { if (dirEntry.isFile) { const extension = dirEntry.name.split('.').pop()?.toLowerCase(); if (extension && validExtensions.includes(extension)) { files.push(dirEntry.name); } } } // Filter out files that have already been uploaded const unuploadedFiles = files.filter(file => !uploadedFiles.has(`${folderPath}/${file}`)); if (unuploadedFiles.length === 0) { console.log('All files have been uploaded. Selecting a random URL to publish.'); return null; } const randomIndex = Math.floor(Math.random() * unuploadedFiles.length); return `${folderPath}/${unuploadedFiles[randomIndex]}`; } // Function to publish a Nostr event with a random URL async function publishRandomUrlEvent(urlFilePath: string) { try { const urls = (await Deno.readTextFile(urlFilePath)).trim().split('\n'); if (urls.length === 0) { console.error('No URLs found in the URL file.'); return; } const randomUrl = urls[Math.floor(Math.random() * urls.length)]; // Create event data const eventData = { kind: 1, content: `${randomUrl}`, tags: [], created_at: Math.floor(Date.now() / 1000), }; // Sign the event to get id and sig const signedEvent = await signer.signEvent(eventData); const completeEventData = { ...eventData, id: signedEvent.id, pubkey: await signer.getPublicKey(), sig: signedEvent.sig, }; // Parse and validate the complete event data using NSchema const event = n.event().parse(completeEventData); console.log('Parsed and validated event:', event); for (const { relay, url } of relays) { try { console.log(`Sending event to relay ${url}`); await relay.event(event); console.log(`Event sent successfully to ${url}`); } catch (error) { console.error(`Error sending event to relay ${url}:`, error); } finally { try { await relay.close(); console.log(`Relay ${url} closed`); } catch (closeError) { console.error(`Error closing relay ${url}:`, closeError); } } } console.log("Published random URL event successfully!"); } catch (error) { console.error('Error publishing random URL event:', error); } } // Example usage const folderPath = Deno.env.get('MEDIA_FOLDER_PATH'); if (folderPath) { try { const uploadedFiles = await readUploadedFiles(); const randomFilePath = await getRandomValidFileFromFolder(folderPath, uploadedFiles); if (randomFilePath) { await signAndUploadMedia(randomFilePath, uploadedFiles, urlFilePath); } else { await publishRandomUrlEvent(urlFilePath); } } catch (error) { console.error('Error during execution:', error); } finally { Deno.exit(); } } else { console.error('Environment variable "MEDIA_FOLDER_PATH" is not set.'); Deno.exit(1); } } // Execute main function main(); ``` Better handling.
``` import { NSecSigner, NRelay1, NSchema as n } from '@nostrify/nostrify'; import { BlossomUploader } from '@nostrify/nostrify/uploaders'; import * as nip19 from 'nostr-tools/nip19'; // Helper function to convert a hex string to Uint8Array function hexToUint8Array(hex: string): Uint8Array { if (hex.length % 2 !== 0) { throw new Error("Hex string must have an even length"); } const array = new Uint8Array(hex.length / 2); for (let i = 0; i < hex.length; i += 2) { array[i / 2] = parseInt(hex.substr(i, 2), 16); } return array; } // Function to read uploaded files from JSON async function readUploadedFiles(uploadedFilesPath: string): Promise<Set<string>> { try { const data = await Deno.readTextFile(uploadedFilesPath); return new Set(JSON.parse(data)); } catch { return new Set(); } } // Function to write uploaded files to JSON async function writeUploadedFiles(uploadedFiles: Set<string>, uploadedFilesPath: string) { await Deno.writeTextFile(uploadedFilesPath, JSON.stringify(Array.from(uploadedFiles))); } // Function to append a URL to a text file async function appendUrlToFile(fileUrl: string, urlFilePath: string) { try { await Deno.writeTextFile(urlFilePath, fileUrl + '\n', { append: true }); console.log(`Appended URL to file: ${fileUrl}`); } catch (error) { console.error('Error appending URL to file:', error); } } // Function to sign, parse, and upload a media file async function signAndUploadMedia( filePath: string, uploadedFiles: Set<string>, urlFilePath: string, signer: NSecSigner, relays: { relay: NRelay1; url: string }[] ) { try { // Check if the file has already been uploaded if (uploadedFiles.has(filePath)) { console.log(`File ${filePath} has already been uploaded. Skipping.`); return; } // Get the public key from the signer const pubkey = await signer.getPublicKey(); // Initialize the uploader const uploader = new BlossomUploader({ servers: [ 'https://cdn.satellite.earth', 'https://nstore.nostrver.se', 'https://blossom.puhcho.me', 'https://blossom.primal.net', 'https://cdn.nostrcheck.me' ], signer: signer, }); // Read the file const fileBuffer = await Deno.readFile(filePath); const file = new File([fileBuffer], filePath.split('/').pop()!); // Upload the file and get the tags const tags = await uploader.upload(file); // Find the URL in the tags let fileUrl = 'Unknown URL'; for (const tag of tags) { if (tag[0] === "url" && tag[1]) { fileUrl = tag[1]; break; } } // Append the URL to the text file await appendUrlToFile(fileUrl, urlFilePath); // Create event data const eventData = { kind: 1, content: `${fileUrl}`, tags: tags, created_at: Math.floor(Date.now() / 1000), }; // Sign the event to get id and sig const signedEvent = await signer.signEvent(eventData); const completeEventData = { ...eventData, id: signedEvent.id, pubkey: pubkey, sig: signedEvent.sig, }; // Parse and validate the complete event data using NSchema const event = n.event().parse(completeEventData); console.log('Parsed and validated event:', event); // Send the event to each relay for (const { relay, url } of relays) { try { console.log(`Sending event to relay ${url}`); await relay.event(event); console.log(`Event sent successfully to ${url}`); } catch (error) { console.error(`Error sending event to relay ${url}:`, error); } finally { try { await relay.close(); console.log(`Relay ${url} closed`); } catch (closeError) { console.error(`Error closing relay ${url}:`, closeError); } } } // Add the file to the uploaded files set and update the JSON file uploadedFiles.add(filePath); await writeUploadedFiles(uploadedFiles, './uploaded_files.json'); console.log("Done!"); } catch (error) { console.error('Error signing and uploading media:', error); } } // Function to select a random valid file from a folder async function getRandomValidFileFromFolder(folderPath: string, uploadedFiles: Set<string>): Promise<string | null> { const validExtensions = ['jpg', 'mp4', 'webp', 'gif']; const files: string[] = []; for await (const dirEntry of Deno.readDir(folderPath)) { if (dirEntry.isFile) { const extension = dirEntry.name.split('.').pop()?.toLowerCase(); if (extension && validExtensions.includes(extension)) { files.push(dirEntry.name); } } } // Filter out files that have already been uploaded const unuploadedFiles = files.filter(file => !uploadedFiles.has(`${folderPath}/${file}`)); if (unuploadedFiles.length === 0) { console.log('All files have been uploaded. Selecting a random URL to publish.'); return null; } const randomIndex = Math.floor(Math.random() * unuploadedFiles.length); return `${folderPath}/${unuploadedFiles[randomIndex]}`; } // Function to publish a Nostr event with a random URL async function publishRandomUrlEvent(urlFilePath: string, signer: NSecSigner, relays: { relay: NRelay1; url: string }[]) { try { const urls = (await Deno.readTextFile(urlFilePath)).trim().split('\n'); if (urls.length === 0) { console.error('No URLs found in the URL file.'); return; } const randomUrl = urls[Math.floor(Math.random() * urls.length)]; // Create event data const eventData = { kind: 1, content: `${randomUrl}`, tags: [], created_at: Math.floor(Date.now() / 1000), }; // Sign the event to get id and sig const signedEvent = await signer.signEvent(eventData); const completeEventData = { ...eventData, id: signedEvent.id, pubkey: await signer.getPublicKey(), sig: signedEvent.sig, }; // Parse and validate the complete event data using NSchema const event = n.event().parse(completeEventData); console.log('Parsed and validated event:', event); for (const { relay, url } of relays) { try { console.log(`Sending event to relay ${url}`); await relay.event(event); console.log(`Event sent successfully to ${url}`); } catch (error) { console.error(`Error sending event to relay ${url}:`, error); } finally { try { await relay.close(); console.log(`Relay ${url} closed`); } catch (closeError) { console.error(`Error closing relay ${url}:`, closeError); } } } console.log("Published random URL event successfully!"); } catch (error) { console.error('Error publishing random URL event:', error); } } // Main function to execute the script async function main() { const hexSecretKey = Deno.env.get('SECRET_KEY_HEX'); if (!hexSecretKey) { console.error('Environment variable "SECRET_KEY_HEX" is not set.'); Deno.exit(1); return; } const secretKey: Uint8Array = hexToUint8Array(hexSecretKey); // Initialize the signer with your secret key const signer = new NSecSigner(secretKey); // Define the relay URLs const relayUrls = [ 'wss://nostr.mom', 'wss://nos.lol', 'wss://relay.primal.net', 'wss://e.nos.lol', 'wss://relay.damus.io', 'wss://nostr.lu.ke', 'wss://nostr.oxtr.dev', 'wss://relay.nostrcheck.me', 'wss://nostr.data.haus', 'wss://ditto.puhcho.me/relay', 'wss://offchain.pub', 'wss://strfry.iris.to' ]; // Create an array of NRelay1 instances with their URLs const relays = relayUrls.map(url => ({ relay: new NRelay1(url), url })); // Path to the JSON file that stores uploaded files const uploadedFilesPath = './home/user/test_bloom/uploaded_files.json'; // Path to the text file that stores uploaded URLs const urlFilePath = './home/user/test_bloom/uploaded_urls.txt'; // Example usage const folderPath = Deno.env.get('MEDIA_FOLDER_PATH'); if (folderPath) { try { const uploadedFiles = await readUploadedFiles(uploadedFilesPath); const randomFilePath = await getRandomValidFileFromFolder(folderPath, uploadedFiles); if (randomFilePath) { await signAndUploadMedia(randomFilePath, uploadedFiles, urlFilePath, signer, relays); } else { await publishRandomUrlEvent(urlFilePath, signer, relays); } } catch (error) { console.error('Error during execution:', error); } finally { Deno.exit(); } } else { console.error('Environment variable "MEDIA_FOLDER_PATH" is not set.'); Deno.exit(1); } } // Execute main function main(); ``` Better as it was uploading to just one blossom.
Niel Liesmons @nielliesmons - 3mo
How do you guys currently bookmark your GIF's? #askgifstr
I'm new to even really using GIF's and prefer using more niche ones from old movies etc... I constantly come across great ones that I don't have an immediate use for, but of which I know they're a great match for the kind of conversations I'm having.
What FrameFlipBot did you buy to make 'em move? I can't decide π€
Autoupload them to blossoms for the kids so they can send whatever they like to amethyst's gallery. Then note id etc. https://video.nostr.build/94e07f0010d96c85d033e11b6cfa92ff82aa33769d9a089f3ce007be4454267f.mp4
Classic nostr:npub1gzuushllat7pet0ccv9yuhygvc8ldeyhrgxuwg744dn5khnpk3gs3ea5ds bragging about the size of his desk
Catstr is very popular in the hosehold, also the mandalorian π€£ then quoting the note id the gif is in from the gallery in a note, reply etc. If you already have a gif folder, that goes up easy. Or scraping tenor etc for old movies gifs and so on. nostr:nevent1qqs0s2l0gj5xx9s3yv20q4xfkugqz5klrp790wmamumww5wve7aduaspy9mhxue69uhkxenjv4kxz7fwwp6kscmgduh8wmmjddjhyuewv3jhvtczyzj3f2ema7m4974wk5mpjtgyjswuykuggk8gjsrlj5umhz0nnkhhsqcyqqqqqqge6s7yn
> then quoting the note id the gif is in from the gallery in a note Wait, I don't fully get it. What do you mean? You bookmark the GIF's as blossom hashes or as images in note ids?
Second. They are already in gallery if shared. The ui would be good to have quote in note from that but amethyst is not there yet. So you just put the note id like this nostr:nevent1qqstqfhggxlfdj7s8t03e6z85nep86dtrzand3f7ppdxck3lh088t6gprdmhxue69uhkg6t5w3hjuur4dp3ksmewd4jj7un9d3shjq3qj42rxzt0eg3r72ddl4f2psk7q73u2t4ksrumfk0td3rdjxtv2s8qxpqqqqqqzy8csdf