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
107
.eleventy.js
107
.eleventy.js
|
@ -1,4 +1,6 @@
|
|||
const pluginRss = require("@11ty/eleventy-plugin-rss");
|
||||
const EleventyFetch = require("@11ty/eleventy-fetch");
|
||||
const { mf2 } = require("microformats-parser");
|
||||
|
||||
module.exports = function(eleventyConfig) {
|
||||
|
||||
|
@ -34,8 +36,92 @@ module.exports = function(eleventyConfig) {
|
|||
return collectionApi.getFilteredByGlob("_content/notes/*.md").reverse();
|
||||
});
|
||||
|
||||
eleventyConfig.addCollection("feed", function(collectionApi) {
|
||||
return collectionApi.getAllSorted().reverse().filter(item => {
|
||||
async function getAuthor(author) {
|
||||
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;
|
||||
if(!item.data.published) return false
|
||||
if(item.filePathStem.startsWith('/notes/')){
|
||||
|
@ -68,12 +154,21 @@ module.exports = function(eleventyConfig) {
|
|||
showInFeed = true;
|
||||
}
|
||||
|
||||
// if(showInFeed) {
|
||||
// item.data.author =
|
||||
// }
|
||||
|
||||
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) {
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
{{ entryMacro(ctx(), url, content) }}
|
||||
{{ entryMacro(ctx(), author, url, content) }}
|
||||
|
||||
{% layoutblock 'foot' %}
|
||||
<script src="/assets/js/relative-time.js"></script>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{% 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 }}">
|
||||
<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">
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{% from "macro-card-head.njk" import cardHeadMacro %}
|
||||
{% 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 }}">
|
||||
{{ cardHeadMacro(item.author, item.date, url) }}
|
||||
{{ cardHeadMacro(author, item.date, url) }}
|
||||
<div class="card-body">
|
||||
{{ summaryMacro(item, url) }}
|
||||
{% if item.postType == 'article' and summaryOnly %}
|
||||
|
|
|
@ -11,7 +11,7 @@ eleventyExcludeFromCollections: true
|
|||
<ul>
|
||||
{% for item in pagination.items %}
|
||||
<li>
|
||||
{{ entryMacro(item.data, item.url, item.templateContent, true) }}
|
||||
{{ entryMacro(item.data, item.data.author, item.url, item.templateContent, true) }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
36
package-lock.json
generated
36
package-lock.json
generated
|
@ -11,7 +11,8 @@
|
|||
"devDependencies": {
|
||||
"@11ty/eleventy": "^1.0.2",
|
||||
"@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": {
|
||||
|
@ -2223,6 +2224,18 @@
|
|||
"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": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
||||
|
@ -2529,6 +2542,12 @@
|
|||
"integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==",
|
||||
"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": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
|
@ -5628,6 +5647,15 @@
|
|||
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||
"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": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
||||
|
@ -5845,6 +5873,12 @@
|
|||
"integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==",
|
||||
"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": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"devDependencies": {
|
||||
"@11ty/eleventy": "^1.0.2",
|
||||
"@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