Den*_*nko 17 icalendar outlook gmail sendgrid sendgrid-api-v3
我正在尝试.ics通过 SendGrid (从节点服务器)发送日历邀请,以便它在 Outlook 或 Gmail 等客户端中呈现为实际邀请(带有接受/拒绝按钮),而不仅仅是作为附件文件。
我花了几天时间研究这个问题(数十个 Stackoverflow 问题、RFC -5545、RFC-2446、iCalendar规范摘录、Sendgrid 的 GitHub 问题线程:1、2、3 、 SendGrid文档、源等)。
然而,似乎没有答案(或者我错过了什么?)。
到目前为止我发现Content-Type附件在这里非常重要,尤其是method=REQUEST部分。甚至文件中属性的顺序也会有所不同。
尽管这里有很多关于 SO 的问题,但由于某种原因,大多数问题仍然没有得到解答。
这是我设置attachment对象的方法:
const SendGrid = require("@sendgrid/mail");
const attachment = {
filename: 'invite.ics',
name: 'invite.ics',
content: Buffer.from(data).toString('base64'),
disposition: 'attachment',
contentId: uuid(),
type: 'application/ics'
};
SendGrid.send({
attachments: [attachment],
templateId,
from: {
email: config.emailSender,
name: config.emailName,
},
to: user.email,
dynamicTemplateData: {
...rest,
user,
},
headers: {
'List-Unsubscribe': `<mailto:unsubscribe.link`,
},
});
Run Code Online (Sandbox Code Playgroud)
至于type财产,我尝试过以下变体:
1. type: 'text/calendar; method=REQUEST'
2. type: 'application/ics'
3. type: 'text/calendar;method=REQUEST;name=\"invite.ics\"'
4. type: 'text/calendar; method=REQUEST; charset=UTF-8; component=vevent'
5. type: 'text/calendar'
Run Code Online (Sandbox Code Playgroud)
但是,除了'text/calendar'and之外,没有任何作用'application/ics'(并且它们之间似乎没有任何区别)。
Content-Type根据 SendGrid 文档,它是一个保留标头,因此不可能通过headers属性或其他方式设置它。
该disposition: 'inline'选项也根本不起作用(仅disposition: 'attachment')。
我生成的文件如下所示.ics:
BEGIN:VCALENDAR
PRODID:-//Organization//Organization App//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:REQUEST
BEGIN:VEVENT
DTSTART:20210426T160000Z
DTEND:20210426T170000Z
DTSTAMP:20210418T134622Z
ORGANIZER;CN=John Smith:MAILTO:john.smith+test1@gmail.com
UID:dcfd5905-be85-4c8f-8a27-475b0ec67d8b
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=John Smith;X-NUM-GUESTS=0:MAILTO:john.smith+test1@gmail.com
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=John Test;X-NUM-GUESTS=0:MAILTO:john.smith+test2@gmail.com
CREATED:20210418T134622Z
DESCRIPTION:my description
LAST-MODIFIED:20210418T134622Z
LOCATION:https://location.url
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:my summary
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR
Run Code Online (Sandbox Code Playgroud)
该文件完全有效,并且可以在 iCalendar 中无缝打开。
但为什么它没有在 Outlook 或 Gmail 中呈现?
目前,将事件添加到日历的唯一方法是单击附件上的“下载” invite.ics,然后将其打开,只有在此之后,日历应用程序才会打开,您可以确认邀请。
PS:我所说的rendering邀请.ics是指 Outlook 或 Gmail 自动识别.ics附件并将其显示为如下图所示(抱歉出现红线):

如果有什么区别,我正在使用@sendgrid/mail v6.3.1
您能帮我解决我的问题吗?我究竟做错了什么?
如何使电子邮件客户端识别我的.ics文件并允许用户在电子邮件客户端本身中接受/拒绝这些邀请,而无需手动下载文件并打开它?
Den*_*nko 19
好吧,经过大量的尝试和错误,我终于成功了。我希望代码对其他人有帮助。
因此,首先,我所做的是从 iCalendar 发送实际的活动邀请并接收此.ics邀请(实际上在 Outlook 和 Gmail 中均呈现)。我查看了这个文件与我生成的文件有何不同,发现了一个奇怪的事情:
让这个工作的关键是......
魔法弦
是的,完全随机的、奇怪的魔法弦。
下面我发布了.ics对我有用的文件内容。
TOTTALLY-RANDOM-MAGIC-STRING- 是完全随机字符串的占位符,例如 uuid 或者您的组织电子邮件或其他任何内容。
关键是: 在文件中使用这些字符串,Outlook 和 Gmail 可以正确呈现邀请,如果没有它们,则不会。很奇怪,但工作正常。
我在文档或 RFC 中找不到任何对此有意义的信息,所以我想现在调用这些magic strings是安全的。
第一个神奇的字符串是TOTTALLY-RANDOM-MAGIC-STRING@imip.me.com。
第二个神奇的字符串是/TOTTALLY-RANDOM-MAGIC-STRING/principal/。
BEGIN:VCALENDAR
PRODID:-//Organisation//Organisation App//EN
METHOD:REQUEST
VERSION:2.0
BEGIN:VEVENT
DTEND:20210427T160000Z
ORGANIZER;CN=Organization Name;EMAIL=admin@organisation.com:mailto:TOTTALLY-RANDOM-MAGIC-STRING@imip.me.com
UID:D670DA52-3E7F-4F61-97E2-CB8878954504
DTSTAMP:20210419T181455Z
LOCATION:virtual.event.location.com
DESCRIPTION:description
URL;VALUE=URI:http://organization.com/invite
SEQUENCE:0
SUMMARY:my summary
LAST-MODIFIED:20210419T181455Z
DTSTART:20210427T150000Z
CREATED:20210419T181455Z
ATTENDEE;CUTYPE=INDIVIDUAL;EMAIL=my.email1@gmail.com:mailto:my.email1@gmail.com
ATTENDEE;CUTYPE=INDIVIDUAL;EMAIL=my.email2@gmail.com:mailto:my.email2@gmail.com
ATTENDEE;CN=Organisation Name;CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED;ROLE=CHAIR;EMAIL=admin@organisation.com:/TOTTALLY-RANDOM-MAGIC-STRING/principal/
END:VEVENT
END:VCALENDAR
Run Code Online (Sandbox Code Playgroud)
和代码:
BEGIN:VCALENDAR
PRODID:-//Organisation//Organisation App//EN
METHOD:REQUEST
VERSION:2.0
BEGIN:VEVENT
DTEND:20210427T160000Z
ORGANIZER;CN=Organization Name;EMAIL=admin@organisation.com:mailto:TOTTALLY-RANDOM-MAGIC-STRING@imip.me.com
UID:D670DA52-3E7F-4F61-97E2-CB8878954504
DTSTAMP:20210419T181455Z
LOCATION:virtual.event.location.com
DESCRIPTION:description
URL;VALUE=URI:http://organization.com/invite
SEQUENCE:0
SUMMARY:my summary
LAST-MODIFIED:20210419T181455Z
DTSTART:20210427T150000Z
CREATED:20210419T181455Z
ATTENDEE;CUTYPE=INDIVIDUAL;EMAIL=my.email1@gmail.com:mailto:my.email1@gmail.com
ATTENDEE;CUTYPE=INDIVIDUAL;EMAIL=my.email2@gmail.com:mailto:my.email2@gmail.com
ATTENDEE;CN=Organisation Name;CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED;ROLE=CHAIR;EMAIL=admin@organisation.com:/TOTTALLY-RANDOM-MAGIC-STRING/principal/
END:VEVENT
END:VCALENDAR
Run Code Online (Sandbox Code Playgroud)
我希望这能为尝试让这些.ics东西发挥作用的人们节省一些时间。
因此,经过多次试验,我终于成功了,这里有一个全面的解释和注释,说明我如何解决可能出现的问题。
首先,我使用ics生成日历文件。所以你将定义你的事件,例如
const event = {
start: [2018, 5, 30, 6, 30],
duration: { hours: 1, minutes: 30 },
title,
description,
location: 'Folsom Field, University of Colorado (finish line)', // you can use a link here if it is online
status: 'CONFIRMED',
organizer: { name: 'Admin', email: 'Race@BolderBOULDER.com' },
attendees: [
{ name: 'Adam Gibbons', email: 'adam@example.com', rsvp: true, partstat: 'NEED-ACTIONS', role: 'REQ-PARTICIPANT' },
{ name: 'Brittany Seaton', email: 'brittany@example2.org', rsvp: true, partstat: 'NEED-ACTIONS', role: 'OPT-PARTICIPANT' }
],
method: "REQUEST",
recurrence: "FREQ=WEEKLY;INTERVAL=2", //weekly
}
Run Code Online (Sandbox Code Playgroud)
您可以在此处添加其他几个键值对,请查看ics以获取完整列表。
这里有一些你应该注意的事情
content:[
{
type: 'text/calendar; method=REQUEST',
value
}
]
Run Code Online (Sandbox Code Playgroud)
如果您不熟悉它并且甚至需要循环,您可以将其用于循环规则生成器。
确保每位服务员;指定了rsvp、role和partstat 。
由于此处已指定组织者的电子邮件,因此您不应将邀请邮件发送给组织者,因为它不会很好地呈现,也不会自动添加到他们的日历中,此答案中详细解释了该问题。
因此,如果您也打算将电子邮件发送给组织者,以便它可以自动添加到他的日历中,您应该考虑让他成为与会者,并将您公司的详细信息作为组织者,例如
{
...
organizer: { name: 'Company Name', email: 'mail@company.com' },
attendees: [
{ name: 'Admin', email: 'Race@BolderBOULDER.com', rsvp: true, partstat: 'ACCEPTED', role: 'REQ-PARTICIPANT' },
{ name: 'Adam Gibbons', email: 'adam@example.com', rsvp: true, partstat: 'NEED-ACTIONS', role: 'REQ-PARTICIPANT' },
{ name: 'Brittany Seaton', email: 'brittany@example2.org', rsvp: true, partstat: 'NEED-ACTIONS', role: 'OPT-PARTICIPANT' }
]
...
}
Run Code Online (Sandbox Code Playgroud)
因此,真正的组织者已被添加为访客,并自动将其指定partstat为accepted。这样,您可以将电子邮件发送给组织者和来宾,以便它可以自动添加到他们的日历中。
继续前进,createEvent从此,
const {value} = ics.createEvent(event)
Run Code Online (Sandbox Code Playgroud)
然后,最后将电子邮件发送出去
await sgMail.sendMultiple({
to: attendees,
subject,
from: { name, email},
content:[
{
type: 'text/calendar; method=REQUEST',
value // from ics createEvent
}
],
attachments: [
{
content: Buffer.from(value).toString("base64"),
type: "application/ics",
namw: "invite.ics",
filename: "invite.ics",
disposition: "attachment",
},
],
})
Run Code Online (Sandbox Code Playgroud)
在这里,我使用sendMultiple一次向所有与会者触发事件,除了ics内容之外,还添加一个文件附件作为某些电子邮件客户端的后备(因此,如果需要,用户可以单击、打开并添加到日历中) 。
再次提醒您,您不应将真正的组织者添加到电子邮件的收件人中;因此,如果真正的组织者在与会者列表中,那么您应该将slice其删除,或者像我一样 - 将他添加为客人并始终使用公司的详细信息作为标准主持人,然后您可以向所有人发送电子邮件。
如果一切都正确完成,每个人都会收到这封电子邮件,其中包含回复和所有精美的渲染,具体取决于他们各自的电子邮件客户端,gmail做得非常出色,然后它也会自动添加到他们的日历中。
| 归档时间: |
|
| 查看次数: |
10221 次 |
| 最近记录: |