├── Code.gs
└── README.md
/Code.gs:
--------------------------------------------------------------------------------
1 | // source: https://github.com/qoomon/gmail-rss-feed
2 | // Version: 1.0.1
3 |
4 | const {doGet} = (() => {
5 | // --------------- config ---------------------
6 | const rssFeedGmailRootLabel = 'RSS Feed'
7 | const rssFeedMaxItems = 50
8 |
9 | // --------------- entrypoint ---------------------
10 | function doGet(event) {
11 | Logger.log("[EVENT]")
12 | Logger.log('Parameters:', event.parameter)
13 | const response = doGetRssFeedXml(event)
14 | Logger.log('[RESPONSE]')
15 | Logger.log('MimeType:', response.getMimeType())
16 | Logger.log('Content...')
17 | Logger.log(response.getContent())
18 | return response
19 | }
20 |
21 | function doGetRssFeedXml(event) {
22 | // --------------- input parameters---------------------
23 |
24 | const rssFeedName = event.parameter['gmail-rss-feed']
25 | if (!rssFeedName) {
26 | return ContentService.createTextOutput("400 - 'feed' parameter missing")
27 | }
28 |
29 | const rssFeedTitle = event.parameter['title']
30 |
31 | const isMultiAuthor = isAnyOf(event.parameter['multi-author'], ['', 'true'])
32 |
33 | // --------------- gather rss mails---------------------
34 |
35 | const gmailFeedLabel = GmailApp.getUserLabelByName(`${rssFeedGmailRootLabel}/${rssFeedName}`)
36 | if (!gmailFeedLabel) {
37 | return ContentService.createTextOutput(`400 - '${rssFeedName}' feed not found`)
38 | }
39 |
40 | const gmailFeedMessages = gmailFeedLabel.getThreads(0, rssFeedMaxItems)
41 | .map(thread => thread.getMessages()[0])
42 |
43 | // --------------- create rss feed ---------------------
44 |
45 | const rssFeed = {
46 | title: rssFeedTitle || `${rssFeedName} - Gmail RSS Feed`,
47 | description: `${rssFeedName} - Gmail RSS Feed`,
48 | link: `https://mail.google.com/#label/${gmailFeedLabel.getName()}`,
49 | image: 'https://ssl.gstatic.com/ui/v1/icons/mail/favicon.ico',
50 | atomLink: ScriptApp.getService().getUrl(),
51 | items: gmailFeedMessages.map(gmailMessage => ({
52 | guid: gmailMessage.getId(),
53 | author: gmailMessage.getFrom(),
54 | title: (isMultiAuthor ? `${getSenderDisplayName(gmailMessage.getFrom())} - `: "")
55 | + gmailMessage.getSubject(),
56 | link: `https://mail.google.com/mail/u/0/#label/${gmailFeedLabel.getName()}/${gmailMessage.getId()}`,
57 | description: gmailMessage.getBody(),
58 | pubDate: gmailMessage.getDate()
59 | }))
60 | }
61 |
62 | const rssFeedXmlElement = createRssFeedXmlElement(rssFeed)
63 | const rssFeedXmlDocument = XmlService.createDocument(rssFeedXmlElement)
64 | const rssFeedXmlText = XmlService.getPrettyFormat().format(rssFeedXmlDocument)
65 | return ContentService.createTextOutput(rssFeedXmlText)
66 | .setMimeType(ContentService.MimeType.RSS)
67 | }
68 |
69 | function createRssFeedXmlElement(rssObject) {
70 | const rss = XmlService.parse("").detachRootElement()
71 | return rss.addContent(xmlElement('channel', channel => {
72 | channel.addContent(xmlElement('title').setText(rssObject.title))
73 | channel.addContent(xmlElement('description').setText(rssObject.description))
74 | channel.addContent(xmlElement('link').setText(rssObject.link))
75 | if (rssObject.image) {
76 | channel.addContent(xmlElement('image')
77 | .addContent(xmlElement('url').setText(rssObject.image))
78 | .addContent(xmlElement('title').setText(rssObject.title))
79 | .addContent(xmlElement('link').setText(rssObject.link)))
80 | }
81 | if (rssObject.atomLink) {
82 | channel.addContent(XmlService.createElement('link')
83 | .setNamespace(rss.getNamespace('atom'))
84 | .setAttribute('href', rssObject.atomLink)
85 | .setAttribute('rel', 'self')
86 | .setAttribute('type', 'application/rss+xml'))
87 | }
88 | rssObject.items.forEach(itemObject => {
89 | channel.addContent(createRssFeedItemXmlElement(itemObject))
90 | })
91 | }))
92 | }
93 |
94 | function createRssFeedItemXmlElement(itemObject) {
95 | return xmlElement('item', item => {
96 | item.addContent(xmlElement('title').setText(itemObject.title))
97 | item.addContent(xmlElement('link').setText(itemObject.link))
98 | item.addContent(xmlElement('description')
99 | .addContent(XmlService.createCdata(itemObject.description)))
100 | if (itemObject.pubDate) {
101 | item.addContent(xmlElement('pubDate')
102 | .setText(Utilities.formatDate(itemObject.pubDate, 'UTC', "EEE, dd MMM yyyy HH:mm:ss Z")))
103 | }
104 | if (itemObject.guid) {
105 | item.addContent(xmlElement('guid').setText(itemObject.guid))
106 | }
107 | if (itemObject.author) {
108 | item.addContent(xmlElement('author').setText(itemObject.author))
109 | }
110 | })
111 | }
112 |
113 | function xmlElement(name, modifier) {
114 | const element = XmlService.createElement(name)
115 | if (modifier) {
116 | modifier(element)
117 | }
118 | return element
119 | }
120 |
121 | function isAnyOf(value, ...validValues) {
122 | validValues.includes(value)
123 | }
124 |
125 | function getSenderDisplayName(sender) {
126 | return sender.trim().match(/^(?[^<]+)<.*>/)?.groups?.name?.trim()
127 | ?? sender.trim().match(/^<(?[^>]*)>/)?.groups?.name?.trim()
128 | ?? sender.trim()
129 | }
130 |
131 | return {doGet}
132 | })();
133 |
134 | // --------------- test ---------------------
135 |
136 | function doGet_sample_event() {
137 | doGet({
138 | parameter: {
139 | 'gmail-rss-feed': 'Newsletter',
140 | 'multi-account': 'true'
141 | }
142 | })
143 | }
144 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Gmail RSS Feed
2 | A [Google Scripts](https://script.google.com) Web App to dynamicaly generate an RSS feed based on labeled emails (`RSS Feed/`)
3 |
4 | ### Installation
5 | * Create a blank project on https://script.google.com/
6 | * Copy content of [`Code.gs`](Code.gs) into your new project
7 | * Run the script once by clicking Menu `Run > doGet`
8 | * You will be asked to grant this script access to your Gmail account
9 | * Accept the request
10 | * Create a first version by clicking Menu `File > Manage Versions`
11 | * Deploy this script by clicking Menu `Publish > Deploy as web app`
12 | * Select `Execute the app as : me`
13 | * Select `Who has access to the app : Anonyone, even anonymous`
14 | * Copy `Current web app URL` e.g. `https://script.google.com/macros/s//exec`
15 | * ⚠️ Everyone with this URL is potentialy able to read all mails labled with `RSS Feed/`
16 | * Label your first emails with a nested `RSS` label (`RSS Feed/`) e.g. `RSS Feed/Newsletter`
17 | * Create your feed URL by add following query parameters to `Current web app URL`
18 | * `gmail-rss-feed=` selects rss feed name of coresponding Gmail label (`RSS Feed/`)
19 | * `multi-author` enables author name to be prepand to feed item titles
20 | * Example URL `https://script.google.com/macros/s//exec?gmail-rss-feed=Newsletter&multi-author`
21 |
--------------------------------------------------------------------------------