Skip to content

Instantly share code, notes, and snippets.

@bobbyflowstate
Last active March 25, 2025 21:33
Show Gist options
  • Save bobbyflowstate/f706bc00d2283c5b412760f20b3e30fd to your computer and use it in GitHub Desktop.
Save bobbyflowstate/f706bc00d2283c5b412760f20b3e30fd to your computer and use it in GitHub Desktop.
Automated Instagram Poster — Node.js + Dropbox + instagram-private-api
/**
* 🤖 Automated Instagram Poster — Node.js + Dropbox + instagram-private-api
*
* This script:
* - Pulls a random image from your Dropbox `/Public` folder
* - Posts it to Instagram using instagram-private-api
* - Adds a random emoji-only caption
* - Deletes the file from Dropbox after posting
* - Runs daily with PM2 cron scheduling
*
* 🔧 SETUP INSTRUCTIONS:
*
* // P.S. Music while you code: FlowStateRadio.com
*
* 1. Create a Dropbox app and generate an API token (https://dropbox.com/developers/apps)
* - Enable `files.content.read`, `files.metadata.read`, and `sharing.write` scopes
* - Generate an access token and copy it
*
* 2. Create a `.env` file in the same folder with the following:
*
* IG_USERNAME=your_instagram_username
* IG_PASSWORD=your_instagram_password
* DROPBOX_TOKEN=your_dropbox_token
* DROPBOX_FOLDER=/Public/your_folder_path
*
* 3. Initialize your project and install dependencies:
*
* npm init -y
* npm install instagram-private-api axios dotenv fs
*
* 4. (Optional but recommended) Add this to `.gitignore`:
*
* .env
* ig-session.json
*
* 5. Run the script manually to test:
*
* node postToInstagram.js
*
* 6. Schedule it daily with PM2:
*
* npm install -g pm2
* pm2 start /full/path/to/postToInstagram.js --name ig-poster --cron "30 14 * * *" --no-autorestart
* pm2 save
* pm2 startup
*
* # Confirm it's scheduled:
* pm2 list
* pm2 show ig-poster
*
* # Check logs:
* pm2 logs ig-poster
*
* ✅ Your Instagram post will now go out automatically every day at 2:30 PM.
*
* 🚫 DISCLAIMER: This is not using the official Instagram Graph API.
* It's intended for personal/creative use, not large-scale automation or spamming.
*/
import { IgApiClient } from 'instagram-private-api';
import fs from 'fs/promises';
import axios from 'axios';
import dotenv from 'dotenv';
dotenv.config();
const {
IG_USERNAME,
IG_PASSWORD,
DROPBOX_TOKEN,
DROPBOX_FOLDER
} = process.env;
const SESSION_FILE = 'ig-session.json';
async function loadSession(ig) {
try {
const data = await fs.readFile(SESSION_FILE, 'utf8');
await ig.state.deserialize(JSON.parse(data));
console.log("✅ Session loaded.");
} catch {
console.log("⚠️ No existing session found.");
}
}
async function saveSession(ig) {
const state = await ig.state.serialize();
delete state.constants;
await fs.writeFile(SESSION_FILE, JSON.stringify(state), 'utf8');
console.log("✅ Session saved.");
}
async function deleteDropboxFile(path) {
console.log("🗑️ Deleting file from Dropbox:", path);
try {
await axios.post(
'https://api.dropboxapi.com/2/files/delete_v2',
{ path: path },
{
headers: {
Authorization: `Bearer ${DROPBOX_TOKEN}`,
'Content-Type': 'application/json',
},
}
);
console.log("✅ File deleted successfully");
} catch (error) {
console.error("❌ Error deleting file:", error.message);
throw error;
}
}
async function getRandomDropboxFile() {
console.log("📁 Fetching files from Dropbox folder...");
const listRes = await axios.post(
'https://api.dropboxapi.com/2/files/list_folder',
{ path: DROPBOX_FOLDER },
{
headers: {
Authorization: `Bearer ${DROPBOX_TOKEN}`,
'Content-Type': 'application/json',
},
}i
);
const files = listRes.data.entries.filter(f => f['.tag'] === 'file');
console.log(`📊 Found ${files.length} files in Dropbox folder`);
if (files.length === 0) throw new Error('No files found in folder.');
const randomFile = files[Math.floor(Math.random() * files.length)];
console.log(`🎲 Selected random file: ${randomFile.name}`);
// Get share link
console.log("🔗 Getting share link...");
let rawUrl;
try {
const shareRes = await axios.post(
'https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings',
{ path: randomFile.path_lower, settings: { requested_visibility: "public" } },
{ headers: { Authorization: `Bearer ${DROPBOX_TOKEN}`, 'Content-Type': 'application/json' } }
);
rawUrl = shareRes.data.url.replace("www.dropbox.com", "dl.dropboxusercontent.com").replace("?dl=0", "?raw=1");
} catch (err) {
if (
err.response?.data?.error?.['.tag'] === 'shared_link_already_exists'
) {
const listLinkRes = await axios.post(
'https://api.dropboxapi.com/2/sharing/list_shared_links',
{ path: randomFile.path_lower, direct_only: true },
{ headers: { Authorization: `Bearer ${DROPBOX_TOKEN}` } }
);
const existing = listLinkRes.data.links[0]?.url;
rawUrl = existing.replace("www.dropbox.com", "dl.dropboxusercontent.com").replace("?dl=0", "?raw=1");
} else {
throw err;
}
}
// Download file
console.log("⬇️ Downloading file...");
const imgRes = await axios.get(rawUrl, {
responseType: 'arraybuffer',
headers: { 'User-Agent': 'Mozilla/5.0' }
});
console.log("✅ File downloaded successfully");
return {
buffer: imgRes.data,
name: randomFile.name,
path: randomFile.path_lower,
};
}
function generateRandomCaption() {
const positiveEmojis = [
// Nature & Light
"✨", "🌟", "💫", "🌈", "🎨", "📸", "🎉", "💖", "🌺", "🦋",
"🌅", "🌄", "🎆", "🌻", "🌸", "💝", "🔆", "⭐️", "🌞", "🌼",
"💫", "🍀", "🌿", "🪴", "🎵", "💕", "🌹", "🪷", "✌️", "🙌",
// Positive Faces & Expressions
"🥰", "😊", "😍", "🤗", "😌", "😎", "🥳", "😇", "☺️", "🤩",
// Additional Positive Symbols
"💫", "💝", "💞", "💓", "💗", "💜", "💙", "🧡", "💛", "💚",
"🌠", "🎵", "🎶", "🍃", "🌱", "🌊", "🎪", "🎈", "🎀", "🎇"
];
const numEmojis = Math.floor(Math.random() * 4) + 3; // 3 to 6 emojis
const caption = Array.from({ length: numEmojis }, () =>
positiveEmojis[Math.floor(Math.random() * positiveEmojis.length)]
).join("");
return caption;
}
async function postToInstagram() {
console.log("🚀 Starting Instagram post process...");
const ig = new IgApiClient();
ig.state.generateDevice(IG_USERNAME);
await loadSession(ig);
if (!ig.state.checkpoint) {
console.log("🔐 Logging in...");
await ig.account.login(IG_USERNAME, IG_PASSWORD);
await saveSession(ig);
}
console.log("📸 Getting random file from Dropbox...");
const { buffer, name, path } = await getRandomDropboxFile();
const caption = generateRandomCaption();
console.log("💭 Generated caption:", caption);
console.log("📤 Publishing to Instagram...");
const result = await ig.publish.photo({
file: Buffer.from(buffer),
caption: caption,
});
console.log("✅ Posted to Instagram:", result.media.code);
console.log(`🔗 Post URL: https://instagram.com/p/${result.media.code}`);
// Delete the file after successful upload
await deleteDropboxFile(path);
}
postToInstagram().catch(err => {
console.error("❌ Error posting to Instagram:", err.message);
process.exit(1);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment