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 不工作 /conversion.js 未找到