惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 三生石上(FineUI控件)
T
Threat Research - Cisco Blogs
月光博客
月光博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
爱范儿
爱范儿
Hugging Face - Blog
Hugging Face - Blog
腾讯CDC
云风的 BLOG
云风的 BLOG
D
Docker
罗磊的独立博客
U
Unit 42
博客园 - 聂微东
人人都是产品经理
人人都是产品经理
P
Proofpoint News Feed
博客园 - Franky
Apple Machine Learning Research
Apple Machine Learning Research
MyScale Blog
MyScale Blog
B
Blog RSS Feed
美团技术团队
J
Java Code Geeks
S
Securelist
Cyberwarzone
Cyberwarzone
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
NISL@THU
NISL@THU
Security Latest
Security Latest
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Recorded Future
Recorded Future
Hacker News - Newest:
Hacker News - Newest: "LLM"
L
LINUX DO - 热门话题
Recent Announcements
Recent Announcements
Last Week in AI
Last Week in AI
A
About on SuperTechFans
MongoDB | Blog
MongoDB | Blog
Spread Privacy
Spread Privacy
T
Tenable Blog
I
Intezer
N
News | PayPal Newsroom
大猫的无限游戏
大猫的无限游戏
A
Arctic Wolf
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
V
V2EX - 技术
S
Schneier on Security
S
SegmentFault 最新的问题
Latest news
Latest news
宝玉的分享
宝玉的分享
V
Visual Studio Blog
V
V2EX
T
Tor Project blog
C
Comments on: Blog

DEV Community

pypdf vs PdfPig: Text Extraction at Scale NetworkX vs CSR + TensorPrimitives: PageRank on 28M Edges CareSync: A Local Health Memory Agent for Family Caregivers The compiler caught a lot. It didn't catch enough. Automated Client Reporting Count, Length, or Size? Avoiding ActiveRecord Performance Traps AI Cost Attribution: LLM Chargeback by Business Unit Challenges and Solutions in High-Resolution Camera Design Replit agents just got a financial identity — and Visa backed it Hermes Agent: How Nous Research Built an AI That Actually Learns from Its Own Building a Local AI Market Trader with Hermes Agent How to Handle JavaScript-Rendered Pages Without a Full Browser Why Your Scraper Works in the Browser But Fails in Python pocket-db vs lowdb vs LokiJS: an honest embedded database benchmark CLAUDE.md Security Rules: What to Add Now That Claude Code Reviews Your Code 🛡️ Building PatchPoint: Unifying DevOps Security Silos with Coral SQL Agent Substrate: The Agentic AI Isolation Layer On K8s loadComponent vs loadChildren in Angular 19: Choosing the Right Lazy-Loading Boundary Hermes Agent Challenge Build a Production RAG System on AWS Bedrock from Scratch Context-Aware Code Summarizer 25/30 Days System Design Questions! MSW vs Hosted Mock APIs: When To Use Each How to Build Long-Running AI Agents with Google Gen AI SDK The Statistical Casino Building a 100% Client-Side HEIC to JPG Converter: Zero Servers, Zero Uploads I Gave an AI Agent My Vacation. It Planned Better Than I Did. What Nobody Tells You About Running Hermes Agent Locally (M-Series Mac Edition) I taught Hermes Agent to predict which API changes will break my system StuxCTF — TryHackMe Writeup How I built a 12-section Shopify page using only AI agents (and a Cowork audit) How to Build an agent using coral LeetCode Solution: 3. Longest Substring Without Repeating Characters How WhatsApp Works Without Internet: Offline Messaging and Synchronization Explained Designing an Open-Source Toolkit for AI Agent Resources Building a Psychological Safety Framework for Engineering Teams I built a freelance client + invoice tracker in ~3 hours using Cursor — here's everything I shipped How to Parse Invoices in Python Using an API (2026 Guide) Shifting from Mobile to Web: How I Built a 3-Pane Desktop AI Interface with Expo Web & FastAPI LeetCode Solution: 17. Letter Combinations of a Phone Number It's time to get familiar with what FinOps for AI is Four themes for a terminal you read more than you syntax-highlight Building Truly Cross-Platform Claude Code Hooks with Go, Bash, PowerShell, WSL, and Git-Bash I Added a 71-Line Black Box to My Python Agent, Then Queried the $200 Crash With DuckDB LeetCode Solution: 15. 3Sum Lottie vs Framer Motion: Which Should You Use? Why Great Software Engineers Get Rejected Before a Human Reads Their Resume Ladybug x Icebug notebooks are out!! The notebooks explain how icebug-format brings graph database (ladybugDB) and high performance analytics(icebug) under one roof. https://github.com/LadybugDB/ladybug-icebug-notebooks/ The Industry Needs an Open Reasoning Spec. Seven Papers Explain What Goes In It. Getting Started with eslint-plugin-mongodb-security Modern Web Security Attacks Every Developer Must Know (2026 Guide) Clickjacking COMPUTER ARCHITECTURE: THEORIES A plugin for Observability + Budget Guardrails built with Hermes Agent Vendor Chunking: The React Optimization I Wish I'd Known Earlier ExtensionsbyBunny I built rails-persona — behavioral analytics for Rails with zero external services Designing Scalable Multi-Tenant SaaS Applications How to monitor a brand across 5 Chinese social platforms with Python in 2026 — the cross-platform dedup problem and how to handle it The Solo Developer Who Ships What Entire Teams Once Built. How I Built VoxCalc — An AI-Inspired Next-Gen Calculator with Flutter, Google ML Kit & Voice NLP How my OS is indexing by Google? From Eclipses to P95 Latency: What the Joseon Dynasty Can Teach Us About Incident Response Building AutoStack.Identity: A Zero-Dependency .NET 10 Library for SAML 2.0, JWT, and XML Signing What a Go Engineer Learns Building Their First Real Python Service System Design - 9.Database Sharding & Replication, How Facebook Serves a Billion Reads Per Second I Spent 15 Months Porting a 20-Year-Old Computational Chemistry Binary to the Cloud. Alone. I shipped the wrong abstraction, then deleted it Week 1: what it looks like when an AI agent runs an open-source project solo I didn't have a PC for my database class, so I built my own T-SQL Sandbox in the browser How to Export Google Patents to CSV (Honest Guide to Every Real Path) Most AI Forgets. Hermes Agent Learns. Building a self-hosted reverse proxy with WireGuard for my homelab behind CGNAT FlockUI is Open for Contributors — Let's Build the Flutter UI Library We Always Wanted What a Port State Control Inspection Actually Looks Like Your RL Agent Failed a 12-Step Task. Which Step Was Wrong? (The Supervision Problem in Agentic RL) Token Budgeting The Fastest Part of Your Stack Is Already Installed: Rethinking Web IDEs How a Small Product Sync Automation Changed Onboarding at Scale A .NET Dinosaur in Web3. Day 18 - Automated Market Maker Micro-Frontends, One Year On: The Workarounds That Made Single-SPA Reliable for Us From Specs to Tickets: Automating Jira Setup with Node.js and the Jira API How to Build a Power BI Financial Dashboard for Healthcare Notes on Federated Learning and Differential Privacy Notes on Serving LLMs with TensorRT-LLM and Triton JWT Explained: What's Actually Inside That Token (with a free decoder) 0% vs 50%: Making a RAG Agent Refuse to Hallucinate Where Tensor-Parallel Inference Hits the NVLink Wall Building a Comprehensive Accessibility Testing Framework for Web Applications I open-sourced a World Cup 2026 prediction model — and tested it honestly Database WAL Bloat Management: The Core Anatomy for Performance WordPress Emails Were Failing Silently on DigitalOcean. Here's What Broke. Reading Belgium's KBO/CBE registry: what the live API returns 🤫 I Built CodeMoji: A VS Code Extension That Turns Code Into Emojis 5 AI Pair Programming Patterns That Actually Speed Up Development LLD Object-Oriented Design: From Requirements to Classes (Bridging Thinking to Domain Modeling) How We Built a CTO-Grade Grafana Dashboard With Codex How We Built a CTO-Grade Grafana Dashboard With Codex T-Slot Bolts and Nuts for Secure Industrial Clamping From Tools to Economic Actors: Why AI Agents Need Independent Financial Infrastructure Designing a Scalable Event-Driven Data Processing Pipeline with Apache Kafka Streams
Google Apps Script Automation
White Oak Intelligence · 2026-06-01 · via DEV Community

White Oak Intelligence

In This Article


When Apps Script Is the Right Tool

Google Apps Script occupies a useful niche: it runs inside the Google ecosystem with no server provisioning, no deployment pipeline, and no infrastructure cost. For organizations whose operational data lives primarily in Google Sheets, Gmail, and Google Drive, it is the fastest path from "we need to automate this" to a running system.

The typical use cases where Apps Script outcompetes a Python ETL pipeline are: scheduled reporting that reads from Sheets and emails summaries, lightweight CRM sync between a Sheets-based pipeline and a downstream system, and triggered workflows that respond to form submissions or calendar events. For anything requiring complex data transformation at scale, a proper ETL pipeline is the right choice. But for operational automation where the data is already in Google Workspace, Apps Script often delivers in hours what a full pipeline takes days to deploy.

The Key Constraint

Apps Script triggers run in UTC by default. Any automation that should fire during business hours requires explicit timezone conversion — a detail that causes subtle failures in production when overlooked. Always set triggers using inTimezone('America/New_York') (or the appropriate zone) and validate with a test run at the edge of business hours.

Programmatic Trigger Management

Manually configured triggers break silently — they do not survive script republishing, and they accumulate duplicates when developers run setup functions multiple times. The production approach is a setupTriggers() function that deletes all existing triggers before creating new ones. Call it once after each deployment; it is idempotent by construction.

const CONFIG = {
  DASHBOARD_TAB: 'Dashboard',
  ARCHIVE_TAB:   'Archive',
  THRESHOLDS: {
    MIN_REVENUE:  10000,
    MAX_VARIANCE: 0.15,
  },
  EXEC_EMAILS: ['cfo@company.com', 'ceo@company.com'],
};

function setupTriggers() {
  // Delete all existing triggers to prevent duplicates
  ScriptApp.getProjectTriggers().forEach(t => ScriptApp.deleteTrigger(t));

  // Daily metric export at 7 AM Eastern
  ScriptApp.newTrigger('exportDailyMetrics')
    .timeBased()
    .atHour(7)
    .everyDays(1)
    .inTimezone('America/New_York')
    .create();

  // Weekly executive summary — Monday 6 AM Eastern
  ScriptApp.newTrigger('sendWeeklyExecutiveSummary')
    .timeBased()
    .onWeekDay(ScriptApp.WeekDay.MONDAY)
    .atHour(6)
    .inTimezone('America/New_York')
    .create();

  // CRM sync every 2 hours
  ScriptApp.newTrigger('syncCRMData')
    .timeBased()
    .everyHours(2)
    .create();
}

Enter fullscreen mode Exit fullscreen mode

Daily Metric Export

The daily export reads the current dashboard state, appends a timestamped row to the archive tab, and checks values against the defined thresholds. Threshold violations send an alert email before the function returns — so the operations team knows about out-of-range metrics by the time they open their inbox in the morning.

function exportDailyMetrics() {
  const ss        = SpreadsheetApp.getActiveSpreadsheet();
  const dashboard = ss.getSheetByName(CONFIG.DASHBOARD_TAB);
  const archive   = ss.getSheetByName(CONFIG.ARCHIVE_TAB);

  // Read current KPI snapshot from dashboard
  const revenue    = dashboard.getRange('B2').getValue();
  const variance   = dashboard.getRange('B3').getValue();
  const timestamp  = new Date();

  archive.appendRow([timestamp, revenue, variance]);

  if (revenue < CONFIG.THRESHOLDS.MIN_REVENUE) {
    MailApp.sendEmail({
      to:      CONFIG.EXEC_EMAILS.join(','),
      subject: `[Alert] Daily revenue below threshold: 

![equation](https://latex.codecogs.com/png.latex?_white&space;%7Brevenue.toFixed%280%29%7D%60%2C%0A%20%20%20%20%20%20body%3A%20%20%20%20%60Revenue%20of)

{revenue.toFixed(0)} is below the minimum threshold of 

![equation](https://latex.codecogs.com/png.latex?_white&space;%7BCONFIG.THRESHOLDS.MIN_REVENUE%7D.%60%0A%20%20%20%20%7D%29%3B%0A%20%20%7D%0A%7D%0A%60%60%60%0A%0A%23%23%20Weekly%20Executive%20Summary%0A%0AThe%20weekly%20summary%20aggregates%20the%20last%20seven%20rows%20of%20the%20archive%20tab%20and%20emails%20a%20formatted%20digest%20to%20the%20executive%20distribution%20list.%20Apps%20Script%27s%20%60MailApp.sendEmail%28%29%60%20supports%20HTML%20bodies%2C%20so%20the%20summary%20can%20include%20basic%20formatting%20%E2%80%94%20bold%20headers%2C%20colored%20variance%20indicators%20%E2%80%94%20without%20requiring%20any%20external%20email%20service.%0A%0A%60%60%60javascript%0Afunction%20sendWeeklyExecutiveSummary%28%29%20%7B%0A%20%20const%20archive%20%20%3D%20SpreadsheetApp.getActiveSpreadsheet%28%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.getSheetByName%28CONFIG.ARCHIVE_TAB%29%3B%0A%20%20const%20lastRow%20%20%3D%20archive.getLastRow%28%29%3B%0A%20%20const%20weekData%20%3D%20archive.getRange%28lastRow%20-%206%2C%201%2C%207%2C%203%29.getValues%28%29%3B%0A%0A%20%20const%20totalRevenue%20%3D%20weekData.reduce%28%28sum%2C%20row%29%20%3D%3E%20sum%20%2B%20row%5B1%5D%2C%200%29%3B%0A%20%20const%20avgVariance%20%20%3D%20weekData.reduce%28%28sum%2C%20row%29%20%3D%3E%20sum%20%2B%20row%5B2%5D%2C%200%29%20%2F%20weekData.length%3B%0A%0A%20%20const%20body%20%3D%20%60%3Ch2%3EWeekly%20Performance%20Summary%3C%2Fh2%3E%0A%20%20%20%20%3Cp%3E%3Cstrong%3ETotal%20Revenue%20%287-day%29%3A%3C%2Fstrong%3E)

{totalRevenue.toFixed(0)}</p>
    <p><strong>Average Variance:</strong> ${(avgVariance * 100).toFixed(1)}%</p>`;

  MailApp.sendEmail({
    to:      CONFIG.EXEC_EMAILS.join(','),
    subject: `Weekly Summary — Week of ${weekData[0][0].toLocaleDateString()}`,
    htmlBody: body
  });
}

Enter fullscreen mode Exit fullscreen mode

CRM Sync with Business Hours Guard

CRM sync triggers every two hours around the clock, but external CRM APIs often have rate limits, maintenance windows, or simply should not be hit during off-hours when the data they supply is stale. A business hours guard — checking that the current time in the business's timezone falls within weekday working hours before executing the sync — prevents unnecessary API calls and avoids waking anyone up with an off-hours failure alert.

function syncCRMData() {
  // Business hours guard: weekdays 8 AM – 6 PM Eastern only
  const now     = new Date();
  const eastern = Utilities.formatDate(now, 'America/New_York', 'HH');
  const hour    = parseInt(eastern);
  const day     = now.getDay(); // 0=Sun, 6=Sat

  if (day === 0 || day === 6 || hour < 8 || hour >= 18) {
    return; // outside business hours
  }

  // Perform CRM API call and update Sheets
  const response = UrlFetchApp.fetch('https://api.your-crm.com/deals', {
    method: 'GET',
    headers: { 'Authorization': 'Bearer ' + PropertiesService.getScriptProperties().getProperty('CRM_TOKEN') }
  });
  const deals = JSON.parse(response.getContentText());
  // ... write deals to CRM tab ...
}

Enter fullscreen mode Exit fullscreen mode

Timezone Handling

Apps Script's built-in new Date() returns UTC. When business logic depends on local time — business hours guards, day-of-week checks, daily report timing — use Utilities.formatDate(date, timezone, format) to convert explicitly. Store the timezone string in the CONFIG object so it appears in exactly one place and changing it for a client in a different region is a one-line update.


This post was originally published on White Oak Intelligence. Read the full article there for formatted diagrams, code examples, and related content.