Oddbean new post about | logout
 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