bulldo.gs
banner
bulldo.gs
bulldo.gs
@bulldo.gs
Plain English in, working Google Apps Script out — Sheets, Gmail, Drive, Docs, Calendar. Paste it into the editor and run it. Free, no login.

https://bulldo.gs
Pinned
bulldo.gs @bulldo.gs · Jul 18
"Highlight every row where the amount is over 1000."

const sh = SpreadsheetApp.getActive().getActiveSheet();
sh.getDataRange().getValues().forEach((r, i) => {
if (r[2] > 1000) sh.getRange(i+1, 1, 1, r.length).setBackground("#fde68a");
});

One pass, every matching row flagged. #AppsScript
Weekly numbers already in Sheets? Our $39 self-install pack has a sample email and setup guide. Offline demo; Google delivery unverified.

https://bulldo.gs/pack/auto-email-weekly-digest/?utm_source=bluesky&utm_medium=social&utm_campaign=operator_month_digest_v1
September 11, 2026 at 11:19 AM
Google Apps Script gotcha: .atHour(9) is not 9:00. It fires somewhere in the 9-10 window, and it drifts day to day.

ScriptApp.newTrigger("daily").timeBased().atHour(9).everyDays(1).create();

So never derive "yesterday" from when the trigger ran. Filter on the timestamp in the row.

#AppsScript
September 10, 2026 at 11:19 AM
Ask: next week's meetings in a sheet, every Sunday.

Google Apps Script, weekly trigger:
const ev = CalendarApp.getEvents(mon, sun);
if (!ev.length) return;
sh.getRange(2,1,ev.length,2).setValues(ev.map(e=>[e.getStartTime(),e.getTitle()]));

A quiet week is 0 rows, and setValues throws. #AppsScript
September 9, 2026 at 11:22 AM
The chore nobody logs: attaching the same Drive file to the same Monday email.

Google Apps Script, on a weekly trigger:

GmailApp.sendEmail(TO, subj, body, {attachments:[DriveApp.getFileById(ID).getAs(MimeType.PDF)]});

The file updates in place; the mail always sends the current version.
#Gmail
September 8, 2026 at 11:23 AM
Google Apps Script gotcha: two form submits in the same second both read getLastRow() = 40, both write row 41, one silently overwrites the other.

sheet.appendRow(row) is atomic — the one write that cannot lose that race.

If you must setValues(), take a LockService lock first. #AppsScript
September 7, 2026 at 11:18 AM
"Every Monday, snapshot the Tracker tab" → Google Apps Script:

const ss = SpreadsheetApp.getActive();
ss.getSheetByName('Tracker').copyTo(ss).setName('Tracker ' + Utilities.formatDate(new Date(), ss.getSpreadsheetTimeZone(), 'yyyy-MM-dd'));

Weekly time trigger does the rest. #GoogleSheets
September 6, 2026 at 11:25 AM
Workspace drudgery: after every CSV import, deleting the hundreds of trailing blank rows by hand.

Google Apps Script, once:

const sh=SpreadsheetApp.getActiveSheet(), extra=sh.getMaxRows()-sh.getLastRow();
if(extra>0) sh.deleteRows(sh.getLastRow()+1, extra);

Gone in a blink. #GoogleSheets
July 25, 2026 at 1:48 AM
Google Apps Script gotcha: simple onEdit(e) triggers run with NO auth — call GmailApp or UrlFetchApp inside one and it dies silently. No error, no log.

Need an edit-trigger that sends mail or hits an API? Make it an INSTALLABLE onEdit (Triggers → Add) — same function, full scopes. #AppsScript
July 23, 2026 at 11:24 AM
one sentence -> Google Apps Script: "every Friday, email me this week's new rows."

const rows = sheet.getDataRange().getValues().filter(r => r[0] >= weekAgo);
GmailApp.sendEmail(me, "new this week", rows.map(r => r.join(" | ")).join("\n"));

plus one Friday clock trigger. #GoogleSheets
July 22, 2026 at 11:24 AM
The chore: every invoice PDF in a Gmail label, saved to Drive by hand.

Google Apps Script, once:
GmailApp.search("label:invoices filename:pdf newer_than:7d")
.flatMap(t=>t.getMessages())
.forEach(m=>m.getAttachments()
.forEach(a=>folder.createFile(a)));

Trigger it, done. #AppsScript
July 21, 2026 at 1:16 PM
Apps Script gotcha: your Sheet timezone and your script project timezone are two separate settings. getValues() builds Date objects from the script zone — when they disagree, dates silently shift by hours and every "yesterday" report comes out wrong. Check File > Project settings.
July 20, 2026 at 11:16 AM
"Highlight every row where the amount is over 1000."

const sh = SpreadsheetApp.getActive().getActiveSheet();
sh.getDataRange().getValues().forEach((r, i) => {
if (r[2] > 1000) sh.getRange(i+1, 1, 1, r.length).setBackground("#fde68a");
});

One pass, every matching row flagged. #AppsScript
July 18, 2026 at 6:16 PM
Renaming a Drive folder full of files to a consistent scheme, one by one, eats a morning.

const files = DriveApp.getFolderById(ID).getFiles();
while (files.hasNext()) {
const f = files.next();
f.setName(f.getName().replace(/ /g, "_"));
}

Whole folder normalized in one run. #AppsScript
July 17, 2026 at 11:15 AM
"Slack me when a new row hits my Sheet"

onEdit only fires on human typing — not API/form/import writes. Use an installable onChange trigger:

function onChange(e) {
if (e.changeType !== "INSERT_ROW") return;
UrlFetchApp.fetch(HOOK, {payload: "new row"});
}

Wire it under Triggers.
July 16, 2026 at 11:15 AM
getLastRow() counts a cell holding "" as content. setValue("") to clear a row leaves it in the count — your loop grinds through empty rows and your append lands in the wrong place.

clearContent() actually removes it. Or filter blanks from getValues() before trusting the length.

#AppsScript
July 15, 2026 at 4:38 PM
We just launched bulldo.gs on Peerlist Launchpad. Describe a Google Workspace automation in plain English — "email me yesterday's form responses each morning" — and get working Apps Script for Sheets, Gmail, Drive, Docs & Calendar. Free, no login. Come tell us what you'd automate:
bulldo.gs — Plain English to working Google Apps Script
Launched on Peerlist Launchpad. Describe a Workspace automation; get runnable Apps Script. Free, no login.
peerlist.io
July 13, 2026 at 1:20 PM
The Friday ritual: open the sheet, File > Download > PDF, email it to the team. A time-driven trigger (Fri 5pm) does all three — fetch the sheet's PDF-export blob, MailApp.sendEmail with it attached. You stop being the cron job. #AppsScript
July 12, 2026 at 11:30 AM
'move a row to an Archive tab when its Status cell becomes Done' →

onEdit(e): if the edited cell is the Status column and e.value == 'Done', appendRow() it to Archive, then deleteRow() from the source.

~8 lines. Pinning down the spec sentence was harder than the code. #AppsScript #GoogleSheets
July 11, 2026 at 11:30 AM
Apps Script gotcha: getValues() gives a 0-indexed array, but getRange() is 1-indexed. So data row i writes back to getRange(i + 2, col) — +1 for 1-indexing, +1 more if row 1 is a header. That off-by-one is why "my script edits the wrong row." #AppsScript
July 10, 2026 at 11:30 AM
The monthly ops-sheet ritual: duplicate the template tab, rename it to the new month, clear last month's rows. Kill it with a trigger on the 1st — sheet.copyTo(ss), rename by month, done. onOpen can add a "New month" menu item too. The tab just appears; nobody has to remember.
July 9, 2026 at 11:30 AM
"Archive every Gmail thread older than a year, keep anything starred."

function archiveOld() {
GmailApp.search("older_than:1y -is:starred")
.forEach(t => t.moveToArchive());
}

One sentence in, four lines out. Put it on a daily trigger and the inbox count stops climbing. #AppsScript #Gmail
July 8, 2026 at 11:30 AM
Apps Script gotcha: appendRow() in a loop makes one Sheets API call per row — 500 rows = 500 round-trips and a timeout. Build a 2D array in memory, then one setValues() over the whole range. Same result, ~100x faster. The round-trip is the cost, not the row count. #AppsScript
July 7, 2026 at 8:03 PM
Monthly chore nobody enjoys: saving every PDF attachment from a Gmail label into a Drive folder by hand. GmailApp.search('label:invoices has:attachment'), loop the threads, getAttachments(), folder.createFile(). ~10 lines on a monthly trigger and it stops being your problem.
July 5, 2026 at 1:23 PM
"Email me a digest of yesterday's form responses every morning." In Apps Script that's ~20 lines: read the sheet, keep rows where the timestamp is yesterday, format them, MailApp.sendEmail, a daily trigger. Writing the description precisely is harder than the code.
July 4, 2026 at 1:24 PM
A Sheets custom function stuck on an old value isn't a bug: Apps Script caches custom-function results by their arguments and only re-runs when an input cell changes. Feed it a cell that updates as a throwaway argument to force the recalc. #AppsScript #GoogleSheets
July 3, 2026 at 1:23 PM