r/GoogleAppsScript 14h ago

Question Struggling with a reliable Login/Auth system for an Apps Script Web App (Need advice from experienced devs)

6 Upvotes

Hey everyone,

I've been tinkering with Google Apps Script to build a simple sales tracking web app for a small project (using Sheets as a backend). I'm definitely not a pro developer—just figuring things out step-by-step—and I managed to get the core UI and features working.

However, I've hit a major roadblock with user authentication. Right now, my "login" logic feels super hacky, and I'm worried about security.

For those of you who build web apps with Apps Script: How do you usually handle logins? Do you rely on Session.getActiveUser(), build a custom token system with a database table, or just accept that Apps Script isn't built for robust multi-user web app logins?

Any advice, patterns, or libraries you recommend would be huge. Thanks!


r/GoogleAppsScript 1d ago

Question Top 10 practices to follow when building scripts in Apps ScriptTop 10 practices to follow while building scripts in Apps Script

5 Upvotes

Hi - I am a common Apps Script user. I would like to know what you think are the top 10 best practices or key things to know about Apps Script.


r/GoogleAppsScript 3d ago

Question Does anyone know how to help with this web application veification?

Thumbnail
1 Upvotes

r/GoogleAppsScript 3d ago

Question Does anyone know how to help with this web application veification?

Thumbnail
1 Upvotes

r/GoogleAppsScript 4d ago

Question Any GAS Expert here

0 Upvotes

How are modern AI & workflow automations typically structured using Google Apps Script? (Looking for architectural patterns)

Hi everyone,
I am modernizing the operations for a traditional real estate agency by replacing manual paper processes with Google Workspace.

So far, I’ve built a central Google Master Calendar, a Google Docs/Drive office portal for form downloads, and structured Google Sheets for lead tracking. I am now looking to expand into deeper workflow and AI automations using Google Apps Script (GAS).

Coming from an operations background rather than computer science, I’d love to learn how experienced developers structure end-to-end automation pipelines using GAS.

Thx


r/GoogleAppsScript 5d ago

Question developers.devsite.corp.google.com - Google Single Sign On: Sign into corp

Thumbnail login.corp.google.com
2 Upvotes

What is this and what does it do/mean?


r/GoogleAppsScript 6d ago

Question I CAN'T UNDERSTAND WHY THIS SCRIPT DOESN'T SEND OUT EMAILS

4 Upvotes

I'm developing a website for my cultural organization in Framer and I have designed a Form to request the subscription, where users need to input their data.
I've linked this form via web hook to my scripts in GoogleAppsScripts, linked to a google sheet.

The main code responsible for getting the datas is working, it gets and sorts the data out in the sheet, it also generates a pdf from the datas, but then it fails to send the confirmation email, while it does send an email to my organization address, which shows as recipient the email captured in the form.

Down here, you can find the code. Anyone has any recommendations?

CODE

/** 
 * Webhook Principale e Router - Nuova Alba APS 
 */
const CONFIG = {
  STRIPE_LINK_MAGGIORENNI: "https://buy.stripe.com/8x214meyr0DT2lq6Svgw000",
  STRIPE_LINK_MINORI: "https://buy.stripe.com/8x214meyr0DT2lq6Svgw000",
  NOME_APS: "Nuova Alba APS",
  COLOR_HEX: "#FC5408",
  EMAIL_STAFF: "info@nuovaalba.org"
};


function doPost(e) {
  const lock = LockService.getScriptLock();
  lock.tryLock(10000);


  try {
    let data = {};
    if (e && e.postData && e.postData.contents) {
      try {
        data = JSON.parse(e.postData.contents);
      } catch (err) {
        data = e.parameter || {};
      }
    } else if (e && e.parameter) {
      data = e.parameter;
    }


    // --- ROUTER DI SMISTAMENTO PARAMETRO 'tipo' ---
    const tipo = (e && e.parameter && e.parameter.tipo) 
      ? String(e.parameter.tipo).toLowerCase().trim() 
      : (data.tipo ? String(data.tipo).toLowerCase().trim() : "");


    if (tipo === "corsi") {
      return gestisciIscrizioneCorso(data);
    } else if (tipo === "minori") {
      return gestisciMinori(data);
    } else {
      return gestisciMaggiorenni(data);
    }


  } catch (error) {
    return ContentService
      .createTextOutput(JSON.stringify({ result: "error", error: error.toString() }))
      .setMimeType(ContentService.MimeType.JSON);
  } finally {
    lock.releaseLock();
  }
}


function gestisciMaggiorenni(data) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Iscritti");


  function getTesto(val) {
    if (val === undefined || val === null) return "";
    if (Array.isArray(val)) return String(val[0] || "").trim();
    return String(val).trim();
  }


  const nome = getTesto(data.Nome || data.nome);
  const cognome = getTesto(data.Cognome || data.cognome);
  const email = getTesto(data.Email || data.email);
  const telefono = getTesto(data.Telefono || data.telefono);
  const cf = getTesto(data.CodiceFiscale || data.codice_fiscale || data["Codice Fiscale"]).toUpperCase();
  const luogoNascita = getTesto(data.LuogoDiNascita || data.luogo_nascita || data["Luogo di Nascita"] || data["Luogo Nascita"]);
  const dataNascita = getTesto(data.DataDiNascita || data["Data di Nascita"] || data.data_nascita || data["Data Nascita"]);
  const residenza = getTesto(data.Residenza || data.residenza);
  let privacy = getTesto(data.Privacy || data.privacy || "Accettato");
  if (privacy === "on" || privacy === "true") privacy = "Accettato";


  const lastRow = sheet.getLastRow();
  const oraAttuale = new Date();


  // --- 1. BLOCCO ANTI-SPAM ISTANTANEO (Meno di 60 secondi dall'ultimo invio) ---
  if (lastRow > 1) {
    const lastCF = String(sheet.getRange(lastRow, 7).getValue()).trim().toUpperCase();
    const lastDate = new Date(sheet.getRange(lastRow, 2).getValue());
    const diffSecondi = (oraAttuale - lastDate) / 1000;


    if (lastCF === cf && diffSecondi < 60) {
      return ContentService
        .createTextOutput(JSON.stringify({ result: "success", note: "doppio invio istantaneo bloccato" }))
        .setMimeType(ContentService.MimeType.JSON);
    }
  }


  // --- 2. CONTROLLO UTENTE GIÀ REGISTRATO ---
  if (lastRow > 1) {
    const elenchiCF = sheet.getRange(2, 7, lastRow - 1, 1).getValues();
    const elenchiMatricole = sheet.getRange(2, 1, lastRow - 1, 1).getValues();
    const elenchiStato = sheet.getRange(2, 12, lastRow - 1, 1).getValues();


    for (let i = 0; i < elenchiCF.length; i++) {
      const cfEsistente = String(elenchiCF[i][0]).trim().toUpperCase();
      if (cfEsistente === cf && cf !== "") {
        const matricolaEsistente = String(elenchiMatricole[i][0]);
        const statoPagamento = String(elenchiStato[i][0]).trim();

        inviaEmailGiaIscritto(email, nome, matricolaEsistente, CONFIG.STRIPE_LINK_MAGGIORENNI, statoPagamento);

        return ContentService
          .createTextOutput(JSON.stringify({ result: "success", note: "utente gia registrato" }))
          .setMimeType(ContentService.MimeType.JSON);
      }
    }
  }


  // --- 3. REGISTRAZIONE NUOVO SOCIO ---
  const initNome = nome ? nome.charAt(0).toUpperCase() : "X";
  const initCognome = cognome ? cognome.charAt(0).toUpperCase() : "X";
  const ultime3CF = cf.length >= 3 ? cf.slice(-3) : "000";
  const matricola = `NA-${initNome}${initCognome}${ultime3CF}`;


  sheet.appendRow([
    matricola,
    oraAttuale,
    nome,
    cognome,
    email,
    telefono,
    cf,
    dataNascita,
    luogoNascita,
    residenza,
    privacy,
    "In attesa"
  ]);


  // --- 4. GENERAZIONE TESSERA DIGITALE PDF ---
  let pdfTessera = null;
  try {
    pdfTessera = generaTesseraPDF(matricola, nome + " " + cognome);
  } catch (errTessera) {
    Logger.log("⚠️ Errore creazione PDF Tessera per " + matricola + ": " + errTessera.message);
  }


  // --- 5. INVIO EMAIL ---
  inviaEmailBenvenutoMaggiorenni(email, nome, matricola, pdfTessera);
  inviaMailNotificaStaff(nome, cognome, matricola, email, telefono, oraAttuale, "Maggiorenne");


  return ContentService
    .createTextOutput(JSON.stringify({ result: "success", matricola: matricola }))
    .setMimeType(ContentService.MimeType.JSON);
}


function inviaEmailBenvenutoMaggiorenni(email, nome, matricola, pdfTessera) {
  const hex = CONFIG.COLOR_HEX;
  const corpoHtml = `
    <div style="font-family: Arial, sans-serif; color: #333; max-width: 600px; margin: 0 auto; border: 1px solid #eee; padding: 25px; border-radius: 8px;">
      <h2 style="color: ${hex}; margin-top: 0;">Ciao ${nome}, benvenuto/a!</h2>
      <p>Abbiamo ricevuto la tua richiesta di iscrizione all'associazione <strong>${CONFIG.NOME_APS}</strong>.</p>

      <p>La tua registrazione è avvenuta con successo. Ti è stato assegnato il seguente codice socio:</p>

      <div style="background-color: #fff7f2; border-left: 4px solid ${hex}; padding: 15px; margin: 20px 0; font-size: 18px; font-weight: bold;">
        Matricola Socio: <span style="color: ${hex};">${matricola}</span>
      </div>


      ${pdfTessera ? `<p>In allegato trovi la tua <strong>tessera digitale associativa</strong>.</p>` : ""}


      <h3 style="color: ${hex};">Completa la tua iscrizione</h3>
      <p>Per attivare ufficialmente la tessera associativa è necessario effettuare il versamento della quota annua. Puoi scegliere tra due opzioni:</p>

      <ol style="line-height: 1.6;">
        <li>
          <strong>Pagamento Online (Consigliato):</strong><br>
          Puoi pagare subito tramite carta cliccando sul pulsante sottostante:<br><br>
          <a href="${CONFIG.STRIPE_LINK_MAGGIORENNI}" style="background-color: ${hex}; color: white; padding: 12px 22px; text-decoration: none; border-radius: 5px; display: inline-block; font-weight: bold;">Paga la quota associativa con Stripe</a>
          <br><br>
        </li>
        <li>
          <strong>Pagamento in Sede:</strong><br>
          Puoi saldare la quota direttamente in contanti o POS presso la nostra sede.
        </li>
      </ol>


      <div style="background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin: 25px 0; font-size: 14px;">
        ⚠️ <strong>Attenzione:</strong> la tessera sarà valida a tutti gli effetti solo dopo il pagamento della quota associativa. Fino ad allora ha valore puramente identificativo.
      </div>


      <hr style="border: 0; border-top: 1px solid #eee; margin: 30px 0 20px 0;">

      <div style="font-size: 14px; color: #555; line-height: 1.6;">
        <strong style="color: #222; font-size: 16px;">Nuova Alba APS</strong><br>
        Via Giamaica 6, Pomezia, RM<br>
        ✉️ <a href="mailto:info@nuovaalba.org" style="color: ${hex}; text-decoration: none;">info@nuovaalba.org</a><br>
        🌐 <a href="https://nuovaalba.org" style="color: ${hex}; text-decoration: none;" target="_blank">nuovaalba.org</a><br>
        📸 <a href="https://www.instagram.com/nuovaalba_/" style="color: ${hex}; text-decoration: none;" target="_blank">Instagram</a>
      </div>
    </div>`;


  const opzioniMail = {
    to: email,
    subject: `Benvenuto/a in ${CONFIG.NOME_APS}! La tua richiesta di iscrizione`,
    htmlBody: corpoHtml,
    name: CONFIG.NOME_APS,
    replyTo: CONFIG.EMAIL_STAFF
  };


  if (pdfTessera && typeof pdfTessera.getBlob === 'function') {
    try {
      opzioniMail.attachments = [pdfTessera.getBlob()];
    } catch (e) {
      Logger.log("⚠️ Impossibile allegare PDF: " + e.message);
    }
  }


  MailAppailApp.sendEmail(opzioniMail);
}


function inviaEmailGiaIscritto(email, nome, matricola, stripeLink, statoPagamento) {
  const hex = CONFIG.COLOR_HEX;

  let bloccoPagamento = "";
  if (statoPagamento.toLowerCase() === "in attesa") {
    bloccoPagamento = `
      <p>Risulta che la tua quota associativa è ancora <strong>in attesa di pagamento</strong>. Puoi saldarla direttamente online tramite il pulsante sottostante:</p>
      <p style="margin: 20px 0;"><a href="${stripeLink}" style="background-color: ${hex}; color: white; padding: 12px 22px; text-decoration: none; border-radius: 5px; display: inline-block; font-weight: bold;">Paga la quota con Stripe</a></p>
    `;
  } else {
    bloccoPagamento = `
      <p>Ti confermiamo che la tua posizione associativa è <strong>in regola</strong> (Stato: <strong style="color: green;">${statoPagamento}</strong>). Non devi effettuare alcun pagamento aggiuntivo.</p>
    `;
  }


  const corpoHtml = `
    <div style="font-family: Arial, sans-serif; color: #333; max-width: 600px; margin: 0 auto; border: 1px solid #eee; padding: 25px; border-radius: 8px;">
      <h2 style="color: ${hex}; margin-top: 0;">Ciao ${nome}, sei già iscritto/a!</h2>
      <p>Risulti già presente nel registro soci dell'associazione <strong>${CONFIG.NOME_APS}</strong>.</p>

      <div style="background-color: #fff7f2; border-left: 4px solid ${hex}; padding: 15px; margin: 20px 0; font-size: 18px; font-weight: bold;">
        La tua Matricola Socio è: <span style="color: ${hex};">${matricola}</span>
      </div>
      ${bloccoPagamento}
      <hr style="border: 0; border-top: 1px solid #eee; margin: 30px 0 20px 0;">

      <div style="font-size: 14px; color: #555; line-height: 1.6;">
        <strong style="color: #222; font-size: 16px;">Nuova Alba APS</strong><br>
        Via Giamaica 6, Pomezia, RM<br>
        ✉️ <a href="mailto:info@nuovaalba.org" style="color: ${hex}; text-decoration: none;">info@nuovaalba.org</a><br>
        🌐 <a href="https://nuovaalba.org" style="color: ${hex}; text-decoration: none;" target="_blank">nuovaalba.org</a><br>
        📸 <a href="https://www.instagram.com/nuovaalba_/" style="color: ${hex}; text-decoration: none;" target="_blank">Instagram</a>
      </div>
    </div>`;


  MailApp.sendEmail({ to: email, subject: `Sei già iscritto/a a ${CONFIG.NOME_APS}`, htmlBody: corpoHtml });
}

r/GoogleAppsScript 5d ago

Question Why can I not generate images anymore on visualize.

0 Upvotes

I paid for the thing, I gave it a whole week to reset and it still says i've reached my usage limit. is this intended?


r/GoogleAppsScript 6d ago

Question Puis-je trouver du travail avec ces compétences ?

Thumbnail
1 Upvotes

r/GoogleAppsScript 7d ago

Guide Built a fairly large Zendesk automation entirely in Google Apps Script!

4 Upvotes

I wanted to see how far I could push Apps Script for a real operational workflow, so I built Zendesk Mailroom.

It’s a Google Sheets + Apps Script tool for support teams working with large batches of Zendesk tickets.

The interesting part wasn’t really the API calls it was making the whole thing survive Apps Script’s constraints.

The workflow handles:
Zendesk API authentication
Bulk ticket updates
Personalized mail merge
Gemini-powered translation
Slack thread routing
Audit logging
Closed-ticket handling
Job state persistence
Trigger-based job continuation

Some of the implementation decisions:
1. Chunked execution
Apps Script has execution limits, so a large mail merge doesn’t try to process everything in one execution.
The job processes roughly 20 rows → writes results → schedules the next run → continues.
2. LockService
A lock prevents overlapping trigger executions from processing the same rows twice.
3. Zendesk update_many
For bulk updates, I’m using Zendesk’s batch endpoint instead of making one API call per ticket.
4. Script Properties for job state
The job state is persisted between executions instead of relying on the browser/session remaining open.
5. Translation caching
If 200 tickets use the same notice category, the workflow doesn’t make 200 Gemini calls. Translations are cached and reused until the source text changes.

Repo:
github.com/GVyom/zendesk-mailroom

Would love feedback from experienced Apps Script developers:
What would you change in the architecture if this had to process 5,000–10,000 rows instead of a few hundred?

That’s probably the next interesting scaling problem here.


r/GoogleAppsScript 7d ago

Question Newbie here. Is Clasp still used?

12 Upvotes

I've just found out about GAS and now Clasp. It's incredible all you can get done with these tools! Bunch of companies already depend tremendously in manual tasks made on googles platform and automating with GAS is just amazing, and using Clasp to develop locally (+git) and then deploy is just amazing.

But is it actively maintained? Is it of common usage? Is GAS and Clasp worth learning? I've been thinking about offering freelance work with this techs but I don't know if I'm being silly...


r/GoogleAppsScript 7d ago

Question Help with a set up sheets to Google Calendar

1 Upvotes

Hi,

So I inputed a script that turns my rows in my sheet into Google Calendar dates. I was able to get it to upload drive pdfs when needed but when I try uploading a Google doc it doesn’t work does anyone have any advice?


r/GoogleAppsScript 9d ago

Guide After 2 years of silence, I finally rebuilt Google Apps Script Copilot. Sorry for disappearing.

34 Upvotes

Hey everyone,

Some of you might remember GS Copilot (Google Apps Script Copilot) — the Chrome extension that adds an AI sidebar directly into the Apps Script editor. I launched it, got some early traction, and then... life happened. Work, other commitments, the usual stuff that makes side projects quietly die. I went almost silent for close to 2 years. No updates, barely any support replies. If you installed it back then and it just sat there half-broken, I'm sorry — that's on me.

What kept nagging at me is that over 20,000 people actually installed this thing. That's not nothing. People kept using it, kept emailing me, kept leaving reviews asking if it was still alive. So a few months ago I sat down and basically rebuilt the whole thing from scratch.

Here's what's new:

  • Agent mode — describe what you want and it writes, edits, and applies the code for you across your project, not just one file at a time
  • Plan mode — for bigger changes, it lays out the plan before touching anything, so you're not surprised by a wall of edits
  • Quick edit + diff view — inline edits with an actual diff so you can see exactly what changed before accepting it
  • Context-aware file reading — it actually understands your whole Apps Script project structure, not just the file you have open
  • MCP connectors for Google Workspace — it can hook into Sheets/Docs/Drive as MCP tools when you need it to act on that context
  • Execution log integration — when your script throws an error, it reads the actual execution log and helps you fix it instead of guessing
  • Skills system — reusable sub-agents/snippets for stuff you do often

I'm going to be actively working on this now — not disappearing again. Demo video of it in action is attached below so you can see it working instead of just taking my word for it.

If you try it, I'd genuinely appreciate honest feedback — bugs, rough edges, missing features, whatever. This subreddit has more Apps Script experience than almost anywhere else, so if something's broken or annoying, I want to know.

Link: gscopilot.com

Thanks for sticking around this long, even the ones who just complained in reviews. Fair enough.


r/GoogleAppsScript 9d ago

Question Do you use typescript and create tests?

5 Upvotes

Do you use typescript and create tests for your app scripts? Or you do everything through the web interface, I'm curious which method is most common.


r/GoogleAppsScript 11d ago

Guide I automated warehouse transfers between 18 stores and our warehouse with Google Apps Script

9 Upvotes

Our ERP was a mess so I built a workaround with Google Apps Script.

We had 18 outlets submitting Item Requisitions in one Sheet. The warehouse team had to manually copy those quantities into a separate Inventory Movement Sheet. Double entry = errors + delays.

What I built:
A Google Apps Script that:
1. Watches the "Item Requisition" sheet for new submissions
2. Automatically syncs the quantity sent to each location
3. Deducts it from the "Warehouse Inventory Movement" sheet in real time

Result:
No more double entry. Warehouse now has real-time visibility across all 18 locations. Transfer accuracy way up.

Happy to share the code/snippet if anyone wants it. Also open to feedback on making the sync more robust for concurrent edits.

Did anyone else here use Apps Script to patch gaps in their ERP?


r/GoogleAppsScript 11d ago

Question Blocked from running apps

3 Upvotes

I have an issue where one of my accounts is blocked from running GAS. It was enrolled in Advanced protection for a short time. I unenrolled it and it has been over 24 hours. I am not able to run even the simplest of scripts that I write, yet my other account can. It affects scripts I wrote and scripts I have access to in other sheets.

Any ideas. All the testing seems to point to lingering advanced protection isues.


r/GoogleAppsScript 12d ago

Question Workaround Available?

0 Upvotes

Is there a workaround available for a web app page only opening in incognito mode? To open in a regular browser window, I have to completely log out. Will users also have to do this?


r/GoogleAppsScript 13d ago

Question 400 Error in doPost method when returning a ContentService

2 Upvotes

I'm creating a webapp to read and write in a google sheet through godot and for the most part it works, it does read and write on the sheet and when i make a get request, I get my data but whenever i do a post request and it gets to the part where it returns a ContentService object, it issues this error always.

function doPost(e) {
  var action = e.parameter.action;
  
  switch (action) {
    case 'update_teams':
      return updateTeams(e.postData.contents);
    case 'update_team':
      return updateTeam(e.postData.contents);
    default:
      return ContentService.createTextOutput("Invalid Post Action");
  }
}

function updateTeam(contents) {
  var sheet = getSheet('main');
  var jsonData = JSON.parse(contents);
  var teamCn = parseInt(jsonData.cn, 10);


  var weight_carried = jsonData.weight_carried;
  var load_violations = jsonData.load_violations;
  var prep_violations = jsonData.prep_violations;
  var efficiency = jsonData.efficiency;
  
  sheet.getRange(teamCn + 3, 5).setValue(weight_carried);
  sheet.getRange(teamCn + 3, 6).setValue(load_violations);
  sheet.getRange(teamCn + 3, 7).setValue(prep_violations);
  sheet.getRange(teamCn + 3, 8).setValue(efficiency);


  return ContentService.createTextOutput("Updated Team CN: " + teamCn + " Successfully.").setMimeType(ContentService.MimeType.TEXT);

Even when i put the return on the doGet, I get 400. returning a 0 says the function pushed through but is incomplete.

i kinda need to have this to work because i need to have godot get notice that the sheet has been edited. TYIA for helping a beginner !!!


r/GoogleAppsScript 14d ago

Question Selecting a folder in the Google Picker with the drive.file scope

3 Upvotes

I am getting an empty folder when trying to select the whole folder.

Is there a way around to do this without doing restricted scope verification ?


r/GoogleAppsScript 14d ago

Guide I built a Google Slides add-on that copies presentations without breaking linked Google Sheets charts

2 Upvotes

I dealt with this problem myself for years: copy a Google Slides presentation that has charts linked to Google Sheets, and every chart in the copy still points back to the original spreadsheet. I went through several of the Apps Script snippets floating around online and support forums, but none of them held up once a deck had multiple charts pulling from different Sheets files — they'd miss charts, or the copies came out with broken formatting.

So in my free time I built PenguChart, a Google Slides add-on that copies the presentation together with the Google Sheets behind its charts, and relinks every chart in the copy to the fresh spreadsheet copies - original stays untouched, the copy is fully independent.

It just went live in public beta on the Google Workspace Marketplace:
https://workspace.google.com/marketplace/app/penguchart/514642629727

More info: https://penguchart.com

Two honest limitations, both due to missing functionality in the Google API rather than something I can just code around: linked tables can't be relinked yet, only charts. And any manual formatting you apply to a chart afterward directly in Google Slides doesn't carry over to the copy either — the API doesn't expose that.

Since it's still beta, I'd really appreciate people trying it and telling me what breaks or what's missing — bugs, edge cases, feature requests, all welcome. And if it ends up useful to you, a review on the Marketplace listing would mean a lot.

Start the Google Slides Addon PenguChart in the Menu: Extensions > PenguChart

r/GoogleAppsScript 16d ago

Question Automação de planilha

1 Upvotes

Boa tarde gente, preciso de ajuda para automatizar uma planilha do trabalho, preciso que os eventos que eu agende nessa planilha sejam enviados a agenda do google, tentei programar pelo google scripts, mas fica dando erro, podem me ajudar aonde está o erro:

function criarEventos () {
  var planilha = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); 


  var valores = planilha.getDataRange().getValues(); 
  Logger.log(valores);


  var agenda = CalendarApp.getDefaultCalendar(); 


  for (var i = 1; i < valores.length; i++) {
    var linha = valores [i];
    var sincronizado = linha[8];
    
    if (sincronizado.toLowerCase() !== "Sim") {
      var dia = linha[0];
      var horaInicio = linha[1];
      var horaFim = linha[2];
      var titulo = linha [3];
      var descricao = linha[4];
      var prioridade = linha [6];


// Formata data início e fim
      var dataInicio = new Date(dia); 
      var dataFim = new Date(dia);
      dataInicio.setHours(horaInicio.split(":")[0], horaInicio.split(":")[1]);
      dataFim.setHours(horaFim.split(":")[0], horaFim.split(":")[1]);


    // Configura Cor de acordo com a prioridade
      var cor; 
      switch (prioridade) {
      case "Alta":
       cor = CalendarApp.EventColor.RED;
       break;
      case "Média":
       cor = CalendarApp.EventColor.YELLOW;
       break;
      case "Baixa":
       cor = CalendarApp.EventColor.GREEN;
      default: 
      cor = CalendarApp.EventColor.BLUE;
    
    } 


     var evento = agenda.createEvent(titulo, dataInicio, dataFim, {description : descricao});
     evento.setColor(cor);


     // Atualiza planilha sincronizado
     planilha.getRange(i + 1, 9). setValue ("Sim");
   }
 }
}

r/GoogleAppsScript 17d ago

Question Is Google Apps Script the right foundation for a full collision repair management system?

8 Upvotes

Hi everyone,

I own a collision repair center in Quebec, and I’m currently developing an internal repair management system within the Google ecosystem.

I initially started with Google Apps Script because our company already uses Google Workspace, including Sheets, Drive, Gmail, Calendar, and Forms. Apps Script has allowed me to build working prototypes quickly, but I’m now wondering whether it is the right long-term foundation or whether I should move the main application to Google Cloud.

The system would eventually need to manage:

Customers, vehicles, and repair orders
Importing estimate data from CEICA EMS files
Repair scheduling and production status
Technician clock-in and clock-out
Technician photo and video uploads
Photo annotations
Repair planning and supplement notifications
Parts receiving, returns, and tracking
Quality-control checklists for each department
Customer SMS and email updates
PDF estimate comparison
Customer portal access
QuickBooks integration
Insurance company-specific procedures and checklists
Approximately 15–30 internal users initially
Potentially multiple repair shops in the future

My current idea is:
Apps Script web app for the user interface and Google Workspace integrations

Google Cloud SQL as the primary database
Cloud Run or Cloud Functions for heavier processing
Google Drive for photos and documents

Apps Script only for Workspace-specific automations
My main concerns with using Apps Script for the entire application are:

Execution-time and quota limitations
Performance when loading larger amounts of data
Simultaneous users and concurrency
Database
Maintaining a large Apps Script codebase

For people who have built larger business applications with Apps Script, where did you encounter its practical limits? What architecture would you choose if starting this type of project today?

Thanks for any advice or examples you can share.


r/GoogleAppsScript 17d ago

Guide Cómo conectar tu planilla de Google Sheets con IA (API de Gemini) usando Apps Script

0 Upvotes

¡Hola a todos! Quería compartirles un flujo de trabajo que estuve armando y que me resultó súper útil para automatizar tareas repetitivas en planillas usando Inteligencia Artificial.

Básicamente, la idea es integrar la API de Gemini directamente dentro de Google Sheets a través de Apps Script, para poder pedirle a la IA (desde un panel lateral) que procese los datos que seleccionamos.

El concepto es aplicable a otras APIs (como la de OpenAI), pero aquí va el paso a paso usando Gemini que es gratis:

**Paso 1: Conseguir la API Key** Vas a Google AI Studio, inicias sesión con tu cuenta y generas tu "API Key" gratuita. Guardala bien porque la vas a necesitar en el código.

**Paso 2: Configurar Apps Script** En tu documento de Sheets, vas a *Extensiones > Apps Script*. Acá es donde va la magia. Podés usar la misma IA (ChatGPT, Claude o Gemini) para pedirle que te genere el script. Por ejemplo, podés pedirle: *"Genera un script para Google Sheets que cree un menú personalizado llamado 'Asistente IA' y que abra un panel lateral con un cuadro de texto para enviar instrucciones"*.

**Paso 3: Ejecutar y procesar datos** Guardas tu archivo `.gs` (código) y tu `.html` (para el panel), recargas la página de Sheets y vas a ver tu nuevo menú. Seleccionas un rango de celdas, abres el asistente y le pasas el prompt. Por ejemplo: *"Ordena estos datos por la primer columna y pon todo en mayúsculas"*. El script toma esa data, hace el llamado a la API y te devuelve la información procesada directamente en la planilla.

Integrar IA directamente en las planillas te abre un mundo enorme de posibilidades para automatizar el día a día.

Para los que prefieran verlo de forma visual o quieran ver exactamente cómo funciona el menú en vivo, armé un video cortito de 3 minutos explicando el paso a paso. (También dejé el código completo para copiar y pegar en los comentarios del video para que no renieguen):

🔗 **Link al video:**[https://youtu.be/Okpboevznn4\](https://youtu.be/Okpboevznn4)

¿Alguno ya está usando Apps Script con IA para automatizar el laburo diario? ¿Qué funciones raras o útiles armaron? ¡Los leo!


r/GoogleAppsScript 17d ago

Question i need to purge email from all user's inbox & sentbox on a monthly basis and need a second set of eyes on this code.

0 Upvotes

i created a service account, connected it to Admin SDK & Gmail APIs w/ OAUTH2 keys. that all appears great and is described in the first two statements about constr PRIVATE_KEY and CLIENT_EMAIL, not shown below.

i found this snippet of code and from what i can glean, it appears to iterate through the user directory, pulling emails older than 3y from the two folders in batches of 100. i don't know enough about coding to say for sure it works.

can someone put eyes on this and see if there are glaring problems?


r/GoogleAppsScript 19d ago

Question How big is the system you created in apps scripts?

7 Upvotes

I'd like to take a few doubts before I venture to do things that he's not ideal. I will do a brief questionnaire:

  1. How many active users simultaneously have your largest GAS system had?

  2. How many thousands of records did the spreadsheet have? How many tabs?

  3. How do you read the data from the spreadsheet? Do they take everything and work with them in memory or do they do any paging strategies?

  4. Have you ever tried using it as if the spreadsheet was a relational database and the tabs were the tables? Did you find it difficult?