windsor-bot 0.1.0
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 +128 -0
- package/SPEC.md +179 -0
- package/assets/pokemon-sprites/1.png +0 -0
- package/assets/pokemon-sprites/10.png +0 -0
- package/assets/pokemon-sprites/100.png +0 -0
- package/assets/pokemon-sprites/101.png +0 -0
- package/assets/pokemon-sprites/102.png +0 -0
- package/assets/pokemon-sprites/103.png +0 -0
- package/assets/pokemon-sprites/104.png +0 -0
- package/assets/pokemon-sprites/105.png +0 -0
- package/assets/pokemon-sprites/106.png +0 -0
- package/assets/pokemon-sprites/107.png +0 -0
- package/assets/pokemon-sprites/108.png +0 -0
- package/assets/pokemon-sprites/109.png +0 -0
- package/assets/pokemon-sprites/11.png +0 -0
- package/assets/pokemon-sprites/110.png +0 -0
- package/assets/pokemon-sprites/111.png +0 -0
- package/assets/pokemon-sprites/112.png +0 -0
- package/assets/pokemon-sprites/113.png +0 -0
- package/assets/pokemon-sprites/114.png +0 -0
- package/assets/pokemon-sprites/115.png +0 -0
- package/assets/pokemon-sprites/116.png +0 -0
- package/assets/pokemon-sprites/117.png +0 -0
- package/assets/pokemon-sprites/118.png +0 -0
- package/assets/pokemon-sprites/119.png +0 -0
- package/assets/pokemon-sprites/12.png +0 -0
- package/assets/pokemon-sprites/120.png +0 -0
- package/assets/pokemon-sprites/121.png +0 -0
- package/assets/pokemon-sprites/122.png +0 -0
- package/assets/pokemon-sprites/123.png +0 -0
- package/assets/pokemon-sprites/124.png +0 -0
- package/assets/pokemon-sprites/125.png +0 -0
- package/assets/pokemon-sprites/126.png +0 -0
- package/assets/pokemon-sprites/127.png +0 -0
- package/assets/pokemon-sprites/128.png +0 -0
- package/assets/pokemon-sprites/129.png +0 -0
- package/assets/pokemon-sprites/13.png +0 -0
- package/assets/pokemon-sprites/130.png +0 -0
- package/assets/pokemon-sprites/131.png +0 -0
- package/assets/pokemon-sprites/132.png +0 -0
- package/assets/pokemon-sprites/133.png +0 -0
- package/assets/pokemon-sprites/134.png +0 -0
- package/assets/pokemon-sprites/135.png +0 -0
- package/assets/pokemon-sprites/136.png +0 -0
- package/assets/pokemon-sprites/137.png +0 -0
- package/assets/pokemon-sprites/138.png +0 -0
- package/assets/pokemon-sprites/139.png +0 -0
- package/assets/pokemon-sprites/14.png +0 -0
- package/assets/pokemon-sprites/140.png +0 -0
- package/assets/pokemon-sprites/141.png +0 -0
- package/assets/pokemon-sprites/142.png +0 -0
- package/assets/pokemon-sprites/143.png +0 -0
- package/assets/pokemon-sprites/144.png +0 -0
- package/assets/pokemon-sprites/145.png +0 -0
- package/assets/pokemon-sprites/146.png +0 -0
- package/assets/pokemon-sprites/147.png +0 -0
- package/assets/pokemon-sprites/148.png +0 -0
- package/assets/pokemon-sprites/149.png +0 -0
- package/assets/pokemon-sprites/15.png +0 -0
- package/assets/pokemon-sprites/150.png +0 -0
- package/assets/pokemon-sprites/151.png +0 -0
- package/assets/pokemon-sprites/16.png +0 -0
- package/assets/pokemon-sprites/17.png +0 -0
- package/assets/pokemon-sprites/18.png +0 -0
- package/assets/pokemon-sprites/19.png +0 -0
- package/assets/pokemon-sprites/2.png +0 -0
- package/assets/pokemon-sprites/20.png +0 -0
- package/assets/pokemon-sprites/21.png +0 -0
- package/assets/pokemon-sprites/22.png +0 -0
- package/assets/pokemon-sprites/23.png +0 -0
- package/assets/pokemon-sprites/24.png +0 -0
- package/assets/pokemon-sprites/25.png +0 -0
- package/assets/pokemon-sprites/26.png +0 -0
- package/assets/pokemon-sprites/27.png +0 -0
- package/assets/pokemon-sprites/28.png +0 -0
- package/assets/pokemon-sprites/29.png +0 -0
- package/assets/pokemon-sprites/3.png +0 -0
- package/assets/pokemon-sprites/30.png +0 -0
- package/assets/pokemon-sprites/31.png +0 -0
- package/assets/pokemon-sprites/32.png +0 -0
- package/assets/pokemon-sprites/33.png +0 -0
- package/assets/pokemon-sprites/34.png +0 -0
- package/assets/pokemon-sprites/35.png +0 -0
- package/assets/pokemon-sprites/36.png +0 -0
- package/assets/pokemon-sprites/37.png +0 -0
- package/assets/pokemon-sprites/38.png +0 -0
- package/assets/pokemon-sprites/39.png +0 -0
- package/assets/pokemon-sprites/4.png +0 -0
- package/assets/pokemon-sprites/40.png +0 -0
- package/assets/pokemon-sprites/41.png +0 -0
- package/assets/pokemon-sprites/42.png +0 -0
- package/assets/pokemon-sprites/43.png +0 -0
- package/assets/pokemon-sprites/44.png +0 -0
- package/assets/pokemon-sprites/45.png +0 -0
- package/assets/pokemon-sprites/46.png +0 -0
- package/assets/pokemon-sprites/47.png +0 -0
- package/assets/pokemon-sprites/48.png +0 -0
- package/assets/pokemon-sprites/49.png +0 -0
- package/assets/pokemon-sprites/5.png +0 -0
- package/assets/pokemon-sprites/50.png +0 -0
- package/assets/pokemon-sprites/51.png +0 -0
- package/assets/pokemon-sprites/52.png +0 -0
- package/assets/pokemon-sprites/53.png +0 -0
- package/assets/pokemon-sprites/54.png +0 -0
- package/assets/pokemon-sprites/55.png +0 -0
- package/assets/pokemon-sprites/56.png +0 -0
- package/assets/pokemon-sprites/57.png +0 -0
- package/assets/pokemon-sprites/58.png +0 -0
- package/assets/pokemon-sprites/59.png +0 -0
- package/assets/pokemon-sprites/6.png +0 -0
- package/assets/pokemon-sprites/60.png +0 -0
- package/assets/pokemon-sprites/61.png +0 -0
- package/assets/pokemon-sprites/62.png +0 -0
- package/assets/pokemon-sprites/63.png +0 -0
- package/assets/pokemon-sprites/64.png +0 -0
- package/assets/pokemon-sprites/65.png +0 -0
- package/assets/pokemon-sprites/66.png +0 -0
- package/assets/pokemon-sprites/67.png +0 -0
- package/assets/pokemon-sprites/68.png +0 -0
- package/assets/pokemon-sprites/69.png +0 -0
- package/assets/pokemon-sprites/7.png +0 -0
- package/assets/pokemon-sprites/70.png +0 -0
- package/assets/pokemon-sprites/71.png +0 -0
- package/assets/pokemon-sprites/72.png +0 -0
- package/assets/pokemon-sprites/73.png +0 -0
- package/assets/pokemon-sprites/74.png +0 -0
- package/assets/pokemon-sprites/75.png +0 -0
- package/assets/pokemon-sprites/76.png +0 -0
- package/assets/pokemon-sprites/77.png +0 -0
- package/assets/pokemon-sprites/78.png +0 -0
- package/assets/pokemon-sprites/79.png +0 -0
- package/assets/pokemon-sprites/8.png +0 -0
- package/assets/pokemon-sprites/80.png +0 -0
- package/assets/pokemon-sprites/81.png +0 -0
- package/assets/pokemon-sprites/82.png +0 -0
- package/assets/pokemon-sprites/83.png +0 -0
- package/assets/pokemon-sprites/84.png +0 -0
- package/assets/pokemon-sprites/85.png +0 -0
- package/assets/pokemon-sprites/86.png +0 -0
- package/assets/pokemon-sprites/87.png +0 -0
- package/assets/pokemon-sprites/88.png +0 -0
- package/assets/pokemon-sprites/89.png +0 -0
- package/assets/pokemon-sprites/9.png +0 -0
- package/assets/pokemon-sprites/90.png +0 -0
- package/assets/pokemon-sprites/91.png +0 -0
- package/assets/pokemon-sprites/92.png +0 -0
- package/assets/pokemon-sprites/93.png +0 -0
- package/assets/pokemon-sprites/94.png +0 -0
- package/assets/pokemon-sprites/95.png +0 -0
- package/assets/pokemon-sprites/96.png +0 -0
- package/assets/pokemon-sprites/97.png +0 -0
- package/assets/pokemon-sprites/98.png +0 -0
- package/assets/pokemon-sprites/99.png +0 -0
- package/dist/ai.js +193 -0
- package/dist/bot.d.ts +22 -0
- package/dist/bot.d.ts.map +1 -0
- package/dist/bot.js +547 -0
- package/dist/bot.js.map +1 -0
- package/dist/commands/debug-fail.js +8 -0
- package/dist/commands/hello-world.js +12 -0
- package/dist/commands/hello.d.ts +3 -0
- package/dist/commands/hello.d.ts.map +1 -0
- package/dist/commands/hello.js +8 -0
- package/dist/commands/hello.js.map +1 -0
- package/dist/commands/index.d.ts +3 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +12 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/pokemon.js +132 -0
- package/dist/commands/print.d.ts +9 -0
- package/dist/commands/print.d.ts.map +1 -0
- package/dist/commands/print.js +46 -0
- package/dist/commands/print.js.map +1 -0
- package/dist/commands/sudoku.js +79 -0
- package/dist/commands/util.js +13 -0
- package/dist/commands/wordsearch.js +109 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +149 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +85 -0
- package/dist/index.js.map +1 -0
- package/dist/printer.js +245 -0
- package/dist/printing/cups.js +89 -0
- package/dist/printing/escp.js +147 -0
- package/dist/printing/imagefeed.js +212 -0
- package/dist/printing/index.js +54 -0
- package/dist/reactions.js +5 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +312 -0
- package/dist/server.js.map +1 -0
- package/dist/tests/config.test.js +78 -0
- package/dist/tests/urls.test.js +54 -0
- package/dist/types.d.ts +55 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/dist/web/app.js +325 -0
- package/package.json +45 -0
- package/scripts/download-pokemon-sprites.mjs +38 -0
- package/src/ai.ts +214 -0
- package/src/bot.ts +657 -0
- package/src/commands/debug-fail.ts +11 -0
- package/src/commands/hello-world.ts +15 -0
- package/src/commands/index.ts +35 -0
- package/src/commands/pokemon.ts +146 -0
- package/src/commands/sudoku.ts +94 -0
- package/src/commands/util.ts +14 -0
- package/src/commands/wordsearch.ts +118 -0
- package/src/config.ts +166 -0
- package/src/index.ts +98 -0
- package/src/printer.ts +268 -0
- package/src/printing/cups.ts +104 -0
- package/src/printing/escp.ts +158 -0
- package/src/printing/imagefeed.ts +246 -0
- package/src/printing/index.ts +63 -0
- package/src/reactions.ts +6 -0
- package/src/server.ts +355 -0
- package/src/tests/config.test.ts +88 -0
- package/src/tests/urls.test.ts +64 -0
- package/src/types.ts +120 -0
- package/src/web/app.tsx +690 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { reconcileChannels, getCurrentConfig, updateConfig, hashPassword } from '../config.ts';
|
|
4
|
+
import type { ChannelMapping } from '../types.ts';
|
|
5
|
+
|
|
6
|
+
// Setup: initialize config with some channel mappings
|
|
7
|
+
async function withChannels(mappings: ChannelMapping[], fn: () => Promise<void>): Promise<void> {
|
|
8
|
+
const original = getCurrentConfig().channels;
|
|
9
|
+
await updateConfig({ channels: mappings });
|
|
10
|
+
try {
|
|
11
|
+
await fn();
|
|
12
|
+
} finally {
|
|
13
|
+
await updateConfig({ channels: original });
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
test('reconcileChannels: matches by ID and updates name', async () => {
|
|
18
|
+
await withChannels([
|
|
19
|
+
{ channelId: '111', channelName: 'old-name', config: { type: 'on-demand' } },
|
|
20
|
+
], async () => {
|
|
21
|
+
await reconcileChannels([{ id: '111', name: 'new-name' }]);
|
|
22
|
+
const channels = getCurrentConfig().channels;
|
|
23
|
+
assert.equal(channels.length, 1);
|
|
24
|
+
assert.equal(channels[0]?.channelName, 'new-name');
|
|
25
|
+
assert.equal(channels[0]?.channelId, '111');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('reconcileChannels: matches by name and updates ID', async () => {
|
|
30
|
+
await withChannels([
|
|
31
|
+
{ channelId: '111', channelName: 'my-channel', config: { type: 'on-demand' } },
|
|
32
|
+
], async () => {
|
|
33
|
+
await reconcileChannels([{ id: '222', name: 'my-channel' }]);
|
|
34
|
+
const channels = getCurrentConfig().channels;
|
|
35
|
+
assert.equal(channels.length, 1);
|
|
36
|
+
assert.equal(channels[0]?.channelId, '222');
|
|
37
|
+
assert.equal(channels[0]?.channelName, 'my-channel');
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('reconcileChannels: deletes unmatched channels', async () => {
|
|
42
|
+
await withChannels([
|
|
43
|
+
{ channelId: '111', channelName: 'gone-channel', config: { type: 'on-demand' } },
|
|
44
|
+
], async () => {
|
|
45
|
+
await reconcileChannels([{ id: '999', name: 'different-channel' }]);
|
|
46
|
+
const channels = getCurrentConfig().channels;
|
|
47
|
+
assert.equal(channels.length, 0);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('reconcileChannels: does NOT delete if all discord channels missing (server blip)', async () => {
|
|
52
|
+
await withChannels([
|
|
53
|
+
{ channelId: '111', channelName: 'my-channel', config: { type: 'on-demand' } },
|
|
54
|
+
], async () => {
|
|
55
|
+
await reconcileChannels([]);
|
|
56
|
+
const channels = getCurrentConfig().channels;
|
|
57
|
+
assert.equal(channels.length, 1);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('hashPassword produces consistent hash', () => {
|
|
62
|
+
const hash1 = hashPassword('secret');
|
|
63
|
+
const hash2 = hashPassword('secret');
|
|
64
|
+
assert.equal(hash1, hash2);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('hashPassword produces different hashes for different passwords', () => {
|
|
68
|
+
assert.notEqual(hashPassword('abc'), hashPassword('xyz'));
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('token usage 24h tracking', async () => {
|
|
72
|
+
// Setup with stale and recent entries
|
|
73
|
+
const now = new Date();
|
|
74
|
+
const twoHoursAgo = new Date(now.getTime() - 2 * 60 * 60 * 1000).toISOString();
|
|
75
|
+
const twoDaysAgo = new Date(now.getTime() - 48 * 60 * 60 * 1000).toISOString();
|
|
76
|
+
|
|
77
|
+
await updateConfig({
|
|
78
|
+
tokenUsage: [
|
|
79
|
+
{ timestamp: twoDaysAgo, tokens: 10000 },
|
|
80
|
+
{ timestamp: twoHoursAgo, tokens: 5000 },
|
|
81
|
+
],
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const { getTokenUsage24h } = await import('../ai.ts');
|
|
85
|
+
const used = getTokenUsage24h();
|
|
86
|
+
// Should only count the 2-hours-ago entry (5000), not the 2-days-ago one
|
|
87
|
+
assert.equal(used, 5000);
|
|
88
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { extractUrls, stripUrls, replaceUrlsInText } from '../bot.ts';
|
|
4
|
+
|
|
5
|
+
test('extracts a single URL', () => {
|
|
6
|
+
const urls = extractUrls('Check this out https://example.com for more info');
|
|
7
|
+
assert.deepEqual(urls, ['https://example.com']);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test('strips trailing punctuation from URLs', () => {
|
|
11
|
+
const urls = extractUrls('See https://example.com. For details.');
|
|
12
|
+
assert.deepEqual(urls, ['https://example.com']);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('extracts multiple URLs', () => {
|
|
16
|
+
const urls = extractUrls('https://foo.com and https://bar.com are cool');
|
|
17
|
+
assert.deepEqual(urls, ['https://foo.com', 'https://bar.com']);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('caps at 5 URLs', () => {
|
|
21
|
+
const text = 'https://a.com https://b.com https://c.com https://d.com https://e.com https://f.com';
|
|
22
|
+
const urls = extractUrls(text);
|
|
23
|
+
assert.equal(urls.length, 5);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('ignores non-https URLs', () => {
|
|
27
|
+
const urls = extractUrls('Visit http://example.com or ftp://files.com');
|
|
28
|
+
assert.deepEqual(urls, []);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('replaces single URL with [link]', () => {
|
|
32
|
+
const text = 'See https://example.com for info';
|
|
33
|
+
const urls = extractUrls(text);
|
|
34
|
+
const result = replaceUrlsInText(text, urls);
|
|
35
|
+
assert.equal(result, 'See [link] for info');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('replaces multiple URLs with [link N]', () => {
|
|
39
|
+
const text = 'See https://foo.com and https://bar.com';
|
|
40
|
+
const urls = extractUrls(text);
|
|
41
|
+
const result = replaceUrlsInText(text, urls);
|
|
42
|
+
assert.ok(result.includes('[link 1]'));
|
|
43
|
+
assert.ok(result.includes('[link 2]'));
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('strips all URLs from text', () => {
|
|
47
|
+
const text = 'Buy this https://shop.com today!';
|
|
48
|
+
const result = stripUrls(text);
|
|
49
|
+
assert.ok(!result.includes('https://'));
|
|
50
|
+
assert.ok(result.includes('Buy this'));
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('message over 800 chars after URL strip is too long', () => {
|
|
54
|
+
const longText = 'A'.repeat(801);
|
|
55
|
+
const stripped = stripUrls(longText);
|
|
56
|
+
assert.ok(stripped.length > 800);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('message exactly 800 chars is not too long', () => {
|
|
60
|
+
const text = 'A'.repeat(800);
|
|
61
|
+
const stripped = stripUrls(text);
|
|
62
|
+
assert.equal(stripped.length, 800);
|
|
63
|
+
assert.ok(stripped.length <= 800);
|
|
64
|
+
});
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
|
|
2
|
+
export type ChannelBehaviorType = 'immediate-print' | 'accumulating-list' | 'recurring-print' | 'on-demand';
|
|
3
|
+
|
|
4
|
+
export interface ImmediatePrintConfig {
|
|
5
|
+
type: 'immediate-print';
|
|
6
|
+
header?: string;
|
|
7
|
+
footer?: string;
|
|
8
|
+
includeIcon: boolean;
|
|
9
|
+
includeMetadata: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface AccumulatingListConfig {
|
|
13
|
+
type: 'accumulating-list';
|
|
14
|
+
header?: string;
|
|
15
|
+
footer?: string;
|
|
16
|
+
includeChecklist: boolean;
|
|
17
|
+
includeMetadata: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface RecurringPrintConfig {
|
|
21
|
+
type: 'recurring-print';
|
|
22
|
+
header?: string;
|
|
23
|
+
footer?: string;
|
|
24
|
+
includeIcon: boolean;
|
|
25
|
+
includeMetadata: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface OnDemandConfig {
|
|
29
|
+
type: 'on-demand';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type ChannelBehaviorConfig =
|
|
33
|
+
| ImmediatePrintConfig
|
|
34
|
+
| AccumulatingListConfig
|
|
35
|
+
| RecurringPrintConfig
|
|
36
|
+
| OnDemandConfig;
|
|
37
|
+
|
|
38
|
+
export interface ChannelMapping {
|
|
39
|
+
channelId: string;
|
|
40
|
+
channelName: string;
|
|
41
|
+
config: ChannelBehaviorConfig;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
export interface TokenUsageEntry {
|
|
46
|
+
timestamp: string; // ISO string
|
|
47
|
+
tokens: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
export type PrintMode = 'escp' | 'cups' | 'imagefeed';
|
|
52
|
+
|
|
53
|
+
export interface EscpPrintConfig {
|
|
54
|
+
mode: 'escp';
|
|
55
|
+
serialPort: string; // e.g. /dev/ttyUSB0 or COM3
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface CupsPrintConfig {
|
|
59
|
+
mode: 'cups';
|
|
60
|
+
printerName: string;
|
|
61
|
+
paperSize: string; // e.g. "Letter", "A4"
|
|
62
|
+
paperName: string; // human label shown on printout
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface ImageFeedConfig {
|
|
66
|
+
mode: 'imagefeed';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type PrintConfig = EscpPrintConfig | CupsPrintConfig | ImageFeedConfig;
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
export interface WindsorConfig {
|
|
73
|
+
discordToken?: string;
|
|
74
|
+
serverId?: string;
|
|
75
|
+
openaiKey?: string;
|
|
76
|
+
diagnosticsPort: number;
|
|
77
|
+
passwordHash?: string;
|
|
78
|
+
channels: ChannelMapping[];
|
|
79
|
+
iconCacheDir?: string;
|
|
80
|
+
tokenUsage: TokenUsageEntry[];
|
|
81
|
+
printConfig?: PrintConfig;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
export type DiagnosticEventType = 'startup' | 'command' | 'success' | 'error' | 'info' | 'print';
|
|
86
|
+
|
|
87
|
+
export interface DiagnosticEvent {
|
|
88
|
+
timestamp: string; // ISO string
|
|
89
|
+
type: DiagnosticEventType;
|
|
90
|
+
message: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface BotStatus {
|
|
94
|
+
connected: boolean;
|
|
95
|
+
tag: string | null;
|
|
96
|
+
startedAt: string | null;
|
|
97
|
+
guilds: string[];
|
|
98
|
+
configuredServerId: string | null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
export interface PrintJob {
|
|
103
|
+
header?: string;
|
|
104
|
+
lines: string[];
|
|
105
|
+
iconPath?: string;
|
|
106
|
+
urls: string[];
|
|
107
|
+
footer?: string;
|
|
108
|
+
metadataLines?: string[];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
export interface CommandContext {
|
|
113
|
+
channelName: string;
|
|
114
|
+
args: string;
|
|
115
|
+
rawMessage: string;
|
|
116
|
+
messageId: string;
|
|
117
|
+
channelId: string;
|
|
118
|
+
guildId: string;
|
|
119
|
+
}
|
|
120
|
+
|