Emails sent with the Gmail API are not grouped in threads? - python

I am using the gmail API for sending emails. This is the function I am using to create an email:
def createEmailNoAttachments(self, send_to_emails, subject_text, main_message_text, msgID=None, inReplyTo=None, html=False):
try:
fromEmail = self.from_email_total
if (type(main_message_text) == list) or (type(main_message_text) == tuple):
total_text = ""
for line in main_message_text:
if type(line) == str:
total_text = total_text + line + "\n"
main_message_text = total_text
mimeMessage = MIMEMultipart()
if type(send_to_emails) == list:
mimeMessage['to'] = ", ".join(send_to_emails)
else:
mimeMessage['to'] = send_to_emails
mimeMessage['from'] = fromEmail
mimeMessage['subject'] = subject_text
if inReplyTo != None:
mimeMessage["In-Reply-To"] = inReplyTo
mimeMessage["References"] = inReplyTo
if msgID != None:
mimeMessage['Message-ID'] = msgID
if html:
msg= MIMEText(main_message_text, 'html')
else:
msg= MIMEText(main_message_text, "plain")
mimeMessage.attach(msg)
raw = base64.urlsafe_b64encode(mimeMessage.as_bytes())
raw = raw.decode()
body = {'raw': raw}
return body
except:
self.myLogger.error("An error was encountered while attempting to create gmail email")
tb = traceback.format_exc()
self.myLogger.exception(tb)
return False
I then send the email with
def gmailAPISendEmail(self, email_message, deleteFromInbox=False, userID="me"):
try:
self.refreshGmailService()
self.myLogger.info("Attempting to send email message")
request = self.service.users().messages().send(userId=userID, body=email_message)
response = self.executeGmailAPI_withretry(request=request)
if response == False:
self.myLogger.error("An error occurred in executeGmailAPI_withretry while trying to send email message")
return False
else:
try:
responseID = str(response['id'])
if deleteFromInbox == True:
delete_result = self.deleteEmail(emailID=responseID)
if delete_result == False:
self.myLogger.error(f"An error occurred in deleteEmail with responseID ({responseID})")
self.myLogger.info("Successfully sent email message with ID (" + responseID +")")
return responseID
except:
return "CouldNotExtractID"
except:
self.myLogger.error("An error occurred in gmailAPISendEmail")
tb = traceback.format_exc()
self.myLogger.exception(tb)
return False
The problem I am noticing is that similar emails with the same email subject and same sender and recipient are not being grouped under one thread when sent using the above functions (the gmail API). In the recipient email inbox, each individual email appears separately, even though they have the same subject and same sender and receiver email addresses.
I believe the next step would be to manually assign threadid. However, this is far from ideal, as I would need to incorporate some logic to do all of this.
Before when I used SMTP, I didn't have to set a threadid or anything like that. When sending emails with SMTP, emails would automatically group together based on same email subject and same recipient.
Nothing changed between before and now, except that I am sending the same emails with the gmail API in place of SMTP.
Why doesn't the gmail API behave similar to SMTP, even though I am creating the email very similarly? Is there something I can do to have Gmail inboxes to group the emails just like SMTP, without having to build logic and keeping track of threadids?

From sending
If you're trying to send a reply and want the email to thread, make sure that:
The Subject headers match
The References and In-Reply-To headers follow the RFC 2822 standard.
For information on sending a message from a draft, see Creating Drafts.
So you need the message id of the original message you are replying to. This is not the same as thread id.
message['In-Reply-To'] = message_id
message['References'] = message_id

Related

How do I SELECT specific names from a table, and display them in the body of an email?

The code below sends emails fine. However, it doesn't get the names from the MySQL table where status = 'EXPIRED'. I want to show in the body of the email the list of names of materials or a name of the material whose status is EXPIRED in MySQL table.
def email_alert(subject, body, to):
msg = EmailMessage()
msg.set_content(body)
msg['subject'] = subject
msg['to'] = to
user = "*******#server.com"
msg['from'] = user
password = "iuhfiudsfhe"
server = smtplib.SMTP("smtp.gmail.com", 587)
server.starttls()
server.login(user, password)
server.send_message(msg)
server.quit()
try:
email_list = "SELECT Email FROM users"
mycursor.execute(email_list)
emails = mycursor.fetchall()
emails = [i[0] for i in emails]
str(emails).strip('[]')
except Exception as e:
print(e)
DB.rollback
DB.close()
if matexpir <= d1:
#Update material status on table.
matstat = "EXPIRED"
#GET material names from material table.
try:
matnames = "SELECT Name FROM material WHERE Status = 'EXPIRED'"
mycursor.execute(matnames)
#matids = mycursor.fetchall()
#matids = [i[0] for i in matids]
except Exception as e:
print(e)
DB.rollback
DB.close()
#Email alert for expired materials.
email_alert("ALERT!", "your material {} has expired", emails).__format__(matnames)
You should iterate over the "matnames", It is a pointer and not the string result.
You already did using the for to test. Just store the name of the products in a variable and concat THIS variable in the body of the e-mail, not the matnames.
I uncommented the two suggested lines. This still gave me errors until I changes the last line of code to:
email_alert("ALERT!", "your material {} has expired.".format(matids), emails)

Welcome Message isn't work because GetUpdates Telegram Bot API can't record joined notification update in supergroup

Few days ago i made a telegram bot with python using api from https://core.telegram.org/bots/api.
All the code works perfectly until i realize that the GetUpdates api didn't record the joined notification for the new user in my group
My plan is to made a welcome message to new user based on those record. The group not allowing user to send message (broadcast group) and right now i confused that i can't get any single record related to the new user joined, so then the welcome message didn't work perfectly
This is my function for getUpdates api and the welcome message
def get_updates(self, offset=0, timeout=50):
method = 'getUpdates'
params = {'timeout': timeout, 'offset': offset}
resp = requests.get(self.api_url + method, params)
result_json = resp.json()['result']
return result_json
def welcome_msg(self, item):
chat_id = item["message"]["chat"]["id"]
user_id = item["message"]["new_chat_member"]["id"]
if "username" in item["message"]["new_chat_member"]:
user_name = item["message"]["new_chat_member"].get("username", user_id)
else:
user_name = item["message"]["new_chat_member"].get("first_name", user_id)
welcom_msg = "HI WELCOME TO THIS GROUP"
to_url3 = 'https://api.telegram.org/bot{}/sendMessage?chat_id={}&text={}&parse_mode=HTML&disable_web_page_preview=True'.format(token, chat_id, welcom_msg)
resp3 = requests.get(to_url3)
And after that the welcome message should be like this
new_offset = 1
all_updates = get_updates(new_offset)
if len(all_updates) > 0:
for current_update in all_updates:
first_update_id = current_update['update_id']
if old_id < first_update_id:
old_id = int(current_update['update_id'])
try:
if "new_chat_member" in current_update['message']:
welcome_msg(current_update)
chat_id = current_update["message"]["chat"]["id"]
message_id = current_update["message"]["message_id"]
new_offset = first_update_id + 1
except:
pass
Does the restriction of the group (member can't send message) made the getUpdates API couldn't work and the welcome message didn't showing up???
Go to botfather, bot settings and set privacy mode off for this bot, It should work

How do I send emails to multiple people with different body?

I want to group recipients by reason and sent out tailored emails for each reason. The data looks like this.
Email reason
0 one#outlook.com Address
1 two#outlook.com Address
2 three#outlook.com Phone Number
3 one#outlook.com Postcode
import Pandas as pd
data = { 'Email': ['one#outlook.com','two#outlook.com','three#outlook.com','one#outlook.com'],
'reason': ['Address','Address', 'Phone Number','Postcode']}
df = pd.DataFrame(data)
df
This is how I started.
for e in df['reason'].unique():
print(f"Reason: {e}")
print(df[df['reason'] == e]['Email'].to_list())
print('\n')
Reason: Address
['one#outlook.com', 'two#outlook.com']
Reason: Phone Number
['three#outlook.com']
Reason: Postcode
['one#outlook.com']
Not sure how to use this list below.
message = "Update"
email_subject = "Test"
recipients = e
# sending email function
def send_mail(text, subject, recipients):
o = win32com.client.Dispatch("Outlook.Application")
Msg = o.CreateItem(0)
Msg.to = ''
Msg.BCC = "; ".join(recipients)
Msg.Subject = subject
Msg.HTMLBody = text
Msg.Send()
# sending email
send_mail(message,email_subject,recipients)
How to set 3(or more) different email body's and loop around the grouped list to sent separate emails for each reason?
You need to repeat the following code in the loop:
Msg = o.CreateItem(0)
Msg.to = recipient;
Msg.Subject = subject
Msg.HTMLBody = text
Msg.Send()
where recipient is an email address of a single recipient.
You may find the Using Visual Basic for Applications in Outlook article helpful.

CC email and multiple TO email addresses not working

I've created a function in Python which will build an email in html format and send it anonymously through an Exchange server which accepts anonymous email sends.
However, no one in the CC list gets a copy and only the FIRST person in the TO list gets a copy. I've tried 'comma' and 'semicolon' as the email separator but neither works. Curiously, in the email received ALL of the email addresses can be seen (and in the correct format) despite the fact that only one person (the person listed first in the TO list) gets a copy.
This is my code
def send_email(sender, subject, sendAsDraft = True): # sendAsDraft not implemented
global toEmail
global ccEmail
# create the email message
htmlFile = open(saveFolder + '\\' + htmlReportBaseName + '.html',encoding = 'utf-8')
message = htmlFile.read().replace('\n', '')
message = message.replace(chr(8217), "'") # converts a strange single quote into the standard one
message = message.replace("'",''') # makes a single quote html friendly
htmlFile.close()
# get the email body text
emailBody = open('emailBody.txt')
bodyText = emailBody.read()
emailBody.close()
now = dt.datetime.today().replace(second = 0, microsecond = 0)
timeStr = returnReadableDate(now) # returns the date as a string in the 'normal' reading format
# insert the current date/time into the marker in the email body
bodyText = bodyText.replace('xxxx', timeStr)
# insert the email body text into the HTML
message = message.replace('xxxx', bodyText)
msg = MIMEMultipart('Report Email')
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = '; '.join(toEmail)
msg['Cc'] = '; '.join(ccEmail)
msg.add_header('Content-Type','text/html')
msg.set_payload(message)
# complete the email send
message = ''
try:
smtpObj = sl.SMTP('1.2.3.4:5') # obviously IP address anonymised
smtpObj.sendmail(sender, '; '.join(toEmail), msg.as_string())
message = 'Email successfully sent'
except:
message = 'Email could not be sent due to an error'
finally:
# write the outcome to the Instance log
global InstanceLog
# write the log
log(InstanceLog, message)
# close object and exit function
smtpObj.quit()
Is it possible that the issue is at the email server? That where an un-authenticated email can only be sent to one person? That would seem strange in one way, and make sense in another.
Thanks all.
Seamie
Beware. send_message can use the headers of a message to build its recipient list, but sendmail requires a list of addresses or handle a string as a single recipicient address.
And the headers should contain comma instead of semicolons.
So you should have:
...
msg['To'] = ' '.join(toEmail)
msg['Cc'] = ','.join(ccEmail)
...
and later either:
smtpObj.sendmail(sender, toEmail + ccEmail, msg.as_string())
or:
smtpObj.send_message(msg)
Looking at the docs, this should be a comma seperated string:
msg['To'] = ', '.join(toEmail)

Get content of a mail IMAPCLIENT

I am using the IMAPclient to get the content of emails, so i made this piece of code :
messages = server.search(['FROM', user['email']], charset='UTF-8')
if len(messages) > 0:
for mail_id, data in server.fetch(messages, ['ENVELOPE']).items():
envelope = data[b'ENVELOPE']
How can I have the content of the emails?
Don't know if you found the answer elsewhere... try:
messages = server.search(['FROM', user['email']], charset='UTF-8')
if len(messages) > 0:
for mail_id, data in server.fetch(messages,['ENVELOPE','BODY[TEXT]']).items():
envelope = data[b'ENVELOPE']
body = data[b'BODY[TEXT]']
The email content is in body.

Categories

Resources