Как в Node.js отправлять iCal инвайты по E-mail

Понадобилось мне для methodist.io отправлять с google cloud functions приглашения на митинги iCal пользователям. Да так, чтоб как положено: с кнопочками принять и отклонить в Outlook и Gmail. И оказалось, что инфы как это сделать — кот наплакал. Поэтому, не тратя ваше время, перейду к сути.

Сначала нам нужно сгенирировать сам ics файл, а затем отправить его. Для этого я использовал:

Чтобы календарное событие отображалось в почтовых клиентах так, словно его отправили из outlook или gmail (с кнопочками принять/отклонить и просмотром выбранного тайм слота), нужно отправлять особые хедеры. В них и вся фишка. Именно поэтому нужно использовать SMTP клиент типа nodemailer. В нем можно указывать любой хедер для контента.

Вот примеры функций, которые нам необходимы:

// Генерация ics файла события
const ics = require('ics');

function createMeeting(event, organizer, team, eventUrl) {
  const meeting = {
    start: moment.utc(event.date).format('YYYY-M-D-H-m').split('-'),
    end: moment
      .utc(event.date)
      .add({ minutes: event.duration })
      .format('YYYY-M-D-H-m')
      .split('-'),
    startType: 'utc',
    title: `Meeting title`,
    description: `Join our online session`,
    location: 'Online',
    url: eventUrl,
    organizer: organizer,
    attendees: team,
    method: 'REQUEST',
    recurrenceRule: event.recurrenceRule, // подробнее можно прочитать в документации ics
    productId: 'Methodist.io',
    busyStatus: 'BUSY',
  };
  return meeting;
}
// Отправка E-mail с событием через SMTP nodemailer
const nodemailer = require('nodemailer');

function postEvent(teamList, organizer, event, meetingFile, roomUrl) {
  ics.createEvent(meetingFile, (error, icsContent) => {
    if (error) {
      console.log(error);
    }

    const transporter = nodemailer.createTransport({
      host: 'smtp.sendgrid.net',
      port: 587,
      secure: false, // true for 465, false for other ports
      auth: {
        user: 'YOUR_LOGIN',
        pass: 'YOUR_PASSWORD',
      },
    });

    // send mail with defined transport object
    transporter
      .sendMail({
        from: 'Sender name <notify@gmail.com>', // sender address
        to: teamList.map((member) => {
          return `${member.name} <${member.email}>`;
        }), // list of receivers
        subject: `${capitalize(event.type)} session`, // Subject line
        html: `Join online ${event.type} session. <br>
        Room link: <a href="${roomUrl}">methodist.io</a>`, // html body,
        icalEvent: {
          method: 'REQUEST',
          filename: 'meeting.ics',
          content: Buffer.from(icsContent).toString('base64'),
          encoding: 'base64',
        },
      })
      .then((s) => {
        return 'Success';
      })
      .catch((err) => {
        console.log('Emails not sent', err);
      });
  });
}

Если вы хотите использовать не nodemailer, то самое главное – слать контент события с хедером

alternatives: [{
     contentType: 'text/calendar; charset="utf-8"; method=REQUEST',
     content: icsFileContent.toString()
}]

И в тексте ics файла method должен быть request. method: 'REQUEST'

Если method будет прописать везде корректно, то событие будет правильно отображаться даже в Outlook.

P.S. Кстати если вы вдруг используете sendgrid, то он поддерживает отправку не только через API, а и по smtp, что позволит делать рассылку с правильно заданным контентом.

const transporter = nodemailer.createTransport({
      host: 'smtp.sendgrid.net',
      port: 587,
      secure: false, // true for 465, false for other ports
      auth: {
        user: 'apikey', // оставляем как есть
        pass: 'ВАШ_АПИ_КЛЮЧ',
      },
    });

P.P.S Дату в событиях рекомендую использовать в utc формате, чтоб время было корректное в любом часовом поясе.
P.P.P.S Чтоб событие имело правильную продолжительность в аутлуке, нужно указывать не duration, а конечную дату end.
P.P.P.P.S Если отправлять самому себе на почту gmail iCal событие, то gmail отобразит просто как файл, без кнопок RSVP (accept/decline/tentative etc.) в письме.

Подписывайтесь

Для получения уведомлений о новых публикациях подписывайтесь на мой блог или страницы в соц. сетях: Twitter, Facebook. Также у меня есть канал в Telegram (@golosay_net), в котором я чаще всего публикую что-нибудь интересное про Front-End.

Подписаться на блог по эл. почте
Loading