⚡ Ad Campaign Manager

Other Tools src
1316 lines by @nichxbt

src/adCampaignManager.js

How to Use

  1. Navigate to x.com and log in
  2. Open DevTools Console (F12 or Cmd+Option+I)
  3. Paste the script below and press Enter

Configuration Options

OptionDefaultDescription
adsBaseUrl'https:ads.x.com',

Default Configuration

const CONFIG = {
    actionDelay: { min: 1000, max: 3000 },
    scrollDelay: 2000,
    confirmDelay: 800,
    pageLoadDelay: 3000,
    maxRetries: 5,
    adsBaseUrl: 'https://ads.x.com',
    defaultBudget: { daily: 20, total: 500 },
    defaultBidStrategy: 'auto',
    currency: 'USD',
  };

Full Script

Copy and paste this entire script into your browser DevTools console on x.com.

// src/adCampaignManager.js
// Ad campaign management for X/Twitter
// by nichxbt
// 1. Go to ads.x.com or x.com
// 2. Open Developer Console (F12)
// 3. Paste and run
// Last Updated: 30 March 2026
(() => {
  'use strict';

  // ══════════════════════════════════════════════════════════
  // ⚙️  CONFIGURATION
  // ══════════════════════════════════════════════════════════

  const CONFIG = {
    actionDelay: { min: 1000, max: 3000 },
    scrollDelay: 2000,
    confirmDelay: 800,
    pageLoadDelay: 3000,
    maxRetries: 5,
    adsBaseUrl: 'https://ads.x.com',
    defaultBudget: { daily: 20, total: 500 },
    defaultBidStrategy: 'auto',
    currency: 'USD',
  };

  // ══════════════════════════════════════════════════════════
  // 🔧  SELECTORS
  // ══════════════════════════════════════════════════════════

  const SEL = {
    adsDashboard: '[data-testid="adsDashboard"]',
    campaignList: '[data-testid="campaignList"]',
    createCampaign: '[data-testid="createCampaign"]',
    boostButton: '[data-testid="boostButton"]',
    tweet: 'article[data-testid="tweet"]',
    tweetText: '[data-testid="tweetText"]',
    confirmDialog: '[data-testid="confirmationSheetConfirm"]',
    toast: '[data-testid="toast"]',
    searchInput: '[data-testid="SearchBox_Search_Input"]',
    fileInput: '[data-testid="fileInput"]',
    analyticsTab: '[data-testid="analyticsTab"]',
    // Ads-specific selectors (ads.x.com)
    campaignRow: '[data-testid="campaignRow"], .campaign-row, tr[data-campaign-id]',
    campaignName: '[data-testid="campaignName"], .campaign-name',
    campaignStatus: '[data-testid="campaignStatus"], .campaign-status',
    campaignBudget: '[data-testid="campaignBudget"], .campaign-budget',
    campaignSpend: '[data-testid="campaignSpend"], .campaign-spend',
    campaignImpressions: '[data-testid="campaignImpressions"], .campaign-impressions',
    campaignClicks: '[data-testid="campaignClicks"], .campaign-clicks',
    campaignCtr: '[data-testid="campaignCtr"], .campaign-ctr',
    campaignConversions: '[data-testid="campaignConversions"], .campaign-conversions',
    objectiveSelect: '[data-testid="objectiveSelect"], select[name="objective"], .objective-selector',
    budgetInput: '[data-testid="budgetInput"], input[name="budget"], .budget-input',
    dailyBudgetInput: '[data-testid="dailyBudgetInput"], input[name="daily_budget"]',
    totalBudgetInput: '[data-testid="totalBudgetInput"], input[name="total_budget"]',
    bidInput: '[data-testid="bidInput"], input[name="bid"], .bid-input',
    bidStrategySelect: '[data-testid="bidStrategySelect"], select[name="bid_strategy"]',
    startDateInput: '[data-testid="startDateInput"], input[name="start_date"]',
    endDateInput: '[data-testid="endDateInput"], input[name="end_date"]',
    targetingSection: '[data-testid="targetingSection"], .targeting-section',
    demographicAge: '[data-testid="demographicAge"], .demographic-age',
    demographicGender: '[data-testid="demographicGender"], .demographic-gender',
    demographicLocation: '[data-testid="demographicLocation"], .demographic-location',
    interestTargeting: '[data-testid="interestTargeting"], .interest-targeting',
    keywordTargeting: '[data-testid="keywordTargeting"], .keyword-targeting',
    lookalikeTargeting: '[data-testid="lookalikeTargeting"], .lookalike-targeting',
    creativeUpload: '[data-testid="creativeUpload"], .creative-upload, input[type="file"]',
    creativeText: '[data-testid="creativeText"], textarea[name="ad_text"], .creative-text',
    creativeHeadline: '[data-testid="creativeHeadline"], input[name="headline"]',
    previewCard: '[data-testid="adPreview"], .ad-preview',
    pixelCode: '[data-testid="pixelCode"], .pixel-code',
    conversionEvent: '[data-testid="conversionEvent"], .conversion-event',
    submitCampaign: '[data-testid="submitCampaign"], button[type="submit"], .submit-campaign',
    pauseButton: '[data-testid="pauseCampaign"], .pause-campaign',
    resumeButton: '[data-testid="resumeCampaign"], .resume-campaign',
    editButton: '[data-testid="editCampaign"], .edit-campaign',
    deleteButton: '[data-testid="deleteCampaign"], .delete-campaign',
    abTestToggle: '[data-testid="abTestToggle"], .ab-test-toggle',
    abVariantB: '[data-testid="abVariantB"], .variant-b',
    quickPromote: '[data-testid="quickPromote"], .quick-promote',
    metricsTable: '[data-testid="metricsTable"], .metrics-table, table.campaign-metrics',
    exportButton: '[data-testid="exportReport"], .export-report',
  };

  // ══════════════════════════════════════════════════════════
  // 🛠️  UTILITIES
  // ══════════════════════════════════════════════════════════

  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

  const randomDelay = () => {
    const { min, max } = CONFIG.actionDelay;
    return sleep(min + Math.floor(Math.random() * (max - min)));
  };

  const log = (emoji, msg) => console.log(`${emoji} [AdCampaignManager] ${msg}`);

  const waitForElement = async (selector, timeout = 10000) => {
    const start = Date.now();
    while (Date.now() - start < timeout) {
      const el = document.querySelector(selector);
      if (el) return el;
      await sleep(300);
    }
    return null;
  };

  const waitForAny = async (selectors, timeout = 10000) => {
    const start = Date.now();
    while (Date.now() - start < timeout) {
      for (const sel of selectors) {
        const el = document.querySelector(sel);
        if (el) return el;
      }
      await sleep(300);
    }
    return null;
  };

  const clickElement = async (selector) => {
    const el = typeof selector === 'string' ? document.querySelector(selector) : selector;
    if (!el) {
      log('⚠️', `Element not found: ${typeof selector === 'string' ? selector : 'element'}`);
      return false;
    }
    el.scrollIntoView({ behavior: 'smooth', block: 'center' });
    await sleep(300);
    el.click();
    await randomDelay();
    return true;
  };

  const fillInput = async (selector, value) => {
    const input = typeof selector === 'string' ? document.querySelector(selector) : selector;
    if (!input) {
      log('⚠️', `Input not found: ${typeof selector === 'string' ? selector : 'element'}`);
      return false;
    }
    input.focus();
    input.value = '';
    const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
      window.HTMLInputElement.prototype, 'value'
    )?.set || Object.getOwnPropertyDescriptor(
      window.HTMLTextAreaElement.prototype, 'value'
    )?.set;
    if (nativeInputValueSetter) {
      nativeInputValueSetter.call(input, value);
    } else {
      input.value = value;
    }
    input.dispatchEvent(new Event('input', { bubbles: true }));
    input.dispatchEvent(new Event('change', { bubbles: true }));
    await sleep(500);
    return true;
  };

  const selectOption = async (selector, value) => {
    const select = document.querySelector(selector);
    if (!select) {
      log('⚠️', `Select not found: ${selector}`);
      return false;
    }
    select.value = value;
    select.dispatchEvent(new Event('change', { bubbles: true }));
    await sleep(500);
    return true;
  };

  const getSessionState = (key) => {
    try {
      return JSON.parse(sessionStorage.getItem(`xactions_adcampaign_${key}`) || 'null');
    } catch {
      return null;
    }
  };

  const setSessionState = (key, value) => {
    sessionStorage.setItem(`xactions_adcampaign_${key}`, JSON.stringify(value));
  };

  // ══════════════════════════════════════════════════════════
  // 1. 🧭  NAVIGATE TO ADS MANAGER
  // ══════════════════════════════════════════════════════════

  const navigateToAds = async () => {
    log('🧭', 'Navigating to Ads Manager...');
    window.open(CONFIG.adsBaseUrl, '_self');
    await sleep(CONFIG.pageLoadDelay);

    const dashboard = await waitForElement(SEL.adsDashboard, 15000);
    if (dashboard) {
      log('✅', 'Ads Manager loaded successfully');
      return true;
    }

    // Fallback: check if we're on ads.x.com by URL
    if (window.location.hostname.includes('ads')) {
      log('✅', 'On ads domain — dashboard may use non-standard selectors');
      return true;
    }

    log('⚠️', 'Could not confirm Ads Manager loaded. You may need to log in at ads.x.com');
    return false;
  };

  // ══════════════════════════════════════════════════════════
  // 2. 📝  CREATE AD CAMPAIGN
  // ══════════════════════════════════════════════════════════

  const createCampaign = async (options = {}) => {
    const {
      name = `XActions Campaign ${Date.now()}`,
      objective = 'engagement',
      dailyBudget = CONFIG.defaultBudget.daily,
      totalBudget = CONFIG.defaultBudget.total,
      startDate = new Date().toISOString().split('T')[0],
      endDate = null,
      targeting = {},
      adText = '',
      headline = '',
    } = options;

    log('📝', `Creating campaign: "${name}" (objective: ${objective})`);

    // Step 1: Click create campaign
    const createBtn = await waitForElement(SEL.createCampaign);
    if (!createBtn) {
      log('❌', 'Create Campaign button not found. Navigate to ads.x.com first.');
      return null;
    }
    await clickElement(createBtn);
    await sleep(CONFIG.pageLoadDelay);
    log('🔄', 'Campaign creation wizard opened');

    // Step 2: Select objective
    const objectiveMap = {
      engagement: 'ENGAGEMENT',
      followers: 'FOLLOWERS',
      reach: 'REACH',
      video_views: 'VIDEO_VIEWS',
      website_traffic: 'WEBSITE_CLICKS',
      app_installs: 'APP_INSTALLS',
      conversions: 'CONVERSIONS',
    };

    const objectiveValue = objectiveMap[objective] || objective.toUpperCase();
    const objectiveEl = await waitForElement(SEL.objectiveSelect);
    if (objectiveEl) {
      if (objectiveEl.tagName === 'SELECT') {
        await selectOption(SEL.objectiveSelect, objectiveValue);
      } else {
        // May be radio buttons or cards
        const optionBtn = document.querySelector(
          `[data-testid="objective-${objective}"], [data-objective="${objectiveValue}"]`
        );
        if (optionBtn) await clickElement(optionBtn);
      }
      log('✅', `Objective set: ${objective}`);
    } else {
      log('⚠️', 'Objective selector not found, continuing...');
    }
    await randomDelay();

    // Step 3: Set campaign name
    const nameInput = document.querySelector(
      '[data-testid="campaignNameInput"], input[name="campaign_name"], .campaign-name-input'
    );
    if (nameInput) {
      await fillInput(nameInput, name);
      log('✅', `Campaign name set: "${name}"`);
    }
    await randomDelay();

    // Step 4: Set budget
    await setBudget({ dailyBudget, totalBudget });

    // Step 5: Set dates
    const startEl = document.querySelector(SEL.startDateInput);
    if (startEl) {
      await fillInput(startEl, startDate);
      log('✅', `Start date: ${startDate}`);
    }
    if (endDate) {
      const endEl = document.querySelector(SEL.endDateInput);
      if (endEl) {
        await fillInput(endEl, endDate);
        log('✅', `End date: ${endDate}`);
      }
    }
    await randomDelay();

    // Step 6: Targeting
    if (Object.keys(targeting).length > 0) {
      await configureTargeting(targeting);
    }

    // Step 7: Ad creative
    if (adText) {
      const textEl = document.querySelector(SEL.creativeText);
      if (textEl) {
        await fillInput(textEl, adText);
        log('✅', 'Ad text set');
      }
    }
    if (headline) {
      const headlineEl = document.querySelector(SEL.creativeHeadline);
      if (headlineEl) {
        await fillInput(headlineEl, headline);
        log('✅', 'Headline set');
      }
    }
    await randomDelay();

    // Step 8: Submit
    const submitBtn = document.querySelector(SEL.submitCampaign);
    if (submitBtn) {
      await clickElement(submitBtn);
      await sleep(CONFIG.pageLoadDelay);

      const toast = await waitForElement(SEL.toast, 5000);
      if (toast) {
        log('✅', `Campaign "${name}" created successfully!`);
      } else {
        log('✅', `Campaign "${name}" submitted (check Ads Manager for status)`);
      }
    } else {
      log('⚠️', 'Submit button not found. Campaign may need manual submission.');
    }

    const campaignData = { name, objective, dailyBudget, totalBudget, startDate, endDate, targeting };
    setSessionState('lastCampaign', campaignData);
    return campaignData;
  };

  // ══════════════════════════════════════════════════════════
  // 3. 📊  MANAGE CAMPAIGNS
  // ══════════════════════════════════════════════════════════

  const listCampaigns = async () => {
    log('📊', 'Listing active campaigns...');

    const campaigns = [];
    const rows = document.querySelectorAll(SEL.campaignRow);

    if (rows.length === 0) {
      // Try scrolling to load campaigns
      const list = document.querySelector(SEL.campaignList);
      if (list) {
        list.scrollTop = 0;
        await sleep(CONFIG.scrollDelay);
      }
      const retryRows = document.querySelectorAll(SEL.campaignRow);
      if (retryRows.length === 0) {
        log('⚠️', 'No campaigns found. Are you on the Ads Manager page?');
        return campaigns;
      }
    }

    const allRows = document.querySelectorAll(SEL.campaignRow);
    for (const row of allRows) {
      const nameEl = row.querySelector(SEL.campaignName) || row.querySelector('td:first-child');
      const statusEl = row.querySelector(SEL.campaignStatus);
      const budgetEl = row.querySelector(SEL.campaignBudget);
      const spendEl = row.querySelector(SEL.campaignSpend);

      campaigns.push({
        name: nameEl?.textContent?.trim() || 'Unknown',
        status: statusEl?.textContent?.trim() || 'Unknown',
        budget: budgetEl?.textContent?.trim() || 'N/A',
        spend: spendEl?.textContent?.trim() || 'N/A',
        element: row,
      });
    }

    log('✅', `Found ${campaigns.length} campaigns:`);
    campaigns.forEach((c, i) => {
      log('📋', `  ${i + 1}. ${c.name} — Status: ${c.status} | Budget: ${c.budget} | Spend: ${c.spend}`);
    });

    setSessionState('campaigns', campaigns.map(({ element, ...rest }) => rest));
    return campaigns;
  };

  const pauseCampaign = async (campaignIndex) => {
    log('⏸️', `Pausing campaign #${campaignIndex + 1}...`);
    const rows = document.querySelectorAll(SEL.campaignRow);
    if (campaignIndex >= rows.length) {
      log('❌', `Campaign index ${campaignIndex} out of range (${rows.length} campaigns)`);
      return false;
    }

    const row = rows[campaignIndex];
    await clickElement(row);
    await randomDelay();

    const pauseBtn = await waitForElement(SEL.pauseButton, 5000);
    if (pauseBtn) {
      await clickElement(pauseBtn);
      await sleep(CONFIG.confirmDelay);

      const confirmBtn = document.querySelector(SEL.confirmDialog);
      if (confirmBtn) await clickElement(confirmBtn);

      log('✅', 'Campaign paused');
      return true;
    }

    log('⚠️', 'Pause button not found. Campaign may already be paused.');
    return false;
  };

  const resumeCampaign = async (campaignIndex) => {
    log('▶️', `Resuming campaign #${campaignIndex + 1}...`);
    const rows = document.querySelectorAll(SEL.campaignRow);
    if (campaignIndex >= rows.length) {
      log('❌', `Campaign index ${campaignIndex} out of range`);
      return false;
    }

    const row = rows[campaignIndex];
    await clickElement(row);
    await randomDelay();

    const resumeBtn = await waitForElement(SEL.resumeButton, 5000);
    if (resumeBtn) {
      await clickElement(resumeBtn);
      log('✅', 'Campaign resumed');
      return true;
    }

    log('⚠️', 'Resume button not found. Campaign may already be active.');
    return false;
  };

  const editCampaign = async (campaignIndex, updates = {}) => {
    log('✏️', `Editing campaign #${campaignIndex + 1}...`);
    const rows = document.querySelectorAll(SEL.campaignRow);
    if (campaignIndex >= rows.length) {
      log('❌', `Campaign index ${campaignIndex} out of range`);
      return false;
    }

    const row = rows[campaignIndex];
    await clickElement(row);
    await randomDelay();

    const editBtn = await waitForElement(SEL.editButton, 5000);
    if (editBtn) {
      await clickElement(editBtn);
      await sleep(CONFIG.pageLoadDelay);
    }

    if (updates.name) {
      const nameInput = document.querySelector(
        '[data-testid="campaignNameInput"], input[name="campaign_name"]'
      );
      if (nameInput) await fillInput(nameInput, updates.name);
    }

    if (updates.dailyBudget || updates.totalBudget) {
      await setBudget(updates);
    }

    if (updates.targeting) {
      await configureTargeting(updates.targeting);
    }

    if (updates.adText) {
      const textEl = document.querySelector(SEL.creativeText);
      if (textEl) await fillInput(textEl, updates.adText);
    }

    // Save changes
    const saveBtn = document.querySelector(
      SEL.submitCampaign + ', [data-testid="saveCampaign"], .save-campaign'
    );
    if (saveBtn) {
      await clickElement(saveBtn);
      await sleep(CONFIG.pageLoadDelay);
      log('✅', 'Campaign updated');
      return true;
    }

    log('⚠️', 'Save button not found. Changes may need manual saving.');
    return false;
  };

  // ══════════════════════════════════════════════════════════
  // 4. 🎨  AD CREATIVE MANAGEMENT
  // ══════════════════════════════════════════════════════════

  const manageCreatives = async (options = {}) => {
    const { action = 'list', text = '', headline = '', mediaType = null } = options;

    log('🎨', `Creative management: ${action}`);

    if (action === 'list') {
      const previews = document.querySelectorAll(SEL.previewCard);
      const creatives = [];
      for (const preview of previews) {
        creatives.push({
          text: preview.querySelector('p, .ad-text')?.textContent?.trim() || '',
          hasImage: !!preview.querySelector('img'),
          hasVideo: !!preview.querySelector('video'),
        });
      }
      log('✅', `Found ${creatives.length} ad creatives`);
      creatives.forEach((c, i) => {
        const mediaTag = c.hasVideo ? '🎬' : c.hasImage ? '🖼️' : '📝';
        log(mediaTag, `  ${i + 1}. "${c.text.substring(0, 60)}${c.text.length > 60 ? '...' : ''}"`);
      });
      return creatives;
    }

    if (action === 'create') {
      if (text) {
        const textEl = document.querySelector(SEL.creativeText);
        if (textEl) {
          await fillInput(textEl, text);
          log('✅', 'Ad text set');
        }
      }

      if (headline) {
        const headlineEl = document.querySelector(SEL.creativeHeadline);
        if (headlineEl) {
          await fillInput(headlineEl, headline);
          log('✅', 'Headline set');
        }
      }

      if (mediaType) {
        log('📎', `To upload ${mediaType}: use the file input when prompted`);
        const uploadEl = document.querySelector(SEL.creativeUpload);
        if (uploadEl) {
          uploadEl.click();
          log('🔄', 'File upload dialog opened — select your media file');
        }
      }

      return true;
    }

    if (action === 'remove') {
      const { index = 0 } = options;
      const previews = document.querySelectorAll(SEL.previewCard);
      if (index < previews.length) {
        const removeBtn = previews[index].querySelector(
          '[data-testid="removeCreative"], .remove-creative, button[aria-label="Remove"]'
        );
        if (removeBtn) {
          await clickElement(removeBtn);
          const confirmBtn = document.querySelector(SEL.confirmDialog);
          if (confirmBtn) await clickElement(confirmBtn);
          log('✅', `Creative #${index + 1} removed`);
          return true;
        }
      }
      log('⚠️', 'Could not remove creative');
      return false;
    }

    return false;
  };

  // ══════════════════════════════════════════════════════════
  // 5. 🎯  AUDIENCE TARGETING
  // ══════════════════════════════════════════════════════════

  const configureTargeting = async (targeting = {}) => {
    const {
      ageMin = null,
      ageMax = null,
      gender = null,
      locations = [],
      interests = [],
      keywords = [],
      lookalike = null,
      languages = [],
      devices = [],
    } = targeting;

    log('🎯', 'Configuring audience targeting...');

    // Demographics — Age
    if (ageMin || ageMax) {
      const ageEl = document.querySelector(SEL.demographicAge);
      if (ageEl) {
        const minInput = ageEl.querySelector('input[name="age_min"], .age-min');
        const maxInput = ageEl.querySelector('input[name="age_max"], .age-max');
        if (minInput && ageMin) await fillInput(minInput, String(ageMin));
        if (maxInput && ageMax) await fillInput(maxInput, String(ageMax));
        log('✅', `Age range: ${ageMin || '13'}–${ageMax || '65+'}`);
      }
    }

    // Demographics — Gender
    if (gender) {
      const genderEl = document.querySelector(SEL.demographicGender);
      if (genderEl) {
        const option = genderEl.querySelector(
          `[data-testid="gender-${gender}"], [value="${gender}"], label:has(input[value="${gender}"])`
        );
        if (option) await clickElement(option);
        log('✅', `Gender: ${gender}`);
      }
    }

    // Location targeting
    if (locations.length > 0) {
      const locationEl = document.querySelector(SEL.demographicLocation);
      if (locationEl) {
        for (const location of locations) {
          const input = locationEl.querySelector('input');
          if (input) {
            await fillInput(input, location);
            await sleep(1500); // Wait for autocomplete
            const suggestion = document.querySelector(
              '[data-testid="locationSuggestion"], .location-suggestion, [role="option"]'
            );
            if (suggestion) await clickElement(suggestion);
            log('✅', `Location added: ${location}`);
          }
          await randomDelay();
        }
      }
    }

    // Interest targeting
    if (interests.length > 0) {
      const interestEl = document.querySelector(SEL.interestTargeting);
      if (interestEl) {
        for (const interest of interests) {
          const input = interestEl.querySelector('input');
          if (input) {
            await fillInput(input, interest);
            await sleep(1500);
            const suggestion = document.querySelector(
              '[data-testid="interestSuggestion"], .interest-suggestion, [role="option"]'
            );
            if (suggestion) await clickElement(suggestion);
            log('✅', `Interest added: ${interest}`);
          }
          await randomDelay();
        }
      }
    }

    // Keyword targeting
    if (keywords.length > 0) {
      const keywordEl = document.querySelector(SEL.keywordTargeting);
      if (keywordEl) {
        for (const keyword of keywords) {
          const input = keywordEl.querySelector('input, textarea');
          if (input) {
            await fillInput(input, keyword);
            await sleep(500);
            // Press Enter to confirm keyword
            input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
            log('✅', `Keyword added: ${keyword}`);
          }
          await randomDelay();
        }
      }
    }

    // Lookalike audiences
    if (lookalike) {
      const lookalikeEl = document.querySelector(SEL.lookalikeTargeting);
      if (lookalikeEl) {
        await clickElement(lookalikeEl);
        await sleep(1000);
        const sourceInput = lookalikeEl.querySelector('input');
        if (sourceInput) {
          await fillInput(sourceInput, lookalike);
          await sleep(1500);
          const suggestion = document.querySelector('[role="option"]');
          if (suggestion) await clickElement(suggestion);
          log('✅', `Lookalike audience: ${lookalike}`);
        }
      }
    }

    // Language targeting
    if (languages.length > 0) {
      const langSection = document.querySelector(
        '[data-testid="languageTargeting"], .language-targeting'
      );
      if (langSection) {
        for (const lang of languages) {
          const input = langSection.querySelector('input');
          if (input) {
            await fillInput(input, lang);
            await sleep(1000);
            const suggestion = document.querySelector('[role="option"]');
            if (suggestion) await clickElement(suggestion);
            log('✅', `Language: ${lang}`);
          }
          await randomDelay();
        }
      }
    }

    // Device targeting
    if (devices.length > 0) {
      const deviceSection = document.querySelector(
        '[data-testid="deviceTargeting"], .device-targeting'
      );
      if (deviceSection) {
        for (const device of devices) {
          const option = deviceSection.querySelector(
            `[data-testid="device-${device}"], [value="${device}"]`
          );
          if (option) {
            await clickElement(option);
            log('✅', `Device: ${device}`);
          }
        }
      }
    }

    log('✅', 'Targeting configuration complete');
    return true;
  };

  // ══════════════════════════════════════════════════════════
  // 6. 💰  BUDGET / BID MANAGEMENT
  // ══════════════════════════════════════════════════════════

  const setBudget = async (options = {}) => {
    const {
      dailyBudget = null,
      totalBudget = null,
      bidStrategy = null,
      bidAmount = null,
    } = options;

    log('💰', 'Setting budget and bid...');

    if (dailyBudget) {
      const dailyEl = document.querySelector(SEL.dailyBudgetInput) ||
                       document.querySelector(SEL.budgetInput);
      if (dailyEl) {
        await fillInput(dailyEl, String(dailyBudget));
        log('✅', `Daily budget: ${CONFIG.currency} ${dailyBudget}`);
      }
    }

    if (totalBudget) {
      const totalEl = document.querySelector(SEL.totalBudgetInput);
      if (totalEl) {
        await fillInput(totalEl, String(totalBudget));
        log('✅', `Total budget: ${CONFIG.currency} ${totalBudget}`);
      }
    }

    if (bidStrategy) {
      const bidStrategyEl = document.querySelector(SEL.bidStrategySelect);
      if (bidStrategyEl) {
        const strategyMap = {
          auto: 'AUTO_BID',
          target: 'TARGET_COST',
          max: 'MAX_BID',
        };
        const value = strategyMap[bidStrategy] || bidStrategy.toUpperCase();
        await selectOption(SEL.bidStrategySelect, value);
        log('✅', `Bid strategy: ${bidStrategy}`);
      }
    }

    if (bidAmount) {
      const bidEl = document.querySelector(SEL.bidInput);
      if (bidEl) {
        await fillInput(bidEl, String(bidAmount));
        log('✅', `Bid amount: ${CONFIG.currency} ${bidAmount}`);
      }
    }

    return true;
  };

  // ══════════════════════════════════════════════════════════
  // 7. 📍  CONVERSION TRACKING
  // ══════════════════════════════════════════════════════════

  const setupConversionTracking = async (options = {}) => {
    const {
      eventName = 'purchase',
      pixelAction = 'view', // 'view' or 'generate'
    } = options;

    log('📍', 'Setting up conversion tracking...');

    if (pixelAction === 'generate') {
      const pixelBtn = document.querySelector(
        '[data-testid="generatePixel"], .generate-pixel, button:has(> span)'
      );
      if (pixelBtn) {
        await clickElement(pixelBtn);
        await sleep(2000);

        const codeEl = document.querySelector(SEL.pixelCode);
        if (codeEl) {
          const code = codeEl.textContent?.trim() || codeEl.value?.trim();
          log('✅', 'Pixel code generated:');
          console.log(code);
          setSessionState('pixelCode', code);
          return { pixelCode: code };
        }
      }
      log('⚠️', 'Could not generate pixel code. Navigate to conversion tracking page.');
      return null;
    }

    if (pixelAction === 'view') {
      const pixelEl = document.querySelector(SEL.pixelCode);
      if (pixelEl) {
        const code = pixelEl.textContent?.trim() || pixelEl.value?.trim();
        log('✅', 'Current pixel code:');
        console.log(code);
        return { pixelCode: code };
      }
      log('⚠️', 'No pixel code found on page');
    }

    // Configure conversion events
    const eventSection = document.querySelector(SEL.conversionEvent);
    if (eventSection) {
      const eventInput = eventSection.querySelector('input, select');
      if (eventInput) {
        if (eventInput.tagName === 'SELECT') {
          await selectOption(eventInput, eventName);
        } else {
          await fillInput(eventInput, eventName);
        }
        log('✅', `Conversion event configured: ${eventName}`);
      }
    }

    const conversionEvents = [
      'page_view', 'purchase', 'sign_up', 'add_to_cart',
      'download', 'lead', 'custom',
    ];
    log('📋', 'Available conversion events:');
    conversionEvents.forEach((e) => log('  🔹', e));

    return { eventName, events: conversionEvents };
  };

  // ══════════════════════════════════════════════════════════
  // 8. 📈  CAMPAIGN ANALYTICS / REPORTING
  // ══════════════════════════════════════════════════════════

  const getCampaignAnalytics = async (options = {}) => {
    const { campaignIndex = null, exportCsv = false } = options;

    log('📈', 'Scraping campaign analytics...');

    const metrics = [];

    // If a specific campaign is selected, click into it
    if (campaignIndex !== null) {
      const rows = document.querySelectorAll(SEL.campaignRow);
      if (campaignIndex < rows.length) {
        await clickElement(rows[campaignIndex]);
        await sleep(CONFIG.pageLoadDelay);
      }
    }

    // Try analytics tab
    const analyticsTab = document.querySelector(SEL.analyticsTab);
    if (analyticsTab) {
      await clickElement(analyticsTab);
      await sleep(CONFIG.pageLoadDelay);
    }

    // Scrape metrics table
    const table = document.querySelector(SEL.metricsTable);
    if (table) {
      const headers = [];
      const headerCells = table.querySelectorAll('th, thead td');
      headerCells.forEach((h) => headers.push(h.textContent?.trim()));

      const bodyRows = table.querySelectorAll('tbody tr');
      for (const row of bodyRows) {
        const cells = row.querySelectorAll('td');
        const rowData = {};
        cells.forEach((cell, i) => {
          const key = headers[i] || `col_${i}`;
          rowData[key] = cell.textContent?.trim();
        });
        metrics.push(rowData);
      }
    }

    // Scrape individual metric elements as fallback
    if (metrics.length === 0) {
      const rows = document.querySelectorAll(SEL.campaignRow);
      for (const row of rows) {
        const data = {
          name: row.querySelector(SEL.campaignName)?.textContent?.trim() || 'Unknown',
          impressions: row.querySelector(SEL.campaignImpressions)?.textContent?.trim() || '0',
          clicks: row.querySelector(SEL.campaignClicks)?.textContent?.trim() || '0',
          ctr: row.querySelector(SEL.campaignCtr)?.textContent?.trim() || '0%',
          spend: row.querySelector(SEL.campaignSpend)?.textContent?.trim() || '$0',
          conversions: row.querySelector(SEL.campaignConversions)?.textContent?.trim() || '0',
          status: row.querySelector(SEL.campaignStatus)?.textContent?.trim() || 'Unknown',
        };
        metrics.push(data);
      }
    }

    if (metrics.length === 0) {
      log('⚠️', 'No analytics data found. Ensure you are on the Ads Manager page.');
      return [];
    }

    log('✅', `Campaign performance (${metrics.length} entries):`);
    console.table(metrics);

    // Export CSV
    if (exportCsv) {
      const exportBtn = document.querySelector(SEL.exportButton);
      if (exportBtn) {
        await clickElement(exportBtn);
        log('✅', 'Export initiated');
      } else {
        // Generate CSV manually
        const csvHeaders = Object.keys(metrics[0]).join(',');
        const csvRows = metrics.map((row) =>
          Object.values(row).map((v) => `"${v}"`).join(',')
        );
        const csv = [csvHeaders, ...csvRows].join('\n');
        const blob = new Blob([csv], { type: 'text/csv' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = `xactions_campaign_report_${Date.now()}.csv`;
        a.click();
        URL.revokeObjectURL(url);
        log('✅', 'CSV report downloaded');
      }
    }

    setSessionState('lastAnalytics', metrics);
    return metrics;
  };

  // ══════════════════════════════════════════════════════════
  // 9. 🚀  PROMOTE SPECIFIC POST
  // ══════════════════════════════════════════════════════════

  const promotePost = async (options = {}) => {
    const {
      tweetUrl = null,
      tweetIndex = 0,
      budget = CONFIG.defaultBudget.daily,
      duration = 7,
    } = options;

    log('🚀', 'Quick promoting a post...');

    if (tweetUrl) {
      window.location.href = tweetUrl;
      await sleep(CONFIG.pageLoadDelay);
    }

    // Find the tweet on the page
    const tweets = document.querySelectorAll(SEL.tweet);
    if (tweets.length === 0) {
      log('❌', 'No tweets found on page. Navigate to a tweet or profile first.');
      return false;
    }

    const targetTweet = tweets[tweetIndex];
    if (!targetTweet) {
      log('❌', `Tweet #${tweetIndex + 1} not found (${tweets.length} tweets on page)`);
      return false;
    }

    const tweetTextEl = targetTweet.querySelector(SEL.tweetText);
    const tweetPreview = tweetTextEl?.textContent?.substring(0, 80) || 'tweet';
    log('🔄', `Promoting: "${tweetPreview}..."`);

    // Look for boost/promote button on the tweet
    const boostBtn = targetTweet.querySelector(SEL.boostButton) ||
                     targetTweet.querySelector(SEL.quickPromote);
    if (boostBtn) {
      await clickElement(boostBtn);
      await sleep(CONFIG.pageLoadDelay);

      // Set promotion budget
      const budgetEl = await waitForElement(SEL.budgetInput, 5000);
      if (budgetEl) {
        await fillInput(budgetEl, String(budget));
        log('✅', `Promotion budget: ${CONFIG.currency} ${budget}`);
      }

      // Set duration
      const durationInput = document.querySelector(
        '[data-testid="promotionDuration"], input[name="duration"], .promotion-duration'
      );
      if (durationInput) {
        await fillInput(durationInput, String(duration));
        log('✅', `Duration: ${duration} days`);
      }

      // Submit promotion
      const submitBtn = document.querySelector(
        SEL.submitCampaign + ', [data-testid="promoteButton"], .promote-submit'
      );
      if (submitBtn) {
        await clickElement(submitBtn);
        await sleep(CONFIG.pageLoadDelay);
        log('✅', `Post promoted! Budget: ${CONFIG.currency} ${budget} for ${duration} days`);
        return true;
      }
    }

    // Fallback: try the share menu approach
    const shareBtn = targetTweet.querySelector('[data-testid="share"]');
    if (shareBtn) {
      await clickElement(shareBtn);
      await sleep(1000);
      const promoteOption = await waitForAny([
        '[data-testid="promote"], [role="menuitem"]:has(span)',
      ], 3000);
      if (promoteOption && promoteOption.textContent?.toLowerCase().includes('promot')) {
        await clickElement(promoteOption);
        await sleep(CONFIG.pageLoadDelay);
        log('🔄', 'Promotion dialog opened — configure budget and submit');
        return true;
      }
    }

    log('⚠️', 'Quick promote not available. Try promoting from ads.x.com directly.');
    return false;
  };

  // ══════════════════════════════════════════════════════════
  // 10. 🎪  CAMPAIGN TYPES
  // ══════════════════════════════════════════════════════════

  const createFollowerCampaign = async (options = {}) => {
    log('👥', 'Creating follower growth campaign...');
    return createCampaign({
      ...options,
      name: options.name || `Follower Growth ${Date.now()}`,
      objective: 'followers',
    });
  };

  const createEngagementCampaign = async (options = {}) => {
    log('💬', 'Creating engagement campaign...');
    return createCampaign({
      ...options,
      name: options.name || `Engagement ${Date.now()}`,
      objective: 'engagement',
    });
  };

  const createVideoViewsCampaign = async (options = {}) => {
    log('🎬', 'Creating video views campaign...');
    return createCampaign({
      ...options,
      name: options.name || `Video Views ${Date.now()}`,
      objective: 'video_views',
    });
  };

  const createWebsiteTrafficCampaign = async (options = {}) => {
    log('🌐', 'Creating website traffic campaign...');
    return createCampaign({
      ...options,
      name: options.name || `Website Traffic ${Date.now()}`,
      objective: 'website_traffic',
    });
  };

  // ══════════════════════════════════════════════════════════
  // 11. 🧪  A/B TEST AD CREATIVES
  // ══════════════════════════════════════════════════════════

  const setupABTest = async (options = {}) => {
    const {
      variantA = {},
      variantB = {},
      splitPercentage = 50,
      metric = 'ctr',
      duration = 7,
    } = options;

    log('🧪', `Setting up A/B test (${splitPercentage}/${100 - splitPercentage} split)...`);

    // Enable A/B testing toggle
    const abToggle = document.querySelector(SEL.abTestToggle);
    if (abToggle) {
      const isEnabled = abToggle.getAttribute('aria-checked') === 'true' ||
                        abToggle.classList.contains('active');
      if (!isEnabled) {
        await clickElement(abToggle);
        await sleep(1000);
        log('✅', 'A/B testing enabled');
      }
    } else {
      log('⚠️', 'A/B test toggle not found. Creating manual split test...');
    }

    // Configure Variant A
    log('🔄', 'Configuring Variant A...');
    if (variantA.text) {
      const textEl = document.querySelector(SEL.creativeText);
      if (textEl) await fillInput(textEl, variantA.text);
    }
    if (variantA.headline) {
      const headlineEl = document.querySelector(SEL.creativeHeadline);
      if (headlineEl) await fillInput(headlineEl, variantA.headline);
    }
    log('✅', 'Variant A configured');
    await randomDelay();

    // Switch to Variant B
    const variantBBtn = document.querySelector(SEL.abVariantB) ||
      document.querySelector('[data-testid="addVariant"], .add-variant');
    if (variantBBtn) {
      await clickElement(variantBBtn);
      await sleep(1500);

      if (variantB.text) {
        const textEl = document.querySelector(SEL.creativeText);
        if (textEl) await fillInput(textEl, variantB.text);
      }
      if (variantB.headline) {
        const headlineEl = document.querySelector(SEL.creativeHeadline);
        if (headlineEl) await fillInput(headlineEl, variantB.headline);
      }
      log('✅', 'Variant B configured');
    } else {
      log('⚠️', 'Could not add Variant B. You may need to create two separate campaigns.');
      log('💡', 'Tip: Create two campaigns with identical targeting but different creatives');
    }

    // Set split percentage
    const splitInput = document.querySelector(
      '[data-testid="splitPercentage"], input[name="split"], .split-percentage'
    );
    if (splitInput) {
      await fillInput(splitInput, String(splitPercentage));
      log('✅', `Split: ${splitPercentage}% / ${100 - splitPercentage}%`);
    }

    // Set success metric
    const metricSelect = document.querySelector(
      '[data-testid="successMetric"], select[name="success_metric"]'
    );
    if (metricSelect) {
      const metricMap = {
        ctr: 'CLICK_THROUGH_RATE',
        conversions: 'CONVERSIONS',
        engagement: 'ENGAGEMENT_RATE',
        impressions: 'IMPRESSIONS',
        cost: 'COST_PER_RESULT',
      };
      await selectOption(metricSelect, metricMap[metric] || metric.toUpperCase());
      log('✅', `Success metric: ${metric}`);
    }

    const testConfig = {
      variantA,
      variantB,
      splitPercentage,
      metric,
      duration,
      createdAt: new Date().toISOString(),
    };

    setSessionState('abTest', testConfig);
    log('✅', 'A/B test configured:');
    log('📋', `  Variant A: "${variantA.text?.substring(0, 50) || 'default'}"`);
    log('📋', `  Variant B: "${variantB.text?.substring(0, 50) || 'default'}"`);
    log('📋', `  Split: ${splitPercentage}/${100 - splitPercentage} | Metric: ${metric} | Duration: ${duration} days`);

    return testConfig;
  };

  // ══════════════════════════════════════════════════════════
  // 🖥️  EXPOSE API & LOG MENU
  // ══════════════════════════════════════════════════════════

  window.XActions = window.XActions || {};
  window.XActions.adCampaignManager = {
    // 1. Navigation
    navigateToAds,

    // 2. Campaign creation
    createCampaign,

    // 3. Campaign management
    listCampaigns,
    pauseCampaign,
    resumeCampaign,
    editCampaign,

    // 4. Creative management
    manageCreatives,

    // 5. Audience targeting
    configureTargeting,

    // 6. Budget / bid management
    setBudget,

    // 7. Conversion tracking
    setupConversionTracking,

    // 8. Analytics / reporting
    getCampaignAnalytics,

    // 9. Promote specific post
    promotePost,

    // 10. Campaign types
    createFollowerCampaign,
    createEngagementCampaign,
    createVideoViewsCampaign,
    createWebsiteTrafficCampaign,

    // 11. A/B testing
    setupABTest,

    // Utilities
    config: CONFIG,
  };

  // ══════════════════════════════════════════════════════════
  // 📋  MENU
  // ══════════════════════════════════════════════════════════

  console.log(`
╔══════════════════════════════════════════════════════════════╗
║  📢  XActions Ad Campaign Manager — by nichxbt              ║
╠══════════════════════════════════════════════════════════════╣
║                                                              ║
║  🧭  Navigate to Ads Manager                                ║
║     XActions.adCampaignManager.navigateToAds()               ║
║                                                              ║
║  📝  Create Campaign                                         ║
║     XActions.adCampaignManager.createCampaign({              ║
║       name: 'My Campaign',                                   ║
║       objective: 'engagement',  // followers|video_views|... ║
║       dailyBudget: 20, totalBudget: 500,                     ║
║       targeting: { interests: ['tech'], locations: ['US'] }  ║
║     })                                                       ║
║                                                              ║
║  📊  List / Manage Campaigns                                 ║
║     XActions.adCampaignManager.listCampaigns()               ║
║     XActions.adCampaignManager.pauseCampaign(0)              ║
║     XActions.adCampaignManager.resumeCampaign(0)             ║
║     XActions.adCampaignManager.editCampaign(0, { ... })      ║
║                                                              ║
║  🎨  Ad Creative Management                                  ║
║     XActions.adCampaignManager.manageCreatives({             ║
║       action: 'list' | 'create' | 'remove',                 ║
║       text: '...', headline: '...', mediaType: 'image'      ║
║     })                                                       ║
║                                                              ║
║  🎯  Audience Targeting                                      ║
║     XActions.adCampaignManager.configureTargeting({          ║
║       locations: ['US','UK'], interests: ['technology'],     ║
║       keywords: ['AI'], ageMin: 18, ageMax: 45,             ║
║       gender: 'all', lookalike: 'source_audience'            ║
║     })                                                       ║
║                                                              ║
║  💰  Budget / Bid Management                                 ║
║     XActions.adCampaignManager.setBudget({                   ║
║       dailyBudget: 50, totalBudget: 1000,                    ║
║       bidStrategy: 'auto', bidAmount: 0.50                   ║
║     })                                                       ║
║                                                              ║
║  📍  Conversion Tracking                                     ║
║     XActions.adCampaignManager.setupConversionTracking({     ║
║       pixelAction: 'generate', eventName: 'purchase'         ║
║     })                                                       ║
║                                                              ║
║  📈  Campaign Analytics                                      ║
║     XActions.adCampaignManager.getCampaignAnalytics()        ║
║     XActions.adCampaignManager.getCampaignAnalytics({        ║
║       campaignIndex: 0, exportCsv: true                      ║
║     })                                                       ║
║                                                              ║
║  🚀  Quick Promote a Post                                    ║
║     XActions.adCampaignManager.promotePost({                 ║
║       tweetUrl: 'https://x.com/.../status/123',             ║
║       budget: 25, duration: 7                                ║
║     })                                                       ║
║                                                              ║
║  🎪  Campaign Types (shortcuts)                              ║
║     XActions.adCampaignManager.createFollowerCampaign()      ║
║     XActions.adCampaignManager.createEngagementCampaign()    ║
║     XActions.adCampaignManager.createVideoViewsCampaign()    ║
║     XActions.adCampaignManager.createWebsiteTrafficCampaign()║
║                                                              ║
║  🧪  A/B Test Creatives                                      ║
║     XActions.adCampaignManager.setupABTest({                 ║
║       variantA: { text: 'Try our product!' },                ║
║       variantB: { text: 'Discover something new!' },         ║
║       splitPercentage: 50, metric: 'ctr', duration: 7        ║
║     })                                                       ║
║                                                              ║
╚══════════════════════════════════════════════════════════════╝
  `);

  log('✅', 'Ad Campaign Manager loaded! Access via XActions.adCampaignManager');
})();

⚡ More XActions Scripts

Browse 300+ free browser scripts for X/Twitter automation. No API keys, no fees.

Browse All Scripts