Σε αυτή τη σελίδα
Μια λίστα με δώδεκα εγγραφές εκδήλωσης εξάγεται εύκολα μία φορά. Στη δεύτερη εισαγωγή, ένα email μπορεί να εμφανίζεται δύο φορές και μια άλλη επαφή να έχει απεγγραφεί στη Brevo. Η επαναλήψιμη εισαγωγή πρέπει να ελέγχει και τα δύο.
Το script χρησιμοποιεί τα endpoints ανάγνωσης, δημιουργίας και ενημέρωσης επαφών της Brevo. Αφορά μικρές προγραμματισμένες εισαγωγές, όχι μαζική φόρτωση ή εγγενή σύνδεσμο Google Sheets. Δεν στέλνει email. Η τεκμηρίωση συναίνεσης και η λειτουργία του είναι δική σας ευθύνη.
Προετοιμάστε το Sheet και τη λίστα
Δημιουργήστε καρτέλα Contacts με ακριβώς αυτές τις κεφαλίδες. Χρησιμοποιήστε πλαίσια επιλογής για consent και βάλτε TRUE μόνο αν έχετε άδεια για τη συγκεκριμένη χρήση μάρκετινγκ.
| firstName | lastName | consent | |
|---|---|---|---|
| [email protected] | Jane | Doe | TRUE |
| [email protected] | Alex | Lee | FALSE |
Δημιουργήστε ή επιλέξτε λίστα Brevo και αντιγράψτε το αριθμητικό ID της στο LIST_ID. Ελέγξτε ότι υπάρχουν τα γνωρίσματα FNAME και LNAME στη Brevo. Μόνο αυτά αντιστοιχίζονται. Φυλάξτε χωριστά αποδεικτικά πότε και πώς δόθηκε συναίνεση, αν το Sheet δεν τα τεκμηριώνει. Ένα κελί TRUE μόνο του δεν αρκεί.
Στις Extensions → Apps Script → Project Settings → Script Properties προσθέστε BREVO_API_KEY. Οι ιδιότητες είναι κοινές στους χρήστες του script, άρα περιορίστε την επεξεργασία. Μην βάλετε το κλειδί σε κελί ή κώδικα. Δείτε τους κανόνες πρόσβασης της Google.
Το script
Αντικαταστήστε το Code.gs με το παράδειγμα και ορίστε 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); }}Αποθηκεύστε, επιλέξτε syncSheetToBrevo και πατήστε Run. Η Google ζητά άδειες για Sheets και εξωτερικά αιτήματα. Στις Executions θα βρείτε το {written, skipped}. Αν η ανάγνωση επιστρέψει 404, το script δημιουργεί επαφή· με 200 την ενημερώνει. Άλλα σφάλματα σταματούν την εκτέλεση. Επιτυχής εγγραφή επαφής δεν σημαίνει αποστολή καμπάνιας.
Διπλά email αφαιρούνται ανά εκτέλεση, και στην επόμενη το script ξαναρωτά τη Brevo. Δεν ορίζει emailBlacklisted σε false ούτε αλλάζει διεύθυνση. Αν η Brevo αναφέρει αποκλεισμό ή απεγγραφή από τη λίστα, η γραμμή παραλείπεται ακόμη και με TRUE. Μην το χρησιμοποιείτε για άρση απεγγραφής. Η Brevo προειδοποιεί ότι αλλαγή email αποκλεισμένης επαφής μπορεί να άρει τον αποκλεισμό.
Ελέγξτε μια εκτέλεση πριν τον προγραμματισμό
Χρησιμοποιήστε δοκιμαστική λίστα και δικές σας επαφές. Μια γραμμή TRUE, μια FALSE και ένα διπλό email πρέπει να δώσουν μία εγγραφή και δύο παραλείψεις. Απεγγράψτε τη δοκιμαστική επαφή στη Brevo και επαναλάβετε: δεν πρέπει να γραφτεί. Για έλεγχο σφάλματος βάλτε προσωρινά άκυρο LIST_ID, δείτε το HTTP σφάλμα στις Executions, διορθώστε το ID και επαναλάβετε. Μην δοκιμάζετε σε αληθινά δεδομένα πελατών. Ο κώδικας ελέγχθηκε τοπικά με εικονικές αποκρίσεις Brevo, χωρίς ζωντανή εγγραφή.
Για πρόγραμμα ανοίξτε Apps Script → Triggers → Add Trigger, επιλέξτε syncSheetToBrevo και διάστημα. Οι ενεργοποιητές εκτελούνται στον λογαριασμό δημιουργού, ο οποίος πρέπει να διατηρεί πρόσβαση. Ξεκινήστε καθημερινά για μικρό Sheet και παρακολουθήστε σφάλματα. Η Google περιορίζει χρόνο εκτέλεσης και URL Fetch. Σπάστε μεγάλες πηγές σε περιορισμένες εργασίες.
Μετά από 429 το script διαβάζει x-sib-ratelimit-reset και επαναλαμβάνει το πολύ δύο φορές. Αν η αναμονή ξεπερνά 60 δευτερόλεπτα, επιστρέφει 429 για επόμενη εκτέλεση. Άλλα σφάλματα το σταματούν· η επόμενη εκτέλεση αρχίζει από την αρχή. Επαφή μπορεί να απεγγραφεί μεταξύ ανάγνωσης και εγγραφής. Αν αυτό το μικρό παράθυρο δεν είναι αποδεκτό, χρειάζονται συμφωνία συμβάντων, αξιόπιστη πηγή συναίνεσης και τελικός έλεγχος πριν την αποστολή.
Για μεγάλους πίνακες, η ασύγχρονη εισαγωγή της Brevo επιστρέφει processId. Αυτό σημαίνει αποδοχή εργασίας, όχι ολοκλήρωση. Ελέγξτε το αποτέλεσμα. Η διαδικασία ανά επαφή εδώ δείχνει σε ποια γραμμή απέτυχε.
Έχετε Ερωτήσεις; Έχουμε Απαντήσεις
Απαιτείται εγγενής σύνδεσμος Google Sheets;
Όχι. Το παράδειγμα εκτελείται σε έργο Apps Script συνδεδεμένο με το Sheet και καλεί το API επαφών της Brevo. Εσείς συντηρείτε το script, το κλειδί και τον προγραμματισμένο ενεργοποιητή.
Ποιες γραμμές στέλνονται στη Brevo;
Μόνο όσες έχουν consent TRUE και έγκυρο email. Το script αφαιρεί τα διπλά σε κάθε εκτέλεση και ελέγχει τη Brevo πριν γράψει.
Η επανεκτέλεση επανεγγράφει επαφή που απεγράφη;
Παραλείπει επαφές που η Brevo επισημαίνει ως emailBlacklisted ή απεγγεγραμμένες από τη λίστα. Δεν στέλνει emailBlacklisted: false ούτε αλλάζει email. Ελέγξτε και άλλες πηγές συναίνεσης πριν την παραγωγή.
Πώς επαναλαμβάνω αποτυχημένη εκτέλεση;
Διαβάστε το σφάλμα στις εκτελέσεις Apps Script, διορθώστε την αιτία και εκτελέστε ξανά. Ίδιο email και ID λίστας σημαίνουν ότι επιτυχής γραμμή δεν δημιουργεί δεύτερη επαφή.