Integrate SCUM real ops workflows
This commit is contained in:
@@ -1,25 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { sourceRCONChatRequest, sourceRCONRawCommandRequest } from "./sourceRcon";
|
||||
|
||||
describe("Source RCON browser request schemas", () => {
|
||||
it("builds a bounded typed chat request without connection material", () => {
|
||||
expect(sourceRCONChatRequest("server-1", { chatType: 4, message: "hello", targetSteamId: "76561198000000001" }, 12)).toEqual({
|
||||
kind: "chat",
|
||||
chatType: 4,
|
||||
message: "hello",
|
||||
targetSteamId: "76561198000000001",
|
||||
idempotencyKey: "web:source-rcon:chat:server-1:12"
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects framed text and formats raw commands without chat fields", () => {
|
||||
expect(() => sourceRCONChatRequest("server-1", { chatType: 2, message: "line one\nline two" }, 13)).toThrow("受限的单行文本");
|
||||
expect(() => sourceRCONChatRequest("server-1", { chatType: 8, message: "hello" }, 13)).toThrow("聊天类型");
|
||||
expect(sourceRCONRawCommandRequest("server-1", " SetTime 12 ", 14)).toEqual({
|
||||
kind: "command",
|
||||
command: "SetTime 12",
|
||||
idempotencyKey: "web:source-rcon:command:server-1:14"
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,49 +0,0 @@
|
||||
import type { SourceRCONCommandRequest } from "../api/types";
|
||||
|
||||
const maxChatBytes = 1024;
|
||||
const maxCommandBytes = 4000;
|
||||
const steamID64 = /^[0-9]{17}$/;
|
||||
|
||||
export interface SourceRCONChatDraft {
|
||||
chatType: number;
|
||||
message: string;
|
||||
targetSteamId?: string;
|
||||
}
|
||||
|
||||
export function sourceRCONChatRequest(serverInstanceId: string, draft: SourceRCONChatDraft, sequence = Date.now()): SourceRCONCommandRequest {
|
||||
const message = validateSourceRCONText(draft.message, maxChatBytes, "聊天内容");
|
||||
if (!Number.isInteger(draft.chatType) || draft.chatType < 0 || draft.chatType > 7) {
|
||||
throw new Error("聊天类型必须在 0 到 7 之间。");
|
||||
}
|
||||
const targetSteamId = draft.targetSteamId?.trim() ?? "";
|
||||
if (targetSteamId && !steamID64.test(targetSteamId)) {
|
||||
throw new Error("目标 SteamID64 必须为 17 位数字。");
|
||||
}
|
||||
return {
|
||||
kind: "chat",
|
||||
chatType: draft.chatType,
|
||||
message,
|
||||
targetSteamId: targetSteamId || undefined,
|
||||
idempotencyKey: sourceRCONIdempotencyKey("chat", serverInstanceId, sequence)
|
||||
};
|
||||
}
|
||||
|
||||
export function sourceRCONRawCommandRequest(serverInstanceId: string, command: string, sequence = Date.now()): SourceRCONCommandRequest {
|
||||
return {
|
||||
kind: "command",
|
||||
command: validateSourceRCONText(command, maxCommandBytes, "原始指令"),
|
||||
idempotencyKey: sourceRCONIdempotencyKey("command", serverInstanceId, sequence)
|
||||
};
|
||||
}
|
||||
|
||||
function validateSourceRCONText(value: string, maxBytes: number, label: string): string {
|
||||
const normalized = value.trim();
|
||||
if (!normalized || new TextEncoder().encode(normalized).byteLength > maxBytes || /[\u0000\r\n]/.test(normalized)) {
|
||||
throw new Error(`${label}必须是受限的单行文本。`);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function sourceRCONIdempotencyKey(kind: "chat" | "command", serverInstanceId: string, sequence: number): string {
|
||||
return `web:source-rcon:${kind}:${serverInstanceId.trim()}:${Math.max(0, Math.floor(sequence))}`;
|
||||
}
|
||||
Reference in New Issue
Block a user