Google Adword 脚本中止并出现以下错误:传递给回调函数的返回值必须是字符串

Posted

技术标签:

【中文标题】Google Adword 脚本中止并出现以下错误:传递给回调函数的返回值必须是字符串【英文标题】:Google Adword script aborting with following error:The return value passed to the callback function must be a string 【发布时间】:2018-10-04 16:11:10 【问题描述】:

需要有关 google adwords 脚本的帮助。

我的脚本在启动后一秒内中止。脚本确实间歇性地运行。

错误信息说:

2018 年 10 月 4 日上午 10:53:08 中止脚本 10/4/2018 10:53:09 AM 传递给回调函数的返回值必须是字符串。

这是整个脚本:该脚本使用来自 google 的未获批准的关键字更新电子表格。

    /**
* This report Finds broken URLs

*/


//Standard Global Variables
var details = "";
var fobGroupNum = 0;
var runStage = 0;
var fobGroup = ;
var cur_sheet = "";
var fobGroupName = "";
var ss = "";
var accountIds = [];
var reportDescription = '';

//FOB Global Vars
var fobGroups = [
  fobList: ['Apparel'],
  sheets: 
    DisapprovedKeywords: 'https://docs.google.com/spreadsheets/d/abc1',
    NoAdGroups: 'https://docs.google.com/spreadsheets/d/abc2',
    BrokenURLs: 'https://docs.google.com/spreadsheets/d/abc3',
    DisapprovedAds: 'https://docs.google.com/spreadsheets/d/abc4',
    History: 'https://docs.google.com/spreadsheets/d/abc5'
  
, 
  fobList: ['Center Core', 'Hispanic'],
  sheets: 
    DisapprovedKeywords: 'https://docs.google.com/spreadsheets/d/xyz1',
    NoAdGroups: 'https://docs.google.com/spreadsheets/d/xyz2',
    BrokenURLs: 'https://docs.google.com/spreadsheets/d/xyz3',
    DisapprovedAds: 'https://docs.google.com/spreadsheets/d/xyz4',
    History: 'https://docs.google.com/spreadsheets/d/xyz5'
  
, 
  fobList: ['Home'],
  sheets: 
    DisapprovedKeywords: 'https://docs.google.com/spreadsheets/d/def1',
    NoAdGroups: 'https://docs.google.com/spreadsheets/d/def2',
    BrokenURLs: 'https://docs.google.com/spreadsheets/d/def3',
    DisapprovedAds: 'https://docs.google.com/spreadsheets/d/def4',
    History: 'https://docs.google.com/spreadsheets/d/def5'
  
, 
  fobList: ['TM/Seasonal'],
  sheets: 
    DisapprovedKeywords: 'https://docs.google.com/spreadsheets/d/mno1',
    NoAdGroups: 'https://docs.google.com/spreadsheets/d/mno2',
    BrokenURLs: 'https://docs.google.com/spreadsheets/d/mno3',
    DisapprovedAds: 'https://docs.google.com/spreadsheets/d/mno4',
    History: 'https://docs.google.com/spreadsheets/d/mno5'
  
];
var reportDescriptions = 
  DisapprovedKeywords: 'Disapproved Keywords',
  NoAdGroups: 'Ad Groups with No Active Ads',
  BrokenURLs: 'Keywords with Invalid URLs',
  DisapprovedAds: 'Disapproved Ads'


/**
* The function that starts the process
*/
function main()  
  //Initialize Variables
  var canRun = init(true);
  if(!canRun)
    Logger.log("Aborted the script");
    return false;
  

  //Clear all sheets in spreadsheet
  clearSheets(ss);

  //Get the accounts from accountIds
  var accountSelector = MccApp.accounts().withIds(accountIds);

  // Process the account in parallel.
  accountSelector.executeInParallel('processAccount', 'allFinished', 1);


/**
* Post-process the results from processAccount. This method will be called
* once all the accounts have been processed by the executeInParallel method
* call.
*/
function allFinished(results) 
  init();
  var numErrors = 0;
  var sheets = ss.getSheets();
  for(var n in sheets)
    var rows = sheets[n].getLastRow()-1;
    if(rows>0)
      numErrors+=rows;
    
  

  //log if there were issues?
  if(numErrors>0)
    Logger.log("%s had %s %s", fobGroupName, numErrors, reportDescription);
    //Consolidate all data to a single sheet
    consolidateSheets(ss, "All Results");
    //Compile the list of Managers and emails to send report to
    var fobs = getFobs();
    var managers = [];
    var emails = [];

    for( hob in fobGroup.fobList )
      managers = managers.concat(fobs[fobGroup.fobList[hob]].getManagers());
      emails = emails.concat(fobs[fobGroup.fobList[hob]].getContacts());
    
    //Normalize the list to make sure there are no repeat emails sent
    var contacts = ;
    for(var n in managers)
      contacts[emails[n]]=managers[n];
    
    if(debug)
      //overwrite contacts with contacts on the debug list if we're debuggin'
      contacts = "abc@abc.com":"abc", 
                    "xyz@xyz.com":"xyz"  
 else 
;
    

    //Compile the message subject and body
    var subject = (Number(numErrors)-1)+" "+reportDescription+" found in "+fobGroup.fobList.join(' & ')+" Today.";
    var body = "There are "+subject+"\n The detailed report can be accessed at the following URL: \n "+fobGroup.sheets[reportName];

    //Send message to each contact individually (not sure how to do it all at once)
    for(var e in contacts)
      MailApp.sendEmail(e, subject, "Hi "+contacts[e].split(" ")[0]+", \n"+body);
      Logger.log("Sending email to %s at %s", contacts[e], e);
    

   else 
    Logger.log("%s had no %s", fobGroupName, reportDescription);
  
  details.setErrors(numErrors);
  details.setStatus("Complete");
  Logger.log("The script is done");


//A safe way to select or add a sheet (tab) to a spreadsheet
function getSheetByAccount(ss, accountName)
 try 
   ss.insertSheet(accountName);
   Logger.log("Sheet %s was created", accountName);
  catch (e) 
   Logger.log("Sheet %s exists, Selecting it %s", accountName, e);

 
  return ss.getSheetByName(accountName);


//Add sheet headers to sheet
function addSheetHeaders(sheet)
  // Append header rows based on the selected column from the report including Account Name.
  for(var n in sheetHeaders)
    sheet.getRange(1, Number(n)+1).setValue(sheetHeaders[n]);
  
  //Set the formatting of the headers
  var heads = sheet.getRange(1, 1, 1, sheetHeaders.length);
  heads.setBorder(false, true, true, true, true, null).setFontWeight('bold');
  sheet.setFrozenRows(1);


//Consolidate all sheets into one and delete the individual results
function consolidateSheets(ss, sheetName)
  var sName = sheetName || "Default";
  var resSheet = getSheetByAccount(ss, sName);
  resSheet.clear();
  addSheetHeaders(resSheet);
  var resRow = 2;
  var cols = resSheet.getMaxColumns();
  var sheets = ss.getSheets();
  for(var n in sheets)
    //Skip the Results sheet
    if(sheets[n].getName()!=resSheet.getName())
      //Get number of rows in sheet
      Logger.log("Getting rows for %s Sheet.", sheets[n].getName());
      var rows = 0;
      try 
        rows = sheets[n].getLastRow()-1;
       catch (e)
        Logger.log("Oops, can't get rows in sheet %s : %s", sheets[n].getName(), e);
      
      if(rows>0)
        //Get the range to copy start at second row to not copy header
        var fromRange = sheets[n].getRange(2, 1, rows, cols);

        //Copy data to consolidated sheet
        fromRange.copyValuesToRange(resSheet, 1, cols, resRow, rows+resRow);

        //Add better headers to the account's sheet
        try 
          addSheetHeaders(sheets[n]);
         catch(e)
          Logger.log("Oops, can't set headers for sheet %s : %s", sheets[n].getName(), e);
        

        //Increment the starting row of consolidated sheet
        resRow+=rows;
       else 
        //Delete the sheet with no data
        try 
          ss.deleteSheet(sheets[n]);
         catch(e) 
          Logger.log("Can't delete sheet %s : %s", sheets[n].getName(), e);
        
      
    
  


//Delete all of the sheets except for one (can't delete all of them)
function clearSheets(ss, sName)
  var sheetName = sName || "Default";
  //See if there's a default sheet
  getSheetByAccount(ss, sheetName);
  //Clear all sheets in spreadsheet
  var sheets = ss.getSheets();
  for(var n in sheets)
    if(sheets[n].getName() == sheetName)
      sheets[n].clear();
     else 
      ss.deleteSheet(sheets[n]);
    
  
  Logger.log("Removed %s sheets from spreadsheet", sheets.length);


//Don't change this stuff


function getGroupNum()
  var details = runDetails();
  return details.getFOB();


/* Run Details Manager
* This portion takes care of the FOB group selection 
* so this report can be run multiple times to handle
* all of the FOBs with a single script as a workaround
* for the executeInParallel limitation
*/
function runDetails()
  this.dateRow = 2;
  this.fobRow = 3;
  this.statusRow = 4;
  this.stageRow = 5;
  this.errorsRow = 5;
  this.reportField = 0;
  this.group = 0;
  this.run_sheet_url = 'https://docs.google.com/spreadsheets/d/stu';
  this.run_ss = SpreadsheetApp.openByUrl(this.run_sheet_url);
  this.run_sheet = this.run_ss.getActiveSheet();
  this.runDate = "";
  this.runFOB = "";
  this.runStatus = "";
  this.runStage = "";
  this.getDetails = function()
    var runData = this.run_ss.getDataRange().getValues();
    //get the mapping of date field and report field
    for(var n in runData[0])
      if(runData[0][n]==reportName)
        //Logger.log("We found the report name in %s", n);
        this.reportField = n;
        break;
      
    
    //Logger.log("runData = %s", runData);
    var lastDate = runData[dateRow-1][reportField];
    //var lastDate = runData[1][1];
    var numRuns = runData[fobRow-1][reportField];
    this.runDate = lastDate;
    this.runFOB = numRuns;
    this.runStatus = runData[statusRow-1][reportField];
    this.runStage = runData[stageRow-1][reportField];
    return 
      'date':this.runDate,
      'FOB':this.runFOB,
      'status':this.runStatus,
      'stage':this.runStage
    ;
  
  this.getFOB = function()
    var runData = this.getDetails();
    //Converting report field to sheet-compatible (starts at 1 not 0);
    var thiscol = Number(this.reportField)+1;
    var runtimes = 0;

    var today = getDateString();
    if(runData.date == today)
      if(runData.status=="Running")
        return runData.FOB;
       else 
        Logger.log("FOB report done for %s, attempting to run it for %s", runData.FOB, Number(runData.FOB)+1);
        this.setStatus("Running");
        //this.setStage(1);
        this.setFOB(Number(runData.FOB)+1);
        return this.runFOB;
      
     else 
      //wasn't run today yet
      this.setStatus("Running");
      //this.setStage(1);
      this.setDate(today);
      this.setFOB(0);
      return 0;
    
  ;
  this.setStatus = function(status)
    var thiscol = Number(this.reportField)+1;
    this.runStatus = status;
    Logger.log("Attempting to set status to %s in row %s column %s", status, this.statusRow, thiscol);
    this.run_sheet.getRange(this.statusRow, thiscol).setValue(status);
  ;
  this.setFOB = function(num)
    var thiscol = Number(this.reportField)+1;
    this.runFOB = num;
    this.run_sheet.getRange(this.fobRow, thiscol).setValue(num);
  ;
  this.setDate = function(day)
    var thiscol = Number(this.reportField)+1;
    this.runDate = day;
    this.run_sheet.getRange(this.dateRow, thiscol).setValue(day);
  ;
  this.setStage = function(stage)
    var thiscol = Number(this.reportField)+1;
    this.runStage = stage;
    Logger.log("Setting to stage %s", stage);
    this.run_sheet.getRange(this.stageRow, thiscol).setValue(stage);
  ;
  this.setErrors = function(errors)
    var thiscol = Number(this.reportField)+1;
    var thisrow = this.errorsRow + this.runFOB;
    this.run_sheet.getRange(thisrow, thiscol).setValue(errors);
  
  this.getDetails();
  return this;


/**
* Initialize the process by getting FOBs, sheets and account IDs
* this function doesn't have much value anymore since accessing the sheet
* variable from the global scope was quite problematic
*/
function init(begin)
  details = runDetails();
  fobGroupNum = details.getFOB();
  fobGroup = fobGroups[fobGroupNum];
  reportDescription = reportDescriptions[reportName];

  //Fails if there's no FOB group so it won't keep running and sending erroneous emails
  if(!fobGroup)
    return false;
  
  ss = SpreadsheetApp.openByUrl(fobGroup.sheets[reportName]);

  fobGroupName = fobGroup.fobList.join(', ');

  if(begin)
    //do this only for the first run to get account IDs for accountSelector
    var fobs = getFobs();
    // Select the accounts to be processed. You can process up to 50 accounts.
    // Get the account IDs in this fob group
    for( hob in fobGroup.fobList )
      accountIds = accountIds.concat(fobs[fobGroup.fobList[hob]].getAccountIds());
    
    Logger.log("Opening %s %s FOB Accounts", accountIds.length, fobGroupName);
  
  return true;



/**
* Get the FOBs
*/
function AccountListMapper(head)
  that = ;
  that.accountName = head.indexOf("Account");
  that.accountId = head.indexOf("Customer ID");
  that.fob = head.indexOf("FOB");
  that.hob = head.indexOf("HOB");
  that.manager = head.indexOf("FOB Manager");
  that.contact = head.indexOf("Contact E-mail");
  return that;

function Fob(options, mapper)
  this.name = options[mapper.hob];
  this.accounts = [];
  this.accountIds = [];
  this.managers = options[mapper.manager].replace(", ", ",").split(",");
  this.contacts = options[mapper.contact].replace(", ", ",").split(",");
 // this.mapper = mapper;
  this.getName = function()
    return this.name;
  ;
  this.getManagers = function()
    return this.managers;
  
  this.getContacts = function()
    return this.contacts;
  
  this.addAccount = function(data)
    //make sure the account fob matches
    if(this.getName() == data[mapper.hob])
      var account = 
        name: data[mapper.accountName],
        id: data[mapper.accountId],
        fob: data[mapper.fob],
        hob: data[mapper.hob]
      ;
      this.accounts.push(account);
      this.accountIds.push(account.id);
      //Logger.log("Adding account %s %s to %s FOB", data[mapper.accountName], data[mapper.accountId], data[mapper.hob]);
      return true;
     else 
      //Logger.log("%s is not %s", this.getName(), data[mapper.hob]);
    
    return false;
  
  this.getAccountIds = function()
    return this.accountIds;
  
  this.getAccounts = function()
    return this.accounts;
  
  this.getAccount = function(id)
    return this.accounts[this.accountIds.indexOf(id)];
  

/**
 * Left pad numbers to normalize dates
 */ 
function padLeft(nr, n, str)
  if(String(nr).length >= n)
    return nr;
   else 
     return Array(n-String(nr).length+1).join(str||'0')+nr;
  

/**
 * Normalize Any Date String
 */
var normalizeDateString = function(dateString)
  outDate = "";
  if(dateString.length < 12)
    d = new Date();
    return(dateString + "T" + padLeft(d.getHours(), 2) + ":" +
      padLeft(d.getMinutes(), 2) + ":" + padLeft(d.getSeconds(), 2)+"-"+
      padLeft(d.getTimezoneOffset()/60, 2)+":"+
      padLeft(d.getTimezoneOffset()%60, 2));
   else 
    return(dateString);
  

function getDateString()
  return getTodayDateString()+" What!?";


function getTodayDateString()
  var d = new Date();
  return d.getFullYear() + "-" + padLeft(d.getMonth()+1, 2) + "-" + padLeft(d.getDate(), 2);



function getFobs()
 var sheet_URL = 'https://docs.google.com/spreadsheets/d/stu'; 
  //var fob_col = "D";
  var ss = SpreadsheetApp.openByUrl(sheet_URL);
  //var rows = ss.getDataRange().getNumRows(); //number of rows in sheet
  var fobData = ss.getDataRange().getValues();
  var fobs = new Object();
  var mapper = AccountListMapper(fobData[0]);
  for(i=1; i<fobData.length; i++)
    if(!fobs[fobData[i][mapper.hob]])
      fobs[fobData[i][mapper.hob]]=new Fob(fobData[i], mapper);
    
    fobs[fobData[i][mapper.hob]].addAccount(fobData[i]);
    //Logger.log("Added %s to %s FOB", fobData[i][mapper.accountName], fobData[i][mapper.hob]);
  
  return fobs;

【问题讨论】:

您好@user10354614,欢迎来到 ***。您是否可以创建一个 plunkr 或类似的东西来重现该问题?您可以编辑您的问题并将 URL 粘贴到 plunkr。这将使社区更容易提供反馈 究竟是什么函数给你这个错误?是executeInParallel吗,可以给个函数原型吗? @JSmith..添加了整个脚本 @user10354614 ***.com/help/mcve 您不应发布完整的复杂脚本。 【参考方案1】:

executeInParallel(functionName, optionalCallbackFunctionName, optionalInput) 仅接受 string 格式的 optionalInput 参数。

可选。可以指定的字符串,将传递给每个帐户正在执行的函数。

您的代码提供的是整数而不是字符串:

accountSelector.executeInParallel('processAccount', 'allFinished', 1);

【讨论】:

以上是关于Google Adword 脚本中止并出现以下错误:传递给回调函数的返回值必须是字符串的主要内容,如果未能解决你的问题,请参考以下文章

Google 应用脚本中的 Adword 脚本

Google Adword 和 asp.net 母版页

Google Adword 不工作 /conversion.js 未找到

AdWord 脚本导出到 BigQuery“空响应”

如何仅从 google adword api 整合地理位置?

Google Adword URL 减慢网站