I am trying to extract outlook email body, recipient address, subject and received date.
I was able to extract subject and received date but unable to extract body and recipient address:
Below is my code for subject and received date:
outlook = win32com.client.Dispatch('Outlook.Application').GetNamespace('MAPI')
namespace = outlook.Session
recipient = namespace.CreateRecipient("abc#xyz.com")
inbox = outlook.GetSharedDefaultFolder(recipient, 6)
messages = inbox.Items
email_subject = []
email_date = []
email_date_time = []
for x in messages:
sub = x.Subject
received_date = x.senton.date()
received_date_time = str(x.ReceivedTime)
email_subject.append(sub)
email_date.append(received_date)
email_date_time.append(received_date_time)
For body i am trying:
for x in messages:
body = x.Body
print(body)
but this is not working and i am getting the error below:
Traceback (most recent call last):
File "<ipython-input-85-d79967933b99>", line 2, in <module>
sub = x.Body
File "C:\ProgramData\Anaconda3\lib\site-packages\win32com\client\dynamic.py", line 516, in __getattr__
ret = self._oleobj_.Invoke(retEntry.dispid,0,invoke_type,1)
com_error: (-2147467259, 'Unspecified error', None, None)
I've just run similar code on my computer, in an inbox with 3,000+ items of mixed type (skype message notifications, calendar invites/notifications, emails, etc.) and I cannot replicate this error, even for items where not m.Body -- I thought that was a possible culprit, maybe a certain type of item doesn't have a body would throw an error -- but that appears to not be the case:
>>> for m in messages:
... if not m.body:
... print(m.Subject)
... print(m.Body)
...
Accepted: Tables discussion
Message Recall Failure: tables/ new data status
Message Recall Failure: A few issues with the data
You should probably add a print(m.Class) because I still think that maybe certain types of items do not have a Body property.
This thread suggests that there may be a user/security setting that prevents programmatic access to Outlook, so you might want to double-check on that (though I think if not allowed, none of your code would work -- still, worth looking in to!).
I have figured out the source of this error. We are running into issues with the comObjectModelGaurd. Our group policy recently changed to disallow programatic access to protected mailItem objects.
Modifying Outlook Users Trust settings or the registry will fix the problem.
Since I can't replicate the error, perhaps I can still help you debug and identify the source of the problem, from which we can probably come up with a good solution.
Use a function to get the item's body, and use try/except to identify which items are causing error.
def getBody(m):
s = ""
try:
s = m.Body
except COMError:
s = '\t'.join(('Error!', m.Class, m.senton.date(), m.Subject))
return s
for m in messages:
print(getBody(m))
I think that I found a solution that worked. For me it was an issue with permissions, but I made the registry edits in https://www.slipstick.com/developer/change-programmatic-access-options/ and it worked a treat.
EDIT: I think this worked by unblocking some lower level permissions that enabled an outside program to access the outlook client.
Recalled Emails would have no Body hence we can find that via the MessageClass and exclude that particular Class
for i in messages:
if email.MessageClass != "IPM.Outlook.Recall":
Related
Using the python google client I able to create new messages within an existing thread with id threadId as follows:
message = (service.users().messages().send(userId='me', body={'threadId': <threadId>}, media_body=message['media_body'])
.execute())
(here I'm using media_body as it supports the /upload endpoint for large attachments)
This works fine for messages, and the threadId optional parameter is documented at https://developers.google.com/gmail/api/v1/reference/users/messages/send
However I have been unsuccessful doing the same when creating new draft messages, and don't see any docs about it at https://developers.google.com/gmail/api/v1/reference/users/drafts/create
I tried adding threadId to the draft body when doing draft = service.users().drafts().create(userId=user_id, body={'threadId': <threadId>}, media_body=message_body['media_body']).execute() at the create draft stage and it simply is ignored.
I also tried adding threadId to the body at the send draft stage: message = service.users().drafts().send( userId='me', body={'id': draft_id, threadId': <threadId>}).execute() and this was also ignored.
Either way the draft message just gets created in its own, new thread.
How can I create a new draft message within an existing thread with the gmail API (and specifically with Python client)?
email = self.service.users().messages().get(userId='me', id='1756f9403f66ac1d').execute() # sub in your message id
def get_field(self, email, field_name):
header = email['payload']['headers']
for m in header:
if m['name'] == field_name:
return m['value']
def add_draft(self, email, body):
message = MIMEText(body)
message['to'] = 'to#email.com'
message['from'] = 'from#email.com'
message['subject'] = self.get_field(email, 'Subject')
message['In-Reply-To'] = self.get_field(email, 'Message-ID')
message['References'] = self.get_field(email, 'Message-ID')# + ',' + self.get_field_a(email, 'References')
email_body = {'message' : {'threadId' : email['threadId'], 'raw' : base64.urlsafe_b64encode(message.as_string().encode('utf-8')).decode()}}
draft = self.service.users().drafts().create(userId='me', body=email_body).execute()
In this above example I was adding a draft as a response to a brand new thread that had one message sent to me. That particular email didn't have a value 'References' in the header so I commented out the section where I was extracting that field from the received email and adding it to the draft. For adding a draft to longer threads I believe you'll need to read and append this field. A function to safely append the field is left as an exercise to the reader :)
A great resource for seeing examples of what you should be sending is the GMail 'show original'. Open an email -> three buttons -> Show Original. This opens a page where you can see the values of the actual fields in the emails gmail is sending for you when you (the human you) sends emails. For example, here is the reference and in reply to section of the second message in a different thread:
References: <1243993195.RE.1603730339394.ref#mail.yahoo.com>,<1243993195.REDACTED.1603730339394#mail.yahoo.com>
In-Reply-To: <1243993195.REDACTED.1603730339394#mail.yahoo.com>
In this case, 'In-Reply-To' was set as the 'Message-Id' field of the prior message, and 'References' was set as prior 'Message-Id' plus the prior 'References' fields
Here's a screenshot of the final result:
As per documentation, a draft can be added to a thread as part of creating, updating, or sending a draft message.
In order to be part of a thread, a message or draft must meet the following criteria:
The requested threadId must be specified on the Message or
Draft.Message you supply with your request.
The References and In-Reply-To headers must be set in compliance
with the RFC 2822 standard.
The Subject headers must match.
The documentation also states that in both examples for creating a draft and sending a message, you would simply add a threadId key paired with a thread ID to a message's metadata, the message object.
I'm working with Outlook, using mapi library and Python. I try to get the first message out of few similar messages( only subject is different). Somehow, I get second message only. Here is my code:
self.outlook = win32com.client.Dispatch("Outlook.Application")
self.mapi = self.outlook.GetNamespace("MAPI")
folderHandle = self.mapi.GetDefaultFolder(folder)
messages = folderHandle.Items
message = messages.GetFirst()
The subject of the message I get -is the subject of the second email in the list( the email are similar except the subject), instead of the first message subject.
Items collection is not sorted in any particular order until you explicitly call Items.Sort.
We are working on a project where the python client makes RPC call on a Java method
String uploadFile(String name, String Id)
Now, this client code has to send an attachment!
def sendFile(self, Id, filePath):
uploadFileMethod = getattr(self.client.service, "uploadFile")
attachment_id = Id
attachment_content = (filePath, attachment_id)
with_soap_attachment(uploadFileMethod, attachment_content)
Since, suds does not support attachments and I luckily found a scrpit mentioned that it does. The script is mentioned here
Now, when I execute, I am getting the error
AttributeError: 'Client' object has no attribute 'location'
line 75, in with_soap_attachment
Can anyone help me why its coming and how to fix it?
thanks
what worked for me was to replace
request = Request(suds_method.client.location(), request_text)
with
request = Request(soap_method.location(), request_text)
Not sure if there's a better way to do this but I have a sign up page on my site and after a user signs up I add their initial data(stuff in the __init__ data model) then I start adding some other info in the same section which is giving me a broken pipe error. Oddly the code seems to work since the entries I'm expecting are in the database. I have tried moving around the .flush() command to see if it helps but it doesn't seem to be.
Traceback (most recent call last):
File "/Users/me/Dropbox/code/eclipseWorkSpace/website/pyramidwiki/lib/python2.7/site-packages/waitress-0.8.1-py2.7.egg/waitress/channel.py", line 134, in handle_write
flush()
File "/Users/me/Dropbox/code/eclipseWorkSpace/website/pyramidwiki/lib/python2.7/site-packages/waitress-0.8.1-py2.7.egg/waitress/channel.py", line 249, in _flush_some
num_sent = self.send(chunk)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/asyncore.py", line 365, in send
result = self.socket.send(data)
error: [Errno 32] Broken pipe
Here's my code:
if 'form.submitted' in request.params:
firstname = request.params['firstname']
lastname = request.params['lastname']
email = request.params['email']
password = request.params['password']
try:
new_user = Users(email, firstname, lastname, password)
DBSession.add(new_user)
#DBSession.flush() #commit so we get error if any
#add some other info
user_data = DBSession.query(Users).filter(Users.email==email).first()
user_data.join_date = datetime.datetime.now()
#create random number for verification url
user_data.vertified = id_generator(50)
DBSession.flush() #doesn't seem to make a difference where the flush is
return HTTPFound(location = request.route_url('new'))
Any ideas?
This may not answer you question directly, but "you're doing it all wrong"(tm) :)
You don't need to re-query User object after you added it to the session - what's more, trying to query it from the database without doing session.flush() first will result in an error because there's no record in the database yet. I'd do something like this:
if 'form.submitted' in request.params:
firstname = request.params['firstname']
lastname = request.params['lastname']
email = request.params['email']
password = request.params['password']
try:
new_user = Users(email, firstname, lastname, password)
new_user.join_date = datetime.datetime.now()
new_user.verified = id_generator(50)
DBSession.add(new_user)
DBSession.flush() # should fail if user email is in the database
return HTTPFound(location = request.route_url('new'))
Also, you need to check that all branches of execution (i.e. the except: clause, the else: clause of "if 'form.submitted' in request.params" return something meaningful - you may be getting the exception because your view function returns None in some conditions. Actually, that's probably what was happening - the "user_data = DBSession.query(Users)" line was raising an exception, and the except: part was not returning anything
I too struggled with the same problem on my Pyramid project and contrary to comments on github, it wasn't with waitress.
In my case, the problem of Error: Broken Pipe only occured whenever I used redirects (HTTPFound, HTTPMovedPermanently etc.). Since your function also uses HTTPFound, I think the problem is the same.
Atleast for me, this error was due to pyramid_debugtoolbar extension. The reason is probably that whenever our view does soemthing like
return HTTPFound(foo)
it sends out a 302 in the header and a Connection:Close. The pyramid_debugtoolbar extension adds a lengthy body to the response. The client on seeing the header, closes the connection and does not accept the lengthy debug body. Hence the Broken Pipe message (Atleast that is what Wireshark shows).
Try disabling the pyramid_debugtoolbar in your app's .ini, it might help.
I've developed a Python application to automate sending emails and meeting requests for internal office events. To keep these separate from my regular communications, we've set up an alternate email address that I can use to send the official announcements. I've modified my application to handle this for emails by using the SentOnBehalfOfName for the alternate sender - however, I haven't been able to duplicate this for meeting requests. My attempt based on a series of web searches follows. When running this, though, I get the error:
Traceback (most recent call last):
File "mailer_test.py", line 49, in <module> test_sender)
File "mailer_test.py", line 38, in send_meeting_request
mtg.Send()
File "<COMObject CreateItem>", line 2, in Send
pywintypes.com_error: (-2147024809, 'The parameter is incorrect.', None, None)
This happens when I add in the option for an alternate sender - removing this results in the message sent successfully from my account. The test code which reproduces the error is below - I've removed my actual email address, but everything else is the same.
import win32com.client
OUTLOOK_APPOINTMENT_ITEM = 1
OUTLOOK_MEETING = 1
OUTLOOK_ORGANIZER = 0
OUTLOOK_OPTIONAL_ATTENDEE = 2
ONE_HOUR = 60
THIRTY_MINUTES = 30
OUTLOOK_FORMAT = '%m/%d/%Y %H:%M'
outlook_date = lambda dt: dt.strftime(OUTLOOK_FORMAT)
class OutlookClient(object):
def __init__(self):
self.outlook = win32com.client.Dispatch('Outlook.Application')
def send_meeting_request(self, subject, time, location, recipients, body,
sender=None):
mtg = self.outlook.CreateItem(OUTLOOK_APPOINTMENT_ITEM)
mtg.MeetingStatus = OUTLOOK_MEETING
mtg.Location = location
if sender:
# Want to set the sender to an address specified in the call
# This is the portion of the code that does not work
organizer = mtg.Recipients.Add(sender)
organizer.Type = OUTLOOK_ORGANIZER
for recipient in recipients:
invitee = mtg.Recipients.Add(recipient)
invitee.Type = OUTLOOK_OPTIONAL_ATTENDEE
mtg.Subject = subject
mtg.Start = outlook_date(time)
mtg.Duration = ONE_HOUR
mtg.ReminderMinutesBeforeStart = THIRTY_MINUTES
mtg.ResponseRequested = False
mtg.Body = body
mtg.Send()
if __name__ == "__main__":
import datetime
ol = OutlookClient()
meeting_time = datetime.datetime.now() + datetime.timedelta(hours=3)
test_recipients = ['me#example.com']
test_sender = 'alternate#example.com'
ol.send_meeting_request('Test Meeting', meeting_time, 'Nowhere',
test_recipients, 'This is a test meeting.',
test_sender)
Note: This is not the same problem as this question, since I'm not using C# and I'm also not trying to edit the meeting request after the fact.
Update:
As Marnix Klooster suggested, I've been looking through the UI to see how I can do this, and it doesn't appear to be easy (if even possible). The one way I have done it is to go into the other user's calendar and create a new appointment there and add invitees. That mailbox is added by going to the Advanced tab from the More Settings... button in the Server Settings dialog displayed when changing the Account Settings. An alternate answer to this question would be how to use this mailbox as the default originator when accessing Outlook via COM.
According to this page, you can send meeting requests on behalf of another person, but you need to have access to that person's calendar. The other person must appoint you as a delegate.