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?
 
    