István Erdő

Erdő István

a Bridge Budapest ösztöndíjasa, Samebug, Budapest, 2017

Automatikus felhasználói levelek a Google Apps Script segítségével (2. rész)

Az e-mailek létrehozása és személyre szabása

Az <a href=”Link to Part 1”>1. részben</a> megnéztük, hogyan kell adatbázist létrehozni, kezelhető formátumúvá átalakítani, és hogyan működik a createDraft függvény. Most ugyanazzal a createDraft függvénnyel dolgozunk, de ahelyett, hogy mi magunk paramétereznénk, ezt az információt automatikusan a korábban létrehozott CSV-fájlból emeljük át.

A teljes kód hozzáférhető az én Github-fiókomban:

https://github.com/istvanerdo/google-script-autodraft/blob/master/code

Piszkozat létrehozása

Létre kell hoznunk egy személyre szabott piszkozatot a felhasználóinknak. Az alkalmazásnak ezt a részét érteni kell, ezért egyenként megyünk végig az egyes sorokon és elmagyarázom, mit is jelentenek. Kezdésként hozzunk létre egy új szkriptfájlt a projektben.

Hozzunk létre egy függvényt, amely 1 argumentummal (adattal) hajtja végre a feladatainkat, nevezzük így: makeDraft:

function makeDraft(data) {

}

A { } kapcsos zárójelek közé el kezdjük beírni a kódunkat (a //-jellel kezdődő sorok az én megjegyzéseim, vagyis amikor én vagy más a kódot olvassa, érteni fogja, mit is csinálnak a bizonyos részek):

Létrehozunk egy változót („adat” elnevezéssel), amely a CSV-fájlunkból veszi át az adatokat. A javascriptben egy pont és egyenként egy új művelet elhelyezésével több művelet gyorsan egymás után is végrehajtható. Most hívjuk a DriveApp-ot, a .getFileById(„xxxxxxxxxx„) segítségével megnyitunk egy fájlt, a .getBlob() segítségével kinyerjük a tartalmát (a blob alapvetően nem más, mint a nyers adat), majd végül ezt a nyers adatot az alábbiakban látható sztringgé (karakterlánccá) konvertáljuk:

//Get file content

var data = DriveApp.getFileById(„xxxxxxxxxx„).getBlob().getDataAsString()

Nekünk a Sheet.csv adataira van szükségünk, szóval kattintsunk a jobb egérgombbal a meghajtó Sheet1.csv fájljára, majd a „Get shareable link” lehetőségre (magyarul: Megosztható link létrehozása), és a link automatikusan a vágólapra kerül. Ezután cseréljük ki a „xxxxxxxxxx” karaktersort a linkkel, és töröljünk a linkből mindent, ami nem az azonosító része. Ne feledkezzünk meg az idézőjelekről, azoknak a helyükön kell maradniuk.

Az ilyen változóban az a legjobb, hogy adatfelvitelkor az alkalmazás az egyenlőségjel utáni összes dolgot elvégzi.

Tipp: A változók megtekintéséhez használjuk a Logger.log(varname) lehetőséget. Jelen esetben: Logger.log(data). Az alkalmazás fentiekben bemutatott lefuttatása után menjünk a View (Nézet) → Logs (Naplók) lehetőségre.

Adataink a következőképpen néznek majd ki:

Probléma adódott: noha láthatóan több adatsor van, az alkalmazásunk nem ismeri fel őket, úgyhogy valahogyan fel kell ismertetnünk vele. Ez így képzelhető el: jelenleg egyetlen adatsorunk van, amely vesszőkkel és sortörésekkel elválasztott értékekből áll. Nekünk mátrixra van szükségünk, úgyhogy először arra utasítjuk az alkalmazást, hogy amikor sortérést lát, vegye új sorként. Így n darab sorunk lesz. A .split() paranccsal létrehozunk egy másik, allRows elnevezésű változót. A sortörés kódja az \n, vagyis ez lesz az argumentumunk.

//Split into lines

var allRows = data.split(„\n”)

Ugyanúgy néz majd ki, mint korábban, de az alkalmazás tudni fogja, hogy az enterek új sorokat jelentenek:

Most pedig meghatározzuk a sablonjainkat. Én most minden egyes sablonomhoz egyetlen tárgyat használok: a saját esetünkben viszont hozzunk létre változót a sablonunkhoz.

var subject = „Please provide feedback”

Az üzenet megírásához bármilyen szövegszerkesztő használható. Az alkalmazás továbbra sem kezeli karakterként az entereket, ezért azt javaslom, először írjunk enterrel, majd cseréljük ki az entereket \n-re. A később személyre szabni kívánt helyeken hagyjunk egyedi szavakat, a példában láthatók is erre javaslatok. Menjünk vissza a szkripthez és határozzuk meg a listát (ez egy sorbarendezett tétellista), majd illesszük be az üzeneteket vesszővel elválasztva. Így:

var messages = [

        „1Hi $name,\n\nLorem ipsum dolor sit amet LINK, consectetur adipiscing elit. Fusce sed.\n\nThank you,\nIstvan\n– \nIstvan Erdo\nCustomer Relations @samebug\nhttps://samebug.io”,

        „2Hi $name,\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sed.\n\nThank you,\nIstvan\n– \nIstvan Erdo\nCustomer Relations @samebug\nhttps://samebug.io”,

        „3Hi $name,\n\nLorem ipsum dolor sit amet LINK, consectetur adipiscing elit LINK. Fusce sed.\n\nThank you,\nIstvan\n– \nIstvan Erdo\nCustomer Relations @samebug\nhttps://samebug.io”,

        „4Hi $name,\n\nLorem ipsum dolor sit amet LINK, consectetur adipiscing elit. Fusce sed.\n\nThank you,\nIstvan\n– \nIstvan Erdo\nCustomer Relations @samebug\nhttps://samebug.io”

  ]

Fontos, hogy sorrendben írjunk: az 1 számmal jelölt sablon legyen az első, a 2 számmal jelölt a második, és így tovább. Ennek majd a következőkben látjuk a jelentőségét.

Az egyes e-mailekben szerepelni fog az adott személy neve, némelyikükben nem lesz link, lesz olyan amelyiknek 1 linkje lesz, a többinek pedig 2. Az egyes üzenetek elejére írott szám mutatja, hogy az alkalmazás melyik sablonokat használta, aminek később, a piszkozatok tesztelésekor lesz hasznos. Nyugodtan töröljük őket, ha biztosak vagyunk benne, hogy minden esetben a megfelelő sablont használja az alkalmazás. Az eredmények a naplóban jelennek meg:

Ezután végig kell mennünk minden soron (ezt a programozási szaknyelv looping-nak nevezi), és minden egyes sorral kezdeni kell valamit. Határozzunk meg egy „i”-változót, amely a 0 értéket veszi fel. Az allRows ismételten ugyanazt hajtja majd végre rajtuk a .map() segítségével. A Map-nak egy függvény() lesz majd az argumentuma. Ez a függvény végrehajtja majd a szükséges feladatokat a következő 2 argumentummal: adatsor és kulcs:

// Loop through all rows

allRows.map(function(line, key) {

})

A kódnak az ebben a szakaszban olvasható többi részét írjuk a { } kapcsos zárójelek közé. 

Az első adatsor csak a címeket tartalmazza, úgyhogy a következő írásával megszabadulunk tőle:

if(key === 0) return

Mivel a számítógépek 0-val kezdik a számolást, ezért az első sor lesz a 0. tétel.

Most pedig határozzunk meg még többet a későbbiekben használandó változók közül. Az egyes adatsorok végén lévő enterektől a .trim() segítségével szabadulunk meg. Ezt csupán elővigyázatosságból tesszük. A mátrix befejezéséhez pedig ismét ráengedjük a .split()-et az adatsorokra. Határozzunk meg egy „cell” elnevezésű változót, amely a vesszőnél elválaszott és a végén enter nélküli adatsor-argumentumunkkal lesz egyenlő.

var cell = line.trim().split(„,”)

Most pedig minden egyes oszlopra létrehozunk egy új változót:

var userId = cell[0]

var displayName = cell[1]    

var name = cell[2]

var email = cell[3]

var templateNumber = cell[4]

var search = cell[5]

Ellenőrizzük, hogy ezeket a változókat a konkrét esetnek megfelelően módosítottuk-e, és ügyeljünk a számozásra: az első oszlop a 0., a második az 1. és így tovább.

Egy további változóra van még szükségünk, hiszen – amint az a táblázatban is látható – egyszerre rendelkezünk megjelenített névvel és teljes névvel, és a teljes névből (ha van) a keresztnevet szeretnénk használni, ha pedig üres, akkor a megjelenített nevet. Emlékezzünk, hogy a teljes nevet „name”-nek hívtuk, a megjelenített nevet pedig „displayName”-nek.

var hasName = name && name !== „”

Most van egy hasName változónk, amely akkor IGAZ értékű, ha van névváltozója és nem üres.

Ezután írjunk egy logikai kifejezést, amely először ellenőrzi, hogy a hasName IGAZ-e – ebben az esetben szétbontja a nevet (a szóköznél) két szóra: a kereszt- és a vezetéknévre, és a displayName-t a keresztnévre cseréli le. Az alábbi kód utolsó kifejezése a következőképpen működik: a displayName megegyezik a fullName értékével? Ha igen, akkor a fullName[0] értékét veszi (ilyenkor a nevet 2 szóra bontja, ebből az első van a 0. helyen). Ha nem, akkor a displayName értékét veszi.

// Use first name as displayname

if(hasName) {

   var fullName = name.split(” „)

   displayName = fullName ? fullName[0] : displayName

}

Most a displayName változó a felhasználó keresztneve lesz, ha megadta a teljes nevét, ha nem, akkor a megjelenített neve.

Érdekesség: A Samebug sok kínai felhasználóval rendelkezik, akiknek a teljes neve gyakran ilyesmi: 林长安. Ilyen esetben nincsen szóköz a kereszt- és a vezetéknév között. Kínában bevett szokás, hogy az emberek akkor is teljes nevükön szólítják egymást, amikor kötetlenül beszélgetnek egymással. Kiderült, hogy az alkalmazásunk a kulturális követelményeket is teljesíti azzal, hogy nem választja el a kínai neveket 🙂

A következő lépésben a különös esetekben alkalmazandó kiegészítő feltételeket határozzuk meg.

Ha az adott személynek nincs e-mail címe, ne történjen semmi:

//Return if no email

if(email === „NULL”) return

A fájl végén néha van egy további üres adatsor, amely hibát generálhat – szóval ha nincs felhasználóazonosító (userId), akkor ne tegyen semmit:

//Return if no user ID

if(userId === „”) return

Már majdnem készen van a piszkozatgenerátor!

Emlékszünk ugye, hogyan határoztuk meg valahol még ennek a fejezetnek az elején az e-mailek sablonjait? Most viszont meg is határozzuk a használandó sablont és kicseréljük a következő szavakat: $name és LINK. Létrehozzuk a sablon elnevezésű változót, amely az üzenetlista konkrét tételével egyezik meg. A sablon száma már megvan a templateNumber változóban, de ellenőrizni kell, hogy tényleg számról van-e szó. Ezért tehát megszorozzuk 1-gyel, hiszen a lista számozása 0-val kezdődik és a mi számaink 1-től vannak. Ezután jön a .replace(), amelynek az első argumentuma az, amit le szeretnénk cserélni, a második argumentuma pedig amire az első argumentumot cseréljük. Egyedi tételek cseréléséhez használjunk idézőjelet, minden szó cseréléséhez pedig használjuk a /yourword/g lehetőséget:

//Set and personalise messages

var template = messages[templateNumber * 1 – 1]

.replace(„$name”, displayName)

.replace(/LINK/g, search)

Sikerült végül létrehoznunk az összes változót: e-mail, tárgy és sablon – vagyis már létrehozhatjuk a piszkozatot:

GmailApp.createDraft(email, subject, template) (az argumentumok magyarul: e-mail, tárgy, sablon)

Ellenőrizzük a kód hibamentességét. Ennek a szakasznak a teljes szkriptje pedig itt olvasható:

function makeDraft(data) {  

  // Get file content

  var data = DriveApp.getFileById(„xxxxxxxxxx„).getBlob().getDataAsString()

  // Split into lines

  var allRows = data.split(„\n”)

  var subject = „Please provide feedback”

  var messages = [

        „1Hi $name,\n\nLorem ipsum dolor sit amet LINK, consectetur adipiscing elit. Fusce sed.\n\nThank you,\nIstvan\n– \nIstvan Erdo\nCustomer Relations @samebug\nhttps://samebug.io”,

        „2Hi $name,\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sed.\n\nThank you,\nIstvan\n– \nIstvan Erdo\nCustomer Relations @samebug\nhttps://samebug.io”,

        „3Hi $name,\n\nLorem ipsum dolor sit amet LINK, consectetur adipiscing elit LINK. Fusce sed.\n\nThank you,\nIstvan\n– \nIstvan Erdo\nCustomer Relations @samebug\nhttps://samebug.io”,

        „4Hi $name,\n\nLorem ipsum dolor sit amet LINK, consectetur adipiscing elit. Fusce sed.\n\nThank you,\nIstvan\n– \nIstvan Erdo\nCustomer Relations @samebug\nhttps://samebug.io”

  ]

  // Loop through all rows

  allRows.map(function(line, key) {

    if(key === 0) return

    var cell = line.split(„,”)

    var userId = cell[0]

    var displayName = cell[1]    

    var name = cell[2]

    var email = cell[3]

    var templateNumber = cell[4]

    var search = cell[5]

    var hasName = name && name !== „”

    // Use first name as displayname

    if(hasName) {

      var fullName = name.split(” „)

      displayName = fullName ? fullName[0] : displayName

    }

    //Return if no email

    if(email === „NULL”) return

    //Return if no user ID

    if(userId === „”) return

    //Set and personalise messages

    var template = messages[templateNumber * 1 – 1]

    .replace(„$name”, displayName)

    .replace(/LINK/g, search)

    GmailApp.createDraft(email, subject, template)

  })

}

Ha már nincsenek hibák, akkor futtassuk le és nézzük meg az eredményt.

Az eredmény a következő: 4 piszkozat, mindegyik különböző sablonnal, az e-mail tekintetében NULL-értékű személy nem szerepel, minden $name és LINK helyett az adott felhasználó adatai szerepelnek. Ne aggódjunk a nem linkként formázott (aláhúzás, kék betűszín) linkek miatt, az e-mailek kiküldése után a Google automatikusan formázza őket:

Következtetés és a következő lépés

Hosszú utat jártunk be: létrehoztunk egy teljesen automatikus, egyedi piszkozatokat készítő alkalmazást, amely CSV-fájlból nyeri az adatokat. Most pedig jöjjön a befejező rész: <a href=”Link to Part 3”>3. rész</a>. Ebben elvégezzük az alkalmazásunk utolsó simításait, például szkripteket egyesítünk, hogy ne legyen annyi CSV-fájlunk, valamint létrehozunk egy függvényt, amely időbélyegzőt helyez el az adatbázisunkban a Levél elküldve fejlécű oszlopba.