windsor-bot 0.1.11 → 0.1.13
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/assets/poker/README.txt +6 -0
- package/assets/poker/club.svg +11 -0
- package/assets/poker/diamond.svg +8 -0
- package/assets/poker/heart.svg +9 -0
- package/assets/poker/spade.svg +9 -0
- package/dist/bot.js +110 -17
- package/dist/commands/index.js +2 -0
- package/dist/commands/poker.js +139 -0
- package/dist/index.js +9 -17
- package/dist/printing/index.js +39 -13
- package/dist/reactions.js +2 -1
- package/dist/server.js +52 -0
- package/dist/tests/config.test.js +8 -1
- package/dist/tests/poker.test.js +12 -0
- package/dist/tests/urls.test.js +11 -1
- package/dist/web/app.bundle.js +42 -2
- package/dist/web/app.js +41 -1
- package/package.json +2 -1
package/dist/server.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { createServer } from 'http';
|
|
2
|
+
import { execFile } from 'child_process';
|
|
2
3
|
import { build } from 'esbuild';
|
|
3
4
|
import { readFile, stat } from 'fs/promises';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
import { spawn } from 'child_process';
|
|
4
7
|
import { dirname, join } from 'path';
|
|
5
8
|
import { fileURLToPath } from 'url';
|
|
6
9
|
import { getCurrentConfig, updateConfig, checkPassword, hashPassword, getConfigFilePath, } from './config.js';
|
|
7
10
|
import { printTestPage, getImageFeed, clearImageFeed, renderFeedHtml } from './printing/index.js';
|
|
8
11
|
const MAX_EVENTS = 200;
|
|
9
12
|
const events = [];
|
|
13
|
+
const execFileAsync = promisify(execFile);
|
|
10
14
|
export const status = {
|
|
11
15
|
connected: false,
|
|
12
16
|
tag: null,
|
|
@@ -53,6 +57,38 @@ function sendJson(res, statusCode, payload) {
|
|
|
53
57
|
});
|
|
54
58
|
res.end(body);
|
|
55
59
|
}
|
|
60
|
+
async function readPackageVersion() {
|
|
61
|
+
const runtimeDir = dirname(fileURLToPath(import.meta.url));
|
|
62
|
+
const packageJson = JSON.parse(await readFile(join(runtimeDir, '..', 'package.json'), 'utf8'));
|
|
63
|
+
if (typeof packageJson.version !== 'string')
|
|
64
|
+
throw new Error('package.json does not contain a valid version');
|
|
65
|
+
return packageJson.version;
|
|
66
|
+
}
|
|
67
|
+
async function updateGlobalBot() {
|
|
68
|
+
await execFileAsync('npm', ['install', '-g', 'windsor-bot@latest']);
|
|
69
|
+
const { stdout } = await execFileAsync('npm', ['root', '-g']);
|
|
70
|
+
const packageJsonPath = join(stdout.trim(), 'windsor-bot', 'package.json');
|
|
71
|
+
const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8'));
|
|
72
|
+
if (typeof packageJson.version !== 'string')
|
|
73
|
+
throw new Error('Updated package does not contain a valid version');
|
|
74
|
+
return packageJson.version;
|
|
75
|
+
}
|
|
76
|
+
function hardRestart() {
|
|
77
|
+
const child = spawn('/bin/sh', [
|
|
78
|
+
'-c',
|
|
79
|
+
'sleep 1; exec "$@"',
|
|
80
|
+
'windsor-hard-restart',
|
|
81
|
+
process.execPath,
|
|
82
|
+
...process.argv.slice(1),
|
|
83
|
+
], {
|
|
84
|
+
detached: true,
|
|
85
|
+
stdio: 'inherit',
|
|
86
|
+
cwd: process.cwd(),
|
|
87
|
+
env: process.env,
|
|
88
|
+
});
|
|
89
|
+
child.unref();
|
|
90
|
+
setTimeout(() => process.kill(process.pid, 'SIGTERM'), 250);
|
|
91
|
+
}
|
|
56
92
|
async function readJsonBody(req) {
|
|
57
93
|
const chunks = [];
|
|
58
94
|
let total = 0;
|
|
@@ -185,6 +221,10 @@ export function startDiagnosticsServer(port) {
|
|
|
185
221
|
sendJson(res, 200, status);
|
|
186
222
|
return;
|
|
187
223
|
}
|
|
224
|
+
if (method === 'GET' && urlPath === '/api/version') {
|
|
225
|
+
sendJson(res, 200, { version: await readPackageVersion() });
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
188
228
|
if (method === 'GET' && urlPath === '/api/config') {
|
|
189
229
|
const cfg = getCurrentConfig();
|
|
190
230
|
sendJson(res, 200, {
|
|
@@ -300,6 +340,18 @@ export function startDiagnosticsServer(port) {
|
|
|
300
340
|
}
|
|
301
341
|
return;
|
|
302
342
|
}
|
|
343
|
+
if (method === 'POST' && urlPath === '/api/update') {
|
|
344
|
+
const version = await updateGlobalBot();
|
|
345
|
+
logEvent('info', `Updated windsor-bot to version ${version}`);
|
|
346
|
+
sendJson(res, 200, { version });
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
if (method === 'POST' && urlPath === '/api/hard-restart') {
|
|
350
|
+
logEvent('info', 'Hard restart requested via API');
|
|
351
|
+
sendJson(res, 200, { ok: true });
|
|
352
|
+
hardRestart();
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
303
355
|
if (method === 'POST' && urlPath === '/api/channels/refresh') {
|
|
304
356
|
if (_refreshChannelsHandler) {
|
|
305
357
|
await _refreshChannelsHandler();
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
import { mkdtemp, rm } from 'fs/promises';
|
|
2
|
+
import { tmpdir } from 'os';
|
|
1
3
|
import { test } from 'node:test';
|
|
2
4
|
import assert from 'node:assert/strict';
|
|
3
|
-
|
|
5
|
+
const configDir = await mkdtemp(`${tmpdir()}/windsor-config-test-`);
|
|
6
|
+
process.env['WINDSOR_CONFIG_PATH'] = `${configDir}/windsor.config.json`;
|
|
7
|
+
const { reconcileChannels, getCurrentConfig, updateConfig, hashPassword } = await import('../config.js');
|
|
8
|
+
test.after(async () => {
|
|
9
|
+
await rm(configDir, { recursive: true, force: true });
|
|
10
|
+
});
|
|
4
11
|
// Setup: initialize config with some channel mappings
|
|
5
12
|
async function withChannels(mappings, fn) {
|
|
6
13
|
const original = getCurrentConfig().channels;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { dealPokerHand } from '../commands/poker.js';
|
|
4
|
+
test('deals five board cards and four two-card hands without duplicates', () => {
|
|
5
|
+
const deal = dealPokerHand(() => 0.5);
|
|
6
|
+
const cards = [...deal.board, ...deal.hands.flat()];
|
|
7
|
+
const keys = cards.map(card => `${card.rank}${card.suit}`);
|
|
8
|
+
assert.equal(deal.board.length, 5);
|
|
9
|
+
assert.equal(deal.hands.length, 4);
|
|
10
|
+
assert.equal(cards.length, 13);
|
|
11
|
+
assert.equal(new Set(keys).size, cards.length);
|
|
12
|
+
});
|
package/dist/tests/urls.test.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { test } from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import { extractUrls, stripUrls, replaceUrlsInText } from '../bot.js';
|
|
3
|
+
import { extractUrls, parseCommand, stripUrls, replaceUrlsInText } from '../bot.js';
|
|
4
|
+
test('parses commands with or without a space after the prefix', () => {
|
|
5
|
+
assert.deepEqual(parseCommand('!command'), { name: 'command', args: '' });
|
|
6
|
+
assert.deepEqual(parseCommand('! command'), { name: 'command', args: '' });
|
|
7
|
+
});
|
|
8
|
+
test('parses command arguments after optional prefix whitespace', () => {
|
|
9
|
+
assert.deepEqual(parseCommand('! command first second'), {
|
|
10
|
+
name: 'command',
|
|
11
|
+
args: 'first second',
|
|
12
|
+
});
|
|
13
|
+
});
|
|
4
14
|
test('extracts a single URL', () => {
|
|
5
15
|
const urls = extractUrls('Check this out https://example.com for more info');
|
|
6
16
|
assert.deepEqual(urls, ['https://example.com']);
|