← 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(),
  };

};