Initial commit

This commit is contained in:
neri 2022-09-23 12:05:34 +02:00
commit c8f1067c77
6 changed files with 213 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules/

4
Dockerfile Normal file
View File

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

47
README.md Normal file
View File

@ -0,0 +1,47 @@
# WikiJs Metabot
Ein Skript was die Page-Listings auf [https://wiki.ctdo.de](https://wiki.ctdo.de)
automatisch aktuell hält
## 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:
```markdown
<!-- \pagelist QUERY -->
```
Dabei muss `QUERY` eine Parameterlist für die `PageQuery::list`-Methode der WikiJs-GraphQL-API sein.
Folgende Parameter werden unterstützt:
```text
limit: Int
orderBy: Enum CREATED | ID | PATH | TITLE | UPDATED
orderByDirection: Enum ASC | DESC
tags: [String]
locale: String
creatorId: Int
authorId: Int
```
### Beispiele
Liste aller Seiten die mit den Tags `top` und `new` versehen sind:
```markdown
<!-- \pagelist tags: ["top", "new"] -->
```
Liste der 10 zuletzt bearbeiteten Seiten:
```markdown
<!-- \pagelist orderBy: UPDATED, orderByDirection: DESC, limit: 10 -->
```
### 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 `Int` und vom Typ `Enum` müssen ohne Anführungszeichen angegeben werden

138
index.js Normal file
View File

@ -0,0 +1,138 @@
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();

15
package.json Normal file
View File

@ -0,0 +1,15 @@
{
"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"
}

8
yarn.lock Normal file
View File

@ -0,0 +1,8 @@
# 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==