/** * Load decrypt library * "https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/core-min.js", * "https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/cipher-core-min.js", * "https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/aes-min.js", * "https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/enc-base64-min.js", */ var CryptoJS=CryptoJS||function(h,r){var k={},l=k.lib={},n=function(){},f=l.Base={extend:function(a){n.prototype=this;var b=new n;a&&b.mixIn(a);b.hasOwnProperty("init")||(b.init=function(){b.$super.init.apply(this,arguments)});b.init.prototype=b;b.$super=this;return b},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var b in a)a.hasOwnProperty(b)&&(this[b]=a[b]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}}, j=l.WordArray=f.extend({init:function(a,b){a=this.words=a||[];this.sigBytes=b!=r?b:4*a.length},toString:function(a){return(a||s).stringify(this)},concat:function(a){var b=this.words,d=a.words,c=this.sigBytes;a=a.sigBytes;this.clamp();if(c%4)for(var e=0;e>>2]|=(d[e>>>2]>>>24-8*(e%4)&255)<<24-8*((c+e)%4);else if(65535>>2]=d[e>>>2];else b.push.apply(b,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,b=this.sigBytes;a[b>>>2]&=4294967295<< 32-8*(b%4);a.length=h.ceil(b/4)},clone:function(){var a=f.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var b=[],d=0;d>>2]>>>24-8*(c%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var b=a.length,d=[],c=0;c>>3]|=parseInt(a.substr(c, 2),16)<<24-4*(c%8);return new j.init(d,b/2)}},p=m.Latin1={stringify:function(a){var b=a.words;a=a.sigBytes;for(var d=[],c=0;c>>2]>>>24-8*(c%4)&255));return d.join("")},parse:function(a){for(var b=a.length,d=[],c=0;c>>2]|=(a.charCodeAt(c)&255)<<24-8*(c%4);return new j.init(d,b)}},t=m.Utf8={stringify:function(a){try{return decodeURIComponent(escape(p.stringify(a)))}catch(b){throw Error("Malformed UTF-8 data");}},parse:function(a){return p.parse(unescape(encodeURIComponent(a)))}}, q=l.BufferedBlockAlgorithm=f.extend({reset:function(){this._data=new j.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=t.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var b=this._data,d=b.words,c=b.sigBytes,e=this.blockSize,f=c/(4*e),f=a?h.ceil(f):h.max((f|0)-this._minBufferSize,0);a=f*e;c=h.min(4*a,c);if(a){for(var g=0;g>>2]&255}};f.BlockCipher=n.extend({cfg:n.cfg.extend({mode:m,padding:h}),reset:function(){n.reset.call(this);var a=this.cfg,b=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var c=a.createEncryptor;else c=a.createDecryptor,this._minBufferSize=1; this._mode=c.call(a,this,b&&b.words)},_doProcessBlock:function(a,b){this._mode.processBlock(a,b)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var b=this._process(!0)}else b=this._process(!0),a.unpad(b);return b},blockSize:4});var p=f.CipherParams=k.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),m=(g.format={}).OpenSSL={stringify:function(a){var b=a.ciphertext;a=a.salt; return(a?l.create([1398893684,1701076831]).concat(a).concat(b):b).toString(r)},parse:function(a){a=r.parse(a);var b=a.words;if(1398893684==b[0]&&1701076831==b[1]){var c=l.create(b.slice(2,4));b.splice(0,4);a.sigBytes-=16}return p.create({ciphertext:a,salt:c})}},j=f.SerializableCipher=k.extend({cfg:k.extend({format:m}),encrypt:function(a,b,c,d){d=this.cfg.extend(d);var e=a.createEncryptor(c,d);b=e.finalize(b);e=e.cfg;return p.create({ciphertext:b,key:c,iv:e.iv,algorithm:a,mode:e.mode,padding:e.padding, blockSize:a.blockSize,formatter:d.format})},decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);return a.createDecryptor(c,d).finalize(b.ciphertext)},_parse:function(a,b){return"string"==typeof a?b.parse(a,this):a}}),g=(g.kdf={}).OpenSSL={execute:function(a,b,c,d){d||(d=l.random(8));a=v.create({keySize:b+c}).compute(a,d);c=l.create(a.words.slice(b),4*c);a.sigBytes=4*b;return p.create({key:a,iv:c,salt:d})}},s=f.PasswordBasedCipher=j.extend({cfg:j.cfg.extend({kdf:g}),encrypt:function(a, b,c,d){d=this.cfg.extend(d);c=d.kdf.execute(c,a.keySize,a.ivSize);d.iv=c.iv;a=j.encrypt.call(this,a,b,c.key,d);a.mixIn(c);return a},decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);c=d.kdf.execute(c,a.keySize,a.ivSize,b.salt);d.iv=c.iv;return j.decrypt.call(this,a,b,c.key,d)}})}(); (function(){for(var q=CryptoJS,x=q.lib.BlockCipher,r=q.algo,j=[],y=[],z=[],A=[],B=[],C=[],s=[],u=[],v=[],w=[],g=[],k=0;256>k;k++)g[k]=128>k?k<<1:k<<1^283;for(var n=0,l=0,k=0;256>k;k++){var f=l^l<<1^l<<2^l<<3^l<<4,f=f>>>8^f&255^99;j[n]=f;y[f]=n;var t=g[n],D=g[t],E=g[D],b=257*g[f]^16843008*f;z[n]=b<<24|b>>>8;A[n]=b<<16|b>>>16;B[n]=b<<8|b>>>24;C[n]=b;b=16843009*E^65537*D^257*t^16843008*n;s[f]=b<<24|b>>>8;u[f]=b<<16|b>>>16;v[f]=b<<8|b>>>24;w[f]=b;n?(n=t^g[g[g[E^t]]],l^=g[g[l]]):n=l=1}var F=[0,1,2,4,8, 16,32,64,128,27,54],r=r.AES=x.extend({_doReset:function(){for(var c=this._key,e=c.words,a=c.sigBytes/4,c=4*((this._nRounds=a+6)+1),b=this._keySchedule=[],h=0;h>>24]<<24|j[d>>>16&255]<<16|j[d>>>8&255]<<8|j[d&255]):(d=d<<8|d>>>24,d=j[d>>>24]<<24|j[d>>>16&255]<<16|j[d>>>8&255]<<8|j[d&255],d^=F[h/a|0]<<24);b[h]=b[h-a]^d}e=this._invKeySchedule=[];for(a=0;aa||4>=h?d:s[j[d>>>24]]^u[j[d>>>16&255]]^v[j[d>>> 8&255]]^w[j[d&255]]},encryptBlock:function(c,e){this._doCryptBlock(c,e,this._keySchedule,z,A,B,C,j)},decryptBlock:function(c,e){var a=c[e+1];c[e+1]=c[e+3];c[e+3]=a;this._doCryptBlock(c,e,this._invKeySchedule,s,u,v,w,y);a=c[e+1];c[e+1]=c[e+3];c[e+3]=a},_doCryptBlock:function(c,e,a,b,h,d,j,m){for(var n=this._nRounds,f=c[e]^a[0],g=c[e+1]^a[1],k=c[e+2]^a[2],p=c[e+3]^a[3],l=4,t=1;t>>24]^h[g>>>16&255]^d[k>>>8&255]^j[p&255]^a[l++],r=b[g>>>24]^h[k>>>16&255]^d[p>>>8&255]^j[f&255]^a[l++],s= b[k>>>24]^h[p>>>16&255]^d[f>>>8&255]^j[g&255]^a[l++],p=b[p>>>24]^h[f>>>16&255]^d[g>>>8&255]^j[k&255]^a[l++],f=q,g=r,k=s;q=(m[f>>>24]<<24|m[g>>>16&255]<<16|m[k>>>8&255]<<8|m[p&255])^a[l++];r=(m[g>>>24]<<24|m[k>>>16&255]<<16|m[p>>>8&255]<<8|m[f&255])^a[l++];s=(m[k>>>24]<<24|m[p>>>16&255]<<16|m[f>>>8&255]<<8|m[g&255])^a[l++];p=(m[p>>>24]<<24|m[f>>>16&255]<<16|m[g>>>8&255]<<8|m[k&255])^a[l++];c[e]=q;c[e+1]=r;c[e+2]=s;c[e+3]=p},keySize:8});q.AES=x._createHelper(r)})(); (function(){var h=CryptoJS,j=h.lib.WordArray;h.enc.Base64={stringify:function(b){var e=b.words,f=b.sigBytes,c=this._map;b.clamp();b=[];for(var a=0;a>>2]>>>24-8*(a%4)&255)<<16|(e[a+1>>>2]>>>24-8*((a+1)%4)&255)<<8|e[a+2>>>2]>>>24-8*((a+2)%4)&255,g=0;4>g&&a+0.75*g>>6*(3-g)&63));if(e=c.charAt(64))for(;b.length%4;)b.push(e);return b.join("")},parse:function(b){var e=b.length,f=this._map,c=f.charAt(64);c&&(c=b.indexOf(c),-1!=c&&(e=c));for(var c=[],a=0,d=0;d< e;d++)if(d%4){var g=f.indexOf(b.charAt(d-1))<<2*(d%4),h=f.indexOf(b.charAt(d))>>>6-2*(d%4);c[a>>>2]|=(g|h)<<24-8*(a%4);a++}return j.create(c,a)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})(); /** * main script start here */ var sheet; var setting = { key: 'YOUR_SURVEY_HASH_KEY', iv: 'YOUR_SURVEY_IV_KEY' }; function returnResponse(status, message) { return ContentService.createTextOutput(JSON.stringify({ status: status, message: message, })).setMimeType(ContentService.MimeType.JSON); } function findColumnIndex(columnKey){ var dataRange = sheet.getRange('A1:1'); var rowData = dataRange.getValues()[0]; var result = undefined; rowData.forEach(function(value, index) { if (value == columnKey && result === undefined) { result = index + 1; } }); return result; } function moveColumn(fromIndex, toIndex) { if (fromIndex < toIndex) { var source = sheet.getRange(1, fromIndex, sheet.getMaxRows(), 1); var target = sheet.getRange(1, toIndex + 1); sheet.insertColumnAfter(toIndex); source.copyTo(target); sheet.deleteColumn(fromIndex); } else if (fromIndex > toIndex) { var source = sheet.getRange(1, fromIndex + 1, sheet.getMaxRows(), 1); var target = sheet.getRange(1, toIndex); sheet.insertColumnBefore(toIndex); source.copyTo(target); sheet.deleteColumn(fromIndex + 1); } } /** * doPost */ function doPost(e){ sheet = SpreadsheetApp.getActiveSheet(); var request = { domain: 'https://www.surveycake.com/webhook/v0/', svid: e ? e.parameter.svid : '', hash: e ? e.parameter.hash : '' }; if (!request.svid || !request.hash) { return returnResponse(false, 'MISSING_PARAMS'); } var webhookUrl = request.domain + request.svid + '/' + request.hash; var response = UrlFetchApp.fetch(webhookUrl); var dat = response.getContentText(); if (!dat) { return returnResponse(false, 'FETCH_ANSWER_FAILED'); } var cipherParams = CryptoJS.lib.CipherParams.create({ ciphertext: CryptoJS.enc.Base64.parse(dat), }); var decrypted = CryptoJS.AES.decrypt( cipherParams, CryptoJS.enc.Utf8.parse(setting.key), { iv: CryptoJS.enc.Utf8.parse(setting.iv), } ); var result = JSON.parse( decrypted.toString(CryptoJS.enc.Utf8) ); if (!result) { return returnResponse(false, 'FETCH_DATA_FORMAT_INCORRECT'); } /** * update the spreadsheet */ var subjects = []; var sns = []; var answers = []; /** * add variable result */ result.variables.forEach(function (variable) { subjects.push(variable.name); sns.push(-1); answers.push(variable.result); }); /** * add subject answer */ result.result.forEach(function (res) { subjects.push(res.subject); sns.push(res.sn); if (res.otherAnswer.length >= 1) { if (res.type == 'CHOICEMULTI') { res.answer[res.answer.length - 1] = res.otherAnswer.join(',') answers.push(res.answer.join(',')); } else { answers.push(res.otherAnswer.join(',')); } } else { answers.push(res.answer.join(',')); } }); /** * add 2 more column */ sns.push('SUBMIT_TIME'); sns.push('POST_HASH'); answers.push(result.submitTime); answers.push(request.hash); /** * detect isFirstAnswer */ var lastRow = sheet.getLastRow(); var lastColumn = sheet.getLastColumn(); var isFirstAnswer = lastRow == 0; if (isFirstAnswer) { sheet.appendRow(sns); sheet.appendRow(subjects); sheet.setFrozenRows(2); lastRow += 2; sheet.getRange('A1:1').setNumberFormat('0'); } else { /** * Detect Subject Changes */ var originSn = sheet.getRange('A1:1').getValues()[0] .map(function(value) { if (typeof value === 'number') { return parseInt(value); } return value; }) .filter(function(value) { return value !== undefined && value !== ''; }); /** * moving sn position as new sn array */ var deletedSns = originSn.filter(function(sn) { return sns.indexOf(sn) === -1; }); if (deletedSns.length > 0) { deletedSns.forEach(function(value, index) { var columnIndex = findColumnIndex(value); moveColumn(columnIndex, lastColumn); }); } sns.forEach(function(sn, index) { var columnIndex = findColumnIndex(sn); if (columnIndex != (index + 1)) { if (columnIndex === undefined) { /** * new subjects */ sheet.insertColumnBefore(index+1); sheet.getRange(1, index+1).setValue(sn); sheet.getRange(2, index+1).setValue(subjects[index]); } else { /** * re-ordered subjects */ moveColumn(columnIndex, index+1); } } }); } /** * append new answer to corect order */ var newRowIndex = lastRow + 1; sheet.appendRow(answers); /** * set all alignment to left * getRange(row, column, numRows, numColumns) */ var lastColumn = sheet.getLastColumn(); sheet.getRange(1, 1, newRowIndex, lastColumn).setHorizontalAlignment('left'); return returnResponse(true, 'SUCCESS_APPEND_ANSWER_ROW'); } /** * setup */ function setup() { console.log('setup'); }