Using Google Apps Script (), I know from the docs, how to send, forward, move to trash messages, etc. but I don't find how to remove a file attachement of an email, i.e.:
- keep the text content (either in HTML or just plain text would be fine)
- keep the original sender, keep the recipient
- keep the original message date/hour (important!)
- remove the attachment
If it's not possible via the API, is there a way to resend the message to myself, while keeping 1, 2 and 3?
Note: the GmailAttachment
class looks interesting and allows to list recipients:
var threads = GmailApp.getInboxThreads(0, 10);
var msgs = GmailApp.getMessagesForThreads(threads);
for (var i = 0 ; i < msgs.length; i++) {
for (var j = 0; j < msgs[i].length; j++) {
var attachments = msgs[i][j].getAttachments();
for (var k = 0; k < attachments.length; k++) {
Logger.log('Message "%s" contains the attachment "%s" (%s bytes)',
msgs[i][j].getSubject(), attachments[k].getName(), attachments[k].getSize());
}
}
}
but I don't find how to remove an attachment.
Note: I've already studied many other solutions for doing this, I've already read nearly every article about this (solutions with dedicated web services, with local clients like Thunderbird + Attachment extractor plugin, etc.), but none of them are really really cool. That's why I was looking for a solution to do it manually via Google Apps Script.
Using Google Apps Script (http://script.google.), I know from the docs, how to send, forward, move to trash messages, etc. but I don't find how to remove a file attachement of an email, i.e.:
- keep the text content (either in HTML or just plain text would be fine)
- keep the original sender, keep the recipient
- keep the original message date/hour (important!)
- remove the attachment
If it's not possible via the API, is there a way to resend the message to myself, while keeping 1, 2 and 3?
Note: the GmailAttachment
class looks interesting and allows to list recipients:
var threads = GmailApp.getInboxThreads(0, 10);
var msgs = GmailApp.getMessagesForThreads(threads);
for (var i = 0 ; i < msgs.length; i++) {
for (var j = 0; j < msgs[i].length; j++) {
var attachments = msgs[i][j].getAttachments();
for (var k = 0; k < attachments.length; k++) {
Logger.log('Message "%s" contains the attachment "%s" (%s bytes)',
msgs[i][j].getSubject(), attachments[k].getName(), attachments[k].getSize());
}
}
}
but I don't find how to remove an attachment.
Note: I've already studied many other solutions for doing this, I've already read nearly every article about this (solutions with dedicated web services, with local clients like Thunderbird + Attachment extractor plugin, etc.), but none of them are really really cool. That's why I was looking for a solution to do it manually via Google Apps Script.
Share Improve this question edited Oct 3, 2017 at 19:56 Basj asked Sep 26, 2017 at 19:28 BasjBasj 46.6k110 gold badges452 silver badges801 bronze badges 1- Can you provide fiddle? – artgb Commented Oct 5, 2017 at 17:06
2 Answers
Reset to default 13 +150Looks like messages will have to be re-created-ish:
Messages are immutable: they can only be created and deleted. No message properties can be changed other than the labels applied to a given message.
Using Advanced Gmail Service with the Gmail API insert() you can hack your way around it using: Gmail.Users.Messages.insert(resource, userId)
This advanced service must be enabled before use.
Example: [fill in the EMAIL_ID
with an email_id
or in whatever way you want to get the email]
function removeAttachments () {
// Get the `raw` email
var email = GmailApp.getMessageById("EMAIL_ID").getRawContent();
// Find the end boundary of html or plain-text email
var re_html = /(-*\w*)(\r)*(\n)*(?=Content-Type: text\/html;)/.exec(email);
var re = re_html || /(-*\w*)(\r)*(\n)*(?=Content-Type: text\/plain;)/.exec(email);
// Find the index of the end of message boundary
var start = re[1].length + re.index;
var boundary = email.indexOf(re[1], start);
// Remove the attachments & Encode the attachment-free RFC 2822 formatted email string
var base64_encoded_email = Utilities.base64EncodeWebSafe(email.substr(0, boundary));
// Set the base64Encoded string to the `raw` required property
var resource = {'raw': base64_encoded_email}
// Re-insert the email into the user gmail account with the insert time
/* var response = Gmail.Users.Messages.insert(resource, 'me'); */
// Re-insert the email with the original date/time
var response = Gmail.Users.Messages.insert(resource, 'me',
null, {'internalDateSource': 'dateHeader'});
Logger.log("The inserted email id is: %s",response.id)
}
This will remove the attachments from the email and re-insert it into your mailbox.
edit/update: New RegExp to work with html&plain-text only emails - should now work on multiple boundary strings
"is there a way to resend the message to myself, while keeping 1, 2 and 3?"
Yes, I decided to e up with a Python solution which mostly follows the answer by random-parts.
The full code is available here: https://gist.github./davidair/cac8a7fb130959b3110ef29aa7d0bbac
The key ponent is calling insert()
, same as in the original answer:
raw_message = urlsafe_b64encode(modified_message.as_bytes()).decode()
service.users().messages().insert(
userId='me',
body={'raw': raw_message},
internalDateSource='dateHeader'
).execute()
My tool makes it easier to find messages (just passing in Gmail queries) and removing attachments in bulk. The tool also saves original messages locally and trashes them on Gmail (so they will eventually be removed).
The code has been tested but just barely, so use at your own risk. Requires a GCP project (Gist has some pointers on setup in the description).
More information: the full implementation has two main mands: find_emails
and remove_attachments
. You first call find_emails
passing in a search query, so you should be able to call it with label:remove_attachment
, and using the -o
flag will print email ids as ma-separated. Second, you call remove_attachments
with the ma-separated list of ids, and it will download emails, strip attachments and then trash/recreate them. You have to pass the --make-changes
flag to actually do the deletion, otherwise it will download emails and cache them locally but not make changes (it's a safety feature)