CÓDIGO DE PRODUÇÃO & AUTOMAÇÃO

Script Auditor: Detector Automático de Erros 404 em Anúncios

Script Google Ads para varredura diária de URLs finais de anúncios e detecção imediata de páginas de destino com erro 404 por Wagner Hörlle.

💬 Suporte Técnico no WhatsApp
/**
 * SCRIPT 4: AUDITOR AUTOMÁTICO DE LINKS QUEBRADOS (404 / 500) E PÁGINAS SEM ESTOQUE
 * Pacote de Proteção de Conta Google Ads - Growth OS
 * 
 * Finalidade:
 * Testa as URLs finais de todos os anúncios ativos da conta.
 * Se uma página retornar código HTTP de erro (404 Not Found, 500 Server Error)
 * ou contiver strings críticas como "Produto Esgotado", ele pausa o anúncio
 * imediatamente e envia um e-mail de alerta, evitando queimar verba em páginas mortas.
 */

const CONFIG = {
  // Pausar o anúncio automaticamente se a URL estiver quebrada?
  PAUSE_BROKEN_ADS: true,
  
  // Criar e aplicar uma etiqueta nos anúncios pausados pelo robô
  LABEL_NAME: 'ALERTA_URL_QUEBRADA',
  
  // Expressões no texto da página que indicam ruptura de estoque
  OUT_OF_STOCK_PHRASES: [
    'produto esgotado',
    'produto indisponível',
    'fora de estoque',
    'avise-me quando chegar'
  ],
  
  EMAIL_ALERTS: 'seu-email@dominio.com.br'
};

function main() {
  Logger.log('Iniciando auditoria de integridade de URLs e estoque...');
  ensureLabelExists(CONFIG.LABEL_NAME);

  const brokenAds = [];
  const adIterator = AdsApp.ads()
    .withCondition('Status = ENABLED')
    .withCondition('CampaignStatus = ENABLED')
    .withCondition('AdGroupStatus = ENABLED')
    .get();

  while (adIterator.hasNext()) {
    const ad = adIterator.next();
    const finalUrl = ad.urls().getFinalUrl();
    if (!finalUrl) continue;

    try {
      const response = UrlFetchApp.fetch(finalUrl, {
        muteHttpExceptions: true,
        followRedirects: true
      });
      
      const statusCode = response.getResponseCode();
      const content = response.getContentText().toLowerCase();
      let isProblem = false;
      let reason = '';

      if (statusCode >= 400) {
        isProblem = true;
        reason = `Erro HTTP ${statusCode}`;
      } else {
        // Checar por ruptura de estoque
        for (let i = 0; i < CONFIG.OUT_OF_STOCK_PHRASES.length; i++) {
          if (content.indexOf(CONFIG.OUT_OF_STOCK_PHRASES[i]) !== -1) {
            isProblem = true;
            reason = `Detectado "${CONFIG.OUT_OF_STOCK_PHRASES[i]}" no HTML`;
            break;
          }
        }
      }

      if (isProblem) {
        Logger.log(`🚨 Anúncio com falha detectado: ID ${ad.getId()} | Motivo: ${reason} | URL: ${finalUrl}`);
        
        if (CONFIG.PAUSE_BROKEN_ADS) {
          ad.pause();
          ad.applyLabel(CONFIG.LABEL_NAME);
          Logger.log(`-> Anúncio ${ad.getId()} pausado e etiquetado com ${CONFIG.LABEL_NAME}`);
        }

        brokenAds.push(`Anúncio ID: ${ad.getId()} | ${reason} | URL: ${finalUrl}`);
      }

    } catch (fetchErr) {
      Logger.log(`Falha de conexão na URL ${finalUrl}: ` + fetchErr.toString());
      brokenAds.push(`Falha de conexão: ${finalUrl}`);
    }
  }

  Logger.log(`Auditoria finalizada. Total de links com falha: ${brokenAds.length}`);

  if (brokenAds.length > 0 && CONFIG.EMAIL_ALERTS) {
    MailApp.sendEmail(
      CONFIG.EMAIL_ALERTS,
      `[Alerta Google Ads] ${brokenAds.length} anúncios com links quebrados ou sem estoque!`,
      `Os seguintes anúncios foram pausados automaticamente para proteger o orçamento:\n\n` +
      brokenAds.join('\n')
    );
  }
}

function ensureLabelExists(labelName) {
  const labelIterator = AdsApp.labels().withCondition(`Name = "${labelName}"`).get();
  if (!labelIterator.hasNext()) {
    AdsApp.createLabel(labelName, 'Anúncios pausados pelo script de auditoria de URL', '#d93025');
  }
}