CONTEXT
I'm using a script to fetch emails that I found here (from Niclas, I think): fetching emails script
I've adapted it to my needs and it works very well!
WHAT I'VE TESTED
I saw getDescription() from the Google script Class Attachment but couldn't get it to work and I'm not even sure if that's the way to go
WHAT I WOULD WANT
In addition, I would like to fetch the attachments filenames since It's a distinctive mark on each email
Any help would me very appreciated. Thanks in advance
Modifications:
Do the following modifications to the answer of the post you mentioned in your question:
Add these lines:
var attachments = messages[maxIndex].getAttachments();
var attNames = attachments.map(att=>att.getName());
and modify this one:
ss.appendRow([from, cc, time, sub,...attNames ,'https://mail.google.com/mail/u/0/#inbox/'+mId])
Solution:
function myFunction() {
// Use sheet
var ss = SpreadsheetApp.getActiveSheet();
// Gmail query
var query = "label:support -label:trash -label:support-done -from:me";
// Search in Gmail, bind to array
var threads = GmailApp.search(query);
// Loop through query results
for (var i = 0; i < threads.length; i++)
{
// Get messages in thread, add to array
var messages = threads[i].getMessages();
// Used to find max index in array
var max = messages[0];
var maxIndex = 0;
// Loop through array to find maxIndexD = most recent mail
for (var j = 0; j < messages.length; j++) {
if (messages[j] > max) {
maxIndex = j;
max = messages[j];
}
}
// Find data
var mId = messages[maxIndex].getId() // ID used to create mail link
var from = messages[maxIndex].getFrom();
var cc = messages[maxIndex].getCc();
var time = threads[i].getLastMessageDate()
var sub = messages[maxIndex].getSubject();
var attachments = messages[maxIndex].getAttachments();
var attNames = attachments.map(att=>att.getName());
// Write data to sheet
ss.appendRow([from, cc, time, sub,...attNames ,'https://mail.google.com/mail/u/0/#inbox/'+mId])
}
}
Don't forget to change the value of query to your needs.
References:
Class GmailMessage
map()
Rest parameters
You have to enable V8 runtime to be able to use the snippet.
Related
I'm working on a project that take "profiles" stored in a Google Sheet, makes a unique Google Doc for each profile, and then updates the unique Google Doc with any new information when you push a button on the Google Sheet.
I have some other automations built into my original code, but I simplified most of it to what's pertinent to the error I'm getting, which is this:
Exception: Document is missing (perhaps it was deleted, or you don't have read access?
It happens on Line 52 of my script in the fileUpdate funtion. Here's the appropriate line for reference:
var file = DocumentApp.openById(fileName);
And this is the rest of my code:
function manageFiles() {
//Basic setup. Defining the range and retrieving the spreadsheet to store as an array.
var date = new Date();
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var array = sheet.getDataRange().getValues();
var arrayL = sheet.getLastRow();
var arrayW = sheet.getLastColumn();
for (var i = 1; i < arrayL; i++) {
if (array[i][arrayW-2] == "") {
//Collect the data from the current sheet.
//Create the document and retrieve some information from it.
var docTitle = array[i , 0]
var doc = DocumentApp.create(docTitle);
var docBody = doc.getBody();
var docLink = doc.getUrl();
//Use a for function to collect the unique data from each cell in the row.
docBody.insertParagraph(0 , "Last Updated: "+date);
for (var j = 2; j <= arrayW; j++) {
var colName = array[0][arrayW-j];
var data = array[i][arrayW-j];
if (colName !== "Filed?") {
docBody.insertParagraph(0 , colName+": "+data);
}
}
//Insert a hyperlink to the file in the cell containing the SID
sheet.getRange(i+1 , 1).setFormula('=HYPERLINK("'+docLink+'", "'+SID+'")');
//Insert a checkbox and check it.
sheet.getRange(i+1 , arrayW-1).insertCheckboxes();
sheet.getRange(i+1 , arrayW-1).setFormula('=TRUE');
}
else if (array[i][arrayW-2] !== "") {
updateFiles(i);
}
}
sheet.getRange(1 , arrayW).setValue('Last Update: '+date);
}
//Note: I hate how cluttered updateFiles is. I'm going to clean it up later.
function fileUpdate(rowNum) {
//now you do the whole thing over again from createFiles()
//Basic setup. Defining the range and retrieving the spreadsheet to store as an array.
var date = new Date();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var array = sheet.getDataRange().getValues();
var arrayL = sheet.getLastRow();
var arrayW = sheet.getLastColumn();
//Collect the data from the current sheet.
var fileName = array[rowNum][0];
var file = DocumentApp.openById(fileName);
//retrieve the body of the document and clear the text, making it blank.
file.getBody().setText("");
//Use a for function to collect the the unique date from every non-blank cell in the row.
file.getBody().insertParagraph(0 , "Last Updated: "+date);
for (var j = 2; j <= arrayW; j++) {
var colName = array[0][arrayW-j];
var data = array[rowNum][arrayW-j];
file.getBody().insertParagraph(0 , colName+": "+data);
}
}
If you'd like to take a look at my sample spreadsheet, you can see it here. I suggest you make a copy though, because you won't have permissions to the Google Docs my script created.
I've looked at some other forums with this same error and tried several of the prescribed solutions (signing out of other Google Accounts, clearing my cookies, completing the URL with a backslash, widening permissions to everyone with the link), but to no avail.
**Note to anyone offended by my janky code or formatting: I'm self-taught, so I do apologize if my work is difficult to read.
The problem (in the updated code attached to your sheet) comes from your URL
Side Note:
In your initial question, you define DocumentApp.openById(fileName);
I assume your realized that this is not correct, since you updated
your code to DocumentApp.openByUrl(docURL);, so I will discuss the
problem of the latter in the following.
The URLs in your sheet are of the form
https://docs.google.com/open?id=1pT5kr7V11TMH0pJea281VhZg_1bOt8YDRrh9thrUV0w
while DocumentApp.openByUrl expects a URL of form
https://docs.google.com/document/d/1pT5kr7V11TMH0pJea281VhZg_1bOt8YDRrh9thrUV0w/
Just adding a / is not enough!
Either create the expected URL manually, or - much easier / use the method DocumentApp.openById(id) instead.
For this, you can extract the id from your URL as following:
var id = docURL.split("https://docs.google.com/open?id=")[1];
var file = DocumentApp.openById(id)
I'm new to Javascript/Coding in general and am trying to solve a problem.
I have a program that pulls all my contact emails into an array then sorts through them and adds new emails. Pulling all the contacts takes too long (2000 contacts!) and I want to have Google Apps Script run this part automatically every 5 minutes to I have an updated list if I want to run the sort function.
Is it possible to make the contact pulling part of my function it's own function then use logger.log to save the values for use later? How can a seperate function access the logged info from this new function?
function getEmailAddressList(){
var addrA = [];
var contact = ContactsApp.getContacts();
//////////
for(var i = 0; i < contact.length; i++){
var addresses = contact[i].getEmails();
for(var j = 0;j < addresses.length; j++){
var address = addresses[j].getAddress();
addrA.push(address);
logger.log(addrA);
};
};
Depending upon the size of your data, you can store the data in "Properties Service" or "Cache." Properties Service can hold 9k of data per property name and a total of 500k. If the data will ever be larger than that, then you will need a different data storage option. For example, you could store the contact information in a text file in your Google Drive. You could obviously save your data to a database also. The point is, you need to store the contact information somewhere. You can not store it in a global variable, because a global variable will loose it's data as soon as the code stops running. Logger.log() won't help you in this situation.
You can run code on a time interval with a time based trigger. You will need to set that up from the "Edit" menu and "Current project's triggers."
Here is an example of how you might store the data in a text file:
function getEmailAddressList(){
var addrA,addresses,addressFile,allContacts,
contactsAsJSON,i,j,thisAddress,thisContact;
allContacts = ContactsApp.getContacts();
//////////
addrA = [];
for (i = 0; i < allContacts.length; i++) {
addresses = allContacts[i].getEmails();
for (j = 0;j < addresses.length; j++) {
thisAddress = addresses[j].getAddress();
thisContact = addresses[j].getDisplayName();
addrA.push(thisAddress);
Logger.log(thisContact);
}
}
contactsAsJSON = JSON.stringify(addrA);
try{
addressFile = DriveApp.getFileById('id');
}catch(e){}
if (!addressFile) {
addressFile = DriveApp.createFile('My Contacts', contactsAsJSON);
Logger.log('New file ID: ' + addressFile.getId());//Get the new file ID and hard code into
//the code above
}
addressFile.setContent(contactsAsJSON);
};
Here is an example of how you might store all contacts in Cache Service with each contact being one property name:
function getEmailAddressList(){
var addresses,allContacts,Cache,i,j,thisAddress,thisContact;
Cache = CacheService.getDocumentCache();
allContacts = ContactsApp.getContacts();
//////////
for (i = 0; i < allContacts.length; i++) {
addresses = contact[i].getEmails();
for (j = 0;j < addresses.length; j++) {
thisAddress = addresses[j].getAddress();
thisContact = addresses[j].getDisplayName();
Cache.put(thisContact, thisAddress)
logger.log(thisContact);
}
}
};
I am very new to Google Apps Scripts and am curious how I can use functions created in my own project. For example, I have a script bound to a spreadsheet with just one function:
function addOrder(title, content) {
var sheet = SpreadsheetApp.getActiveSheet();
sheet.appendRow([ Date(), title, content]);
}
It simply takes 2 arguments and adds a row to the spreadsheet with that data. I have deployed it as a web app, but I'm not sure how to use this function in an environment like JSFiddle. Any help is appreciated.
Thanks
Spreadsheet bound scripts run server side and the SpreadsheetApp.getActiveSheet() method you are using will only work in the context of a spreadsheet bound script since it's the only case where the script actually "sees" an active spreadsheet. When you deploy this as a webapp you will have to tell the script which spreadsheet it must look at using for example the SpreadsheetApp.openById('spreadsheet ID') method.
But even doing so will not allow for using such a code outside of Google environment (as in JS fiddle for example) since SpreadsheetApp is specific to Google Apps service.
You have to remember that Google Apps Script is based on JavaScript but is not "plain" JavaScript , it uses a lot of specific services that work only in relation with Google Apps.
edit to answer your comment below :
the code used in the spreadsheet to work as a data server goes like this : (this is deployed as a webapp without user interface. It runs as a service
function doGet(e) {
if(e.parameter.mode==null){return ContentService.createTextOutput("error, wrong request").setMimeType(ContentService.MimeType.TEXT)};
var mode = e.parameter.mode;
var value = e.parameter.value;
var ss = SpreadsheetApp.openById('1yad5sZZt-X6bIftpR--OSyf3VZWf3Jxx8UJBhh7Arwg');
var sh = ss.getSheets()[0];
if(mode=='read'){
var sheetValues = sh.getDataRange().getValues();// get data from sheet
var valToReturn = ContentService.createTextOutput(JSON.stringify(sheetValues)).setMimeType(ContentService.MimeType.JSON);
return valToReturn;// send it as JSon string
}
if(mode=='write'){
var val = Utilities.base64Decode(value,Utilities.Charset.UTF_8);// decode base64 and get an array of numbers
Logger.log(val);// see it !
var stringVal = ''; // create an empty string
for(var n in val){
stringVal += String.fromCharCode(val[n]);// add each character in turn
}
var sheetValues = JSON.parse(stringVal);// convert the string into an object (2D array)
Logger.log(sheetValues);// check result
sh.getRange(1,1,sheetValues.length,sheetValues[0].length).setValues(sheetValues);// update the sheet
return ContentService.createTextOutput(JSON.stringify(sheetValues)).setMimeType(ContentService.MimeType.JSON);// send back the result as a string
}
return ContentService.createTextOutput('error').setMimeType(ContentService.MimeType.TEXT);// in case mode is not 'read' nor 'write'... should not happen !
}
you can call this service by its url + parameters and it will get / set values in the spreadsheet. This is a basic example but it works nicely.
below it the webapp code of the Ui that uses this service in this spreadsheet
var stylePanel = {'padding':'50px', 'background':'#FFA'};
var styleButton = {'padding':'5px', 'border-radius':'5px', 'borderWidth':'1px', 'borderColor':'#DDD','fontSize':'12pt'};
var styleTextItalic = {'fontSize':'12pt','fontStyle':'italic','fontFamily':'arial,sans-serif','color':'#F00'};
var styleTextNormal = {'fontSize':'12pt','fontStyle':'normal','fontFamily':'arial,sans-serif','color':'#00F'};
var styleLabel = {'fontSize':'12pt','color':'#F00'};
var url = 'https://script.google.com/macros/s/AKfycbwPioVjYMSrmhKnJOaF2GG83dnstLWI7isU9SF1vxPV8td-g9E7/exec';
var numRow = 21;// the number of rows in the grid = number of rows in the SS + 1
;
function doGet() {
var app = UiApp.createApplication().setTitle('url_fetch_demo');
var panel = app.createVerticalPanel().setStyleAttributes(stylePanel);
var headers = ['Field Name','Your answer'];// grid title
var grid = app.createGrid(numRow+2,2);// create the grid with right size
var wait = app.createImage('https://dl.dropboxusercontent.com/u/211279/loading3T.gif').setId('wait').setVisible(false);// get a spinner image in animated gif
var handlerWrite = app.createServerHandler('writeSheet').addCallbackElement(grid);// 2 handlers for the buttons
var handlerRead = app.createServerHandler('readSheet').addCallbackElement(grid);
var Chandler = app.createClientHandler().forTargets(wait).setVisible(true);// a client handler for the spinner
var buttonWrite = app.createButton('Write to Sheet',handlerWrite).addClickHandler(Chandler).setStyleAttributes(styleButton);
var buttonRead = app.createButton('Read from Sheet',handlerRead).addClickHandler(Chandler).setStyleAttributes(styleButton);
for(var n=1 ; n < numRow ; n++){
for(var m=0 ; m < 2 ; m++){ // create all the textBoxes with names & IDs
var textBox = app.createTextBox().setText('no value').setName('text'+n+'-'+m).setId('text'+n+'-'+m).setStyleAttributes(styleTextNormal);
//if(m==0){textBox.setEnabled(false)};// prevent writing to left column (optional)
grid.setWidget(n,m,textBox);// place widgets
}
}
grid.setWidget(numRow,0,buttonRead).setWidget(numRow,1,buttonWrite).setWidget(numRow+1,1,wait) // place buttons
.setWidget(0,0,app.createLabel(headers[0]).setStyleAttributes(styleLabel)) // and headers
.setWidget(0,1,app.createLabel(headers[1]).setStyleAttributes(styleLabel));
app.add(panel.add(grid));
return app; // show Ui
}
function writeSheet(e){
var app = UiApp.getActiveApplication();
app.getElementById('wait').setVisible(false);// spinner will be hidden when fct returns
var dataArrayImage = [];// an array to get typed values
for(var n=1 ; n < numRow ; n++){
var row=[];
for(var m=0 ; m < 2 ; m++){
row.push(e.parameter['text'+n+'-'+m]); // get every value in every "cell"
var textBox = app.getElementById('text'+n+'-'+m).setStyleAttributes(styleTextItalic);// update "cells" style
//textBox.setText('written value = '+e.parameter['text'+n+'-'+m]);// rewrite to the cells - not usefull but serves to check while debugging
}
dataArrayImage.push(row);// store one row(=2cells)
}
var UiValues = JSON.stringify(dataArrayImage);// stringfy the array
var newValues = url+'?mode=write&value='+Utilities.base64Encode(UiValues,Utilities.Charset.UTF_8);// add to url & parameters+ encode in pure ASCII characters
Logger.log(newValues);// check in logger
var check = UrlFetchApp.fetch(newValues).getContent();// get back the result
Logger.log(check);// check result = newValues sent back in bytes format
return app;//update Ui
}
function readSheet(e){
var app = UiApp.getActiveApplication();
app.getElementById('wait').setVisible(false);
var returnedValue = UrlFetchApp.fetch(url+'?mode=read').getContentText();// get data from server
Logger.log(returnedValue);// check values
var sheetValues = JSON.parse(returnedValue);
for(var n=1 ; n < numRow ; n++){
for(var m=0 ; m < 2 ; m++){
var textBox = app.getElementById('text'+n+'-'+m).setStyleAttributes(styleTextNormal);
textBox.setText(sheetValues[n-1][m]);// iterate and update cells values
}
}
return app;// update Ui
}
I am trying to parse forex values (all of them) for http://indicador.eof.cl/rss XML feed into a Gooogle Sites trough Google Apps Script.
The script as follow>
function doGet(){
var response = UrlFetchApp.fetch("http://indicador.eof.cl/rss").getContentText();
var parsedResponse = Xml.parse(response, false);
var root = parsedResponse.getElement();
var entries = root.getElement('channel').getElements("item");
for (var i=0; i<entries.length; i++) {
var e = entries[i];
var title = e.getElement("title").getText();
var description = e.getElement("description").getText();
}
var app = UiApp.createApplication();
var TopVar = app.createHorizontalPanel();
TopVar.add(app.createLabel(title).setStyleAttribute("fontSize","12px"));
TopVar.add(app.createLabel(description).setStyleAttribute("fontSize","12px"));
app.add(TopVar);
return app;
}
The issue is the code just bring me the first value no all of them, what i am forgetting?
Best Regards,
Try to move TopVar.add(...); lines inside for loop :
var app = UiApp.createApplication();
var TopVar = app.createHorizontalPanel();
for (var i=0; i<entries.length; i++) {
var e = entries[i];
var title = e.getElement("title").getText();
var description = e.getElement("description").getText();
TopVar.add(app.createLabel(title).setStyleAttribute("fontSize","12px"));
TopVar.add(app.createLabel(description).setStyleAttribute("fontSize","12px"));
}
Actually, I know nothing about google-apps-script. But your current code logic seems a bit off. It doesn't make use of values of local variables declare inside for loop (e, title, and description). Value of those variables changed in every iteration without any code using it.
I copied this function from somewhere on the web. My goal is to import the body of specifically labeled emails in my gmail account to a google spreadsheet. While I'm not completely inept when it comes to coding, I'm not familiar with this stuff.
Some details that may be relevant: Each of these Emails I am trying to import are NOT conversations, they are a single email received, no response and no earlier messages in the thread. I want the entire body of each Email placed in a single cell in the spreadsheet.
As it is, only the subject is being placed in my spreadsheet. How can I get it to bring the body, as well? I feel like it's being placed in the array, but when looping the setValue it gets skipped.
Much love, folks!
Code:
function getMessagesWithLabel() {
var destArray = new Array();
var threads = GmailApp.getUserLabelByName('abc').getThreads(1,10);
for(var n in threads){
var msg = threads[n].getMessages();
var destArrayRow = new Array();
destArrayRow.push('thread has '+threads[n].getMessageCount()+' messages');
for(var m in msg){
destArrayRow.push(msg[m].getSubject());
}
destArray.push(destArrayRow);
}
Logger.log(destArray);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getActiveSheet();
if(ss.getLastRow()==0){sh.getRange(1,1).setValue('getMessagesWithLabel() RESULTS')};
sh.getRange(ss.getLastRow()+1,1,destArray.length,destArray[0].length).setValues(destArray)
}
You're not employing .getBody() anywhere in your script? In a case like this I'd store each message as an object with subject and body as separate values:
var msg = threads[n].getMessages();
var contentArray = [];
for(var i = 0; i < msg.length; i++){
var obj = {
subject: msg[i].getSubject(),
body: msg[i].getBody()
};
contentArray.push(obj);
}
These values can then be iterated through using the following notation:
console.log(contentArray[0].subject);
console.log(contentArray[0].body);