Preglednico z dvanajstimi prijavami na dogodek je enkrat lahko izvoziti. Pri drugem uvozu se en naslov ponovi, druga oseba pa se je medtem odjavila v Brevo. Ponovljiv uvoz mora opaziti oboje.
Skripta uporablja končne točke Brevo za branje, ustvarjanje in posodabljanje stikov. Namenjena je majhnim načrtovanim uvozom, ne množičnemu nalaganju ali vlogi izvornega povezovalnika Google Sheets. Ne pošilja e-pošte. Za dokaz privolitve in delovanje skripte ste odgovorni vi.
Pripravite preglednico in seznam
Ustvarite zavihek Contacts s točno temi naslovi stolpcev. Za consent uporabite potrditvena polja in označite TRUE samo, če imate privolitev za predvideno trženje.
| firstName | lastName | consent | |
|---|---|---|---|
| [email protected] | Jane | Doe | TRUE |
| [email protected] | Alex | Lee | FALSE |
Ustvarite ali izberite seznam Brevo in njegovo številčno oznako vnesite kot LIST_ID. Preverite, ali atributa FNAME in LNAME obstajata v Brevo. Preslikani sta samo ti polji. Če preglednica ne more dokazati, kdaj in kako je bila privolitev dana, shranite dokaz drugje. Celica TRUE ni dokaz.
Pod Razširitve → Apps Script → Nastavitve projekta → Lastnosti skripte dodajte BREVO_API_KEY. Lastnosti si delijo uporabniki skripte, zato omejite urejanje projekta. Ključa ne shranite v celico ali kodo. Glejte Googlova pravila dostopa.
Skripta
Zamenjajte Code.gs s primerom in pred prvim zagonom nastavite LIST_ID.
const LIST_ID = 42; // Replace with your existing Brevo list ID.const TAB = 'Contacts';const BASE = 'https://api.brevo.com/v3';
function syncSheetToBrevo() { const key = PropertiesService.getScriptProperties().getProperty('BREVO_API_KEY'); if (!key) throw new Error('Set BREVO_API_KEY in Script Properties first'); const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(TAB); if (!sheet) throw new Error('Contacts tab missing'); const values = sheet.getDataRange().getValues(); const headers = values.shift().map(h => String(h).trim()); const columns = ['email', 'firstName', 'lastName', 'consent'] .map(name => headers.indexOf(name)); if (columns.includes(-1)) throw new Error('Required header missing'); const [emailCol, firstCol, lastCol, consentCol] = columns; const seen = new Set(); const result = { written: 0, skipped: 0 };
for (const row of values) { const email = String(row[emailCol] || '').trim().toLowerCase(); if (row[consentCol] !== true || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) || seen.has(email)) { result.skipped++; continue; } seen.add(email); const path = '/contacts/' + encodeURIComponent(email); const current = brevo_(key, path, 'get'); if (current.code !== 200 && current.code !== 404) { throw new Error(`Contact read failed (${current.code}) for row ${result.written + result.skipped + 2}`); } if (current.code === 200) { const contact = JSON.parse(current.text); if (contact.emailBlacklisted || (contact.listUnsubscribed || []).includes(LIST_ID)) { result.skipped++; continue; } } const attributes = {}; if (row[firstCol] !== '') attributes.FNAME = String(row[firstCol]); if (row[lastCol] !== '') attributes.LNAME = String(row[lastCol]); const body = { attributes, listIds: [LIST_ID] }; const saved = current.code === 404 ? brevo_(key, '/contacts', 'post', { email, ...body }) : brevo_(key, path, 'put', body); if (![201, 204].includes(saved.code)) { throw new Error(`Contact write failed (${saved.code}) for ${email}: ${saved.text.slice(0, 200)}`); } result.written++; } Logger.log(JSON.stringify(result)); return result;}
function brevo_(key, path, method, body) { for (let attempt = 0; attempt < 3; attempt++) { const response = UrlFetchApp.fetch(BASE + path, { method, contentType: 'application/json', headers: { 'api-key': key }, payload: body ? JSON.stringify(body) : undefined, muteHttpExceptions: true, }); const code = response.getResponseCode(); if (code !== 429) return { code, text: response.getContentText() }; if (attempt === 2) return { code, text: response.getContentText() }; const reset = Object.entries(response.getHeaders()).find( ([name]) => name.toLowerCase() === 'x-sib-ratelimit-reset' ); const raw = Number(reset && reset[1]); const seconds = Number.isFinite(raw) && raw > 0 ? raw : 2 ** attempt; const waitSeconds = seconds + 2 ** attempt; if (waitSeconds > 60) return { code, text: response.getContentText() }; Utilities.sleep(waitSeconds * 1000); }}Shranite projekt, izberite syncSheetToBrevo in kliknite Zaženi. Google zahteva dostop do Sheets in zunanjih zahtev. Pod Izvajanja je končni zapis {written, skipped}. Odgovor 404 pri branju vodi do ustvarjanja, 200 do posodobitve. Druge napake zaustavijo zagon. Uspešen zapis potrdi zahtevo za stik, ne pošiljanja kampanje.
Skripta odstrani dvojne naslove v enem zagonu, ob naslednjem pa znova preveri Brevo. Nikoli ne nastavi emailBlacklisted na false in ne spremeni e-poštnega naslova. Če Brevo kaže blokado ali odjavo s tega seznama, vrstico preskoči tudi pri TRUE v preglednici. Ne uporabljajte je za razveljavitev odjave. Brevo opozarja, da lahko sprememba naslova blokiranega stika odstrani blokado.
Preverite zagon pred načrtovanjem
Uporabite preizkusni seznam in stike pod vašim nadzorom. Zaženite z eno vrstico TRUE, eno FALSE in podvojenim naslovom: pričakujte en zapis in dve preskočeni vrstici. Preizkusni stik odjavite v Brevo in znova zaženite: ne sme biti zapisan. Za preizkus napake začasno nastavite neveljaven LIST_ID, preverite napako HTTP pod Izvajanja, obnovite ID in ponovite. Ne preizkušajte na pravih podatkih strank. Koda je bila lokalno preverjena tudi s simuliranimi odgovori Brevo, brez pravega zapisa.
Za urnik odprite Apps Script → Sprožilci → Dodaj sprožilec, izberite syncSheetToBrevo in časovni interval. Namestljivi sprožilci tečejo pod računom ustvarjalca, ki mora ohraniti dostop do preglednice. Za majhno preglednico začnite dnevno in pregledujte napake. Google omejuje trajanje in klice URL Fetch; večje vire razdelite na omejene naloge.
Pri 429 skripta prebere x-sib-ratelimit-reset in ponovi največ dvakrat. Če zahtevano čakanje preseže 60 sekund, vrne 429 za kasnejši zagon. Druge napake zaustavijo zagon; naslednji začne od začetka. Stik se lahko odjavi med branjem in zapisom. Primer uporabite le, če je to kratko okno sprejemljivo. Strožje jamstvo zahteva usklajevanje dogodkov, avtoritativen vir privolitve in zadnje preverjanje pred pošiljanjem.
Za večje preglednice asinhrona končna točka za uvoz Brevo vrne processId. Potrdi sprejem naloge, ne njenega zaključka. Preden razglasite uspeh, preverite rezultat. Ta pot po posameznih stikih pokaže napako pri zadevni vrstici.
Pogosto Zastavljena Vprašanja
Ali potrebujete izvorni povezovalnik Google Sheets?
Ne. Primer teče v projektu Google Apps Script, vezanem na preglednico, in kliče API za stike Brevo. Sami vzdržujete skripto, ključ API in časovni sprožilec.
Katere vrstice gredo v Brevo?
Le vrstice z vrednostjo TRUE v consent in veljavnim e-poštnim naslovom. Skripta odstrani dvojnike pri vsakem zagonu in preveri Brevo pred zapisom.
Ali naslednji zagon znova prijavi odjavljeno osebo?
Skripta preskoči stike, ki jih Brevo označi kot emailBlacklisted ali odjavljene s ciljnega seznama. Nikoli ne pošlje emailBlacklisted: false in ne spremeni e-poštnega naslova. Pred produkcijo preverite tudi druge vire privolitve.
Kako ponovite neuspešen zagon?
Preberite napako v izvajanjih Apps Script, odpravite vzrok in znova zaženite isto funkcijo. Zapisi uporabljajo isti naslov in ID seznama, zato uspešno obdelana vrstica ne ustvari še enega stika.