← back to Norma
agents/instagram-agent/skills/monitor.js
107 lines
/**
* Instagram Monitor Skill
*
* Checks Instagram account insights (reach, impressions, follower_count).
* Runs in simulation mode when IG_ACCESS_TOKEN is not set.
*
* Real endpoint:
* GET https://graph.facebook.com/v19.0/{ig_user_id}/insights
* Params: metric=reach,impressions,follower_count,profile_views&period=day&access_token=TOKEN
*
* GET https://graph.facebook.com/v19.0/{ig_user_id}
* Params: fields=followers_count,follows_count,media_count&access_token=TOKEN
*/
const AGENT = 'instagram-agent';
const PLATFORM = 'instagram';
function hasCredentials() {
return !!(process.env.IG_USER_ID && process.env.IG_ACCESS_TOKEN);
}
/**
* @param {Object} params - Request body
* @param {string} [params.period=day] - Insights period (day, week, days_28, lifetime)
* @param {boolean} [params.push_to_pulse=true] - Push results to Pulse
* @returns {Promise<Object>}
*/
module.exports = async function monitor(params) {
const period = params.period || 'day';
const simulated = !hasCredentials();
console.log(`[${AGENT}] Monitor skill invoked — simulation=${simulated}, period=${period}`);
if (simulated) {
// --- Simulation mode: return realistic-looking insights ---
const insights = {
reach: Math.floor(Math.random() * 8000) + 1000,
impressions: Math.floor(Math.random() * 20000) + 3000,
follower_count: Math.floor(Math.random() * 5000) + 500,
profile_views: Math.floor(Math.random() * 1500) + 200,
website_clicks: Math.floor(Math.random() * 100) + 10,
email_contacts: Math.floor(Math.random() * 20) + 2,
};
const accountInfo = {
followers_count: insights.follower_count,
follows_count: Math.floor(Math.random() * 300) + 50,
media_count: Math.floor(Math.random() * 200) + 30,
};
console.log(`[${AGENT}] SIMULATED monitor: reach=${insights.reach}, followers=${insights.follower_count}`);
return {
insights,
account: accountInfo,
period,
simulated: true,
platform: PLATFORM,
checked_at: new Date().toISOString(),
};
}
// --- Real API call (commented out until credentials are configured) ---
const igUserId = process.env.IG_USER_ID;
const accessToken = process.env.IG_ACCESS_TOKEN;
// Fetch insights
// Graph v21+: impressions/email_contacts deprecated; profile_views and
// website_clicks require metric_type=total_value (separate call)
const insights = {};
const calls = [
`https://graph.facebook.com/v21.0/${igUserId}/insights?metric=reach,follower_count&period=${period}&access_token=${accessToken}`,
`https://graph.facebook.com/v21.0/${igUserId}/insights?metric=profile_views,website_clicks&metric_type=total_value&period=${period}&access_token=${accessToken}`,
];
for (const url of calls) {
const resp = await fetch(url);
const data = await resp.json();
if (data.error) {
throw new Error(`Instagram API error (insights): ${data.error.message}`);
}
for (const entry of data.data) {
insights[entry.name] = entry.total_value?.value ??
(entry.values?.[entry.values.length - 1]?.value || 0);
}
}
// Fetch account info
const accountUrl = `https://graph.facebook.com/v19.0/${igUserId}?fields=followers_count,follows_count,media_count&access_token=${accessToken}`;
const accountResponse = await fetch(accountUrl);
const accountData = await accountResponse.json();
return {
insights,
account: {
followers_count: accountData.followers_count,
follows_count: accountData.follows_count,
media_count: accountData.media_count,
},
period,
simulated: false,
platform: PLATFORM,
checked_at: new Date().toISOString(),
};
};