This commit is contained in:
Jay Lee
2026-03-18 20:11:57 +00:00
5 changed files with 179 additions and 24 deletions

View File

@@ -4926,10 +4926,10 @@ gam print schema|schemas [todrive <ToDriveAttribute>*]
gam sendemail [recipient|to] <RecipientEntity> gam sendemail [recipient|to] <RecipientEntity>
[from <EmailAddress>] [mailbox <EmailAddress>] [replyto <EmailAddress>] [from <EmailAddress>] [mailbox <EmailAddress>] [replyto <EmailAddress>]
[cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage] [cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage]
[subject <String>] [<MessageContent>] [subject <String>] [<MessageContent>] [html [<Boolean>]]
(replace <Tag> <String>)* (replace <Tag> <String>)*
(replaceregex <RESearchPattern> <RESubstitution> <Tag> <String>)* (replaceregex <RESearchPattern> <RESubstitution> <Tag> <String>)*
[html [<Boolean>]] (attach <FileName> [charset <Charset>])* (attach <FileName> [charset <Charset>])*
(embedimage <FileName> <String>)* (embedimage <FileName> <String>)*
[newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>] [newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>]
(<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)* (<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
@@ -4937,10 +4937,10 @@ gam sendemail [recipient|to] <RecipientEntity>
gam <UserTypeEntity> sendemail recipient|to <RecipientEntity> gam <UserTypeEntity> sendemail recipient|to <RecipientEntity>
[replyto <EmailAddress>] [replyto <EmailAddress>]
[cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage] [cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage]
[subject <String>] [<MessageContent>] [subject <String>] [<MessageContent>] [html [<Boolean>]]
(replace <Tag> <String>)* (replace <Tag> <String>)*
(replaceregex <RESearchPattern> <RESubstitution> <Tag> <String>)* (replaceregex <RESearchPattern> <RESubstitution> <Tag> <String>)*
[html [<Boolean>]] (attach <FileName> [charset <Charset>])* (attach <FileName> [charset <Charset>])*
(embedimage <FileName> <String>)* (embedimage <FileName> <String>)*
[newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>] [newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>]
(<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)* (<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
@@ -4948,14 +4948,21 @@ gam <UserTypeEntity> sendemail recipient|to <RecipientEntity>
gam <UserTypeEntity> sendemail from <EmailAddress> gam <UserTypeEntity> sendemail from <EmailAddress>
[replyto <EmailAddress>] [replyto <EmailAddress>]
[cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage] [cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage]
[subject <String>] [<MessageContent>] [subject <String>] [<MessageContent>] [html [<Boolean>]]
(replace <Tag> <String>)* (replace <Tag> <String>)*
(replaceregex <RESearchPattern> <RESubstitution> <Tag> <String>)* (replaceregex <RESearchPattern> <RESubstitution> <Tag> <String>)*
[html [<Boolean>]] (attach <FileName> [charset <Charset>])* (attach <FileName> [charset <Charset>])*
(embedimage <FileName> <String>)* (embedimage <FileName> <String>)*
[newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>] [newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>]
(<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)* (<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
[threadid <String>] [threadid <String>]
gam <UserTypeEntity> sendreply
(((query <QueryGmail> [querytime<String> <Date>]*) [or|and])+) | (ids <MessageIDEntity>)
[replyto <EmailAddress>]
[subject <String>] [<MessageContent>] [html [<Boolean>]]
(attach <FileName> [charset <CharSet>])*
(embedimage <FileName> <String>)*
(<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
# Shared Drives - Administrator # Shared Drives - Administrator

View File

@@ -1,3 +1,23 @@
7.36.03
Added command to send email replies that causes Gmail to recognize the message
in conversation mode for the user sending the reply and the user receiving the reply;
GAM supplies the necessary headers and options.
```
gam <UserTypeEntity> sendreply
(((query <QueryGmail> [querytime<String> <Date>]*) [or|and])+) | (ids <MessageIDEntity>)
[replyto <EmailAddress>]
[subject <String>] [<MessageContent>] [html [<Boolean>]]
(attach <FileName> [charset <CharSet>])*
(embedimage <FileName> <String>)*
(<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
gam user user@domain.com sendreply query "rfc822MsgId:<CAAMmEdqj43...1OsQ@mail.gmail.com>" textmessage "Thanks for the information"
gam user user@domain.com sendreply ids 19cfc3506c02c22b textmessage "Thanks for the information"
```
* See: https://github.com/GAM-team/GAM/wiki/Send-Email#conversation-mode
7.36.02 7.36.02
Added option `threadid <String>` to `gam [<UserTypeEntity>] sendemail` that causes Gmail to recognize the message Added option `threadid <String>` to `gam [<UserTypeEntity>] sendemail` that causes Gmail to recognize the message

View File

@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
""" """
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>' __author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
__version__ = '7.36.02' __version__ = '7.36.03'
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)' __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
# pylint: disable=wrong-import-position # pylint: disable=wrong-import-position
@@ -7341,7 +7341,8 @@ def _addEmbeddedImagesToMessage(message, embeddedImages):
# Send an email # Send an email
def send_email(msgSubject, msgBody, msgTo, i=0, count=0, clientAccess=False, msgFrom=None, msgReplyTo=None, def send_email(msgSubject, msgBody, msgTo, i=0, count=0, clientAccess=False, msgFrom=None, msgReplyTo=None,
html=False, charset=UTF8, attachments=None, embeddedImages=None, html=False, charset=UTF8, attachments=None, embeddedImages=None,
msgHeaders=None, ccRecipients=None, bccRecipients=None, mailBox=None, threadId=None): msgHeaders=None, ccRecipients=None, bccRecipients=None, mailBox=None, threadId=None,
action=Act.SENDEMAIL):
def checkResult(entityType, recipients): def checkResult(entityType, recipients):
if not recipients: if not recipients:
return return
@@ -7397,12 +7398,13 @@ def send_email(msgSubject, msgBody, msgTo, i=0, count=0, clientAccess=False, msg
if mailBox is None: if mailBox is None:
mailBox = msgFromAddr mailBox = msgFromAddr
_, mailBoxAddr = cleanAddr(mailBox) _, mailBoxAddr = cleanAddr(mailBox)
action = Act.Get() parentAction = Act.Get()
Act.Set(Act.SENDEMAIL) Act.Set(action)
if not GC.Values[GC.SMTP_HOST]: if not GC.Values[GC.SMTP_HOST]:
if not clientAccess: if not clientAccess:
userId, gmail = buildGAPIServiceObject(API.GMAIL, mailBoxAddr) userId, gmail = buildGAPIServiceObject(API.GMAIL, mailBoxAddr)
if not gmail: if not gmail:
Act.Set(parentAction)
return return
else: else:
userId = mailBoxAddr userId = mailBoxAddr
@@ -7444,7 +7446,7 @@ def send_email(msgSubject, msgBody, msgTo, i=0, count=0, clientAccess=False, msg
server.quit() server.quit()
except Exception: except Exception:
pass pass
Act.Set(action) Act.Set(parentAction)
def addFieldToFieldsList(fieldName, fieldsChoiceMap, fieldsList): def addFieldToFieldsList(fieldName, fieldsChoiceMap, fieldsList):
fields = fieldsChoiceMap[fieldName.lower()] fields = fieldsChoiceMap[fieldName.lower()]
@@ -15385,32 +15387,35 @@ def getRecipients():
return [normalizeEmailAddressOrUID(emailAddress, noUid=True, noLower=True) for emailAddress in recipients] return [normalizeEmailAddressOrUID(emailAddress, noUid=True, noLower=True) for emailAddress in recipients]
return getNormalizedEmailAddressEntity(shlexSplit=True, noLower=True) return getNormalizedEmailAddressEntity(shlexSplit=True, noLower=True)
# gam sendemail [recipient|to] <RecipientEntity> [from <EmailAddress>] [mailbox <EmailAddress>] [replyto <EmailAddress>] # gam sendemail [recipient|to] <RecipientEntity>
# [from <EmailAddress>] [mailbox <EmailAddress>] [replyto <EmailAddress>]
# [cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage] # [cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage]
# [subject <String>] [<MessageContent>] # [subject <String>] [<MessageContent>] [html [<Boolean>]]
# (replace <Tag> <String>)* # (replace <Tag> <String>)*
# (replaceregex <REMatchPattern> <RESubstitution> <Tag> <String>)* # (replaceregex <REMatchPattern> <RESubstitution> <Tag> <String>)*
# [html [<Boolean>]] (attach <FileName> [charset <CharSet>])* # (attach <FileName> [charset <CharSet>])*
# (embedimage <FileName> <String>)* # (embedimage <FileName> <String>)*
# [newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>] # [newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>]
# (<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)* # (<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
# [threadid <String>] # [threadid <String>]
# gam <UserTypeEntity> sendemail recipient|to <RecipientEntity> [replyto <EmailAddress>] # gam <UserTypeEntity> sendemail recipient|to <RecipientEntity>
# [replyto <EmailAddress>]
# [cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage] # [cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage]
# [subject <String>] [<MessageContent>] # [subject <String>] [<MessageContent>] [html [<Boolean>]]
# (replace <Tag> <String>)* # (replace <Tag> <String>)*
# (replaceregex <REMatchPattern> <RESubstitution> <Tag> <String>)* # (replaceregex <REMatchPattern> <RESubstitution> <Tag> <String>)*
# [html [<Boolean>]] (attach <FileName> [charset <CharSet>])* # (attach <FileName> [charset <CharSet>])*
# (embedimage <FileName> <String>)* # (embedimage <FileName> <String>)*
# [newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>] # [newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>]
# (<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)* # (<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
# [threadid <String>] # [threadid <String>]
# gam <UserTypeEntity> sendemail from <EmailAddress> [replyto <EmailAddress>] # gam <UserTypeEntity> sendemail from <EmailAddress>
# [replyto <EmailAddress>]
# [cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage] # [cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage]
# [subject <String>] [<MessageContent>] # [subject <String>] [<MessageContent> ][html [<Boolean>]]
# (replace <Tag> <String>)* # (replace <Tag> <String>)*
# (replaceregex <REMatchPattern> <RESubstitution> <Tag> <String>)* # (replaceregex <REMatchPattern> <RESubstitution> <Tag> <String>)*
# [html [<Boolean>]] (attach <FileName> [charset <CharSet>])* # (attach <FileName> [charset <CharSet>])*
# (embedimage <FileName> <String>)* # (embedimage <FileName> <String>)*
# [newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>] # [newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>]
# (<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)* # (<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
@@ -15531,6 +15536,125 @@ def doSendEmail(users=None):
attachments=attachments, embeddedImages=embeddedImages, msgHeaders=msgHeaders, mailBox=mailBox, threadId=threadId) attachments=attachments, embeddedImages=embeddedImages, msgHeaders=msgHeaders, mailBox=mailBox, threadId=threadId)
Ind.Decrement() Ind.Decrement()
# gam <UserTypeEntity> sendreply
# (((query <QueryGmail> [querytime<String> <Date>]*) [or|and])+) | (ids <MessageIDEntity>)
# [replyto <EmailAddress>]
# [subject <String>] [<MessageContent>] [html [<Boolean>]]
# (attach <FileName> [charset <CharSet>])*
# (embedimage <FileName> <String>)*
# (<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
def doSendReply(users):
def _getHeaderValue(name):
for header in messageInfo['payload']['headers']:
if name == header['name']:
return _decodeHeader(header['value'])
return ''
notify = {'subject': '', 'message': '', 'html': False, 'charset': UTF8}
query = ''
queryTimes = {}
messageIds = []
msgHeaders = {}
msgReplyTo = None
attachments = []
embeddedImages = []
while Cmd.ArgumentsRemaining():
myarg = getArgument()
if myarg == 'query':
selectLocation = Cmd.Location()
if query:
query += ' '
query += f'({getString(Cmd.OB_QUERY)})'
elif myarg.startswith('querytime'):
queryTimes[myarg] = getDateOrDeltaFromNow().replace('-', '/')
elif myarg in {'or', 'and'}:
if query:
query += f' {myarg.upper()}'
elif myarg == 'ids':
selectLocation = Cmd.Location()
messageIds = getEntityList(Cmd.OB_MESSAGE_ID)
elif myarg == 'subject':
notify['subject'] = getString(Cmd.OB_STRING)
elif myarg in SORF_MSG_FILE_ARGUMENTS:
notify['message'], notify['charset'], notify['html'] = getStringOrFile(myarg)
elif myarg == 'replyto':
msgReplyTo = getString(Cmd.OB_EMAIL_ADDRESS)
elif myarg == 'html':
notify['html'] = getBoolean()
elif myarg == 'attach':
attachments.append((getFilename(), getCharSet()))
elif myarg == 'embedimage':
embeddedImages.append((getFilename(), getString(Cmd.OB_STRING)))
elif myarg in SMTP_HEADERS_MAP:
if myarg in SMTP_DATE_HEADERS:
msgDate, _, _ = getTimeOrDeltaFromNow(True)
msgHeaders[SMTP_HEADERS_MAP[myarg]] = formatdate(time.mktime(msgDate.timetuple()) + msgDate.microsecond/1E6, True)
else:
msgHeaders[SMTP_HEADERS_MAP[myarg]] = getString(Cmd.OB_STRING)
elif myarg == 'header':
header = getString(Cmd.OB_STRING, minLen=1)
msgHeaders[SMTP_HEADERS_MAP.get(header.lower(), header)] = getString(Cmd.OB_STRING)
else:
unknownArgumentExit()
if query and messageIds:
Cmd.SetLocation(selectLocation-1)
usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format('query <QueryGmail>', 'ids <MessageIDEntity>'))
notify['message'] = notify['message'].replace('\r', '').replace('\\n', '\n')
i, count, users = getEntityArgument(users)
for user in users:
i += 1
user, gmail = buildGAPIServiceObject(API.GMAIL, user, i, count)
if not gmail:
continue
try:
if query:
printGettingAllEntityItemsForWhom(Ent.MESSAGE, user, i, count, query=query)
listResult = callGAPIpages(gmail.users().messages(), 'list', 'messages',
pageMessage=getPageMessageForWhom(),
throwReasons=GAPI.GMAIL_THROW_REASONS+GAPI.GMAIL_LIST_THROW_REASONS,
userId='me', q=query, fields='nextPageToken,messages(id)',
maxResults=GC.Values[GC.MESSAGE_MAX_RESULTS])
messageIds = [message['id'] for message in listResult]
except (GAPI.failedPrecondition, GAPI.permissionDenied, GAPI.invalid, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.USER, user], str(e), i, count)
continue
except GAPI.serviceNotAvailable:
userGmailServiceNotEnabledWarning(user, i, count)
continue
jcount = len(messageIds)
if jcount == 0:
entityNumEntitiesActionNotPerformedWarning([Ent.USER, user], Ent.MESSAGE, jcount, Msg.NO_ENTITIES_MATCHED.format(Ent.Plural(Ent.MESSAGE)), i, count)
setSysExitRC(NO_ENTITIES_FOUND_RC)
continue
entityPerformActionModifierNumItems([Ent.USER, user], Act.MODIFIER_TO, jcount, Ent.RECIPIENT, i, count)
Ind.Increment()
j = 0
for messageId in messageIds:
j += 1
try:
messageInfo = callGAPI(gmail.users().messages(), 'get',
throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.INVALID_MESSAGE_ID],
userId='me', id=messageId, fields='id,threadId,payload(headers)')
threadId = messageInfo['threadId']
msgHeaders['References'] = msgHeaders['In-Reply-To'] = _getHeaderValue('Message-ID')
msgSubject = notify['subject'] if notify['subject'] else f"Re: {_getHeaderValue('Subject')}"
recipient = _getHeaderValue('From')
send_email(msgSubject, notify['message'], recipient, j, jcount,
msgFrom=user, msgReplyTo=msgReplyTo, html=notify['html'], charset=notify['charset'],
attachments=attachments, embeddedImages=embeddedImages, msgHeaders=msgHeaders, threadId=threadId,
action=Act.SENDREPLY)
except GAPI.notFound:
entityActionFailedWarning([Ent.USER, user, Ent.MESSAGE, messageId], Msg.DOES_NOT_EXIST, j, jcount)
except GAPI.invalidMessageId:
entityActionFailedWarning([Ent.USER, user, Ent.MESSAGE, messageId], Msg.INVALID_MESSAGE_ID, j, jcount)
except (GAPI.failedPrecondition, GAPI.permissionDenied, GAPI.invalid, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.USER, user], str(e), i, count)
break
except GAPI.serviceNotAvailable:
userGmailServiceNotEnabledWarning(user, i, count)
break
Ind.Decrement()
ADDRESS_FIELDS_PRINT_ORDER = ['contactName', 'organizationName', 'addressLine1', 'addressLine2', 'addressLine3', 'locality', 'region', 'postalCode', 'countryCode'] ADDRESS_FIELDS_PRINT_ORDER = ['contactName', 'organizationName', 'addressLine1', 'addressLine2', 'addressLine3', 'locality', 'region', 'postalCode', 'countryCode']
def _showCustomerAddressPhoneNumber(customerInfo): def _showCustomerAddressPhoneNumber(customerInfo):
@@ -80879,6 +81003,7 @@ USER_COMMANDS = {
'profile': (Act.SET, setProfile), 'profile': (Act.SET, setProfile),
'sendas': (Act.ADD, createUpdateSendAs), 'sendas': (Act.ADD, createUpdateSendAs),
'sendemail': (Act.SENDEMAIL, doSendEmail), 'sendemail': (Act.SENDEMAIL, doSendEmail),
'sendreply': (Act.SENDREPLY, doSendReply),
'signature': (Act.SET, setSignature), 'signature': (Act.SET, setSignature),
'signout': (Act.SIGNOUT, signoutTurnoff2SVUsers), 'signout': (Act.SIGNOUT, signoutTurnoff2SVUsers),
'turnoff2sv': (Act.TURNOFF2SV, signoutTurnoff2SVUsers), 'turnoff2sv': (Act.TURNOFF2SV, signoutTurnoff2SVUsers),

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2024 Ross Scroggs All Rights Reserved. # Copyright (C) 2026 Ross Scroggs All Rights Reserved.
# #
# All Rights Reserved. # All Rights Reserved.
# #
@@ -107,6 +107,7 @@ class GamAction():
SAVE = 'save' SAVE = 'save'
SEND = 'send' SEND = 'send'
SENDEMAIL = 'snem' SENDEMAIL = 'snem'
SENDREPLY = 'sner'
SET = 'set ' SET = 'set '
SETUP = 'setu' SETUP = 'setu'
SHARE = 'shar' SHARE = 'shar'
@@ -225,6 +226,7 @@ class GamAction():
SAVE: ['Saved', 'Save'], SAVE: ['Saved', 'Save'],
SEND: ['Sent', 'Send'], SEND: ['Sent', 'Send'],
SENDEMAIL: ['Email Sent', 'Send Email'], SENDEMAIL: ['Email Sent', 'Send Email'],
SENDREPLY: ['Reply Sent', 'Send Reply'],
SET: ['Set', 'Set'], SET: ['Set', 'Set'],
SETUP: ['Set Up', 'Set Up'], SETUP: ['Set Up', 'Set Up'],
SHARE: ['Shared', 'Share'], SHARE: ['Shared', 'Share'],

View File

@@ -465,11 +465,12 @@ gam user recipient@domain.com sendemail to sender@domain.com references "<CAAMab
If you want to have Gmail recognize the reply in conversation mode in the Sent folder of the original recipient, If you want to have Gmail recognize the reply in conversation mode in the Sent folder of the original recipient,
you must include `threadid <String>`; you can get the 'threadId` with: you must include `threadid <String>`; you can get the 'threadId` with:
``` ```
gam user recipient@domain.com show messages query "rfc822MsgId:<CAAMabc...XYZQ@mail.gmail.com>" gam user recipient@domain.com show threads query "rfc822MsgId:<CAAMabc...XYZQ@mail.gmail.com>"
Getting all Messages that match query ((rfc822MsgId:<CAAMabc...XYZQ@mail.gmail.com>)) for recipient@domain.com Getting all Messages that match query ((rfc822MsgId:<CAAMabc...XYZQ@mail.gmail.com>)) for recipient@domain.com
Got 1 Message that matched query ((rfc822MsgId:<CAAMabc...XYZQ@mail.gmail.com>)) for recipient@domain.com... Got 1 Message that matched query ((rfc822MsgId:<CAAMabc...XYZQ@mail.gmail.com>)) for recipient@domain.com...
User: recipient@domain.com, Show 1 Message User: recipient@domain.com, Show 1 Thread
Message: 19cfd414fe48430d Thread: 19cfd414fe48430d
Message: 19cfd414fe48430d
... ...
gam user recipient@domain.com sendemail to sender@domain.com references "<CAAMabc...XYZQ@mail.gmail.com>" in-reply-to "<CAAMabc...XYZQ@mail.gmail.com>" subject "Re: Original subject" textmessage "Reply text" threadid 19cfd414fe48430d gam user recipient@domain.com sendemail to sender@domain.com references "<CAAMabc...XYZQ@mail.gmail.com>" in-reply-to "<CAAMabc...XYZQ@mail.gmail.com>" subject "Re: Original subject" textmessage "Reply text" threadid 19cfd414fe48430d