Create complex automation sequences with conditional logic, multi-step processes, and intelligent decision trees for advanced campaign management.
// Advanced workflow: Intelligent campaign lifecycle automation class CampaignLifecycleWorkflow { constructor() { this.workflowState = this.loadWorkflowState(); this.decisionTree = this.buildDecisionTree(); } execute() { Logger.log("=== Smart Campaign Lifecycle Workflow ==="); const campaigns = this.getCampaignsForEvaluation(); campaigns.forEach(campaign => { const context = this.buildCampaignContext(campaign); const decision = this.evaluateDecisionTree(context); Logger.log(`${campaign.getName()}: Stage=${context.stage}, Decision=${decision.action}`); this.executeAction(campaign, decision, context); this.updateWorkflowState(campaign, decision); }); this.saveWorkflowState(); this.generateWorkflowReport(); } buildCampaignContext(campaign) { const stats30 = campaign.getStatsFor('LAST_30_DAYS'); const stats7 = campaign.getStatsFor('LAST_7_DAYS'); const createdDate = new Date(campaign.getCreatedDate()); const campaignAge = (Date.now() - createdDate.getTime()) / (1000 * 60 * 60 * 24); return { campaign: campaign, age: campaignAge, stage: this.determineCampaignStage(campaignAge, stats30), performance: { roas30: stats30.getConversionValue() / Math.max(stats30.getCost(), 1), roas7: stats7.getConversionValue() / Math.max(stats7.getCost(), 1), clicks30: stats30.getClicks(), conversions30: stats30.getConversions(), ctr: stats30.getCtr(), avgCpc: stats30.getAverageCpc() }, budget: { current: campaign.getBudget(), utilization: stats7.getCost() / (campaign.getBudget() * 7), recommended: this.calculateOptimalBudget(stats30) }, trends: this.analyzeTrends(campaign), competitivePosition: this.getCompetitiveData(campaign) }; } buildDecisionTree() { return { // Learning Phase (0-14 days) learning: { conditions: [ { if: ctx => ctx.age <= 14, then: this.learningPhaseDecisions() } ] }, // Growth Phase (15-45 days) growth: { conditions: [ { if: ctx => ctx.age > 14 && ctx.age <= 45, then: this.growthPhaseDecisions() } ] }, // Maturity Phase (45+ days) maturity: { conditions: [ { if: ctx => ctx.age > 45, then: this.maturityPhaseDecisions() } ] } }; } learningPhaseDecisions() { return [ { if: ctx => ctx.performance.clicks30 < 100, action: 'increase_budget', params: { multiplier: 1.5, reason: 'Insufficient data volume' } }, { if: ctx => ctx.performance.ctr < 0.02, action: 'optimize_ads', params: { focus: 'creative_testing', reason: 'Low CTR indicates poor ad relevance' } }, { if: ctx => ctx.performance.clicks30 >= 100 && ctx.performance.conversions30 === 0, action: 'review_targeting', params: { focus: 'audience_refinement', reason: 'Clicks without conversions' } }, { if: ctx => ctx.performance.roas30 > 3.0 && ctx.performance.conversions30 >= 5, action: 'graduate_to_growth', params: { reason: 'Strong early performance warrants scaling' } } ]; } growthPhaseDecisions() { return [ { if: ctx => ctx.performance.roas30 > 4.0 && ctx.budget.utilization > 0.8, action: 'aggressive_scale', params: { budgetMultiplier: 2.0, bidIncrease: 0.2, reason: 'High ROAS with full budget utilization' } }, { if: ctx => ctx.performance.roas7 < ctx.performance.roas30 * 0.8, action: 'performance_decline_response', params: { investigate: ['seasonality', 'competition', 'audience_saturation'], reason: 'Recent performance decline detected' } }, { if: ctx => ctx.trends.declining && ctx.competitivePosition.pressure > 0.7, action: 'competitive_response', params: { strategy: 'defensive_bidding', reason: 'High competitive pressure with declining trends' } } ]; } maturityPhaseDecisions() { return [ { if: ctx => ctx.performance.roas30 < 2.0, action: 'optimization_or_pause', params: { threshold: 1.5, gracePeriod: 7, reason: 'Consistently poor ROAS in mature campaign' } }, { if: ctx => ctx.trends.stable && ctx.performance.roas30 > 3.0, action: 'maintain_and_optimize', params: { focus: 'efficiency_improvements', reason: 'Stable, profitable mature campaign' } }, { if: ctx => ctx.trends.growth_opportunity, action: 'strategic_expansion', params: { areas: ['new_keywords', 'audience_expansion', 'geo_expansion'], reason: 'Growth opportunity identified in mature campaign' } } ]; } executeAction(campaign, decision, context) { Logger.log(`Executing: ${decision.action} for ${campaign.getName()}`); Logger.log(`Reason: ${decision.params.reason}`); switch (decision.action) { case 'increase_budget': this.increaseBudget(campaign, decision.params.multiplier); break; case 'aggressive_scale': this.aggressiveScale(campaign, decision.params); break; case 'optimize_ads': this.optimizeAds(campaign, decision.params); break; case 'competitive_response': this.competitiveResponse(campaign, decision.params); break; case 'optimization_or_pause': this.optimizationOrPause(campaign, decision.params, context); break; case 'maintain_and_optimize': this.maintainAndOptimize(campaign, decision.params); break; default: Logger.log(`Action not implemented: ${decision.action}`); } // Schedule follow-up evaluation this.scheduleFollowUp(campaign, decision); } increaseBudget(campaign, multiplier) { const currentBudget = campaign.getBudget(); const newBudget = Math.round(currentBudget * multiplier); const maxBudget = 5000; // Safety limit const finalBudget = Math.min(newBudget, maxBudget); campaign.setBudget(finalBudget); Logger.log(`Budget increased: €${currentBudget} → €${finalBudget}`); } aggressiveScale(campaign, params) { // Budget scaling this.increaseBudget(campaign, params.budgetMultiplier); // Bid increases for top keywords const keywords = campaign.keywords() .withCondition('Status = ENABLED') .orderBy('ConversionValue DESC') .withLimit(20) .get(); let keywordsOptimized = 0; while (keywords.hasNext()) { const keyword = keywords.next(); const currentBid = keyword.getMaxCpc(); const newBid = currentBid * (1 + params.bidIncrease); keyword.setMaxCpc(newBid); keywordsOptimized++; } Logger.log(`Aggressive scaling: ${keywordsOptimized} keywords optimized`); } }
This advanced workflow demonstrates intelligent campaign lifecycle management with context-aware decision making, multi-stage optimization, and adaptive strategies.
// Advanced state management for workflow persistence class WorkflowStateManager { constructor() { this.stateKey = 'workflow_state_v2'; this.maxStateAge = 30 * 24 * 60 * 60 * 1000; // 30 days } loadWorkflowState() { try { const stateJson = PropertiesService.getScriptProperties().getProperty(this.stateKey); if (!stateJson) { return this.createInitialState(); } const state = JSON.parse(stateJson); // Validate state age and structure if (this.isStateValid(state)) { Logger.log(`Loaded workflow state with ${Object.keys(state.campaigns).length} campaigns`); return state; } else { Logger.log('Invalid or expired state, creating new state'); return this.createInitialState(); } } catch (error) { Logger.log(`Error loading state: ${error.message}`); return this.createInitialState(); } } saveWorkflowState(state) { try { // Clean old entries before saving const cleanedState = this.cleanOldEntries(state); // Add metadata cleanedState.lastUpdated = Date.now(); cleanedState.version = '2.0'; const stateJson = JSON.stringify(cleanedState); // Check size limits (PropertiesService has 9KB limit per property) if (stateJson.length > 8000) { Logger.log('State too large, compressing...'); const compressedState = this.compressState(cleanedState); PropertiesService.getScriptProperties().setProperty(this.stateKey, JSON.stringify(compressedState)); } else { PropertiesService.getScriptProperties().setProperty(this.stateKey, stateJson); } Logger.log(`Workflow state saved (${stateJson.length} chars)`); } catch (error) { Logger.log(`Error saving state: ${error.message}`); } } updateCampaignState(campaignId, updates) { const state = this.loadWorkflowState(); if (!state.campaigns[campaignId]) { state.campaigns[campaignId] = { created: Date.now(), stage: 'learning', actions: [], metrics: {} }; } // Merge updates Object.assign(state.campaigns[campaignId], updates); // Add timestamp state.campaigns[campaignId].lastUpdated = Date.now(); this.saveWorkflowState(state); } logWorkflowAction(campaignId, action, result) { const state = this.loadWorkflowState(); if (!state.campaigns[campaignId]) { this.updateCampaignState(campaignId, {}); return this.logWorkflowAction(campaignId, action, result); } const actionLog = { timestamp: Date.now(), action: action.action, params: action.params, result: result, success: result.success || false }; if (!state.campaigns[campaignId].actions) { state.campaigns[campaignId].actions = []; } state.campaigns[campaignId].actions.push(actionLog); // Keep only last 50 actions per campaign if (state.campaigns[campaignId].actions.length > 50) { state.campaigns[campaignId].actions = state.campaigns[campaignId].actions.slice(-50); } this.saveWorkflowState(state); } getWorkflowHistory(campaignId, days = 30) { const state = this.loadWorkflowState(); const campaign = state.campaigns[campaignId]; if (!campaign || !campaign.actions) { return []; } const cutoffTime = Date.now() - (days * 24 * 60 * 60 * 1000); return campaign.actions.filter(action => action.timestamp > cutoffTime); } createInitialState() { return { version: '2.0', created: Date.now(), lastUpdated: Date.now(), campaigns: {}, globalSettings: { maxCampaignAge: 90, minConversionsForOptimization: 5, roasThresholds: { excellent: 4.0, good: 3.0, acceptable: 2.0, poor: 1.5 } } }; } isStateValid(state) { if (!state || !state.version || !state.lastUpdated) { return false; } const age = Date.now() - state.lastUpdated; return age < this.maxStateAge; } compressState(state) { // Simple compression: remove old action logs and compress metrics const compressed = { ...state }; Object.keys(compressed.campaigns).forEach(campaignId => { const campaign = compressed.campaigns[campaignId]; // Keep only last 10 actions if (campaign.actions && campaign.actions.length > 10) { campaign.actions = campaign.actions.slice(-10); } // Compress metrics (keep only essential data) if (campaign.metrics) { campaign.metrics = { lastRoas: campaign.metrics.lastRoas, lastUpdate: campaign.metrics.lastUpdate }; } }); return compressed; } }