202406Webinarデモ-配信オンオフ

※このソースコードは MIT License のもとで提供されています。
https://ads-developers.yahoo.co.jp/ja/ads-script/post/30418913.html

スクリプト内容:スプレッドシートで指定した単語を含む名称のものを、指定開始日時に開始時配信状態にし、指定終了日時に終了配信状態にする
※検索広告のアカウント、ディスプレイ広告のアカウント両方でお使いいただけます(内部で自動判定しています)

/*
■スクリプト内容
スプレッドシートで指定した単語を含む名称のものを、指定開始日時に開始時配信状態にし、指定終了日時に終了配信状態にします。
■利用方法
1.当スクリプトを、検索広告またはディスプレイ広告のスクリプトとしてください。
2.各定数を以下のように設定してください。
■定数
・SPREAD_SHEET_ID:スプレッドシートID
・SPREAD_SHEET_NAME:スプレッドシート名
・TEST_EXECUTION:テスト実行時はtrue、本番実行時はfalse
・ENTITY_TYPE:実行するエンティティを設定。キャンペーン:CAMPAIGN、広告グループ:ADGROUP、広告:AD
・MATCH_TYPE:名称の検索方法を設定。完全一致:EXACT 部分一致:BROAD
*/
//設定が必要な定数
const SPREAD_SHEET_ID = 'スプレッドシートID';
const SPREAD_SHEET_NAME = 'シート名';
const TEST_EXECUTION = true; // テスト実行:true  本番実行:false
const ENTITY_TYPE = 'AD'; // CAMPAIGN ADGROUP AD
const MATCH_TYPE = 'BROAD'; //  完全一致:EXACT 部分一致:BROAD
//設定が不要な定数(変更すると動かなくなります)
let PRODUCT_OBJ;
let MAX_NUMBER = 10000;
const accountId = AdsUtilities.getCurrentAccountId();
function main() {
  initServiceSetting();
  const sheetData = getSheetData();
  const operand =getSetOperand(sheetData);
  if(TEST_EXECUTION){
    Logger.log('テストモードで実行します');
    switchStatusText(operand);
  } else {
    switchStatus(operand);
  }
}
function getSheetData(){
  const sh = SpreadsheetApp.openById(SPREAD_SHEET_ID).getSheetByName(SPREAD_SHEET_NAME);
  //スプレットシート情報を取得する
  let dataArray = sh.getDataRange().getValues();
  //もし2行以上の記載がなければ
  if (dataArray.length < 2) {
    throw new Error(SPREAD_SHEET_NAME + 'シートにオンオフの条件を記載してください');
  }
  const toDay = new Date(Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/d H:00'));
  let sheetData = {
    'ACTIVE': [],
    'PAUSED': [],
  };
  for (let i = 1; i < dataArray.length; i++) {
    const row = createRowData(dataArray[i], toDay, i);
    if( row != null ){
      sheetData[row.targetStatus].push(row.targetName);
      Logger.log(i + '行目 名称:' +  row.targetName + ' 配信設定:' + row.targetStatus);
    }
  }
  return sheetData;
}
function createRowData(dataRow, toDay, index){
  const targetName = dataRow[0];
  if (targetName == '') return null;
  //開始日時と設定のチェック
  const startStatus = createSepRowData(dataRow[1], dataRow[2], dataRow[3], toDay, index);
  // 終了日時と設定をチェック
  const endStatus = createSepRowData(dataRow[4], dataRow[5], dataRow[6], toDay, index);
  // 有効な設定を反映
  const targetStatus = ( endStatus != null) ? endStatus : startStatus;
  return (targetStatus != null) ? {
    targetName: targetName,
    targetStatus: targetStatus
  } : null;
}
function createSepRowData(date, time, status, toDay, index){
  if( date == '' && time == '' && status == '' ) return null;
  const targetStatus = isStatus(status, index);
  return isSameDate(toDay, date, time) ? targetStatus : null;
}
function isSameDate(toDay, date, time){
  const targetDate = new Date( date + ' ' + time );
  if(isNaN(targetDate.getDate())){
    throw new Error('日付の形式に間違いがあります。入力例 2024/04/01 12:00 実際に入力された値:' + date + ' ' + time);
  }
  return ( toDay.getFullYear() === targetDate.getFullYear() &&
            toDay.getMonth() === targetDate.getMonth() &&
            toDay.getDate() === targetDate.getDate() &&
            toDay.getHours() === targetDate.getHours() );
}
function getSetOperand(sheetData) {
  let operands = [];
  let num = 0;
  while (true) {
    const entitys = getDataFromGetService(num);
    for (let i = 0; i < entitys.values.length; i++) {
      const operand = getOperand(entitys.values[i]);
      if( chkName(getName(operand), operand.userStatus, sheetData) ){
        operands.push(operand);
      }
    }
    num++;
    if (num * 10000 >= entitys.totalNumEntries) break;
  }
  return operands;
}
function getOperand( value ){
  let entity;
  let operand = {};
  switch (ENTITY_TYPE){
    case 'AD':
      entity = value.adGroupAd;
      operand.adId = entity.adId;
      operand.adGroupId = entity.adGroupId;
      operand.adName = entity.adName;
      break;
    case 'ADGROUP':
      entity = value.adGroup;
      operand.adGroupId = entity.adGroupId;
      operand.adGroupName = entity.adGroupName;
      break;
    case 'CAMPAIGN':
      entity = value.campaign;
      operand.campaignName = entity.campaignName;
      break;
  }
  operand.campaignId = entity.campaignId;
  operand.userStatus = ( entity.userStatus == 'ACTIVE' ) ? 'PAUSED':'ACTIVE';
  return operand;
}
function chkName(name, status, sheetData){
  const targetNames = ( status == 'ACTIVE' ) ? sheetData.ACTIVE : sheetData.PAUSED ;
  switch (MATCH_TYPE){
    case 'BROAD':
      return targetNames.some(value => name.indexOf(value) > -1);
    case 'EXACT':
      return targetNames.some(value => name == value);
    default:
      throw new Error('MATCH_TYPEは、BROADまたはEXACTのいずれかを設定してください。');
  }
}
function getDataFromGetService(num){
  let request = {
    accountId: accountId,
    numberResults: MAX_NUMBER,
    startIndex: num * MAX_NUMBER + 1,
  };
  return PRODUCT_OBJ.get(request).rval;
}
function switchStatus(operand){
  if(operand.length == 0){
    Logger.log('更新対象がありませんでした');
    return;
  }
  let num = 0;
  while (true) {
    const switchRes = PRODUCT_OBJ.set({
      accountId: accountId,
      operand: operand.slice(num * 2000, Math.min((num + 1) * 2000, operand.length)),
    }).rval;
    for (let i = 0; i < switchRes.values.length; i++) {
      if (switchRes.values[i].operationSucceeded) {
        const entity = operand[num * 2000 + i];
        const statusName = (entity.userStatus == 'ACTIVE')? 'オン' : 'オフ';
        Logger.log(getName(entity) + 'を配信' + statusName + 'にしました' );
      } else {
        Logger.log(getName(entity) + 'の配信切替に失敗しました');
      }
    }
    num++;
    if (num * 2000 >= operand.length) break;
  }
}
function switchStatusText(operand){
  if(operand.length == 0){
    Logger.log('更新対象がありませんでした');
    return;
  }
  for (let i = 0; i < operand.length; i++) {
    const entity = operand[i];
    const status =  (entity.userStatus == 'ACTIVE') ? 'オン': 'オフ';
    Logger.log(getName(entity) + 'を配信' + status + 'にしました' );
  }
}
function initServiceSetting(){
  const productType = AdsUtilities.getProductType();
  if (productType == 'SEARCH') {
      PRODUCT_OBJ = getService(Search);
      Logger.log('検索広告のアカウントで実行します。対象は「' + getEntityName() + '」です');
  } else if (productType == 'DISPLAY') {
      PRODUCT_OBJ = getService(Display);
      if( ENTITY_TYPE == 'CAMPAIGN' ){
        MAX_NUMBER = 2000;
      }
      Logger.log('ディスプレイ広告のアカウントで実行します。対象は「' + getEntityName() + '」です');
  } else {
    throw new Error('検索広告またはディスプレイ広告のアカウントで実行してください。');
  }
}
function getService(ojb){
  switch (ENTITY_TYPE){
    case 'AD':
      return ojb.AdGroupAdService;
    case 'ADGROUP':
      return ojb.AdGroupService;
    case 'CAMPAIGN':
      return ojb.CampaignService;
  default:
    throw new Error('ENTITY_TYPEは、CAMPAIGN、ADGROUP、ADのいずれかを設定してください。');
  }
}
function isStatus(status, index){
  if (status == 'オン') {
    return 'ACTIVE';
  } else if (status == 'オフ') {
    return 'PAUSED';
  } else {
    throw new Error(index + '行目の配信設定に間違いがあります。 設定内容:' + status);
  }
}
function getName( operand ){
  switch (ENTITY_TYPE){
    case 'AD':
      return operand.adName;
    case 'ADGROUP':
      return operand.adGroupName;
    case 'CAMPAIGN':
      return operand.campaignName;
  }
}
function getEntityName(){
  switch (ENTITY_TYPE){
    case 'AD':
      return '広告';
    case 'ADGROUP':
      return '広告グループ';
    case 'CAMPAIGN':
      return 'キャンペーン';
  }  
}