replace with embeddable script

This commit is contained in:
neri 2022-09-23 17:44:23 +02:00
parent 5acb247fb0
commit a97f9f4980
7 changed files with 72 additions and 207 deletions

1
.gitignore vendored
View File

@ -1 +0,0 @@
node_modules/

View File

@ -1,4 +0,0 @@
FROM node:18.0-slim
COPY index.js package.json yarn.lock ./
RUN yarn
CMD [ "node", "index.js" ]

View File

@ -1,19 +1,20 @@
# WikiJs Metabot
Ein Skript was die Page-Listings auf [https://wiki.ctdo.de](https://wiki.ctdo.de)
automatisch aktuell hält
automatisch befüllt
## Verwendung
Um auf einer Wiki-Seite ein Page-Listing hinzuzufügen muss der Seite das `metapage`-Tag hinzugefügt
werden und an der gewünschten Stelle ein Kommentar folgender Form eingefügt werden:
Um auf einer Wiki-Seite ein Page-Listing hinzuzufügen muss in den Metadaten der Seite der Inhalt von
`metapage.js.html` als Skript gesetzt werden.
Dann kann an der gewünschten Stelle ein HTML-Element folgender Form eingefügt werden:
```text
<!-- \pagelist QUERY -->
```html
<ul class="pagelist" data-query="QUERY"></ul>
```
Dabei muss `QUERY` eine Parameterlist für die `PageQuery::list`-Methode der WikiJs-GraphQL-API sein.
Folgende Parameter werden unterstützt:
Dabei muss `QUERY` eine Parameterliste für die `PageQuery::list`-Methode der WikiJs-GraphQL-API
sein. Folgende Parameter werden unterstützt:
```text
limit: Int
@ -29,57 +30,33 @@ authorId: Int
Liste aller Seiten die mit den Tags `top` und `new` versehen sind:
```text
<!-- \pagelist tags: ["top", "new"] -->
```html
<ul class="pagelist" data-query="tags: ['top', 'new']"></ul>
```
Liste der 10 zuletzt bearbeiteten Seiten:
```text
<!-- \pagelist orderBy: UPDATED, orderByDirection: DESC, limit: 10 -->
```html
<ul
class="pagelist"
data-query="orderBy: UPDATED, orderByDirection: DESC, limit: 10"
></ul>
```
### Details
- Mehrere Parameter müssen mit einem Komma getrennt werden
- Mehrere Tags im `tags`-Parameter müssen ebenfalls mit Komma getrennt werden
- Parameter vom Typ `String` müssen in doppelte Anführungszeichen `"` eingeschlossen werden
- Parameter vom Typ `String` müssen in einfache Anführungszeichen `'` eingeschlossen werden
- Parameter vom Typ `Int` und vom Typ `Enum` müssen ohne Anführungszeichen angegeben werden
## Features und Einschränkungen
Zur Rekursionsvermeidung können leider die mit `metapage` getaggten Seiten nicht selbst in den
Page-Listings auftauchen.
Eine Seite welche die zuletzt aktualisierten Seiten enthält, kann also nur Seiten enthalten welche
nicht mit `metapage` getaggt sind.
Seiten deren Pfad mit einem Unterstrich `_` beginnt werden als versteckt interpretiert und nicht in
Page-Listings angezeigt.
Das wird beispielsweise auf der Topictreff-Seite verwendet damit das Topic-Template nicht unter den
neuen Topics aufgelistet wird.
Bei gesetzten Limits kommt es vor, dass weniger Seiten als angegeben aufgelistet werden.
Das liegt daran, dass das Filtern zur Rekursionsvermeidung und zum verstecken von Seiten erst nach
dem Ausführen der GraphQL-Query passiert.
Außerdem scheint auch das WikiJs einen ähnlichen Bug zu haben wodurch manche Seiten nicht in dem
Ergebnis zurückgegeben aber trotzdem gezählt werden.
## Ausführen des Bots
Damit der Bot Seiten bearbeiten darf muss die Umgebungsvariable `CTDO_WIKIJS_API_KEY` auf einen
gültigen Api-Key gesetzt sein.
Api-Keys für das WikiJs können hier generiert werden: [https://wiki.ctdo.de/a/api](https://wiki.ctdo.de/a/api).
### Docker commands
Docker Container für den Bot bauen:
```sh
docker build -t wikijs-metabot .
```
Docker container ausführen:
```sh
docker run -it -e CTDO_WIKIJS_API_KEY wikijs-metabot
```
Das scheint ein Bug im WikiJs zu sein wodurch manche Seiten nicht in dem Ergebnis zurückgegeben aber
trotzdem gezählt werden.

138
index.js
View File

@ -1,138 +0,0 @@
import WebSocket from 'ws';
process.on('SIGINT', () => process.exit(0));
const apiKey = process.env['CTDO_WIKIJS_API_KEY'];
async function graphql(query) {
const res = await fetch('https://wiki.ctdo.de/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({ query }),
});
return await res.json();
}
function graphqlSubscribe(query, callback) {
const ws = new WebSocket(
'wss://wiki.ctdo.de/graphql-subscriptions',
'graphql-ws'
);
ws.onmessage = (event) => callback(JSON.parse(event.data));
ws.onopen = () => {
ws.send(JSON.stringify({ type: 'connection_init' }));
ws.send(
JSON.stringify({
id: '1',
type: 'start',
payload: { query: `subscription ${query}` },
})
);
};
}
async function update() {
const pageIds = (
await graphql('query { pages { list(tags: ["metapage"]) { id } } }')
).data.pages.list.map((page) => page.id);
console.log('checking metapages', { pageIds });
pageIds.forEach((pageId) => updatePage(pageId));
}
async function updatePage(pageId) {
console.log('checking page', { pageId });
const page = (
await graphql(`query { pages { single(id: ${pageId}) {
id
content
description
editor
isPrivate
isPublished
locale
path
publishEndDate
publishStartDate
scriptCss
scriptJs
tags { tag }
title
} } }`)
).data.pages.single;
page.tags = page.tags.map((tag) => tag.tag);
let contentLines = page.content.split('\n');
const pagelistConfigs = contentLines.filter((line) =>
line.match(/^\s*<!--\s*\\pagelist[^>]*-->\s*$/)
);
for (const pagelistConfig of pagelistConfigs) {
await updatePagelist(pagelistConfig, contentLines);
}
const updatedContent = contentLines.join('\n');
if (page.content !== updatedContent) {
page.content = updatedContent;
console.log('updating page');
console.log({ page });
const mutation = Object.entries(page)
.map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
.join(', ');
const result = await graphql(
`mutation { pages { update(${mutation}) { responseResult { succeeded, message, errorCode } } } }`
);
console.log({ result: JSON.stringify(result) });
}
}
async function updatePagelist(pagelistConfig, contentLines) {
const query = pagelistConfig
.replace(/^\s*<!--\s*\\pagelist\s*/, '')
.replace(/\s*-->\s*$/, '');
const result = (
await graphql(`query { pages { list(${query}) { path, title, tags } }}`)
).data.pages.list
.filter((page) => !page.path.includes('/_'))
.filter((page) => !page.tags.includes('metapage'))
.map((page) => `- [${page.title}](/${page.path})`);
console.log({ pagelistConfig, query, result });
const startIndex = contentLines.findIndex((line) => line == pagelistConfig);
let endIndex = startIndex + 1;
while (
endIndex < contentLines.length &&
contentLines[endIndex].startsWith('- ')
) {
endIndex++;
}
contentLines.splice(
startIndex,
endIndex - startIndex,
`<!-- \\pagelist ${query} -->`,
...result
);
}
async function main() {
await update();
graphqlSubscribe('{ loggingLiveTrail { output } }', async (message) => {
if (
message.type === 'data' &&
message.payload.data.loggingLiveTrail.output.includes(
'Committing updated file'
)
) {
await update();
}
});
}
main();

54
metapage.js.html Normal file
View File

@ -0,0 +1,54 @@
<script>
// https://git.ctdo.de/ctdo/wikijs-metabot/
async function graphql(query) {
const res = await fetch('https://wiki.ctdo.de/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ query }),
});
return await res.json();
}
async function loadPagelists() {
console.log('populating pagelists');
const pagelists = [...document.getElementsByClassName('pagelist')];
pagelists.forEach(async (element) => {
console.log('processing', element);
const parameters = element
.getAttribute('data-query')
.replaceAll("'", '"');
const query = `query { pages { list(${parameters}) { path, title } }}`;
const result = await graphql(query);
console.log({ query, result });
const pages = result.data.pages.list.filter(
(page) => !page.path.includes('/_')
);
const pagelist = pages.map((page) => {
const link = document.createElement('a');
link.href = `/${page.path}`;
link.classList.add('is-internal-link');
link.classList.add('is-valid-page');
link.textContent = page.title;
const listItem = document.createElement('li');
listItem.appendChild(link);
return listItem;
});
pagelist.forEach((page) => element.appendChild(page));
});
}
async function isElementLoaded(selector) {
while (document.querySelector(selector) === null) {
await new Promise((resolve) => requestAnimationFrame(resolve));
}
return document.querySelector(selector);
}
isElementLoaded('.contents').then(() => loadPagelists());
</script>

View File

@ -1,15 +0,0 @@
{
"name": "wikijs-metabot",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "AGPL-3.0-or-later",
"dependencies": {
"ws": "^8.9.0"
},
"type": "module"
}

View File

@ -1,8 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
ws@^8.9.0:
version "8.9.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.9.0.tgz#2a994bb67144be1b53fe2d23c53c028adeb7f45e"
integrity sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg==