Some updates to try and get author info from webmention notes
This commit is contained in:
parent
e63127fc13
commit
de2cfc6fef
8 changed files with 144 additions and 71 deletions
109
.eleventy.js
109
.eleventy.js
|
@ -1,4 +1,6 @@
|
||||||
const pluginRss = require("@11ty/eleventy-plugin-rss");
|
const pluginRss = require("@11ty/eleventy-plugin-rss");
|
||||||
|
const EleventyFetch = require("@11ty/eleventy-fetch");
|
||||||
|
const { mf2 } = require("microformats-parser");
|
||||||
|
|
||||||
module.exports = function(eleventyConfig) {
|
module.exports = function(eleventyConfig) {
|
||||||
|
|
||||||
|
@ -34,8 +36,92 @@ module.exports = function(eleventyConfig) {
|
||||||
return collectionApi.getFilteredByGlob("_content/notes/*.md").reverse();
|
return collectionApi.getFilteredByGlob("_content/notes/*.md").reverse();
|
||||||
});
|
});
|
||||||
|
|
||||||
eleventyConfig.addCollection("feed", function(collectionApi) {
|
async function getAuthor(author) {
|
||||||
return collectionApi.getAllSorted().reverse().filter(item => {
|
if(!author) author = {
|
||||||
|
"display_name": "death.au",
|
||||||
|
"url": "https://death.id.au", // this is the domain your webfinger is on
|
||||||
|
"avatar": "/assets/img/avatar-tt.svg" // avatar to use by default
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!author.url) author.url = author.uri
|
||||||
|
let authorURL = new URL(author.url)
|
||||||
|
|
||||||
|
if(!author.display_name) author.display_name = author.name || author.preferredUsername || author.username
|
||||||
|
if(!author.handle) author.handle = `@${author.display_name}@${authorURL.hostname}`
|
||||||
|
if(!author.avatar) author.avatar = author.photo || author.image || author.img
|
||||||
|
if(!author.canonical_uri) author.canonical_uri = author.url
|
||||||
|
|
||||||
|
try {
|
||||||
|
// options for the cached fetch request
|
||||||
|
const options = {
|
||||||
|
duration: "1d", // 1 day
|
||||||
|
type: "json", // also supports "text" or "buffer"
|
||||||
|
fetchOptions: {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/activity+json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the webfinger
|
||||||
|
handle = author.handle.startsWith('@') ? author.handle.substring(1) : author.handle;
|
||||||
|
let webfinger = await EleventyFetch(`${authorURL.origin}/.well-known/webfinger?resource=acct:${handle}`, options);
|
||||||
|
|
||||||
|
// get the canonical uri as well
|
||||||
|
let canonical_uri = webfinger?.links?.find(link => link.rel == "canonical_uri")
|
||||||
|
if(canonical_uri) author.canonical_uri = canonical_uri.href
|
||||||
|
|
||||||
|
// get the rel="self" link from the webfinger
|
||||||
|
let self = webfinger?.links?.find(link => link.rel == "self");
|
||||||
|
|
||||||
|
if(self) {
|
||||||
|
// fetch the profile information
|
||||||
|
if(self.type) options.fetchOptions.headers['Content-Type'] = self.type;
|
||||||
|
try{
|
||||||
|
const json = await EleventyFetch(self.href, options);
|
||||||
|
const isJson = typeof json === 'object' && !!json
|
||||||
|
if(isJson){
|
||||||
|
// apply the supplied author information to the result
|
||||||
|
json.handle = author.handle
|
||||||
|
json.avatar = author.avatar
|
||||||
|
json.display_name = author.display_name || json.preferredUsername
|
||||||
|
json.canonical_uri = author.canonical_uri || json.url[0]
|
||||||
|
|
||||||
|
// this is now the author object
|
||||||
|
author = json
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// try parsing microformats
|
||||||
|
const parsed = mf2(json, { baseUrl: new URL(self.href).origin })
|
||||||
|
if(parsed?.items?.length && parsed.items[0].properties?.author?.length){
|
||||||
|
// if we have an author object from here
|
||||||
|
const parsedAuthor = Object.fromEntries(
|
||||||
|
Object.entries(arsed.items[0].properties?.author[0])
|
||||||
|
// filter out empty entries
|
||||||
|
.filter(([key, value]) => (!Array.isArray(value) && !!value) || value.every(v => v))
|
||||||
|
// if entry is an array of length 1, collapse it to just key:value
|
||||||
|
.map(([key, value], index) => Array.isArray(value) && value.length == 1 ? [key, value[0]] : [key, value])
|
||||||
|
)
|
||||||
|
author = { ...element.author, ...parsedAuthor }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
if(!author.canonical_uri) author.canonical_uri = self.href
|
||||||
|
}
|
||||||
|
else throw 'Could not find rel="self" link in webfinger'
|
||||||
|
|
||||||
|
return author
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
console.error("Failed to get webfinger info ", e);
|
||||||
|
return author
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eleventyConfig.addCollection("feed", async function(collectionApi) {
|
||||||
|
let feed = collectionApi.getAllSorted().reverse().filter(item => {
|
||||||
let showInFeed = false;
|
let showInFeed = false;
|
||||||
if(!item.data.published) return false
|
if(!item.data.published) return false
|
||||||
if(item.filePathStem.startsWith('/notes/')){
|
if(item.filePathStem.startsWith('/notes/')){
|
||||||
|
@ -68,12 +154,21 @@ module.exports = function(eleventyConfig) {
|
||||||
showInFeed = true;
|
showInFeed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if(showInFeed) {
|
|
||||||
// item.data.author =
|
|
||||||
// }
|
|
||||||
|
|
||||||
return showInFeed;
|
return showInFeed;
|
||||||
});
|
})
|
||||||
|
|
||||||
|
return await Promise.all(feed.map(async item => {
|
||||||
|
item.data.author = await getAuthor(item.data.author)
|
||||||
|
return item;
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
|
||||||
|
eleventyConfig.addNunjucksAsyncFilter("getAuthorData", (author, callback) => {
|
||||||
|
return getAuthor(author).then(author => callback(null, author)).catch(err => callback(err))
|
||||||
|
})
|
||||||
|
|
||||||
|
eleventyConfig.addNunjucksAsyncFilter("await", function(promise) {
|
||||||
|
return promise.then(res => callback(null, res)).catch(err => callback(err))
|
||||||
});
|
});
|
||||||
|
|
||||||
eleventyConfig.addNunjucksShortcode("getVar", function(varString) {
|
eleventyConfig.addNunjucksShortcode("getVar", function(varString) {
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
const EleventyFetch = require("@11ty/eleventy-fetch");
|
|
||||||
|
|
||||||
module.exports = async function() {
|
|
||||||
// set these default options
|
|
||||||
let author = {
|
|
||||||
"handle": "@death.au@death.id.au",
|
|
||||||
"display_name": "death.au",
|
|
||||||
"url": "https://death.id.au", // this is the domain your webfinger is on
|
|
||||||
"avatar": "/assets/img/avatar-tt.svg" // avatar to use by default
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// options for the cached fetch request
|
|
||||||
const options = {
|
|
||||||
duration: "1d", // 1 day
|
|
||||||
type: "json", // also supports "text" or "buffer"
|
|
||||||
fetchOptions: {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/activity+json'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the webfinger
|
|
||||||
handle = author.handle.startsWith('@') ? author.handle.substring(1) : author.handle;
|
|
||||||
let webfinger = await EleventyFetch(`${author.url}/.well-known/webfinger?resource=acct:${handle}`, options);
|
|
||||||
|
|
||||||
// get the canonical uri as well
|
|
||||||
let canonical_uri = webfinger?.links?.find(link => link.rel == "canonical_uri")
|
|
||||||
if(canonical_uri) author.canonical_uri = canonical_uri.href
|
|
||||||
|
|
||||||
// get the rel="self" link from the webfinger
|
|
||||||
let self = webfinger?.links?.find(link => link.rel == "self");
|
|
||||||
|
|
||||||
if(self) {
|
|
||||||
// fetch the profile information
|
|
||||||
if(self.type) options.fetchOptions.headers['Content-Type'] = self.type;
|
|
||||||
json = await EleventyFetch(self.href, options);
|
|
||||||
|
|
||||||
// apply the supplied author information to the result
|
|
||||||
json.handle = author.handle
|
|
||||||
json.avatar = author.avatar
|
|
||||||
json.display_name = author.display_name || json.preferredUsername
|
|
||||||
json.canonical_uri = author.canonical_uri || json.url[0]
|
|
||||||
|
|
||||||
// this is now the author object
|
|
||||||
author = json
|
|
||||||
}
|
|
||||||
else throw 'Could not find rel="self" link in webfinger'
|
|
||||||
|
|
||||||
return author
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
console.error("Failed to get webfinger info ", e);
|
|
||||||
return author
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -6,7 +6,7 @@
|
||||||
---
|
---
|
||||||
{% from "macro-entry.njk" import entryMacro %}
|
{% from "macro-entry.njk" import entryMacro %}
|
||||||
|
|
||||||
{{ entryMacro(ctx(), url, content) }}
|
{{ entryMacro(ctx(), author, url, content) }}
|
||||||
|
|
||||||
{% layoutblock 'foot' %}
|
{% layoutblock 'foot' %}
|
||||||
<script src="/assets/js/relative-time.js"></script>
|
<script src="/assets/js/relative-time.js"></script>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{% macro authorMacro(author) %}
|
{% macro authorMacro(author) %}
|
||||||
|
{# {{ author | getAuthorData | log }} #}
|
||||||
<a class="p-author h-card u-url" href="{{ author.canonical_uri if author.canonical_uri else author.url }}">
|
<a class="p-author h-card u-url" href="{{ author.canonical_uri if author.canonical_uri else author.url }}">
|
||||||
<img class="u-photo small-avatar" alt="{{ author.icon.name if author.icon.name else "Avatar for " + author.display_name }}" src="{{ author.icon.url if author.icon.url else author.avatar }}"/>
|
<img class="u-photo small-avatar" alt="{{ author.icon.name if author.icon.name else "Avatar for " + author.display_name }}" src="{{ author.icon.url if author.icon.url else author.avatar }}"/>
|
||||||
<span class="display-name">
|
<span class="display-name">
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
{% from "macro-card-head.njk" import cardHeadMacro %}
|
{% from "macro-card-head.njk" import cardHeadMacro %}
|
||||||
{% from "macro-summary.njk" import summaryMacro %}
|
{% from "macro-summary.njk" import summaryMacro %}
|
||||||
{% macro entryMacro(item, url, content, summaryOnly=false) %}
|
{% macro entryMacro(item, author, url, content, summaryOnly=false) %}
|
||||||
|
|
||||||
<div class="h-entry type-{{ item.postType }}">
|
<div class="h-entry type-{{ item.postType }}">
|
||||||
{{ cardHeadMacro(item.author, item.date, url) }}
|
{{ cardHeadMacro(author, item.date, url) }}
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
{{ summaryMacro(item, url) }}
|
{{ summaryMacro(item, url) }}
|
||||||
{% if item.postType == 'article' and summaryOnly %}
|
{% if item.postType == 'article' and summaryOnly %}
|
||||||
|
|
|
@ -11,7 +11,7 @@ eleventyExcludeFromCollections: true
|
||||||
<ul>
|
<ul>
|
||||||
{% for item in pagination.items %}
|
{% for item in pagination.items %}
|
||||||
<li>
|
<li>
|
||||||
{{ entryMacro(item.data, item.url, item.templateContent, true) }}
|
{{ entryMacro(item.data, item.data.author, item.url, item.templateContent, true) }}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
36
package-lock.json
generated
36
package-lock.json
generated
|
@ -11,7 +11,8 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@11ty/eleventy": "^1.0.2",
|
"@11ty/eleventy": "^1.0.2",
|
||||||
"@11ty/eleventy-fetch": "^3.0.0",
|
"@11ty/eleventy-fetch": "^3.0.0",
|
||||||
"@11ty/eleventy-plugin-rss": "^1.2.0"
|
"@11ty/eleventy-plugin-rss": "^1.2.0",
|
||||||
|
"microformats-parser": "^1.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@11ty/dependency-tree": {
|
"node_modules/@11ty/dependency-tree": {
|
||||||
|
@ -2223,6 +2224,18 @@
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/microformats-parser": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/microformats-parser/-/microformats-parser-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-BSg9Y/Aik8hvvme/fkxnXMRvTKuVwOeTapeZdaPQ+92DEubyM31iMtwbgFZ1383om643UvfYY5G23E9s1FY2KQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"parse5": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/micromatch": {
|
"node_modules/micromatch": {
|
||||||
"version": "4.0.5",
|
"version": "4.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
||||||
|
@ -2529,6 +2542,12 @@
|
||||||
"integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==",
|
"integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/parse5": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/parseurl": {
|
"node_modules/parseurl": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
|
@ -5628,6 +5647,15 @@
|
||||||
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"microformats-parser": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/microformats-parser/-/microformats-parser-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-BSg9Y/Aik8hvvme/fkxnXMRvTKuVwOeTapeZdaPQ+92DEubyM31iMtwbgFZ1383om643UvfYY5G23E9s1FY2KQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"parse5": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"micromatch": {
|
"micromatch": {
|
||||||
"version": "4.0.5",
|
"version": "4.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
||||||
|
@ -5845,6 +5873,12 @@
|
||||||
"integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==",
|
"integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"parse5": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"parseurl": {
|
"parseurl": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@11ty/eleventy": "^1.0.2",
|
"@11ty/eleventy": "^1.0.2",
|
||||||
"@11ty/eleventy-fetch": "^3.0.0",
|
"@11ty/eleventy-fetch": "^3.0.0",
|
||||||
"@11ty/eleventy-plugin-rss": "^1.2.0"
|
"@11ty/eleventy-plugin-rss": "^1.2.0",
|
||||||
|
"microformats-parser": "^1.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue