← back to SDCC Stories
lib/instagram-api.js
105 lines
/**
* Instagram API — Meta Graph API integration (cloned from SmokeShop pattern)
* OAuth 2.0 flow → long-lived token → 2-step media publishing
*/
const https = require('https');
const META_API_VERSION = 'v18.0';
// Exchange short-lived code for access token
async function exchangeCodeForToken(appId, appSecret, redirectUri, code) {
const params = new URLSearchParams({
client_id: appId,
client_secret: appSecret,
grant_type: 'authorization_code',
redirect_uri: redirectUri,
code
});
return apiGet(`https://graph.facebook.com/${META_API_VERSION}/oauth/access_token?${params}`);
}
// Exchange short-lived token for 60-day long-lived token
async function getLongLivedToken(appId, appSecret, shortToken) {
const params = new URLSearchParams({
grant_type: 'fb_exchange_token',
client_id: appId,
client_secret: appSecret,
fb_exchange_token: shortToken
});
return apiGet(`https://graph.facebook.com/${META_API_VERSION}/oauth/access_token?${params}`);
}
// Get Instagram Business Account ID from Facebook Page
async function getIGBusinessAccount(accessToken) {
const pages = await apiGet(`https://graph.facebook.com/${META_API_VERSION}/me/accounts?access_token=${accessToken}`);
if (!pages.data || pages.data.length === 0) return null;
const pageId = pages.data[0].id;
const igAccount = await apiGet(`https://graph.facebook.com/${META_API_VERSION}/${pageId}?fields=instagram_business_account&access_token=${accessToken}`);
return igAccount.instagram_business_account?.id || null;
}
// Publish to Instagram (2-step: create container → publish)
async function publishToInstagram(igAccountId, accessToken, imageUrl, caption) {
// Step 1: Create media container
const container = await apiPost(`https://graph.facebook.com/${META_API_VERSION}/${igAccountId}/media`, {
image_url: imageUrl,
caption,
access_token: accessToken
});
if (!container.id) throw new Error('Failed to create media container: ' + JSON.stringify(container));
// Wait for processing
await new Promise(r => setTimeout(r, 3000));
// Step 2: Publish
const published = await apiPost(`https://graph.facebook.com/${META_API_VERSION}/${igAccountId}/media_publish`, {
creation_id: container.id,
access_token: accessToken
});
return published;
}
// ── HTTP helpers ──
function apiGet(url) {
return new Promise((resolve, reject) => {
https.get(url, (res) => {
let data = '';
res.on('data', c => data += c);
res.on('end', () => {
try { resolve(JSON.parse(data)); }
catch (e) { resolve({ error: data }); }
});
}).on('error', reject);
});
}
function apiPost(url, body) {
return new Promise((resolve, reject) => {
const payload = JSON.stringify(body);
const parsed = new URL(url);
const req = https.request({
hostname: parsed.hostname,
path: parsed.pathname + parsed.search,
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) }
}, (res) => {
let data = '';
res.on('data', c => data += c);
res.on('end', () => {
try { resolve(JSON.parse(data)); }
catch (e) { resolve({ error: data }); }
});
});
req.on('error', reject);
req.write(payload);
req.end();
});
}
module.exports = { exchangeCodeForToken, getLongLivedToken, getIGBusinessAccount, publishToInstagram };