Imap_tools Multiple NOT Conditions - python

I am using imap_tools to access and process our business emails. I would like to filter out multiple different from_ messages.
Here's working code for filtering out one from_ address. This returns all emails within the date range except those from notifications#stripe.com
from imap_tools import MailBox, A, AND, OR, NOT
import datetime as dt
from bs4 import BeautifulSoup
username = "info#mysite.co.uk"
password = "mypasword"
imap_server = "mysite.co.uk"
# Get date, subject and body len of all emails from INBOX folder
with MailBox(imap_server).login(username, password) as mailbox:
for msg in mailbox.fetch(
AND(NOT(from_="notifications#stripe.com"), date=dt.date(2022, 9, 9))
):
# print(msg.date, msg.subject, msg.html)
if msg.html:
soup = BeautifulSoup(msg.html, "html.parser")
print(msg.from_)
print(msg.subject)
print(msg.date)
print(soup.prettify)
print(180 * "=")
This is what I have tried:
# Get date, subject and body len of all emails from INBOX folder
with MailBox(imap_server).login(username, password) as mailbox:
for msg in mailbox.fetch(
AND(
NOT(from_=["notifications#stripe.com", "noreply#wise.com"]),
date=dt.date(2022, 9, 9),
)
):
As per the docs one should pass in a list of str. from_: str | List[str] | None = None
But when I try and pass in a list of email addresses that I don't want to fetch, it just returns all the emails within the date range.
Any ideas? Thanks in advance!

Seems it is limitation of your server.
I have checked on YANDEX - the same.
On another server it works.

Related

How can I loop through a list and for each distinct value initiate another loop?

I have a python list, this list is of any document that has been updated within a set timeframe and the documents are ID'd via a set value in the list. There may be one or many in the resulting list. What I am trying to figure out is how I can loop through the list of values (documents) and trigger another loop that iterates through another list I have of emails resulting in one email per address per document in the original list? I tried to "stack" loops on top of each other (code snippet shown below), but this results in multiple emails to each email address all with a full list of documents (i.e. if there are two documents in the list, two emails are sent to each address with the details about both documents).
import boto3
from botocore.exceptions import ClientError
import requests
from datetime import datetime, timedelta
#first get request to pull all current endusers
GET1 = "https://abdc.com/api"
r1 = requests.get(url = GET1, auth=('username/token','APItoken'))
#convert to python dict
data = r1.json()
#create a list of all user's email addresses
emails = [user["email"] for user in data["users"]]
#create an timestamp of previous day and convert to epoch
ts1 = datetime.today() - timedelta(days =1)
ts2 = ts1.strftime("%s")
#set start time attribute as a parameter
params = {'start_time':ts2}
#second GET reqeust to pull all articles updated in the last 24 hrs
GET2 = "https://efdg.com/api"
r2 = requests.get(url = GET2, params=params, auth=('username/token','APItoken'))
#convert to python dict
data2 = r2.json()
#create list of all the target article titles and html url
updated_docs = [articles["html_url"] for articles in data2["articles"]]
doc_title = [articles["title"] for articles in data2["articles"]]
for y in updated_doc:
#create loop to iterate throuhg all the email addresses and send
individual emails to users
for x in emails:
# This address must be verified with Amazon SES.
SENDER = "example#example.com"
#To list
RECIPIENT = x
# Specify a configuration set. If you do not want to use a configuration
# set, comment the following variable, and the
# ConfigurationSetName=CONFIGURATION_SET argument below.
#CONFIGURATION_SET = "ConfigSet"
# If necessary, replace us-west-2 with the AWS Region you're using for Amazon SES.
AWS_REGION = "us-east-1"
# The subject line for the email.
SUBJECT = "blah blah "
# The email body for recipients with non-HTML email clients.
BODY_TEXT = ("Amazon SES Test (Python)\r\n"
"This email was sent with Amazon SES using the "
"AWS SDK for Python (Boto)."
)
# The HTML body of the email.
BODY_HTML = """<html>
<head></head>
<body>
<h1>l Documentation Notification</h1>
<p1>Please click the link below for the most current version of this document.<br>
<br>
"""+str(doc_title)+"""<br>
<br>
"""+str(updated_docs)+"""
</p1>
</body>
</html>
"""
# The character encoding for the email.
CHARSET = "UTF-8"
# Create a new SES resource and specify a region.
client = boto3.client('ses', region_name=AWS_REGION)
# Try to send the email.
try:
# Provide the contents of the email.
response = client.send_email(
Destination={
'ToAddresses': [
RECIPIENT,
],
},
Message={
'Body': {
'Html': {
'Charset': CHARSET,
'Data': BODY_HTML,
},
'Text': {
'Charset': CHARSET,
'Data': BODY_TEXT,
},
},
'Subject': {
'Charset': CHARSET,
'Data': SUBJECT,
},
},
Source=SENDER,
# If you are not using a configuration set, comment or delete the
# following line
#ConfigurationSetName=CONFIGURATION_SET,
)
# Display an error if something goes wrong.
except ClientError as e:
print(e.response['Error']['Message'])
else:
print("Email sent! Message ID:"),
print(response['MessageId'])
So, you've completely changed your code, so I'm well confused, but if I can read between the lines properly you meant something like this:
#create loop to iterate through all documents in the list
for article in data2["articles"]:
#create loop to iterate through all the email addresses and send individual emails to
users
for x in emails:
# This address must be verified with Amazon SES.
SENDER = "example#example.com"
#To list
RECIPIENT = x
# The HTML body of the email.
BODY_HTML = """<html>
<head></head>
<body>
<h1>l Documentation Notification</h1>
<p1>Please click the link below for the most current version of this document.<br>
<br>
"""+str(article["title"])+"""<br>
<br>
"""+str(article["html_url"])+"""
</p1>
</body>
</html>
"""
Notice how I iterate through data2["articles"] and update the BODY_HTML with article["title"] and article["html_url"],

How do I fix SMTPRecipientsRefused: {'': (421, b'4.7.0 Too many protocol errors (6) on this connection, closing transmission channel error Python?

I get this error when sending emails via smptplib in Python to a certain list
SMTPRecipientsRefused: {'': (421, b'4.7.0 Too many protocol errors (6) on this connection, closing transmission channel.')}?
I'm using Office365 SMTP details and a snippet of the code is below:-
import smtplib, ssl
from email.message import EmailMessage
import getpass
ids = df['IDs']
emails_to = df['Emails']
namesofcompanies = df["CompanyNames"]
sendfrom = df["SenderList"]
date_7days = (datetime.now() + timedelta(days=7)).strftime('%d/%m/%Y')
date_14days = (datetime.now() + timedelta(days=13)).strftime('%d/%m/%Y')
email_pass = input() #Office 365 password
context=ssl.create_default_context()
for i in range(len(emails_to)): # iterate through the records
# for every record get the name and the email addresses
ID = str(ids[i])
Emaitstosendto = emails_to[i]
companynames = namesofcompanies[i]
tosendfrom = sendfrom[i]
if my_files_dict.get(ID): #Looks for attachments in the same folder with same name as the corresponding record
smtp_ssl_host = 'smtp.office365.com'
smtp_ssl_port = 587
email_login = "xxx#xxx.com" #Office 365 email
email_from = tosendfrom
email_to = Emaitstosendto
msg = MIMEMultipart()
msg['Subject'] = "Received Emails between "+date_7days+" - "+date_14days
msg['From'] = email_from
msg['To'] = email_to
msg['X-Priority'] = '2'
text = ("XXXX,\n"
f"xxxxxx\n\n")
msg.attach(MIMEText(text))
filename = my_files_dict.get(ID)#Files in the folder matching the ID
fo = open(filename,'rb')
s2 = smtplib.SMTP(smtp_ssl_host, smtp_ssl_port)
s2.starttls(context=context)
s2.login(email_login, email_pass)
attachment = email.mime.application.MIMEApplication(fo.read(),_subtype="xlsx")
fo.close()
attachment.add_header('Content-Disposition','attachment',filename=filename)
msg.attach(attachment)
s2.send_message(msg)
s2.quit()
On average, I will be sending emails to a list separated by a semi-colon(;) of about 8 emails per record. This means that for each attachment, I'll send to about 8 emails and I'm to do it for about 70 of such contacts. In total, that will be about 560 emails. Nothing gets sent out I get the above error the moment I log in. On the contrary, when I try test sending it to a list of 3 emails in the test emails column, the same emails go out very well. Can anyone point to where I may not be getting it right? I suspect either the list of emails is too long or is something with the email addresses thus the protocol error? Is this an SMTPlib limitation?
What you specify in MIMEMultipart() is what appears in the message header, but this is not always equal to the list of recipients. You can try to change server.send_message(msg) to server.sendmail(sender,recipients,msg.as_string())
Keep in mind that sendmail() requires a list of recipients, whereas msg['To'] should be set as one string, so if your variable email_to is comma separated, you should write it like:
s2.sendmail(email_from,email_to.split(','),msg.as_string())
More details about sendmail() can be found here.
I resolved this error by creating a list and joined all addresses from a tuple into a string, using a comma character as separator.
family = the list of all recipients' email addresses
family = [
'name1#example.com',
'name2#example.com',
'name3#example.com',
'name4#example.com',
'name5#example.com',
'name6#example.com',
'name7#example.com',
'name8#example.com'
]
msg['To'] =', '.join(family)

Convert email body to a string

so I'm working on something that uses regex to search something from an email, which is fetched via imaplib module. Right now I can't get it to work, even after using str() function.
result, data = mail.fetch(x, '(RFC822)')
eemail = email.message_from_bytes(data[0][1])
print(str(eemail))
trying to regex it:
print(re.search("button", eemail))
Regex gives me no matches even after making the email a string object.
This is what I use:
import imaplib
import email
import re
mail = imaplib.IMAP4_SSL(SMTP_SERVER, SMTP_PORT)
mail.login(FROM_EMAIL,FROM_PWD)
mail.select('inbox')
status, response = mail.search(None, '(UNSEEN)')
unread_msg_nums = response[0].split()
for e_id in unread_msg_nums:
_, response = mail.fetch(e_id, '(UID BODY[TEXT])')
b = email.message_from_string(response[0][1])
if b.is_multipart():
for payload in b.get_payload(decode=True):
print(re.search("button", payload.get_payload(decode=True)))
else:
print(re.search("button", b.get_payload(decode=True)))

Code times out when trying to run as a lambda function in AWS

Below is my code and I am hoping someone can help me with the cleaning up the code and making it more effiencient. Basically, the code should iterate through all the volumes in my AWS account and then list all untagged volumes and then send out an email. However, it times out when running it as a lambda function in AWS but if i run it locally, it will take over 30 mins to complete (however it does complete). Im sure its iterating through things it doesnt need.
Also if I print the ec2_instances list, I can see duplicate values, so I want to only have unique values so that its not repeating the script for each ec2 instance.
import logging
import boto3
from smtplib import SMTP, SMTPException
from email.mime.text import MIMEText
logger = logging.getLogger()
logger.setLevel(logging.INFO)
session = boto3.Session(profile_name="prod")
client = session.client('ec2')
untagged_volumes = []
detached_volumes = []
ec2_instances = []
response = client.describe_volumes()
for volume in response['Volumes']:
if 'Tags' in str(volume):
continue
else:
if 'available' in str(volume):
detached_volumes.append(volume['VolumeId'])
else:
untagged_volumes.append(volume['VolumeId'])
untagged_volumes.append(volume['Attachments'][0]['InstanceId'])
ec2_instances.append(volume['Attachments'][0]['InstanceId'])
unique_instances = list(set(ec2_instances))
# Create the msg body.
msg_body_list = []
for instance in unique_instances:
desc_instance = client.describe_instances()
# append to the msg_body_list the lines that we would like to show on the email
msg_body_list.append("VolumeID: {}".format(desc_instance['Reservations'][0]['Instances'][0]['BlockDeviceMappings'][0]['Ebs']['VolumeId']))
msg_body_list.append("Attached Instance: {}".format(desc_instance['Reservations'][0]['Instances'][0]['InstanceId']))
# if there are tags, we will append it as singles lines as far we have tags
if 'Tags' in desc_instance['Reservations'][0]['Instances'][0]:
msg_body_list.append("Tags:")
for tag in desc_instance['Reservations'][0]['Instances'][0]['Tags']:
msg_body_list.append(" Key: {} | Value: {}".format(tag['Key'], tag['Value']))
# in case we don't have tags, just append no tags.
else:
msg_body_list.append("Tags: no tags")
msg_body_list.append("--------------------")
# send email
mail_from = "xxx#xxx.com"
mail_to = 'xxx#xxx.com'
msg = MIMEText("\n".join(msg_body_list))
msg["Subject"] = "EBS Tagged Instance Report for"
msg["From"] = mail_from
msg["To"] = mail_to
try:
server = SMTP('xxx.xxx.xxx.xxx', 'xx')
server.sendmail(mail_from, mail_to.split(','), msg.as_string())
server.quit()
print('Email sent')
except SMTPException:
print('ERROR! Unable to send mail')
Lambda functions have a time limit of 15 minutes. That is the reason for the timeout - if you need to run scripts for longer, look up AWS Fargate.

Send an email only if # isn't in csv

I have two CSV files, one with IOC hits and a second that is a watchfile. The watchfile adds an # to the file along with the IOC domain and last seen date. I'm trying to send one email when an IOC hit for that day, but I can't seem to get my loop right. Currently it emails every time, even though the # is present in the watchfile.csv. I've printed the values for val and emailed and they show up in the correct format, but it still emails every time the script is ran.
finIOChit.csv
last: 2017-01-17 query: rabbitons.pw,
watchfile.csv
last: 2017-01-17 query: # rabbitons.pw,
import smtplib
import csv
import os
import re
from datetime import *
today = date.today()
today = datetime.combine(today, datetime.min.time())
# Setup email alerting
sender = 'server#company.com'
receivers = ['user#company.com']
patn = re.compile('20\d{2}-\d{2}-\d{2}')
watchfile = open('watchfile.csv', 'r+w')
alreadyemailed = re.compile('#')
with open('finalIOChit.csv') as finalhit:
for hit in finalhit:
for line in watchfile:
emailed = alreadyemailed.findall(line)
for match in patn.findall(hit):
val = datetime.strptime(match, '%Y-%m-%d')
if val == today and emailed != '#':
hit = re.sub('query: ','query: # ',hit)
watchfile.write(hit)
message = """From:server <server#comapny.com>
To: user <user#company.com>
Subject: Passive DNS hit
"""
subject = ' ' + str(hit)
messagefull = message + subject
try:
smtpObj = smtplib.SMTP('emailserver')
smtpObj.sendmail(sender, receivers, messagefull)
except SMTPException:
print "Error: unable to send email"

Categories