英文:
Why only the first one can get the email invitation via Python icalendar
问题
这段代码主要用于发送Outlook邀请邮件,但在测试中只有接收者列表中的第一个收件人能够收到邀请。问题可能出在邮件的收件人字段设置上,只有第一个收件人被正确处理,而其他的被忽略了。要修复这个问题,你可以尝试以下步骤:
-
更改
msg['To']
的设置,将接收者列表连接成一个逗号分隔的字符串,而不是分号分隔。修改这一行:msg['To'] = ';'.join(self.receivers)
改成:
msg['To'] = ', '.join(self.receivers)
这会将接收者列表中的所有邮件地址放入同一个 To 字段中,以逗号和空格分隔。
-
你的
sendmail
函数调用中,使用msg["To"]
作为邮件的收件人。确保self.receivers
也传递给sendmail
函数,以便每个接收者都会收到邮件。修改这一行:self.smtp.sendmail(msg["From"], [msg["To"]], msg.as_string())
改成:
self.smtp.sendmail(msg["From"], self.receivers, msg.as_string())
这些更改应该确保每个邮件地址都会收到邀请。重新运行代码,应该可以发送给接收者列表中的所有人。
英文:
I have a script that will run periodically to send email invitations to all receivers to inform them about upcoming maintenance.
Here is the code example
import os
import uuid
import smtplib
import icalendar
import datetime
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email import encoders
import pytz
from jinja2 import FileSystemLoader, Environment
class EmailWriter:
SMTP = 'smtp.test.com'
def __init__(self, receivers, cluster_name, dtstart=None, dtend=None, available="", tasks=""):
self.sender = 'sender@test.com'
self.smtp = smtplib.SMTP(EmailWriter.SMTP)
self.receivers = receivers
self.cluster_name = cluster_name
self.dtstart = dtstart
self.dtend = dtend
self.available = available
self.tasks = tasks
def __get_email_subject_and_content(self):
path = os.path.join(os.getcwd(), 'email_templates')
loader = FileSystemLoader(path)
env = Environment(loader=loader)
template_minor = env.get_template('minor_maintenance_email.html')
template_major = env.get_template('major_maintenance_email.html')
if 'unavailability' in self.available.lower():
html_content = template_major.render(
availability=self.available,
maintenance_date=self.dtstart,
start_time=self.dtstart,
expected_end_time=self.dtend,
tasks=self.tasks
)
subject = '{} | Maintenance | {}'.format(self.cluster_name, self.available)
else:
html_content = template_minor.render()
subject = '{} | Maintenance | 100% Availability'.format(self.cluster_name)
print('subject : "{}", receivers : "{}", maintenance_date : "{}", start_time : "{}", expected_end_time : "{}", '
'"task : "{}"'.format(subject, self.receivers, self.dtstart, self.dtstart, self.dtend, self.tasks))
return subject, html_content
def __prepare_event(self, subject, content, start, end):
event = icalendar.Event()
organizer = icalendar.vCalAddress('MAILTO:' + self.sender)
event.add('organizer', organizer)
event.add('status', 'confirmed')
event.add('category', 'Event')
event.add('summary', subject)
event.add('description', content)
event.add('dtstart', start)
event.add('dtend', end)
event.add('dtstamp', datetime.datetime.now())
event['uid'] = uuid.uuid4()
# Set the busy status of the appointment to free
event.add('X-MICROSOFT-CDO-BUSYSTATUS', icalendar.vText('FREE'))
event.add('priority', 5)
event.add('sequence', 0)
event.add('created', datetime.datetime.now())
for participant in self.receivers:
attendee = icalendar.vCalAddress('MAILTO:' + participant)
attendee.params['ROLE'] = icalendar.vText('REQ-PARTICIPANT')
attendee.params['cn'] = icalendar.vText(' '.join(participant.split('@')[0].split('.')))
event.add('attendee', attendee, encode=0)
return event
def __prepare_alarm(self):
alarm = icalendar.Alarm()
alarm.add('action', 'DISPLAY')
alarm.add('description', 'Reminder')
# The only way to convince Outlook to do it correctly
alarm.add('TRIGGER;RELATED=START', '-PT{0}H'.format(1))
return alarm
def __prepare_icalendar(self):
# Build the event itself
cal = icalendar.Calendar()
cal.add('prodid', icalendar.vText('-//Calendar Application//'))
cal.add('version', icalendar.vInt(2.0))
cal.add('method', icalendar.vText('REQUEST'))
# creates one instance of the event
cal.add('X-MS-OLK-FORCEINSPECTOROPEN', icalendar.vBoolean(True))
return cal
def __prepare_email_message(self, subject, content):
# Build the email message
# msg = MIMEMultipart('alternative')
msg = MIMEMultipart('mixed')
msg['Subject'] = subject
msg['From'] = self.sender
msg['To'] = ';'.join(self.receivers)
msg['Content-class'] = 'urn:content-classes:calendarmessage'
msg.attach(MIMEText(content, 'html', 'utf-8'))
return msg
def __prepare_invite_blocker(self, cal):
filename = 'invite.ics'
part = MIMEBase('text', 'calendar', method='REQUEST', name=filename)
part.set_payload(cal.to_ical())
encoders.encode_base64(part)
part.add_header('Content-Description', filename)
part.add_header('Filename', filename)
part.add_header('Path', filename)
return part
def send_appointment(self):
subject, html_content = self.__get_email_subject_and_content()
start = datetime.datetime.combine(self.dtstart, datetime.time(0, 0, 0)).astimezone(pytz.UTC)
end = datetime.datetime.combine(self.dtend, datetime.time(0, 0, 0)).astimezone(pytz.UTC)
cal = self.__prepare_icalendar()
event = self.__prepare_event(subject, html_content, start, end)
alarm = self.__prepare_alarm()
# Add a reminder
event.add_component(alarm)
cal.add_component(event)
part = self.__prepare_invite_blocker(cal)
msg = self.__prepare_email_message(subject, html_content)
msg.attach(part)
# Send the email out
self.smtp.sendmail(msg["From"], [msg["To"]], msg.as_string())
self.smtp.quit()
print('Invitation sent out')
def main():
receivers = ['test1@test.com', 'test2@test.com', 'test3@test.com']
cluster_name = 'TEST NOW (test_now)' # test cluster name
email_writer = EmailWriter(
receivers,
cluster_name,
datetime.datetime.strptime('2023-02-16', '%Y-%m-%d').date(),
datetime.datetime.strptime('2023-02-16', '%Y-%m-%d').date() + datetime.timedelta(days=1),
'100% Availability',
tasks='Minor test'
)
print('Sending email')
email_writer.send_appointment()
if __name__ == '__main__':
main()
However, when I tested the code, I could see only the first recipient in the receivers list can get the outlook invitation.
How to fix the code to let all email account in the list can get the invitation?
答案1
得分: 1
看起来,根据其他示例,msg['To']
对象需要以逗号分隔的字符串格式存在。我相信你目前使用的是分号;
,尝试在你的代码中将它更改为逗号,
,看看是否能解决问题。
原始代码:msg['To'] = ';;'.join(self.receivers)
新代码:msg['To'] = ', '.join(self.receivers)
英文:
Looking at some other examples, it looks like the msg['To'] object needs to be in a string format with a delimiter of ',' I believe you are using ';' try changing that in your code and see if that resolves the issue.
current_code: msg['To'] = ';'.join(self.receivers)
new_code: msg['To'] = ', '.join(self.receivers)
答案2
得分: 0
谢谢你的帮助!
最后,我在这里找到了一个解决方案。
首先,就像上面的答案所说,我需要使用,
作为分隔符。
msg['To'] = ', '.join(self.receivers)
其次,在smtp.sendmail()
函数中,接收参数的类型是一个列表,所以这里我直接给函数一个列表
self.smtp.sendmail(msg['From'], self.receivers, msg.as_string())
sendmail()
的源代码
def sendmail(self, from_addr, to_addrs, msg, mail_options=(),
rcpt_options=()):
"""This command performs an entire mail transaction.
The arguments are:
- to_addrs : A list of addresses to send this mail to. A bare
string will be treated as a list with 1 address.
Example:
>>> import smtplib
>>> s=smtplib.SMTP("localhost")
>>> tolist=["one@one.org","two@two.org","three@three.org","four@four.org"]
>>> msg = '''\\
... From: Me@my.org
... Subject: testin'...
...
... This is a test '''
>>> s.sendmail("me@my.org",tolist,msg)
英文:
Thank you for your help!
Finally, I got a solution here.
First, as the above answer said, I need to use ,
as the delimiter.
msg['To'] = ', '.join(self.receivers)
Second, in the function of smtp.sendmail()
, the receiver parameter type is a list, so here I directly give the function a list
self.smtp.sendmail(msg['From'], self.receivers, msg.as_string())
Source code of sendmail()
def sendmail(self, from_addr, to_addrs, msg, mail_options=(),
rcpt_options=()):
"""This command performs an entire mail transaction.
The arguments are:
- to_addrs : A list of addresses to send this mail to. A bare
string will be treated as a list with 1 address.
Example:
>>> import smtplib
>>> s=smtplib.SMTP("localhost")
>>> tolist=["one@one.org","two@two.org","three@three.org","four@four.org"]
>>> msg = '''\\
... From: Me@my.org
... Subject: testin'...
...
... This is a test '''
>>> s.sendmail("me@my.org",tolist,msg)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论