windsor-bot 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/dist/index.js +6 -7
- package/dist/server.js +24 -7
- package/dist/web/app.bundle.js +955 -0
- package/package.json +3 -3
- package/scripts/build-web.mjs +13 -0
- package/src/index.ts +7 -7
- package/src/server.ts +24 -7
package/README.md
CHANGED
|
@@ -118,11 +118,18 @@ npm run dev
|
|
|
118
118
|
|
|
119
119
|
Then open `http://localhost:8080` to use the web control panel.
|
|
120
120
|
|
|
121
|
+
When installed globally, run `windsor-bot` from the directory containing
|
|
122
|
+
`windsor.config.json`, or set `WINDSOR_CONFIG_PATH` to its full path:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
WINDSOR_CONFIG_PATH=/path/to/windsor.config.json windsor-bot
|
|
126
|
+
```
|
|
127
|
+
|
|
121
128
|
The control panel is a Preact app served by the diagnostics server and can:
|
|
122
129
|
|
|
123
130
|
1. Show runtime bot status and recent events.
|
|
124
131
|
2. Save basic setup into a local `windsor.config.json` file (Discord token, server id, etc).
|
|
125
132
|
|
|
126
|
-
The browser app source lives in `src/web/app.tsx
|
|
133
|
+
The browser app source lives in `src/web/app.tsx`. It is bundled into the npm package during the build and served by the diagnostics server; development runs can still bundle the source on demand.
|
|
127
134
|
|
|
128
135
|
`windsor.config.json` is gitignored because it can contain secrets.
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { loadConfig
|
|
2
|
+
import { loadConfig } from './config.js';
|
|
3
3
|
import { logEvent, startDiagnosticsServer, setDiscordChannels, setRestartHandler, setRefreshChannelsHandler } from './server.js';
|
|
4
4
|
import { createWindsorBot, checkScheduledTasks } from './bot.js';
|
|
5
5
|
import { ChannelType } from 'discord.js';
|
|
6
|
-
const config =
|
|
6
|
+
const { config } = await loadConfig();
|
|
7
7
|
startDiagnosticsServer(config.diagnosticsPort);
|
|
8
8
|
let bot = null;
|
|
9
9
|
let startInFlight = false;
|
|
10
|
-
async function startBot() {
|
|
10
|
+
async function startBot(cfg) {
|
|
11
11
|
if (startInFlight)
|
|
12
12
|
return;
|
|
13
|
-
const cfg = getCurrentConfig();
|
|
14
13
|
if (!cfg.discordToken) {
|
|
15
14
|
logEvent('info', 'No Discord token configured. Visit the control panel to set up.');
|
|
16
15
|
return;
|
|
@@ -51,8 +50,8 @@ export async function restartBot() {
|
|
|
51
50
|
bot = null;
|
|
52
51
|
}
|
|
53
52
|
setDiscordChannels([]);
|
|
54
|
-
await loadConfig();
|
|
55
|
-
await startBot();
|
|
53
|
+
const { config } = await loadConfig();
|
|
54
|
+
await startBot(config);
|
|
56
55
|
}
|
|
57
56
|
// Recurring task checker
|
|
58
57
|
setInterval(() => {
|
|
@@ -82,4 +81,4 @@ async function refreshChannels() {
|
|
|
82
81
|
logEvent('info', `Refreshed ${channels.length} Discord channels`);
|
|
83
82
|
}
|
|
84
83
|
setRefreshChannelsHandler(refreshChannels);
|
|
85
|
-
await startBot();
|
|
84
|
+
await startBot(config);
|
package/dist/server.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createServer } from 'http';
|
|
2
2
|
import { build } from 'esbuild';
|
|
3
|
-
import { stat } from 'fs/promises';
|
|
4
|
-
import { join } from 'path';
|
|
3
|
+
import { readFile, stat } from 'fs/promises';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
5
6
|
import { getCurrentConfig, updateConfig, checkPassword, hashPassword, getConfigFilePath, } from './config.js';
|
|
6
7
|
import { printTestPage, getImageFeed, clearImageFeed, renderFeedHtml } from './printing/index.js';
|
|
7
8
|
const MAX_EVENTS = 200;
|
|
@@ -73,13 +74,29 @@ async function readJsonBody(req) {
|
|
|
73
74
|
}
|
|
74
75
|
let appBundleCache = null;
|
|
75
76
|
async function loadAppScript() {
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
|
|
77
|
+
const runtimeDir = dirname(fileURLToPath(import.meta.url));
|
|
78
|
+
const bundledApp = join(runtimeDir, 'web', 'app.bundle.js');
|
|
79
|
+
const sourceEntryPoint = join(runtimeDir, '..', 'src', 'web', 'app.tsx');
|
|
80
|
+
const appPath = await stat(bundledApp).then(() => bundledApp).catch((error) => {
|
|
81
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
|
|
82
|
+
return sourceEntryPoint;
|
|
83
|
+
}
|
|
84
|
+
throw error;
|
|
85
|
+
});
|
|
86
|
+
const appStat = await stat(appPath);
|
|
87
|
+
if (appPath === bundledApp) {
|
|
88
|
+
if (appBundleCache && appBundleCache.mtimeMs === appStat.mtimeMs) {
|
|
89
|
+
return appBundleCache.script;
|
|
90
|
+
}
|
|
91
|
+
const script = await readFile(appPath, 'utf8');
|
|
92
|
+
appBundleCache = { script, mtimeMs: appStat.mtimeMs };
|
|
93
|
+
return script;
|
|
94
|
+
}
|
|
95
|
+
if (appBundleCache && appBundleCache.mtimeMs === appStat.mtimeMs) {
|
|
79
96
|
return appBundleCache.script;
|
|
80
97
|
}
|
|
81
98
|
const result = await build({
|
|
82
|
-
entryPoints: [
|
|
99
|
+
entryPoints: [sourceEntryPoint],
|
|
83
100
|
bundle: true,
|
|
84
101
|
write: false,
|
|
85
102
|
platform: 'browser',
|
|
@@ -92,7 +109,7 @@ async function loadAppScript() {
|
|
|
92
109
|
const firstFile = result.outputFiles[0];
|
|
93
110
|
if (!firstFile)
|
|
94
111
|
throw new Error('Failed to generate app bundle');
|
|
95
|
-
appBundleCache = { script: firstFile.text, mtimeMs:
|
|
112
|
+
appBundleCache = { script: firstFile.text, mtimeMs: appStat.mtimeMs };
|
|
96
113
|
return firstFile.text;
|
|
97
114
|
}
|
|
98
115
|
function renderHtmlShell() {
|