// Name: Cloudinary Saver
// Description: Saves Reddit posts to Cloudinary
// Author: johnlindquist
import "@johnlindquist/kit"
import cloudinary from 'cloudinary'
// Get Reddit username and Cloudinary credentials from environment variables
const username = await env('REDDIT_USERNAME', { placeholder: 'Your Reddit username',
})
cloudinary.v2.config({
cloud_name: await env('CLOUDINARY_CLOUD_NAME'),
api_key: await env('CLOUDINARY_API_KEY'),
api_secret: await env('CLOUDINARY_API_SECRET'),
})
/**
* Fetches saved posts from Reddit for the specified user.
* @returns {Promise<any[]>} A promise that resolves to an array of Reddit post objects.
*/
const fetchRedditPosts = async () => {
const savedPostsUrl = `https://www.reddit.com/user/${username}/saved/.json`
const response = await get(savedPostsUrl)
if(!response.data || !response.data.data || !response.data.data.children) {
throw new Error(`Invalid response from Reddit API: ${JSON.stringify(response)}`);
}
return response.data.data.children
}
/**
* Uploads content to Cloudinary and returns a markdown link.
* @param {string} content The content to upload (text or image URL).
* @param {string} title The title to use in the markdown link.
* @returns {Promise<string>} A promise that resolves to a markdown image link.
*/
const uploadToCloudinary = async (content: string, title: string) => {
const options = {
public_id: `reddit_posts/${Date.now()}`,
unique_filename: true,
use_filename: true,
overwrite: true,
filename_override: true,
resource_type: 'auto',
};
try {
const uploadResponse = await cloudinary.v2.uploader.upload(content, options);
return `![${title}](${uploadResponse.secure_url})`;
} catch (error) {
console.error("Cloudinary upload failed:", error);
throw new Error(`Failed to upload to Cloudinary: ${error}`);
}
};
/**
* Processes Reddit saved posts, uploads their content to Cloudinary, and displays markdown links in an editor.
*/
const processPosts = async () => {
const posts = await fetchRedditPosts()
if(!posts || posts.length === 0) {
console.warn("No saved posts found for user:", username);
await editor("No saved posts found.");
return;
}
const savedPostLinks: string[] = []
for (const post of posts) {
if (!post?.data) {
console.warn("Skipping post with invalid data:", post);
continue;
}
const title = post.data.title;
const url = post.data.url;
let content:string;
if (post.data.selftext) {
content = post.data.selftext;
} else if (url?.startsWith('https://i.redd.it/')) {
content = url;
} else {
content = url
}
try {
const cloudinaryLink = await uploadToCloudinary(content, title);
savedPostLinks.push(cloudinaryLink)
} catch (error) {
console.error(`Error processing post: ${title}`, error)
}
}
if (savedPostLinks.length === 0) {
console.warn("No posts were successfully processed.");
await editor("No posts were successfully processed.");
return;
}
await editor(savedPostLinks.join('\n\n'))
}
await processPosts()