How can I get more details of attachment using Google Script? - google-apps-script

I am trying to write a small Google Script to extract the details of my emails especially the attachments. In this example I am trying to get the 'user' and 'attachment' details in the Log window.
After running the code, I can see the 'attachment details' as :-
1) GmailAttachment - wherever attachment is present
2) Undefined - wherever attachment isn't present
I would like to check that how can I get more details of Attachment like Name of Attachment, Url of Attachment, type etc. is that possible through Google Scripting?
function testing1() {
Logger.log(Session.getActiveUser().getEmail());
var mail1 = GmailApp.getInboxThreads();
for(var x=0;x<mail1.length;x++){
var msg = mail1[x].getMessages();
for(var i=0;i<msg.length;i++){
var mesg = msg[i].getBody();
var att = msg[i].getAttachments()[0];
Logger.log(att);
}
}
}
Thanks in advance for your guidance.
Regards,
Alok

You can loop through attachments (if available) and get the file name and size from the GmailAttachment class.
for(var i=0;i<msg.length;i++){
var mesg = msg[i].getBody();
var att = msg[i].getAttachments();
for (var a=0; a<att.length; a++) {
Logger.log(att[1].getName());
Logger.log(att[1].getSize());
}
}

Related

Extract Inline images from Gmail Body

I am trying to extract all the images store in Inline body of email, store it in drive folder.
I am tryin to use this code
function GETGMEmails(){
var label = GmailApp.getUserLabelByName ('WHOLESALE REP');
var threads = label.getThreads();
for(var i = threads.length - 1; i >= 0; i--){
var messages = threads[i].getMessages();
for (var j = 0; j < messages.length; j++){
var message = messages[j];
// extractDetails(message,folder)
fetchInlineImage(message)
}
}
}
function fetchInlineImage(message) {
var msg = message;
console.log(message)
var pattern = /<img.*src="([^"]*)"[^>]*>/;
var matches = pattern.exec(msg.getBody());
console.log(matches.length)
if(matches) {
var url = matches[1];
var urlPattern = /^https*\:\/\/.*$/;
// If this matches, that means this was copied and pasted from a browser and it's a
// standard URL that we can urlFetch
if(urlPattern.exec(url)) {
// NO OP!
} else {
// Else this means the user copied and pasted from an OS clipboard and the image
// exists on Google's servers. The image can only be retrieved when logged in. Fortunately,
// if we use URLFetchApp, this will act as a logged in user and be able to URLFetch the image.
// We'll need to prepend a Gmail URL (subject to change)
url = "https://mail.google.com/mail/u/0/" + url;
}
// TODO - there is one more case that we're not covering - embedded images that newsletters use
Logger.log("Fetching image from URL: " + url);
var response = UrlFetchApp.fetch(url);
Logger.log("Response status: " + Utilities.jsonStringify(response.getHeaders()));
var blob = response.getBlob();
Logger.log("Response blob: " + blob.getBytes().length);
Drivefolder.createFile(blob).setName('ss.jpeg')
}
};
Email look like this, with lot of images here and there, and I want to extract each one of them:-
enter image description here
You are trying to access inline images from a GMail message.
When GMail was first introduced there was no ability to access inline images.
In 2012 an Issue:Access to inline images was raised and the script shown in the question was proposed as a workaround. A question was also asked on StackOverflow Parsing inlineImages from Gmail raw content.
These (and some variants) worked for a few years until 2014 when a second Issue:GmailApp.getAttachments Issue was raised.
Workarounds were patchy until 2017 when it was announced that the Issue had been resolved.
In 2018, a new answer was added to the StackOverFlow question. and in 2022 a new StackOverflow question How can I extract inline images from a Gmail email? (all available workarounds do not work anymore) was asked and answered.
In short, the script that you are using is redundant. However, the process to access inline images is simple and straightforward. The key is to examine the body by Regex.
The following script provides a basis for you to identify and log inline images.
Put headers in row 1 of sheet="Images".
A1="ID", B1="Subject", C1="Image"
function so75327302() {
var label = GmailApp.getUserLabelByName ('WHOLESALE REP');
var threads = label.getThreads();
for (var i=0;i<threads.length;i++){
var messages = threads[i].getMessages()
for (m=0;m<messages.length;m++){
// Logger.log("DEBUG: i:"+i+",m:"+m+", subject:"+messages[m].getSubject()+", message ID:"+messages[m].getId())
var body = messages[m].getBody()
var regex1 = RegExp('<img src="([^"]*)"[^>]*>', 'g')
var array1 = new Array
var images = new Array
while ((array1 = regex1.exec(body)) !== null) {
Logger.log("message ID:"+messages[m].getId()+", Subject: "+messages[m].getSubject()+" contains inline images."+ `Found ${array1[0]}`);
images.push([array1[0]])
}
if (images.length > 0){
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheetName = "Images"
var sheet = ss.getSheetByName(sheetName)
var imageLR = sheet.getLastRow()
sheet.getRange(imageLR+1,1).setValue(messages[m].getId())
sheet.getRange(imageLR+1,2).setValue(messages[m].getSubject())
sheet.getRange(imageLR+1,3,images.length).setValues(images)
}
}
}
}

Most efficient way to traverse Gmail attachments and download to Google Drive?

Using Google Apps Script, is there a more efficient way to traverse my Gmail - picking out 'non-starred' emails that have a particular label assigned to them and then download the attachments to Google Drive?
My code works, but typically 'times out' after processing about 25 image attachments (using non-paid Gmail account)
The piece of code that does the work is as follows:
// Loop through the messages for each thread
for (var i = 0 ; i < messages.length; i++) {
for (var j = 0; j < messages[i].length; j++) {
var CurrentMsg = messages[i][j];
if (!CurrentMsg.isStarred()){
var att = CurrentMsg.getAttachments();
// If there were no attachments, create a 'dummy text file' to notify the user that there were no attachments for that email.
var MsgSubject = CurrentMsg.getSubject();
if (att.length == 0){
var file = folder.createFile(MsgSubject,'There were no attachments',MimeType.PLAIN_TEXT);
}
else{
for (var k = 0; k < att.length; k++){
var file = folder.createFile(att[k].copyBlob().getAs('image/jpeg').setName(MsgSubject));
}
}
CurrentMsg.star();
}
}
}
Any tips gratefully received!
If you want to look through certain emails only, like those that are not starred in your case, consider using the search() method. This will help you avoid looping over threads and messages you don't need.
If you need to bypass your maximum execution time, check out this answer and this article (also linked in the answer).
I would recommend limiting results via a query then using the foreach function to go through messages:
// limit the list of messages to iterate through
var query = 'has:attachment label:particular';
var results = Gmail.Users.Messages.list(userId, {q: query});
results.messages.forEach(function (m) {
var msg = GmailApp.getMessageById(m.id);
msg.getAttachments().forEach(function (a) {
var fileName = a.getName();
fileName = saveAttachmentToFolder(folder, a, fileName, msg.getDate(), input.tz);
});
});
The code snippet above is based on a Gmail add-on that I created, specifically for saving attachments to labeled folders in Drive: https://github.com/ellaqezi/archiveByLabel/blob/main/Code.gs#L24
In the label field, you can define nested directories to create in Drive e.g. foo/bar.
In the query field, you can copy the parameters as you would use them in Gmail's search bar.

No item with the given ID can be found onSubmit Google Form

I created a script that runs onSubmit on a Google Form. It should get the ID of the image uploaded to the form, get the Image as Blob and then forward it to some email adress. The thing is, is that sometimes (about 1 in 10), the script gives the following error:
Exception: No item with the given ID could be found, or you do not
have permission to access it.
at on_Submit(Code:6:24)
I figured it would have to do with the time it takes Google to Upload/Move the file into Drive, so I set a timeout to give it some time. The error still appears, a little less frequent. Does anyone understand where this could go wrong?
The code:
function on_Submit(e) {
Utilities.sleep(30 * 1000)
var res = e.response
var image_id = res.getItemResponses()[0].getResponse()
var image = DriveApp.getFileById(image_id).getBlob()}
The on_Submit(e) function is linked to a manual trigger to enable the use of DriveApp.
Thanks
Some of the responses turned out to have multiple file uploads. The response to that question was an array of ID's. Here's the correct code:
Utilities.sleep(30 * 1000)
var res = e.response
var image_id = res.getItemResponses()[0].getResponse()
console.log(image_id)
if(Array.isArray(image_id)){
var images = [];
for(var i in image_id){
var id = image_id[i]
var image = DriveApp.getFileById(id).getBlob()
images.push(image)
}
console.log(images)
GmailApp.sendEmail(SOME_EMAIL, SUBJECT, BODY, {attachments: images})
}
else{
var image = DriveApp.getFileById(image_id).getBlob()
GmailApp.sendEmail(SOME_EMAIL, SUBJECT, BODY, {attachments: [image]})
}

Trying to set permissions for "anyone with link can view" on file uploaded via form "file upload" section

A "File upload" section is used in a form: I need the form user (not owner) to be able to share the file with someone other than the owner of the form.
It appears the results of the file-upload in the form puts the file into the current form user's Drive and provides a URL that the owner of the form can view. I need that URL to be sharable with others (via the script, not manual intervention).
Triggered by the form input, my script runs:
DriveApp.getFileById(id).setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.NONE);
... where id is derived from the URL provided from the form (see Easiest way to get file ID from URL on Google Apps Script ... as I know I'm getting the ID right).
As just user with a link to the form, I get no errors... nor do I see errors entering in the form as the owner.
But, the URL cannot be seen by anybody but the spreadsheet owner (not even the user who entered and owns the file can view the link)... so DriveApp.Access.ANYONE_WITH_LINK is not working. Non-owners of the form will be running this. I'm guessing setSharing is being run as the owner of the form, not the user, and therefore can't set it to be sharable? Is there a way for the script to make the file/link viewable?
But, even if the form owner runs the form, the URL can't be viewed by others. I don't think the ID changes once the file becomes sharable? So I must be calling setSharing incorrectly?
Maybe the ID in the URL provided by the form is n some "no-mans-land" where it's not the true ID of the file?
Example routine that just goes down column 4 of a spreadsheet with these URLs and makes them sharable:
`
function SetPermissions() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var maintsheet = ss.getSheetByName('Maintenance');
var lastmr = maintsheet.getLastRow();
for (var i = 2; i <= lastmr; i++) {
var fileURL = maintsheet.getRange(i,4).getValue();
Logger.log(fileURL);
for (var j in fileURL.split(",")) {
Logger.log(j);
Logger.log(fileURL.split(",")[j]);
var fileID = fileURL.split(",")[j].match(/[-\w]{25,}/)[0]
Logger.log(fileID);
DriveApp.getFileById(fileID).setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.NONE);
}
}
}
`
... run by the owner of the form/spreadsheet, no errors, and the logged data looks correct. But, give the URL to another user, and they get a permissions error.
This fixed it:
function SetPermissions() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var maintsheet = ss.getSheetByName('Maintenance');
var lastmr = maintsheet.getLastRow();
for (var i = 2; i <= lastmr; i++) {
var fileURL = maintsheet.getRange(i,4).getValue();
Logger.log(fileURL);
for (var j in fileURL.split(",")) {
Logger.log(fileURL.split(",")[j]);
var fileID = fileURL.split(",")[j].match(/[-\w]{25,}/)[0]
Logger.log(fileID);
var file = DriveApp.getFileById(fileID);
file.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW);
var newURL = file.getUrl();
maintsheet.getRange(i, 4).setValue(newURL);
}
}
}
Doh: https://developers.google.com/apps-script/reference/drive/permission
... I was telling it: no permissions to anybody with the link! VIEW, is the right option!
Here's a function that given the sort of list of URLs the form will stick in the cell (the form can be setup to allow multiple pics per cell), set the permissions, and return the list of URLs back, this time as Google formats the URL in getURL():
function AnyoneWithLinkCanView(urls) {
var newURLs = "";
if ( urls.length > 0 ) {
for each (var j in urls.split(",")) {
var fileid = j.match(/[-\w]{25,}/)[0];
var file = DriveApp.getFileById(fileid);
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
if ( newURLs.length > 0 ) {
newURLs += ", ";
}
newURLs += file.getUrl();
}
}
return newURLs;
}
In my case, as the URLs will form the message body of an email, my call looks like:
var message = AnyoneWithLinkCanView(dataSheet.getRange(lastr,i).getValue());
Form inputs run as whoever creates the form trigger. If you want the script to run as her give her access to the script and a macro that creates the form trigger.

How to make getAttachments() in gmailapp to exclude images from signature?

I am trying to read Gmail attachments and push it to Google Drive. It works great except for one minor issue/behavior.
Whenever I call the getAttachments() method, it includes the inline signature image which is irrelevant in the drive folder. Is there any way to exclude the signature image (or inline images altogether) so that I will be able to push only the attached files?
Below is my code if you want to review.
var threads = myLabel.getThreads(0,500);
for (var threadIdx=0; threadIdx<threads.length; threadIdx++) {
var thread = threads[threadIdx];
var messages = thread.getMessages();
for (var msgIdx=0; msgIdx<messages.length; msgIdx++) {
var message = messages[msgIdx];
var attachments = message.getAttachments();
Logger.log(attachments.length);
}
}
So I always get the attachments.length to be one more than the actual number of attachments, when there is a signature image.
Can someone help?
Sorry for late reply but I was facing the same issue and found a workaround,
Your message.getBody() method will contain whole message body. This will also contain signature image name. So what we can do we can check signature name from attachment and if my body text contains this signature name then I should emit that attachment. Here's the code snippet,
var textMessage = message.getBody();
if (attachments.length > 0) {
for (var z=0; z<attachments.length; z++) {
var attachment = attachments[z];
if(textMessage.indexOf(attachment.getName()) === -1)
{
folder.createFile(attachment);
}
}
}
I've checked for Inline attachments as well, this code considers those as an attachment only.
Use
var attachments = message.getAttachments({
includeInlineImages : false
});
instead of
var attachments = message.getAttachments();
It will exclude inline images. Signature in the email message is also an inline image which is being extracted as attachment in your case.