🐈⬛ on Nostr: Catstr is very popular in the hosehold, also the mandalorian 🤣 then quoting the ...
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.
quoting nevent1q…s7ynYo, 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 hzrd149 (nprofile…69jx) Alex (nprofile…al0v) and the heads up of provoost (nprofile…s3js) instagram script.
nevent1q…hnsz
```
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> {
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) { 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) { 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): Promise { 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: