Unable to send 2 pywin32 emails within one program runtime - python

So I have the ability to open outlook and insert all the needed values to create a draft of an email, however, when I reopen the window and attempt to send another email it gives met this error.
self.mailItem = olApp.CreateItem(0)
File "<COMObject Outlook.Application>", line 2, in CreateItem
pywintypes.com_error: (-2147023174, 'The RPC server is unavailable.', None, None)
Is it the way I call it or is it the way this pywin32 works, how would I go about resolving this issue to the point where i can send as much emails, using the same code? Replacing self.mailItem.Display() with self.mailItem.Send(), allows me to send the multiple I desire however I want the user to edit any composed email before sending it. thus this not being a option.
my email class
import win32com.client as win32
from PyQt5.QtWidgets import QMessageBox
olApp = win32.Dispatch('Outlook.Application')
olNS = olApp.GetNameSpace('MAPI')
class emailComposition():
def __init__(self):
self.mailItem = olApp.CreateItem(0)
self.mailItem.BodyFormat = 1
self.mailItem.Sensitivity = 2
attachment = self.mailItem.Attachments.Add("C:\\Users\\----\Desktop\\Krypt\\Images\\Logos\\Logo1.png")
attachment.PropertyAccessor.SetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001F", "MyId1")
def setSubject(self, subj):
self.mailItem.Subject = subj
def setSender(self, senderEmail):
self.sender = senderEmail
def setRecipient(self, recipientEmail, recipientName):
self.mailItem.To = recipientEmail
self.rName = recipientName
def setText(self, ItemList):
items = "<br>• xyz <br>• xyz"
# still need to do something with the ItemList, removing it has no effect on the error outcome!
self.mailItem.HTMLBody = "<p>Good Day " + self.rName + " <br>Could you please send me a quote on the following: " + items + " </p> <p>Thanks</p><img src=""cid:MyId1"" width=""100"" height=""100"" >"
def displayEmail(self):
try:
self.mailItem._oleobj_.Invoke(*(64209, 0, 8, 0, olNS.Accounts.Item(self.sender)))
except:
msg = QMessageBox()
msg.setIcon(QMessageBox.Warning)
msg.setText("Unregistered email")
msg.setInformativeText("""The email provided isn't a registered email within outlook.\nA default email will be used.""")
msg.setWindowTitle("Krypt")
msg.exec_()
self.mailItem.Display()
Code i use to call this class, all of it is in a button press connect
def createEmail(self):
input = self.ui.tblQuoteItems.item(0, 0)
if input is not None:
if self.ui.edtSupplier.text() != "" and self.ui.edtSupplierRep.text() != "":
x = emailComposition()
x.setRecipient(self.ui.edtSupplierEmail.text(), self.ui.edtSupplierRep.text())
x.setSubject("Official Quote")
x.setSender(self.ui.edtUserEmail.text())
self.read() #Loads self.itemList with needed items
x.setText(self.itemList)
x.displayEmail()

Try to get a Namespace and any standard folder reference after creating a new Outlook Application instance in the code:
import win32com.client as win32
olApp = win32.Dispatch('Outlook.Application')
olNS = olApp.GetNameSpace('MAPI')
objFolder = objNS.GetDefaultFolder(olFolderInbox)
Read more about that in the Automating Outlook from a Visual Basic Applications article.
Also you may find the Application Shutdown Changes in Outlook article helpful.

Related

I am not able to get the authorization code from program

So I am working on a coding project for my internship in DC. My project involves using python and microsoft graph api to build a program that checks the email addresses of employees obtained at my company to see if another authorization method has been added to the email address. If another authorization method is detected for an email address, it could mean that someone/a bad actor is trying to access information.
I have been referring to the video Getting Started With Microsoft Graph API For Python Development (Set Up & Authentication) by Jie Jenn. So far, I'm able to get a device code and link from the program, but I cannot obtain the authorization code. Aside from that, I am also getting a traceback error in line 31 of demo2.py, another 2 traceback errors in line 12 and 100 of main.py, and TypeError: 'dict' object is not callable in demo2.py.
Here is my code.
Thank You,
Sairam
Errors:
'''
Traceback (most recent call last):
File "C:\Users\S.Soundararajan\Documents\PE Project for Azure\demo2.py", line 31, in
webbrowser.open(flow('verification_uri'))
TypeError: 'dict' object is not callable
'''
'''
Traceback (most recent call last):
File "C:\Users\S.Soundararajan\Documents\PE Project for Azure\main.py", line 100, in
main()
File "C:\Users\S.Soundararajan\Documents\PE Project for Azure\main.py", line 12, in main
graph: Graph = Graph(azure_settings)
TypeError: Graph() takes no arguments
Python Graph Tutorial
'''
demo2.py:
`
#import account as account
import webbrowser
from xmlrpc.client import APPLICATION_ERROR
import requests
import msal
from msal import PublicClientApplication
CLIENT_ID = ''
CLIENT_SECRET = ''
authority_url = ''
base_url = 'https://graph.microsoft.com/v1.0/'
endpoint = base_url + 'me'
SCOPES = ['User.Read', 'Mail.Read', 'Mail.Send']
# Method 2. Login to acquire access_token
app = PublicClientApplication(
CLIENT_ID,
authority = authority_url
)
#accounts = app.get_accounts()
#if accounts:
#app.acquire_token_silent(scopes=SCOPES, account=account[0])
flow = app.initiate_device_flow(scopes=SCOPES)
print(flow)
print(flow['message'])
#app_code = flow['message']
webbrowser.open(flow('verification_uri'))
result = app.acquire_token_by_device_flow(flow)
access_token_id = result['access_token']
headers = {'Authorization': 'Bearer' + access_token_id}
response = requests.get(endpoint, headers=headers)
print(response)
print(response.json())
`
main.py:
import configparser
from graph import Graph
from msal import PublicClientApplication
def main():
print('Python Graph Tutorial\n')
# Load settings
config = configparser.ConfigParser()
config.read(['config.cfg', 'config.dev.cfg'])
azure_settings = config['azure']
graph: Graph = Graph(azure_settings)
greet_user(graph)
choice = -1
while choice != 0:
print('Please choose one of the following options:')
print('0. Exit')
print('1. Display access token')
print('2. List my inbox')
print('3. Send mail')
print('4. List users (requires app-only)')
print('5. Make a Graph call')
try:
choice = int(input())
except ValueError:
choice = -1
if choice == 0:
print('Goodbye...')
elif choice == 1:
display_access_token(graph)
elif choice == 2:
list_inbox(graph)
elif choice == 3:
send_mail(graph)
elif choice == 4:
list_users(graph)
elif choice == 5:
make_graph_call(graph)
else:
print('Invalid choice!\n')
def greet_user(graph: Graph):
user = graph.get_user()
print('Hello,', user['displayName'])
# For Work/school accounts, email is in mail property
# Personal accounts, email is in userPrincipalName
print('Email:', user['mail'] or user['userPrincipalName'], '\n')
def display_access_token(graph: Graph):
token = graph.get_user_token()
print('User token:', token, '\n')
return 1
def list_users(graph: Graph):
users_page = graph.get_users()
# Output each users's details
for user in users_page['value']:
print('User:', user['displayName'])
print(' ID:', user['id'])
print(' Email:', user['mail'])
# If #odata.nextLink is present
more_available = '#odata.nextLink' in users_page
print('\nMore users available?', more_available, '\n')
def list_inbox(graph: Graph):
message_page = graph.get_inbox()
# Output each message's details
for message in message_page['value']:
print('Message:', message['subject'])
print(' From:', message['from']['emailAddress']['name'])
print(' Status:', 'Read' if message['isRead'] else 'Unread')
print(' Received:', message['receivedDateTime'])
# If #odata.nextLink is present
more_available = '#odata.nextLink' in message_page
print('\nMore messages available?', more_available, '\n')
def send_mail(graph: Graph):
# Send mail to the signed-in user
# Get the user for their email address
user = graph.get_user()
user_email = user['mail'] or user['userPrincipalName']
graph.send_mail('Testing Microsoft Graph', 'Hello world!', user_email)
print('Mail sent.\n')
def make_graph_call(graph: Graph):
graph.make_graph_call()
# Run main
main()
`
Issue1:
On the code base, the mentioned url is not declared yet, and flow was not able to find it to be executed.
Usually, the key-value pair should always use square brackets to access the value inside. One of the codes mentioned in the thread is needed to use [] to access elements of a dictionary. Not () else will get the TypeError: The "dict" object is not callable error.
Solution:
authority_url= 'https://docs.python.org/'
webbrowser.open_new(authority_url) // same window
webbrowser.open_new_tab(authority_url) // will open in new tab
Issue 2:
refer this official tutorial.

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.

update categories in emails using python

I am trying to update categories of emails available in one of the data frame using below code.
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6)
messages = inbox.Items
i = 0
for message in messages:
try:
message.Categories = output.iat[i,2]
except:
message.Categories = 'No Category'
i = i + 1
Below script used to move updated emails from inbox to another folder and then remove to inbox
donebox = outlook.GetDefaultFolder(6).Folders[20]
def delay(time):
for j in range(time):
j=j+1
i = 0
while (messages.Count > 0):
print(i,messages.Count)
message = messages.GetLast()
message.Move(donebox)
delay(1000000)
i = i + 1
messages = donebox.Items
i = 0
while (messages.Count > 0):
print(i,messages.Count)
message = messages.GetLast()
message.Move(inbox)
delay(1000000)
i = i + 1
In outlook updated Categories from output dataframe for emails are able visible only once email selected. Is there any option which can refresh outlook and categories will be updated automatically. Please advice.
For everyone who cant save changing of non-selected mail Categories property by Python:
mail.Categories='Red category'
mail.Save()
It worked for me!
import win32com.client
from win32com.client import Dispatch
outlook=win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
folder = outlook.Folders.Item("Inbox")
messages = folder.Items
for i in range(messages.Count):
messages[i].GetInspector()
messages[i].Categories = 'Purple Category'
messages[i].Save()
If only one category needs to be assigned to each email, dmitrii.kotenko has provided the solution.
If you need to append more than one category, pass the categories as a string separated by commas
mail.Categories = "category1, category2, category3"
mail.Save()

How to deal with flooded unseen messages

I have written an email parsing mechanism in python.
It finds a new email and passes the data correctly. I am 99.999% certain that my code is functioning correctly, so there should be no issue there. The problem is that occasionally, the Gmail inbox will get flooded with messages that are considered "unseen". At this point, there is nothing that my code can do.
It fails with:
imaplib.error: FETCH command error: BAD ['Could not parse command']
This is distressing, and I would love to have either
a way to check whether the unseen messages have overflown to this state, or
a way to manually (via imaplib) mark all messages as read, including a way to detect this particular error.
Any thoughts on how to accomplish this?
Here is my code:
#!/usr/bin/env python
import imaplib, re, sys, time, OSC, threading, os
iparg = 'localhost'
oportarg = 9000
iportarg = 9002
usern = 'myusrname#gmail.com'
gpass = 'mypass'
kill_program = False
server = imaplib.IMAP4_SSL('imap.googlemail.com', 993)
oclient = OSC.OSCClient()
email_interval = 2.0
def login():
server.login(usern, gpass)
oclient.connect((iparg, oportarg))
def logout_handle(addr, tags, stuff, source):
print 'received kill call'
global kill_program
kill_program = True
def filter_signature(s): #so annoying; wish i didn't have to do this
try:
a_sig = re.sub(r'Sent|--Sent', '', s)
b_sig = re.sub(r'using SMS-to-email. Reply to this email to text the sender back and', '', a_sig)
c_sig = re.sub(r'save on SMS fees.', '', b_sig)
d_sig = re.sub(r'https://www.google.com/voice', '', c_sig)
no_lines = re.sub(r'\n|=|\r?', '', d_sig) #add weird characters to this as needed
except:
nolines = s
return no_lines
def parse_email(interval):
while True:
server.select('INBOX')
status, ids = server.search(None, 'UnSeen')
print 'status is: ', status
if not ids or ids[0] is '':
print 'no new messages'
else:
try:
print 'found a message; attempting to parse...'
latest_id = ids[0]
status, msg_data = server.fetch(latest_id, '(UID BODY[TEXT])')
raw_data = msg_data[0][1]
raw_filter = raw_data
print 'message result: ', raw_filter
time.sleep(interval)
#execute main block
while not kill_program:
login()
parse_email(email_interval)
st.kill()
sys.exit()
Based upon the error, I would very carefully check the parameters that you're passing to fetch. Gmail is telling you that it could not parse the command that you sent to it.
Also, you can do a STORE +FLAGS \SEEN to mark the messages as read.

Uploading files in Google App Engine. How to get the file to the upload Handler Class

I have a form in google app engine where I want to upload an image and all my text at the same time. Do I have to seperate this into two seperate pages and actions?
Here is my upload handler:
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def upload(self, reseller_id, imgfile):
upload_files = imgfile
blob_info = upload_files[0]
key = blob_info.key()
r = Reseller.get_by_id(reseller_id)
r.blob_key_logo = str(key)
r.put();
Here is my creation of a new reseller object:
class NewReseller(BaseHandler):
def get(self):
if self.user:
self.render("new_reseller.html")
else:
self.redirect("/display_resellers")
def post(self):
name = self.request.get('name')
website = self.request.get('website')
information = self.request.get('information')
address = self.request.get('address')
city = self.request.get('city')
state = self.request.get('state')
zipcode = self.request.get('zipcode')
email = self.request.get('email')
phone = self.request.get('phone')
r = Reseller( name = name,
website = website,
information = information,
address = address,
city = city,
state = state,
zipcode = zipcode,
email = email,
phone = phone)
r.put()
theresellerid = r.key().id()
#And then Upload the image
u = UploadHandler()
logo_img = u.get_uploads('logo_img')
u.upload(theid, logo_img)
self.redirect('/display_resellers')
I think my problem here is this line:
logo_img = u.get_uploads('logo_img')
it pops out the error message
for key, value in self.request.params.items():
AttributeError: 'NoneType' object has no attribute 'params'
Somehow I need this NewReseller class to inherit the .getuploads from BlobstoreUploadHandler so I can do:
logo_img = self.get_uploads('logo_img')
Or there is probably a better way because this seems a little messy.
So my question is how to upload files and data in one form on just one page. I could do it with two seperate pages. One for adding the reseller and one for adding the image but that seems over complicated.
I tried to follow some steps and clues from this question:
Upload files in Google App Engine
******Edit***** Working Implementation Below:
class EditReseller(BaseHandler, blobstore_handlers.BlobstoreUploadHandler):
def get(self, reseller_id):
if self.user:
reseller = Reseller.get_by_id(int(reseller_id))
upload_url = blobstore.create_upload_url('/upload')
image = True
if reseller.blob_key_logo is None:
image = False
self.render('edit_reseller.html', r=reseller, reseller_id=reseller_id, upload_url=upload_url, image=image)
else:
self.redirect('/admin')
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def post(self):
reseller_id = self.request.get('reseller_id')
upload_files = self.get_uploads('logo_img')
if upload_files:
blob_info = upload_files[0]
key = blob_info.key()
r = Reseller.get_by_id(int(reseller_id))
r.blob_key_logo = str(key)
r.put();
name = self.request.get('name')
website = self.request.get('website')
information = self.request.get('information')
address = self.request.get('address')
city = self.request.get('city')
state = self.request.get('state')
zipcode = self.request.get('zipcode')
email = self.request.get('email')
phone = self.request.get('phone')
if name and website and information and email and phone and address and city and state and zipcode:
r = Reseller.get_by_id(int(reseller_id))
r.name = name
r.website = website
r.information = information
r.address = address
r.city = city
r.state = state
r.zipcode = zipcode
r.email = email
r.phone = phone
r.put()
else:
error = "Looks like your missing some critical info"
self.render("edit_reseller.html", name=name, website=website, information=information, address=address, city=city, zipcode=zipcode, email=email, phone=phone, error=error)
self.redirect("/edit_reseller/" + reseller_id)
You just need to put the logic of the UploadHandler inside the Reseller(BaseHandler) and make Reseller inherit from blobstore_handlers.BlobstoreUploadHandler.
The call to get_uploads fails, as the NewReseller Class does not inherit from BlobstoreUploadHandler. The BlobstoreUploadHandler class takes over the upload operation so you do not need to create a post method, just add the corresponding logic from post ( name = self.request.get('name'), r = Reseller(), r.put(), etc. ) and add it to the upload method.
You should not call or create a new a handler instance by hand (unless you know what you are doing), as it would be missing the things that make it work.
The complete app sample at the official docs, might also be helpful.

Categories