Script中心

MCC换链脚本

复制到 Google Ads Scripts 使用。
/**
 * ABOS MCC Tracking Template updater.
 *
 * Copy this script into Google Ads Scripts at MCC level.
 */

var TRACKING_TEMPLATE_URL = 'https://lulutongagent.com/api/tracking-template';
var LOG_URL = 'https://lulutongagent.com/api/script-execution-logs';
var IMPORT_TOKEN = 'ABOS_GoogleAds_2026_x8K9mP2Q7';
var MAX_RETRIES = 3;
var SCRIPT_TYPE = 'mcc_tracking';
var LAST_FETCH_LOGGED = false;

function main() {
  var accountIterator = AdsManagerApp.accounts()
    .withCondition('CanManageClients = FALSE')
    .get();

  while (accountIterator.hasNext()) {
    var account = accountIterator.next();

    try {
      AdsManagerApp.select(account);
      updateCurrentAccountCampaigns();
    } catch (e) {
      Logger.log('Account failed: ' + account.getName() + ' | ' + e);
    }
  }
}

function updateCurrentAccountCampaigns() {
  var account = AdsApp.currentAccount();
  var accountName = account.getName();
  var campaignIterator = AdsApp.campaigns()
    .withCondition('Status = ENABLED')
    .get();

  while (campaignIterator.hasNext()) {
    var campaign = campaignIterator.next();
    var campaignName = campaign.getName();

    try {
      LAST_FETCH_LOGGED = false;
      var template = fetchTrackingTemplate(accountName, campaignName);

      if (template) {
        try {
          campaign.urls().setTrackingTemplate(template);
          Logger.log('Applied: ' + accountName + ' | ' + campaignName);
          postExecutionLog(accountName, campaignName, 'apply_template', 'success', template, 'Applied', null);
        } catch (applyError) {
          Logger.log('Apply failed: ' + accountName + ' | ' + campaignName + ' | ' + applyError);
          postExecutionLog(accountName, campaignName, 'apply_template', 'failed', template, null, String(applyError));
        }
      } else if (!LAST_FETCH_LOGGED) {
        Logger.log('Skipped empty template: ' + accountName + ' | ' + campaignName);
        postExecutionLog(accountName, campaignName, 'fetch_template', 'skipped', null, 'Empty template', null);
      }
    } catch (e) {
      Logger.log('Campaign failed: ' + accountName + ' | ' + campaignName + ' | ' + e);
      postExecutionLog(accountName, campaignName, 'fetch_template', 'failed', null, null, String(e));
    }
  }
}

function fetchTrackingTemplate(accountName, campaignName) {
  var url = TRACKING_TEMPLATE_URL
    + '?google_account_name=' + encodeURIComponent(accountName)
    + '&campaign_name=' + encodeURIComponent(campaignName);

  var response = fetchWithRetries(url);
  var status = response.getResponseCode();
  var body = response.getContentText();

  if (status === 404) {
    Logger.log('No template found: ' + accountName + ' | ' + campaignName);
    postExecutionLog(accountName, campaignName, 'fetch_template', 'skipped', null, 'No template found', null);
    LAST_FETCH_LOGGED = true;
    return '';
  }

  if (status < 200 || status >= 300) {
    Logger.log('Fetch failed: HTTP ' + status + ' | ' + body);
    postExecutionLog(accountName, campaignName, 'fetch_template', 'failed', null, null, 'HTTP ' + status + ' | ' + body);
    LAST_FETCH_LOGGED = true;
    return '';
  }

  return String(body || '').trim();
}

function postExecutionLog(accountName, campaignName, action, status, template, message, errorMessage) {
  try {
    UrlFetchApp.fetch(LOG_URL, {
      method: 'post',
      contentType: 'application/json',
      headers: {
        'X-Import-Token': IMPORT_TOKEN
      },
      payload: JSON.stringify({
        google_account_name: accountName,
        campaign_name: campaignName,
        script_type: SCRIPT_TYPE,
        action: action,
        status: status,
        tracking_template: template,
        message: message,
        error_message: errorMessage,
        executed_at: Utilities.formatDate(new Date(), AdsApp.currentAccount().getTimeZone(), 'yyyy-MM-dd HH:mm:ss')
      }),
      muteHttpExceptions: true
    });
  } catch (e) {
    Logger.log('Log post failed: ' + e);
  }
}

function fetchWithRetries(url) {
  var lastResponse = null;

  for (var i = 0; i < MAX_RETRIES; i++) {
    try {
      lastResponse = UrlFetchApp.fetch(url, {
        method: 'get',
        headers: {
          'X-Import-Token': IMPORT_TOKEN
        },
        muteHttpExceptions: true
      });

      if (lastResponse.getResponseCode() < 500) {
        return lastResponse;
      }
    } catch (e) {
      Logger.log('Fetch attempt failed: ' + (i + 1) + ' | ' + e);
    }

    Utilities.sleep(1000 * (i + 1));
  }

  return lastResponse;
}

单账号换链脚本

复制到 Google Ads Scripts 使用。
/**
 * ABOS single-account Tracking Template updater.
 *
 * Copy this script into Google Ads Scripts at account level.
 */

var TRACKING_TEMPLATE_URL = 'https://lulutongagent.com/api/tracking-template';
var LOG_URL = 'https://lulutongagent.com/api/script-execution-logs';
var IMPORT_TOKEN = 'ABOS_GoogleAds_2026_x8K9mP2Q7';
var MAX_RETRIES = 3;
var SCRIPT_TYPE = 'single_tracking';
var LAST_FETCH_LOGGED = false;

function main() {
  var account = AdsApp.currentAccount();
  var accountName = account.getName();
  var campaignIterator = AdsApp.campaigns()
    .withCondition('Status = ENABLED')
    .get();

  while (campaignIterator.hasNext()) {
    var campaign = campaignIterator.next();
    var campaignName = campaign.getName();

    try {
      LAST_FETCH_LOGGED = false;
      var template = fetchTrackingTemplate(accountName, campaignName);

      if (template) {
        try {
          campaign.urls().setTrackingTemplate(template);
          Logger.log('Applied: ' + accountName + ' | ' + campaignName);
          postExecutionLog(accountName, campaignName, 'apply_template', 'success', template, 'Applied', null);
        } catch (applyError) {
          Logger.log('Apply failed: ' + accountName + ' | ' + campaignName + ' | ' + applyError);
          postExecutionLog(accountName, campaignName, 'apply_template', 'failed', template, null, String(applyError));
        }
      } else if (!LAST_FETCH_LOGGED) {
        Logger.log('Skipped empty template: ' + accountName + ' | ' + campaignName);
        postExecutionLog(accountName, campaignName, 'fetch_template', 'skipped', null, 'Empty template', null);
      }
    } catch (e) {
      Logger.log('Campaign failed: ' + accountName + ' | ' + campaignName + ' | ' + e);
      postExecutionLog(accountName, campaignName, 'fetch_template', 'failed', null, null, String(e));
    }
  }
}

function fetchTrackingTemplate(accountName, campaignName) {
  var url = TRACKING_TEMPLATE_URL
    + '?google_account_name=' + encodeURIComponent(accountName)
    + '&campaign_name=' + encodeURIComponent(campaignName);

  var response = fetchWithRetries(url);
  var status = response.getResponseCode();
  var body = response.getContentText();

  if (status === 404) {
    Logger.log('No template found: ' + accountName + ' | ' + campaignName);
    postExecutionLog(accountName, campaignName, 'fetch_template', 'skipped', null, 'No template found', null);
    LAST_FETCH_LOGGED = true;
    return '';
  }

  if (status < 200 || status >= 300) {
    Logger.log('Fetch failed: HTTP ' + status + ' | ' + body);
    postExecutionLog(accountName, campaignName, 'fetch_template', 'failed', null, null, 'HTTP ' + status + ' | ' + body);
    LAST_FETCH_LOGGED = true;
    return '';
  }

  return String(body || '').trim();
}

function postExecutionLog(accountName, campaignName, action, status, template, message, errorMessage) {
  try {
    UrlFetchApp.fetch(LOG_URL, {
      method: 'post',
      contentType: 'application/json',
      headers: {
        'X-Import-Token': IMPORT_TOKEN
      },
      payload: JSON.stringify({
        google_account_name: accountName,
        campaign_name: campaignName,
        script_type: SCRIPT_TYPE,
        action: action,
        status: status,
        tracking_template: template,
        message: message,
        error_message: errorMessage,
        executed_at: Utilities.formatDate(new Date(), AdsApp.currentAccount().getTimeZone(), 'yyyy-MM-dd HH:mm:ss')
      }),
      muteHttpExceptions: true
    });
  } catch (e) {
    Logger.log('Log post failed: ' + e);
  }
}

function fetchWithRetries(url) {
  var lastResponse = null;

  for (var i = 0; i < MAX_RETRIES; i++) {
    try {
      lastResponse = UrlFetchApp.fetch(url, {
        method: 'get',
        headers: {
          'X-Import-Token': IMPORT_TOKEN
        },
        muteHttpExceptions: true
      });

      if (lastResponse.getResponseCode() < 500) {
        return lastResponse;
      }
    } catch (e) {
      Logger.log('Fetch attempt failed: ' + (i + 1) + ' | ' + e);
    }

    Utilities.sleep(1000 * (i + 1));
  }

  return lastResponse;
}

成本导入脚本

复制到 Google Ads Scripts 使用。
/**
 * Google Ads Scripts -> ABOS Ad Cost import.
 *
 * Copy this script into Google Ads Scripts.
 */

var IMPORT_URL = 'https://lulutongagent.com/api/google-ads/costs/import';
var IMPORT_TOKEN = 'ABOS_GoogleAds_2026_x8K9mP2Q7';

var DEFAULT_PROJECT_ID = null;
var DEFAULT_WEBSITE_ID = null;
var DEFAULT_MERCHANT_ID = null;
var DEFAULT_FUND_POOL_ID = null;
var DEFAULT_CURRENCY = 'USD';

function main() {
  if (typeof AdsManagerApp !== 'undefined') {
    importForMcc();
    return;
  }

  importForCurrentAccount();
}

function importForMcc() {
  var accountIterator = AdsManagerApp.accounts().get();

  while (accountIterator.hasNext()) {
    var account = accountIterator.next();
    AdsManagerApp.select(account);
    importForCurrentAccount();
  }
}

function importForCurrentAccount() {
  var account = AdsApp.currentAccount();
  var accountName = account.getName();
  var currency = account.getCurrencyCode() || DEFAULT_CURRENCY;
  var date = yesterday();
  var query =
    'SELECT campaign.name, metrics.cost_micros ' +
    'FROM campaign ' +
    'WHERE segments.date = "' + date.iso + '" ' +
    'AND campaign.status != "REMOVED"';
  var rows = AdsApp.search(query);

  while (rows.hasNext()) {
    var row = rows.next();
    var cost = microsToMoney(row.metrics.costMicros);

    if (cost <= 0) {
      continue;
    }

    postCost({
      date: date.iso,
      google_account_name: accountName,
      campaign_name: row.campaign.name,
      cost: cost,
      currency: currency,
      project_id: DEFAULT_PROJECT_ID,
      website_id: DEFAULT_WEBSITE_ID,
      merchant_id: DEFAULT_MERCHANT_ID,
      fund_pool_id: DEFAULT_FUND_POOL_ID,
      notes: 'Imported from Google Ads Script'
    });
  }
}

function postCost(payload) {
  var response = UrlFetchApp.fetch(IMPORT_URL, {
    method: 'post',
    contentType: 'application/json',
    headers: {
      'X-Import-Token': IMPORT_TOKEN
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  });

  var status = response.getResponseCode();
  var body = response.getContentText();

  if (status < 200 || status >= 300) {
    Logger.log('Import failed: HTTP ' + status + ' ' + body);
    return;
  }

  Logger.log('Imported: ' + body);
}

function yesterday() {
  var timeZone = AdsApp.currentAccount().getTimeZone();
  var date = new Date();
  date.setDate(date.getDate() - 1);

  return {
    iso: Utilities.formatDate(date, timeZone, 'yyyy-MM-dd'),
    compact: Utilities.formatDate(date, timeZone, 'yyyyMMdd')
  };
}

function microsToMoney(value) {
  if (value === null || value === undefined) {
    return 0;
  }

  return (parseFloat(String(value).replace(/,/g, '')) || 0) / 1000000;
}