@serverkgg/bridge 0.1.5 → 0.1.6
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/package.json +3 -3
- package/src/bot/botCauses.test.ts +8 -0
- package/src/bot/botCauses.ts +1 -1
- package/src/bot/detect/botFrameworks.ts +1 -0
- package/src/bot/detect/botInspectWarning.ts +1 -0
- package/src/bot/detect/corpus/anasty17-mirror-leech-telegram-bot.json +2 -2
- package/src/bot/detect/corpus/anonymousx1025-anonxmusic.json +2 -2
- package/src/bot/detect/corpus/mericcan41-discordjs-v14-template-ts.json +3 -1
- package/src/bot/detect/corpus/tgbot-collection-ytdlbot.json +2 -2
- package/src/bot/detect/detect.test.ts +29 -0
- package/src/bot/detect/detect.ts +30 -0
- package/src/probes/minecraftPing.test.ts +2 -1
- package/src/rcon/rconClient.test.ts +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serverkgg/bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Serverk game bridge and driver runtime, wire protocol, manifest schema, and maintainer CLI",
|
|
5
5
|
"homepage": "https://serverk.gg",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"tsc": "tsc",
|
|
42
|
-
"test": "bun test --parallel"
|
|
42
|
+
"test": "bun test --parallel=8"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"zod": "4.6.
|
|
45
|
+
"zod": "4.6.5"
|
|
46
46
|
}
|
|
47
47
|
}
|
|
@@ -37,6 +37,14 @@ const CASES: readonly {
|
|
|
37
37
|
line: "aiogram.exceptions.TelegramUnauthorizedError: Telegram server says - Unauthorized",
|
|
38
38
|
cause: BridgeCrashCause.TokenInvalid,
|
|
39
39
|
},
|
|
40
|
+
{
|
|
41
|
+
line: "pyrogram.errors.exceptions.bad_request_400.AccessTokenInvalid: Telegram says: [400 ACCESS_TOKEN_INVALID] - The bot access token is invalid",
|
|
42
|
+
cause: BridgeCrashCause.TokenInvalid,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
line: "pyrogram.errors.exceptions.bad_request_400.AccessTokenExpired: Telegram says: [400 ACCESS_TOKEN_EXPIRED] - The bot token has expired",
|
|
46
|
+
cause: BridgeCrashCause.TokenInvalid,
|
|
47
|
+
},
|
|
40
48
|
{
|
|
41
49
|
line: "Error: Used disallowed intents",
|
|
42
50
|
cause: BridgeCrashCause.IntentsDisallowed,
|
package/src/bot/botCauses.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { BridgeEventName } from "../protocol";
|
|
|
4
4
|
export const BOT_CRASH_EVIDENCE: readonly BridgeEventPattern[] = [
|
|
5
5
|
{
|
|
6
6
|
match:
|
|
7
|
-
/\[TokenInvalid\]|An invalid token was provided|LoginFailure: Improper token|\b401:? Unauthorized\b|\bInvalidToken\b|TelegramUnauthorizedError/,
|
|
7
|
+
/\[TokenInvalid\]|An invalid token was provided|LoginFailure: Improper token|\b401:? Unauthorized\b|\bInvalidToken\b|TelegramUnauthorizedError|\bACCESS_TOKEN_(?:INVALID|EXPIRED)\b/,
|
|
8
8
|
emit: BridgeEventName.TokenInvalid,
|
|
9
9
|
},
|
|
10
10
|
{
|
|
@@ -3,6 +3,7 @@ export enum BotInspectWarning {
|
|
|
3
3
|
NodeVersionUnmet = "node-version-unmet",
|
|
4
4
|
LockfileNotHonoured = "lockfile-not-honoured",
|
|
5
5
|
NoStart = "no-start",
|
|
6
|
+
WindowsCommand = "windows-command",
|
|
6
7
|
NoManifest = "no-manifest",
|
|
7
8
|
EnvCommitted = "env-committed",
|
|
8
9
|
NodeModulesCommitted = "node-modules-committed",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"expected": {
|
|
36
36
|
"runtime": "python",
|
|
37
37
|
"packageManager": "pip",
|
|
38
|
-
"framework":
|
|
39
|
-
"platform":
|
|
38
|
+
"framework": "pyrogram",
|
|
39
|
+
"platform": "telegram",
|
|
40
40
|
"install": "uv pip install -r requirements.txt",
|
|
41
41
|
"build": null,
|
|
42
42
|
"start": "python -m bot",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"expected": {
|
|
33
33
|
"runtime": "python",
|
|
34
34
|
"packageManager": "pip",
|
|
35
|
-
"framework":
|
|
36
|
-
"platform":
|
|
35
|
+
"framework": "pyrogram",
|
|
36
|
+
"platform": "telegram",
|
|
37
37
|
"install": "uv pip install -r requirements.txt",
|
|
38
38
|
"build": null,
|
|
39
39
|
"start": "bash start",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"expected": {
|
|
29
29
|
"runtime": "python",
|
|
30
30
|
"packageManager": "pip",
|
|
31
|
-
"framework":
|
|
32
|
-
"platform":
|
|
31
|
+
"framework": "pyrogram",
|
|
32
|
+
"platform": "telegram",
|
|
33
33
|
"install": "uv pip install -r requirements.txt",
|
|
34
34
|
"build": null,
|
|
35
35
|
"start": "python src/main.py",
|
|
@@ -604,6 +604,24 @@ describe("detect warnings", () => {
|
|
|
604
604
|
]);
|
|
605
605
|
});
|
|
606
606
|
|
|
607
|
+
it("warns when a start or build script runs a Windows-only command", () => {
|
|
608
|
+
const scripts = (start: string, build: string) => {
|
|
609
|
+
return project({
|
|
610
|
+
"package.json": JSON.stringify({
|
|
611
|
+
scripts: {
|
|
612
|
+
start,
|
|
613
|
+
build,
|
|
614
|
+
},
|
|
615
|
+
}),
|
|
616
|
+
"package-lock.json": "{}",
|
|
617
|
+
}).warnings;
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
expect(scripts("cls && node build/index.js", "cls && node build.js")).toContain(BotInspectWarning.WindowsCommand);
|
|
621
|
+
expect(scripts("node build/index.js", "CLS&&node build.js")).toContain(BotInspectWarning.WindowsCommand);
|
|
622
|
+
expect(scripts("node index.js", "tsc")).not.toContain(BotInspectWarning.WindowsCommand);
|
|
623
|
+
});
|
|
624
|
+
|
|
607
625
|
it("flags two runtimes and picks the one whose framework it found", () => {
|
|
608
626
|
const result = project({
|
|
609
627
|
...DISCORD_NODE,
|
|
@@ -630,6 +648,17 @@ describe("detect warnings", () => {
|
|
|
630
648
|
expect(result.warnings).toContain(BotInspectWarning.TwoPlatforms);
|
|
631
649
|
});
|
|
632
650
|
|
|
651
|
+
it("reads kurigram, the Pyrogram fork the corpus bots install, as Pyrogram on Telegram", () => {
|
|
652
|
+
const result = project({
|
|
653
|
+
"requirements.txt": "kurigram>=2.2.25\ntgcrypto\n",
|
|
654
|
+
"bot/__main__.py": "",
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
expect(result.framework).toBe(BotFramework.Pyrogram);
|
|
658
|
+
expect(result.platform).toBe(BotPlatform.Telegram);
|
|
659
|
+
expect(result.warnings).not.toContain(BotInspectWarning.TwoPlatforms);
|
|
660
|
+
});
|
|
661
|
+
|
|
633
662
|
it("drops keys the platform owns and warns, but keeps quiet about keys the runtime gives anyway", () => {
|
|
634
663
|
const result = project({
|
|
635
664
|
...DISCORD_NODE,
|
package/src/bot/detect/detect.ts
CHANGED
|
@@ -53,6 +53,12 @@ const NOT_A_BOT_PACKAGE: ReadonlySet<string> = new Set([
|
|
|
53
53
|
"scripts",
|
|
54
54
|
]);
|
|
55
55
|
|
|
56
|
+
const WINDOWS_COMMANDS: ReadonlySet<string> = new Set([
|
|
57
|
+
"cls",
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
const COMMAND_SEPARATOR = /&&|\|\||;|\|/;
|
|
61
|
+
|
|
56
62
|
const MANAGER_FIELD = /^(npm|pnpm|yarn|bun)@(\d+)/;
|
|
57
63
|
|
|
58
64
|
const ENGINE_MANAGERS: readonly string[] = [
|
|
@@ -394,6 +400,18 @@ const frameworksOf = (
|
|
|
394
400
|
return declared.length > 0 ? declared : specs.filter((spec) => spec.modules.some((name) => imports.has(name)));
|
|
395
401
|
};
|
|
396
402
|
|
|
403
|
+
const runsWindowsCommand = (command: string | null) => {
|
|
404
|
+
if (command === null) {
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return command.split(COMMAND_SEPARATOR).some((segment) => {
|
|
409
|
+
const program = segment.trim().split(/\s+/)[0] ?? "";
|
|
410
|
+
|
|
411
|
+
return WINDOWS_COMMANDS.has(program.toLowerCase());
|
|
412
|
+
});
|
|
413
|
+
};
|
|
414
|
+
|
|
397
415
|
const procfileCommand = (text: string | null): DetectedCommand | null => {
|
|
398
416
|
if (text === null) {
|
|
399
417
|
return null;
|
|
@@ -1094,6 +1112,18 @@ export const detect = (input: DetectInput): DetectResult => {
|
|
|
1094
1112
|
warnings.add(BotInspectWarning.NoStart);
|
|
1095
1113
|
}
|
|
1096
1114
|
|
|
1115
|
+
const buildScript = recordOf(packageJson?.scripts)[DETECT_TABLE.node.buildScript];
|
|
1116
|
+
|
|
1117
|
+
const commands = [
|
|
1118
|
+
start?.command ?? null,
|
|
1119
|
+
build?.command ?? null,
|
|
1120
|
+
build?.source === DETECT_TABLE.node.manifest && typeof buildScript === "string" ? buildScript : null,
|
|
1121
|
+
];
|
|
1122
|
+
|
|
1123
|
+
if (commands.some(runsWindowsCommand)) {
|
|
1124
|
+
warnings.add(BotInspectWarning.WindowsCommand);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1097
1127
|
committedWarnings(paths, warnings);
|
|
1098
1128
|
|
|
1099
1129
|
const env = envCollector(warnings);
|
|
@@ -5,6 +5,7 @@ import { parseStatus, probeMinecraftPing } from "./minecraftPing";
|
|
|
5
5
|
const HOST = "127.0.0.1";
|
|
6
6
|
|
|
7
7
|
const CLOSED_PORT = 59_441;
|
|
8
|
+
const CLOSED_HOST = "::1";
|
|
8
9
|
|
|
9
10
|
const varInt = (value: number) => {
|
|
10
11
|
const bytes: number[] = [];
|
|
@@ -294,7 +295,7 @@ describe("probeMinecraftPing", () => {
|
|
|
294
295
|
it("rejects as unreachable when nothing listens on the port", async () => {
|
|
295
296
|
const error = await probeMinecraftPing(
|
|
296
297
|
{
|
|
297
|
-
address:
|
|
298
|
+
address: CLOSED_HOST,
|
|
298
299
|
port: CLOSED_PORT,
|
|
299
300
|
},
|
|
300
301
|
2000,
|
|
@@ -19,6 +19,7 @@ import { createRconWriter, type RconWriter } from "./rconWriter";
|
|
|
19
19
|
const HOST = "127.0.0.1";
|
|
20
20
|
|
|
21
21
|
const CLOSED_PORT = 59_433;
|
|
22
|
+
const CLOSED_HOST = "::1";
|
|
22
23
|
|
|
23
24
|
const PASSWORD = "kX7-forbidden-in-errors";
|
|
24
25
|
|
|
@@ -1005,7 +1006,7 @@ describe("RconClient failures", () => {
|
|
|
1005
1006
|
|
|
1006
1007
|
it("reports a port nothing listens on", async () => {
|
|
1007
1008
|
const client = new RconClient({
|
|
1008
|
-
host:
|
|
1009
|
+
host: CLOSED_HOST,
|
|
1009
1010
|
port: CLOSED_PORT,
|
|
1010
1011
|
password: PASSWORD,
|
|
1011
1012
|
timeoutMs: 1000,
|
|
@@ -1475,7 +1476,7 @@ describe("RconClient secrecy", () => {
|
|
|
1475
1476
|
});
|
|
1476
1477
|
|
|
1477
1478
|
const unreachable = new RconClient({
|
|
1478
|
-
host:
|
|
1479
|
+
host: CLOSED_HOST,
|
|
1479
1480
|
port: CLOSED_PORT,
|
|
1480
1481
|
password: PASSWORD,
|
|
1481
1482
|
timeoutMs: 500,
|