diff --git a/package-lock.json b/package-lock.json
index dd1e39c5..5a2d33d0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26,6 +26,7 @@
"open-graph-scraper": "^4.7.1",
"prismjs": "^1.23.0",
"puppeteer": "^8.0.0",
+ "rss-parser": "^3.12.0",
"simple-git": "^2.36.0",
"svgo": "^2.2.0",
"twemoji-parser": "^13.0.0",
@@ -9475,6 +9476,15 @@
"rimraf": "bin.js"
}
},
+ "node_modules/rss-parser": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/rss-parser/-/rss-parser-3.12.0.tgz",
+ "integrity": "sha512-aqD3E8iavcCdkhVxNDIdg1nkBI17jgqF+9OqPS1orwNaOgySdpvq6B+DoONLhzjzwV8mWg37sb60e4bmLK117A==",
+ "dependencies": {
+ "entities": "^2.0.3",
+ "xml2js": "^0.4.19"
+ }
+ },
"node_modules/rsvp": {
"version": "4.8.5",
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
@@ -19408,6 +19418,15 @@
"glob": "^7.1.3"
}
},
+ "rss-parser": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/rss-parser/-/rss-parser-3.12.0.tgz",
+ "integrity": "sha512-aqD3E8iavcCdkhVxNDIdg1nkBI17jgqF+9OqPS1orwNaOgySdpvq6B+DoONLhzjzwV8mWg37sb60e4bmLK117A==",
+ "requires": {
+ "entities": "^2.0.3",
+ "xml2js": "^0.4.19"
+ }
+ },
"rsvp": {
"version": "4.8.5",
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
diff --git a/package.json b/package.json
index d6f2d1dd..1ab8d0c0 100644
--- a/package.json
+++ b/package.json
@@ -40,6 +40,7 @@
"open-graph-scraper": "^4.7.1",
"prismjs": "^1.23.0",
"puppeteer": "^8.0.0",
+ "rss-parser": "^3.12.0",
"simple-git": "^2.36.0",
"svgo": "^2.2.0",
"twemoji-parser": "^13.0.0",
diff --git a/source/app/metrics/utils.mjs b/source/app/metrics/utils.mjs
index d071c046..ea639a79 100644
--- a/source/app/metrics/utils.mjs
+++ b/source/app/metrics/utils.mjs
@@ -11,9 +11,10 @@
import twemojis from "twemoji-parser"
import jimp from "jimp"
import opengraph from "open-graph-scraper"
+ import rss from "rss-parser"
//Exports
- export {fs, os, paths, url, util, processes, axios, puppeteer, git, opengraph}
+ export {fs, os, paths, url, util, processes, axios, puppeteer, git, opengraph, rss}
/**Returns module __dirname */
export function __module(module) {
diff --git a/source/app/mocks/api/axios/get/rss.mjs b/source/app/mocks/api/axios/get/rss.mjs
new file mode 100644
index 00000000..8f0083f6
--- /dev/null
+++ b/source/app/mocks/api/axios/get/rss.mjs
@@ -0,0 +1,22 @@
+/**Mocked data */
+ export default function({faker, url, options, login = faker.internet.userName()}) {
+ //Stackoverflow api
+ if (/^https:..example.org.rss$/.test(url)) {
+ console.debug(`metrics/compute/mocks > mocking rss feed result > ${url}`)
+ return ({
+ status:200,
+ data:{
+ items:new Array(30).fill(null).map(_ => ({
+ title:faker.lorem.sentence(),
+ link:faker.internet.url(),
+ content:faker.lorem.paragraphs(),
+ contentSnippet:faker.lorem.paragraph(),
+ isoDate:faker.date.recent(),
+ })),
+ title:faker.lorem.sentence(),
+ description:faker.lorem.paragraph(),
+ link:url,
+ },
+ })
+ }
+ }
\ No newline at end of file
diff --git a/source/app/web/statics/app.placeholder.js b/source/app/web/statics/app.placeholder.js
index 1e00621a..9e3eae4c 100644
--- a/source/app/web/statics/app.placeholder.js
+++ b/source/app/web/statics/app.placeholder.js
@@ -230,6 +230,18 @@
favorites:distribution(7).map((value, index, array) => ({name:faker.lorem.word(), color:faker.internet.color(), value, size:faker.random.number(1000000), x:array.slice(0, index).reduce((a, b) => a + b, 0)}))
}
}) : null),
+ //Languages
+ ...(set.plugins.enabled.rss ? ({
+ rss:{
+ source:faker.lorem.words(),
+ description:faker.lorem.paragraph(),
+ link:options["rss.source"],
+ feed:new Array(Number(options["rss.limit"])).fill(null).map(_ => ({
+ title:faker.lorem.sentence(),
+ date:faker.date.recent()
+ })),
+ }
+ }) : null),
//Habits
...(set.plugins.enabled.habits ? ({
habits:{
diff --git a/source/plugins/rss/README.md b/source/plugins/rss/README.md
new file mode 100644
index 00000000..d451f452
--- /dev/null
+++ b/source/plugins/rss/README.md
@@ -0,0 +1,23 @@
+### 🗼 Rss feed
+
+The *rss* plugin displays items from a specified RSS feed.
+
+
+
+
+
+ |
+
+
+#### ℹ️ Examples workflows
+
+[➡️ Available options for this plugin](metadata.yml)
+
+```yaml
+- uses: lowlighter/metrics@latest
+ with:
+ # ... other options
+ plugin_rss: yes
+ plugin_rss_source: https://news.ycombinator.com/rss # RSS feed
+ plugin_rss_limit: 6 # Limit to 6 items
+```
\ No newline at end of file
diff --git a/source/plugins/rss/index.mjs b/source/plugins/rss/index.mjs
new file mode 100644
index 00000000..860cd320
--- /dev/null
+++ b/source/plugins/rss/index.mjs
@@ -0,0 +1,34 @@
+
+//Setup
+ export default async function({login, q, imports, data, account}, {enabled = false} = {}) {
+ //Plugin execution
+ try {
+ //Check if plugin is enabled and requirements are met
+ if ((!enabled)||(!q.rss))
+ return null
+
+ //Load inputs
+ let {source, limit} = imports.metadata.plugins.rss.inputs({data, account, q})
+ if (!source)
+ throw {error:{message:"A RSS feed is required"}}
+
+ //Load rss feed
+ const {title, description, link, items} = await (new imports.rss()).parseURL(source) //eslint-disable-line new-cap
+ const feed = items.map(({title, isoDate:date}) => ({title, date:new Date(date)}))
+
+ //Limit feed
+ if (limit > 0) {
+ console.debug(`metrics/compute/${login}/plugins > rss > keeping only ${limit} items`)
+ feed.splice(limit)
+ }
+
+ //Results
+ return {source:title, description, link, feed}
+ }
+ //Handle errors
+ catch (error) {
+ if (error.error?.message)
+ throw error
+ throw {error:{message:"An error occured", instance:error}}
+ }
+ }
\ No newline at end of file
diff --git a/source/plugins/rss/metadata.yml b/source/plugins/rss/metadata.yml
new file mode 100644
index 00000000..2e058cd6
--- /dev/null
+++ b/source/plugins/rss/metadata.yml
@@ -0,0 +1,30 @@
+name: "🗼 Rss feed"
+cost: N/A
+categorie: social
+index: 6.5
+supports:
+ - user
+ - organization
+ - repository
+inputs:
+
+ # Enable or disable plugin
+ plugin_rss:
+ description: Display RSS feed
+ type: boolean
+ default: no
+
+ # RSS feed source
+ plugin_rss_source:
+ description: RSS feed source
+ type: string
+ default: ""
+
+ # Number of items to display
+ # Set to 0 to disable limitations
+ plugin_rss_limit:
+ description: Maximum number of items to display
+ type: number
+ default: 4
+ min: 0
+ max: 30
\ No newline at end of file
diff --git a/source/plugins/rss/tests.yml b/source/plugins/rss/tests.yml
new file mode 100644
index 00000000..51f70b03
--- /dev/null
+++ b/source/plugins/rss/tests.yml
@@ -0,0 +1,6 @@
+- name: Rss plugin (default)
+ uses: lowlighter/metrics@latest
+ with:
+ token: NOT_NEEDED
+ plugin_rss: yes
+ plugin_source: https://example.org/rss
\ No newline at end of file
diff --git a/source/templates/classic/partials/_.json b/source/templates/classic/partials/_.json
index 50ea4af3..18c62c73 100644
--- a/source/templates/classic/partials/_.json
+++ b/source/templates/classic/partials/_.json
@@ -13,6 +13,7 @@
"music",
"nightscout",
"posts",
+ "rss",
"tweets",
"isocalendar",
"stars",
diff --git a/source/templates/classic/partials/rss.ejs b/source/templates/classic/partials/rss.ejs
new file mode 100644
index 00000000..c1d5f6b3
--- /dev/null
+++ b/source/templates/classic/partials/rss.ejs
@@ -0,0 +1,35 @@
+<% if (plugins.rss) { %>
+
+
+
+ <%= plugins.rss?.source ?? "" %> RSS feed
+
+
+
+ <% if (plugins.rss.error) { %>
+
+
+ <%= plugins.rss.error.message %>
+
+ <% } else { %>
+ <% if (plugins.rss.feed.length) { %>
+ <% for (const {title, date} of plugins.rss.feed) { %>
+
+ <% } %>
+ <% } else { %>
+
+ <% } %>
+ <% } %>
+
+
+
+<% } %>
\ No newline at end of file
diff --git a/source/templates/classic/style.css b/source/templates/classic/style.css
index c4b870ab..e3ebbc1d 100644
--- a/source/templates/classic/style.css
+++ b/source/templates/classic/style.css
@@ -904,6 +904,18 @@
stroke-width: 6;
}
+/* RSS feed */
+ .rss {
+ align-items: flex-start;
+ }
+ .rss .infos {
+ margin-bottom: 3px;
+ }
+ .rss .infos .date {
+ font-size: 10px;
+ color: #666666;
+ }
+
/* Fade animation */
.af {
opacity: 0;
diff --git a/source/templates/repository/partials/_.json b/source/templates/repository/partials/_.json
index 89b92521..617b17d4 100644
--- a/source/templates/repository/partials/_.json
+++ b/source/templates/repository/partials/_.json
@@ -9,5 +9,6 @@
"people",
"activity",
"contributors",
- "licenses"
+ "licenses",
+ "rss"
]
\ No newline at end of file
diff --git a/source/templates/repository/partials/rss.ejs b/source/templates/repository/partials/rss.ejs
new file mode 100644
index 00000000..c1d5f6b3
--- /dev/null
+++ b/source/templates/repository/partials/rss.ejs
@@ -0,0 +1,35 @@
+<% if (plugins.rss) { %>
+
+
+
+ <%= plugins.rss?.source ?? "" %> RSS feed
+
+
+
+ <% if (plugins.rss.error) { %>
+
+
+ <%= plugins.rss.error.message %>
+
+ <% } else { %>
+ <% if (plugins.rss.feed.length) { %>
+ <% for (const {title, date} of plugins.rss.feed) { %>
+
+ <% } %>
+ <% } else { %>
+
+ <% } %>
+ <% } %>
+
+
+
+<% } %>
\ No newline at end of file