mirror of
https://github.com/SikongJueluo/cc-utils.git
synced 2025-12-20 13:37:49 +08:00
reconstruct the access control cli
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
"groupName": "Admin",
|
"groupName": "Admin",
|
||||||
"groupUsers": ["Selcon"],
|
"groupUsers": ["Selcon"],
|
||||||
"isAllowed": true,
|
"isAllowed": true,
|
||||||
"isWarnTarget": true
|
"isNotice": true
|
||||||
},
|
},
|
||||||
"defaultToastConfig": {
|
"defaultToastConfig": {
|
||||||
"title": {
|
"title": {
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
"text": "Hello %groupName% %playerName%",
|
"text": "Hello %groupName% %playerName%",
|
||||||
"color": "green"
|
"color": "green"
|
||||||
},
|
},
|
||||||
"prefix": "桃花源",
|
"prefix": "Taohuayuan",
|
||||||
"brackets": "[]",
|
"brackets": "[]",
|
||||||
"bracketColor": ""
|
"bracketColor": ""
|
||||||
},
|
},
|
||||||
@@ -39,19 +39,19 @@
|
|||||||
"groupName": "user",
|
"groupName": "user",
|
||||||
"groupUsers": [],
|
"groupUsers": [],
|
||||||
"isAllowed": true,
|
"isAllowed": true,
|
||||||
"isWarnTarget": true
|
"isNotice": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"groupName": "VIP",
|
"groupName": "VIP",
|
||||||
"groupUsers": [],
|
"groupUsers": [],
|
||||||
"isAllowed": true,
|
"isAllowed": true,
|
||||||
"isWarnTarget": false
|
"isNotice": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"groupName": "enemy",
|
"groupName": "enemy",
|
||||||
"groupUsers": [],
|
"groupUsers": [],
|
||||||
"isAllowed": false,
|
"isAllowed": false,
|
||||||
"isWarnTarget": false,
|
"isNotice": false,
|
||||||
"toastConfig": {
|
"toastConfig": {
|
||||||
"title": {
|
"title": {
|
||||||
"text": "Warn",
|
"text": "Warn",
|
||||||
|
|||||||
@@ -0,0 +1,672 @@
|
|||||||
|
import { CCLog } from "@/lib/ccLog";
|
||||||
|
import { AccessConfig, UserGroupConfig, saveConfig } from "./config";
|
||||||
|
import { ChatBoxEvent, pullEventAs } from "@/lib/event";
|
||||||
|
import { parseBoolean } from "@/lib/common";
|
||||||
|
|
||||||
|
// CLI命令接口
|
||||||
|
interface CLICommand {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
usage: string;
|
||||||
|
execute: (args: string[], executor: string, context: CLIContext) => CLIResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLI执行结果
|
||||||
|
interface CLIResult {
|
||||||
|
success: boolean;
|
||||||
|
message?: string;
|
||||||
|
shouldSaveConfig?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLI上下文
|
||||||
|
interface CLIContext {
|
||||||
|
config: AccessConfig;
|
||||||
|
configFilepath: string;
|
||||||
|
log: CCLog;
|
||||||
|
chatBox: any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||||
|
groupNames: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 基础命令处理器
|
||||||
|
class CLICommandProcessor {
|
||||||
|
private commands = new Map<string, CLICommand>();
|
||||||
|
private context: CLIContext;
|
||||||
|
|
||||||
|
constructor(context: CLIContext) {
|
||||||
|
this.context = context;
|
||||||
|
this.initializeCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
private initializeCommands() {
|
||||||
|
// 注册所有命令
|
||||||
|
this.registerCommand(new AddCommand());
|
||||||
|
this.registerCommand(new DelCommand());
|
||||||
|
this.registerCommand(new ListCommand());
|
||||||
|
this.registerCommand(new SetCommand());
|
||||||
|
this.registerCommand(new CreateGroupCommand());
|
||||||
|
this.registerCommand(new RemoveGroupCommand());
|
||||||
|
this.registerCommand(new EditCommand());
|
||||||
|
this.registerCommand(new ShowConfigCommand());
|
||||||
|
this.registerCommand(new HelpCommand());
|
||||||
|
}
|
||||||
|
|
||||||
|
private registerCommand(command: CLICommand) {
|
||||||
|
this.commands.set(command.name, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public processCommand(message: string, executor: string): CLIResult {
|
||||||
|
const params = message.split(" ");
|
||||||
|
|
||||||
|
// 移除 "@AC" 前缀
|
||||||
|
if (params.length < 2) {
|
||||||
|
return this.getHelpCommand().execute([], executor, this.context);
|
||||||
|
}
|
||||||
|
|
||||||
|
const commandName = params[1].replace("/", ""); // 移除 "/" 前缀
|
||||||
|
const args = params.slice(2);
|
||||||
|
|
||||||
|
const command = this.commands.get(commandName);
|
||||||
|
if (!command) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Unknown command: ${commandName}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return command.execute(args, executor, this.context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getHelpCommand(): CLICommand {
|
||||||
|
return this.commands.get("help")!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sendResponse(result: CLIResult, executor: string) {
|
||||||
|
if (result.message != null && result.message.length > 0) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
||||||
|
this.context.chatBox.sendMessageToPlayer(
|
||||||
|
result.message,
|
||||||
|
executor,
|
||||||
|
"AccessControl",
|
||||||
|
"[]",
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.shouldSaveConfig === true) {
|
||||||
|
saveConfig(this.context.config, this.context.configFilepath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加用户命令
|
||||||
|
class AddCommand implements CLICommand {
|
||||||
|
name = "add";
|
||||||
|
description = "Add player to group";
|
||||||
|
usage = "add <userGroup> <playerName>";
|
||||||
|
|
||||||
|
execute(args: string[], _executor: string, context: CLIContext): CLIResult {
|
||||||
|
if (args.length !== 2) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Usage: ${this.usage}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const [groupName, playerName] = args;
|
||||||
|
|
||||||
|
if (groupName === "admin") {
|
||||||
|
context.config.adminGroupConfig.groupUsers.push(playerName);
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `Add player ${playerName} to admin`,
|
||||||
|
shouldSaveConfig: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context.groupNames.includes(groupName)) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Invalid group: ${groupName}. Available groups: ${context.groupNames.join(", ")}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupConfig = context.config.usersGroups.find(
|
||||||
|
(value) => value.groupName === groupName,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!groupConfig) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Group ${groupName} not found`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupConfig.groupUsers === undefined) {
|
||||||
|
groupConfig.groupUsers = [playerName];
|
||||||
|
} else {
|
||||||
|
groupConfig.groupUsers.push(playerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `Add player ${playerName} to ${groupConfig.groupName}`,
|
||||||
|
shouldSaveConfig: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除用户命令
|
||||||
|
class DelCommand implements CLICommand {
|
||||||
|
name = "del";
|
||||||
|
description = "Delete player from group";
|
||||||
|
usage = "del <userGroup> <playerName>";
|
||||||
|
|
||||||
|
execute(args: string[], _executor: string, context: CLIContext): CLIResult {
|
||||||
|
if (args.length !== 2) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Usage: ${this.usage}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const [groupName, playerName] = args;
|
||||||
|
|
||||||
|
if (groupName === "admin") {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Could't delete admin, please edit config",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context.groupNames.includes(groupName)) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Invalid group: ${groupName}. Available groups: ${context.groupNames.join(", ")}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupConfig = context.config.usersGroups.find(
|
||||||
|
(value) => value.groupName === groupName,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!groupConfig) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Group ${groupName} not found`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupConfig.groupUsers === undefined) {
|
||||||
|
groupConfig.groupUsers = [];
|
||||||
|
} else {
|
||||||
|
groupConfig.groupUsers = groupConfig.groupUsers.filter(
|
||||||
|
(user) => user !== playerName,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `Delete ${groupConfig.groupName} ${playerName}`,
|
||||||
|
shouldSaveConfig: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表命令
|
||||||
|
class ListCommand implements CLICommand {
|
||||||
|
name = "list";
|
||||||
|
description = "List all players with their groups";
|
||||||
|
usage = "list";
|
||||||
|
|
||||||
|
execute(_args: string[], _executor: string, context: CLIContext): CLIResult {
|
||||||
|
let message = `Admins : [ ${context.config.adminGroupConfig.groupUsers.join(", ")} ]\n`;
|
||||||
|
|
||||||
|
for (const groupConfig of context.config.usersGroups) {
|
||||||
|
const users = groupConfig.groupUsers ?? [];
|
||||||
|
message += `${groupConfig.groupName} : [ ${users.join(", ")} ]\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: message.trim(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置命令
|
||||||
|
class SetCommand implements CLICommand {
|
||||||
|
name = "set";
|
||||||
|
description = "Config access control settings";
|
||||||
|
usage = "set <option> <value>";
|
||||||
|
|
||||||
|
execute(args: string[], _executor: string, context: CLIContext): CLIResult {
|
||||||
|
if (args.length !== 2) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Usage: ${this.usage}\nOptions: warnInterval, detectInterval, detectRange`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const [option, valueStr] = args;
|
||||||
|
const value = parseInt(valueStr);
|
||||||
|
|
||||||
|
if (isNaN(value)) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Invalid value: ${valueStr}. Must be a number.`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (option) {
|
||||||
|
case "warnInterval":
|
||||||
|
context.config.warnInterval = value;
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `Set warn interval to ${context.config.warnInterval}`,
|
||||||
|
shouldSaveConfig: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
case "detectInterval":
|
||||||
|
context.config.detectInterval = value;
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `Set detect interval to ${context.config.detectInterval}`,
|
||||||
|
shouldSaveConfig: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
case "detectRange":
|
||||||
|
context.config.detectRange = value;
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `Set detect range to ${context.config.detectRange}`,
|
||||||
|
shouldSaveConfig: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Unknown option: ${option}. Available options: warnInterval, detectInterval, detectRange`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 帮助命令
|
||||||
|
class HelpCommand implements CLICommand {
|
||||||
|
name = "help";
|
||||||
|
description = "Show command help";
|
||||||
|
usage = "help";
|
||||||
|
|
||||||
|
execute(_args: string[], _executor: string, context: CLIContext): CLIResult {
|
||||||
|
const helpMessage = `
|
||||||
|
Command Usage: @AC /<Command> [args]
|
||||||
|
Commands:
|
||||||
|
- add <userGroup> <playerName>
|
||||||
|
add player to group
|
||||||
|
userGroup: ${context.groupNames.join(", ")}
|
||||||
|
- del <userGroup> <playerName>
|
||||||
|
delete player in the group, except Admin
|
||||||
|
userGroup: ${context.groupNames.join(", ")}
|
||||||
|
- list
|
||||||
|
list all of the player with its group
|
||||||
|
- set <options> [params]
|
||||||
|
config access control settings
|
||||||
|
options: warnInterval, detectInterval, detectRange
|
||||||
|
- creategroup <groupName> <isAllowed> <isNotice>
|
||||||
|
create new user group
|
||||||
|
- removegroup <groupName>
|
||||||
|
remove user group (except admin groups)
|
||||||
|
- edit <target> [args]
|
||||||
|
edit various configurations
|
||||||
|
targets: group (edit group properties)
|
||||||
|
examples: edit group <groupName> <property> <value> (properties: isAllowed, isNotice)
|
||||||
|
- showconfig [type]
|
||||||
|
show configuration (type: groups/toast/all)
|
||||||
|
- help
|
||||||
|
show this help message
|
||||||
|
`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: helpMessage.trim(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建用户组命令
|
||||||
|
class CreateGroupCommand implements CLICommand {
|
||||||
|
name = "creategroup";
|
||||||
|
description = "Create new user group";
|
||||||
|
usage = "creategroup <groupName> <isAllowed> <isNotice>";
|
||||||
|
|
||||||
|
execute(args: string[], _executor: string, context: CLIContext): CLIResult {
|
||||||
|
if (args.length !== 3) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Usage: ${this.usage}\nExample: creategroup VIP2 true false`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const [groupName, isAllowedStr, isNoticeStr] = args;
|
||||||
|
|
||||||
|
// 检查组名是否已存在
|
||||||
|
if (context.groupNames.includes(groupName) || groupName === "admin") {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Group ${groupName} already exists`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAllowed = isAllowedStr.toLowerCase() === "true";
|
||||||
|
const isNotice = isNoticeStr.toLowerCase() === "true";
|
||||||
|
|
||||||
|
const newGroup: UserGroupConfig = {
|
||||||
|
groupName,
|
||||||
|
isAllowed,
|
||||||
|
isNotice,
|
||||||
|
groupUsers: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
context.config.usersGroups.push(newGroup);
|
||||||
|
context.groupNames.push(groupName);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `Created group ${groupName} (allowed: ${isAllowed}, notice: ${isNotice})`,
|
||||||
|
shouldSaveConfig: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除用户组命令
|
||||||
|
class RemoveGroupCommand implements CLICommand {
|
||||||
|
name = "removegroup";
|
||||||
|
description = "Remove user group";
|
||||||
|
usage = "removegroup <groupName>";
|
||||||
|
|
||||||
|
execute(args: string[], _executor: string, context: CLIContext): CLIResult {
|
||||||
|
if (args.length !== 1) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Usage: ${this.usage}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const [groupName] = args;
|
||||||
|
|
||||||
|
if (groupName === "admin") {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Cannot remove admin group",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupIndex = context.config.usersGroups.findIndex(
|
||||||
|
(group) => group.groupName === groupName,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (groupIndex === -1) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Group ${groupName} not found`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
context.config.usersGroups.splice(groupIndex, 1);
|
||||||
|
const nameIndex = context.groupNames.indexOf(groupName);
|
||||||
|
if (nameIndex > -1) {
|
||||||
|
context.groupNames.splice(nameIndex, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `Removed group ${groupName}`,
|
||||||
|
shouldSaveConfig: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统一编辑命令
|
||||||
|
class EditCommand implements CLICommand {
|
||||||
|
name = "edit";
|
||||||
|
description = "Edit various configurations (only group now)";
|
||||||
|
usage = "edit <target> [args]";
|
||||||
|
|
||||||
|
execute(args: string[], _executor: string, context: CLIContext): CLIResult {
|
||||||
|
if (args.length < 1) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Usage: ${this.usage}\nTargets: group`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const [target, ...rest] = args;
|
||||||
|
|
||||||
|
switch (target) {
|
||||||
|
case "group":
|
||||||
|
return this.editGroup(rest, context);
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Unknown target: ${target}. Available: group`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private editGroup(args: string[], context: CLIContext): CLIResult {
|
||||||
|
if (args.length !== 3) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Usage: edit group <groupName> <property> <value>\nProperties: isAllowed, isNotice`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const [groupName, property, valueStr] = args;
|
||||||
|
|
||||||
|
let groupConfig: UserGroupConfig | undefined;
|
||||||
|
|
||||||
|
if (groupName === "admin") {
|
||||||
|
groupConfig = context.config.adminGroupConfig;
|
||||||
|
} else {
|
||||||
|
groupConfig = context.config.usersGroups.find(
|
||||||
|
(group) => group.groupName === groupName,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!groupConfig) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Group ${groupName} not found`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (property) {
|
||||||
|
case "isAllowed": {
|
||||||
|
const val = parseBoolean(valueStr);
|
||||||
|
if (val != undefined) {
|
||||||
|
groupConfig.isAllowed = val;
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `Set ${groupName}.isAllowed to ${groupConfig.isAllowed}`,
|
||||||
|
shouldSaveConfig: true,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Set ${groupName}.isAllowed failed`,
|
||||||
|
shouldSaveConfig: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case "isNotice": {
|
||||||
|
const val = parseBoolean(valueStr);
|
||||||
|
if (val != undefined) {
|
||||||
|
groupConfig.isNotice = val;
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `Set ${groupName}.isNotice to ${groupConfig.isNotice}`,
|
||||||
|
shouldSaveConfig: true,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Set ${groupName}.isAllowed failed`,
|
||||||
|
shouldSaveConfig: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Unknown property: ${property}. Available: isAllowed, isNotice`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示配置命令
|
||||||
|
class ShowConfigCommand implements CLICommand {
|
||||||
|
name = "showconfig";
|
||||||
|
description = "Show configuration";
|
||||||
|
usage = "showconfig [type]";
|
||||||
|
|
||||||
|
execute(args: string[], _executor: string, context: CLIContext): CLIResult {
|
||||||
|
const type = args[0] || "all";
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "groups": {
|
||||||
|
let groupsMessage = `Admin Group: ${context.config.adminGroupConfig.groupName}\n`;
|
||||||
|
groupsMessage += ` Users: [${context.config.adminGroupConfig.groupUsers.join(", ")}]\n`;
|
||||||
|
groupsMessage += ` Allowed: ${context.config.adminGroupConfig.isAllowed}\n`;
|
||||||
|
groupsMessage += ` notice: ${context.config.adminGroupConfig.isNotice}\n\n`;
|
||||||
|
|
||||||
|
for (const group of context.config.usersGroups) {
|
||||||
|
groupsMessage += `Group: ${group.groupName}\n`;
|
||||||
|
groupsMessage += ` Users: [${(group.groupUsers ?? []).join(", ")}]\n`;
|
||||||
|
groupsMessage += ` Allowed: ${group.isAllowed}\n`;
|
||||||
|
groupsMessage += ` Notice: ${group.isNotice}\n`;
|
||||||
|
if (group.toastConfig !== undefined) {
|
||||||
|
groupsMessage += ` Custom Toast Config:\n`;
|
||||||
|
groupsMessage += ` Title: ${group.toastConfig.title.text}\n`;
|
||||||
|
groupsMessage += ` Message: ${group.toastConfig.msg.text}\n`;
|
||||||
|
if (group.toastConfig.prefix !== undefined) {
|
||||||
|
groupsMessage += ` Prefix: ${group.toastConfig.prefix}\n`;
|
||||||
|
}
|
||||||
|
if (group.toastConfig.brackets !== undefined) {
|
||||||
|
groupsMessage += ` Brackets: ${group.toastConfig.brackets}\n`;
|
||||||
|
}
|
||||||
|
if (group.toastConfig.bracketColor !== undefined) {
|
||||||
|
groupsMessage += ` Bracket Color: ${group.toastConfig.bracketColor}\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
groupsMessage += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: groupsMessage.trim(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "toast": {
|
||||||
|
let toastMessage = "Default Toast Config:\n";
|
||||||
|
toastMessage += ` Title: ${context.config.defaultToastConfig.title.text}\n`;
|
||||||
|
toastMessage += ` Message: ${context.config.defaultToastConfig.msg.text}\n`;
|
||||||
|
toastMessage += ` Prefix: ${context.config.defaultToastConfig.prefix ?? "none"}\n`;
|
||||||
|
toastMessage += ` Brackets: ${context.config.defaultToastConfig.brackets ?? "none"}\n`;
|
||||||
|
toastMessage += ` Bracket Color: ${context.config.defaultToastConfig.bracketColor ?? "none"}\n\n`;
|
||||||
|
|
||||||
|
toastMessage += "Warn Toast Config:\n";
|
||||||
|
toastMessage += ` Title: ${context.config.warnToastConfig.title.text}\n`;
|
||||||
|
toastMessage += ` Message: ${context.config.warnToastConfig.msg.text}\n`;
|
||||||
|
toastMessage += ` Prefix: ${context.config.warnToastConfig.prefix ?? "none"}\n`;
|
||||||
|
toastMessage += ` Brackets: ${context.config.warnToastConfig.brackets ?? "none"}\n`;
|
||||||
|
toastMessage += ` Bracket Color: ${context.config.warnToastConfig.bracketColor ?? "none"}`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: toastMessage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "all": {
|
||||||
|
let allMessage = `Detect Range: ${context.config.detectRange}\n`;
|
||||||
|
allMessage += `Detect Interval: ${context.config.detectInterval}\n`;
|
||||||
|
allMessage += `Warn Interval: ${context.config.warnInterval}\n\n`;
|
||||||
|
allMessage +=
|
||||||
|
"Use 'showconfig groups' or 'showconfig toast' for detailed view";
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: allMessage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Invalid type: ${type}. Available: groups, toast, all`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLI循环处理器
|
||||||
|
export class AccessControlCLI {
|
||||||
|
private processor: CLICommandProcessor;
|
||||||
|
private context: CLIContext;
|
||||||
|
|
||||||
|
constructor(context: CLIContext) {
|
||||||
|
this.context = context;
|
||||||
|
this.processor = new CLICommandProcessor(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public startConfigLoop() {
|
||||||
|
while (true) {
|
||||||
|
const ev = pullEventAs(ChatBoxEvent, "chat");
|
||||||
|
|
||||||
|
if (ev === undefined) continue;
|
||||||
|
if (
|
||||||
|
!this.context.config.adminGroupConfig.groupUsers.includes(ev.username)
|
||||||
|
)
|
||||||
|
continue;
|
||||||
|
if (!ev.message.startsWith("@AC")) continue;
|
||||||
|
|
||||||
|
this.context.log.info(
|
||||||
|
`Received command "${ev.message}" from admin ${ev.username}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = this.processor.processCommand(ev.message, ev.username);
|
||||||
|
this.processor.sendResponse(result, ev.username);
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
this.context.log.warn(`Command failed: ${result.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出类型和工厂函数
|
||||||
|
export { CLIContext, CLIResult, CLICommand };
|
||||||
|
|
||||||
|
export function createAccessControlCLI(
|
||||||
|
config: AccessConfig,
|
||||||
|
configFilepath: string,
|
||||||
|
log: CCLog,
|
||||||
|
chatBox: any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||||
|
groupNames: string[],
|
||||||
|
): AccessControlCLI {
|
||||||
|
const context: CLIContext = {
|
||||||
|
config,
|
||||||
|
configFilepath,
|
||||||
|
log,
|
||||||
|
chatBox, // eslint-disable-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
groupNames,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new AccessControlCLI(context);
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ interface ToastConfig {
|
|||||||
interface UserGroupConfig {
|
interface UserGroupConfig {
|
||||||
groupName: string;
|
groupName: string;
|
||||||
isAllowed: boolean;
|
isAllowed: boolean;
|
||||||
isWarnTarget: boolean;
|
isNotice: boolean;
|
||||||
groupUsers: string[];
|
groupUsers: string[];
|
||||||
toastConfig?: ToastConfig;
|
toastConfig?: ToastConfig;
|
||||||
}
|
}
|
||||||
@@ -37,26 +37,26 @@ const defaultConfig: AccessConfig = {
|
|||||||
groupName: "Admin",
|
groupName: "Admin",
|
||||||
groupUsers: ["Selcon"],
|
groupUsers: ["Selcon"],
|
||||||
isAllowed: true,
|
isAllowed: true,
|
||||||
isWarnTarget: false,
|
isNotice: false,
|
||||||
},
|
},
|
||||||
usersGroups: [
|
usersGroups: [
|
||||||
{
|
{
|
||||||
groupName: "user",
|
groupName: "user",
|
||||||
groupUsers: [],
|
groupUsers: [],
|
||||||
isAllowed: true,
|
isAllowed: true,
|
||||||
isWarnTarget: true,
|
isNotice: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
groupName: "VIP",
|
groupName: "VIP",
|
||||||
groupUsers: [],
|
groupUsers: [],
|
||||||
isAllowed: true,
|
isAllowed: true,
|
||||||
isWarnTarget: false,
|
isNotice: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
groupName: "enemies",
|
groupName: "enemies",
|
||||||
groupUsers: [],
|
groupUsers: [],
|
||||||
isAllowed: false,
|
isAllowed: false,
|
||||||
isWarnTarget: false,
|
isNotice: false,
|
||||||
toastConfig: {
|
toastConfig: {
|
||||||
title: {
|
title: {
|
||||||
text: "Warn",
|
text: "Warn",
|
||||||
|
|||||||
@@ -1,33 +1,22 @@
|
|||||||
import { CCLog, DAY } from "@/lib/ccLog";
|
import { CCLog, DAY } from "@/lib/ccLog";
|
||||||
import {
|
import { ToastConfig, UserGroupConfig, loadConfig, setLog } from "./config";
|
||||||
ToastConfig,
|
import { createAccessControlCLI } from "./cli";
|
||||||
UserGroupConfig,
|
|
||||||
loadConfig,
|
|
||||||
saveConfig,
|
|
||||||
setLog,
|
|
||||||
} from "./config";
|
|
||||||
import * as peripheralManager from "../lib/PeripheralManager";
|
import * as peripheralManager from "../lib/PeripheralManager";
|
||||||
import { ChatBoxEvent, pullEventAs } from "@/lib/event";
|
|
||||||
import { quotestring } from "@sikongjueluo/dkjson-types";
|
|
||||||
|
|
||||||
const DEBUG = false;
|
const DEBUG = false;
|
||||||
const args = [...$vararg];
|
const args = [...$vararg];
|
||||||
|
|
||||||
|
// Init Log
|
||||||
const log = new CCLog("accesscontrol.log", DAY);
|
const log = new CCLog("accesscontrol.log", DAY);
|
||||||
setLog(log);
|
setLog(log);
|
||||||
|
|
||||||
|
// Load Config
|
||||||
const configFilepath = `${shell.dir()}/access.config.json`;
|
const configFilepath = `${shell.dir()}/access.config.json`;
|
||||||
const config = loadConfig(configFilepath);
|
const config = loadConfig(configFilepath);
|
||||||
log.info("Load config successfully!");
|
log.info("Load config successfully!");
|
||||||
log.debug(textutils.serialise(config, { allow_repetitions: true }));
|
if (DEBUG) log.debug(textutils.serialise(config, { allow_repetitions: true }));
|
||||||
const groupNames = config.usersGroups.map((value) => value.groupName);
|
const groupNames = config.usersGroups.map((value) => value.groupName);
|
||||||
const warnTargetPlayers = config.adminGroupConfig.groupUsers.concat(
|
let warnTargetPlayers: string[];
|
||||||
config.usersGroups
|
|
||||||
.filter((value) => value.isWarnTarget)
|
|
||||||
.map((value) => value.groupUsers ?? [])
|
|
||||||
.flat(),
|
|
||||||
);
|
|
||||||
|
|
||||||
const playerDetector = peripheralManager.findByNameRequired("playerDetector");
|
const playerDetector = peripheralManager.findByNameRequired("playerDetector");
|
||||||
const chatBox = peripheralManager.findByNameRequired("chatBox");
|
const chatBox = peripheralManager.findByNameRequired("chatBox");
|
||||||
|
|
||||||
@@ -66,7 +55,7 @@ function sendToast(
|
|||||||
groupConfig?.groupName,
|
groupConfig?.groupName,
|
||||||
),
|
),
|
||||||
player,
|
player,
|
||||||
quotestring(toastConfig.prefix ?? config.defaultToastConfig.prefix!),
|
toastConfig.prefix ?? config.defaultToastConfig.prefix,
|
||||||
toastConfig.brackets ?? config.defaultToastConfig.brackets,
|
toastConfig.brackets ?? config.defaultToastConfig.brackets,
|
||||||
toastConfig.bracketColor ?? config.defaultToastConfig.bracketColor,
|
toastConfig.bracketColor ?? config.defaultToastConfig.bracketColor,
|
||||||
undefined,
|
undefined,
|
||||||
@@ -74,9 +63,15 @@ function sendToast(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendWarn(player: string) {
|
function sendWarnAndNotice(player: string) {
|
||||||
const playerPos = playerDetector.getPlayerPos(player);
|
const playerPos = playerDetector.getPlayerPos(player);
|
||||||
const onlinePlayers = playerDetector.getOnlinePlayers();
|
const onlinePlayers = playerDetector.getOnlinePlayers();
|
||||||
|
warnTargetPlayers = config.adminGroupConfig.groupUsers.concat(
|
||||||
|
config.usersGroups
|
||||||
|
.filter((value) => value.isNotice)
|
||||||
|
.map((value) => value.groupUsers ?? [])
|
||||||
|
.flat(),
|
||||||
|
);
|
||||||
|
|
||||||
const warnMsg = `Not Allowed Player ${player} Break in Home at Position ${playerPos?.x}, ${playerPos?.y}, ${playerPos?.z}`;
|
const warnMsg = `Not Allowed Player ${player} Break in Home at Position ${playerPos?.x}, ${playerPos?.y}, ${playerPos?.z}`;
|
||||||
log.warn(warnMsg);
|
log.warn(warnMsg);
|
||||||
@@ -108,43 +103,11 @@ function sendWarn(player: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendCommandHelp(targetPlayer: string) {
|
|
||||||
chatBox.sendMessageToPlayer(
|
|
||||||
`
|
|
||||||
Command Usage: @AC /<Command> [args]
|
|
||||||
Command:
|
|
||||||
- add <userGroup> <playerName>
|
|
||||||
add player to group
|
|
||||||
userGroup: ${groupNames.join(", ")}
|
|
||||||
- del <userGroup> <playerName>
|
|
||||||
delete player in the group, except Admin
|
|
||||||
userGroup: ${groupNames.join(", ")}
|
|
||||||
- list
|
|
||||||
list all of the player with its group
|
|
||||||
- set <options> [params]
|
|
||||||
config access control settins
|
|
||||||
options:
|
|
||||||
- warnInterval <number>
|
|
||||||
set the interval of warn, which is not allowed
|
|
||||||
- detectInterval <number>
|
|
||||||
set the interval of detecting players
|
|
||||||
- detectRange <number>
|
|
||||||
set the sphere range of detect
|
|
||||||
`,
|
|
||||||
targetPlayer,
|
|
||||||
"AccessControl",
|
|
||||||
"[]",
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function warnLoop() {
|
function warnLoop() {
|
||||||
while (true) {
|
while (true) {
|
||||||
for (const player of notAllowedPlayers) {
|
for (const player of notAllowedPlayers) {
|
||||||
if (inRangePlayers.includes(player)) {
|
if (inRangePlayers.includes(player)) {
|
||||||
sendWarn(player);
|
// sendWarnAndNotice(player);
|
||||||
} else {
|
} else {
|
||||||
notAllowedPlayers = notAllowedPlayers.filter(
|
notAllowedPlayers = notAllowedPlayers.filter(
|
||||||
(value) => value != player,
|
(value) => value != player,
|
||||||
@@ -183,7 +146,7 @@ function mainLoop() {
|
|||||||
if (!userGroupConfig.groupUsers.includes(player)) continue;
|
if (!userGroupConfig.groupUsers.includes(player)) continue;
|
||||||
|
|
||||||
if (!userGroupConfig.isAllowed) {
|
if (!userGroupConfig.isAllowed) {
|
||||||
sendWarn(player);
|
sendWarnAndNotice(player);
|
||||||
notAllowedPlayers.push(player);
|
notAllowedPlayers.push(player);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -199,7 +162,7 @@ function mainLoop() {
|
|||||||
}
|
}
|
||||||
if (inUserGroup) continue;
|
if (inUserGroup) continue;
|
||||||
|
|
||||||
sendWarn(player);
|
sendWarnAndNotice(player);
|
||||||
notAllowedPlayers.push(player);
|
notAllowedPlayers.push(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,132 +171,25 @@ function mainLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function configLoop() {
|
|
||||||
while (true) {
|
|
||||||
const ev = pullEventAs(ChatBoxEvent, "chat");
|
|
||||||
|
|
||||||
if (ev == undefined) continue;
|
|
||||||
if (!config.adminGroupConfig.groupUsers.includes(ev.username)) continue;
|
|
||||||
if (!ev.message.startsWith("@AC")) continue;
|
|
||||||
// log.info(`Received "${ev.message}" from admin ${ev.username}`);
|
|
||||||
|
|
||||||
const params = ev.message.split(" ");
|
|
||||||
if (params.length < 2) {
|
|
||||||
sendCommandHelp(ev.username);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params[1] == "/add" && params.length == 4) {
|
|
||||||
if (params[2] == "admin") {
|
|
||||||
config.adminGroupConfig.groupUsers.push(params[3]);
|
|
||||||
chatBox.sendMessageToPlayer(
|
|
||||||
`Add player ${params[3]} to admin`,
|
|
||||||
ev.username,
|
|
||||||
"AccessControl",
|
|
||||||
);
|
|
||||||
} else if (groupNames.includes(params[2])) {
|
|
||||||
const groupConfig = config.usersGroups.find(
|
|
||||||
(value) => value.groupName == params[2],
|
|
||||||
)!;
|
|
||||||
|
|
||||||
if (groupConfig.groupUsers == undefined)
|
|
||||||
groupConfig.groupUsers = [params[3]];
|
|
||||||
else groupConfig.groupUsers.push(params[3]);
|
|
||||||
|
|
||||||
chatBox.sendMessageToPlayer(
|
|
||||||
`Add player ${params[3]} to ${groupConfig.groupName}`,
|
|
||||||
ev.username,
|
|
||||||
"AccessControl",
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
sendCommandHelp(ev.username);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else if (params[1] == "/del" && params.length == 4) {
|
|
||||||
if (params[2] == "admin") {
|
|
||||||
chatBox.sendMessageToPlayer(
|
|
||||||
`Could't delete admin, please edit config`,
|
|
||||||
ev.username,
|
|
||||||
"AccessControl",
|
|
||||||
);
|
|
||||||
} else if (groupNames.includes(params[2])) {
|
|
||||||
const groupConfig = config.usersGroups.find(
|
|
||||||
(value) => value.groupName == params[2],
|
|
||||||
)!;
|
|
||||||
|
|
||||||
if (groupConfig.groupUsers == undefined) groupConfig.groupUsers = [];
|
|
||||||
else
|
|
||||||
groupConfig.groupUsers = groupConfig.groupUsers.filter(
|
|
||||||
(user) => user != params[3],
|
|
||||||
);
|
|
||||||
|
|
||||||
chatBox.sendMessageToPlayer(
|
|
||||||
`Delete ${groupConfig.groupName} ${params[3]}`,
|
|
||||||
ev.username,
|
|
||||||
"AccessControl",
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
sendCommandHelp(ev.username);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else if (params[1] == "/list") {
|
|
||||||
chatBox.sendMessageToPlayer(
|
|
||||||
`Admins : [ ${config.adminGroupConfig.groupUsers.join(", ")} ]`,
|
|
||||||
ev.username,
|
|
||||||
"AccessControl",
|
|
||||||
);
|
|
||||||
for (const groupConfig of config.usersGroups) {
|
|
||||||
chatBox.sendMessageToPlayer(
|
|
||||||
`${groupConfig.groupName} : [ ${config.adminGroupConfig.groupUsers.join(", ")} ]`,
|
|
||||||
ev.username,
|
|
||||||
"AccessControl",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (params[1] == "/set" && params.length == 4) {
|
|
||||||
if (params[2] == "warnInterval") {
|
|
||||||
config.warnInterval = parseInt(params[3]);
|
|
||||||
chatBox.sendMessageToPlayer(
|
|
||||||
`Set warn interval to ${config.warnInterval}`,
|
|
||||||
ev.username,
|
|
||||||
"AccessControl",
|
|
||||||
);
|
|
||||||
} else if (params[2] == "detectInterval") {
|
|
||||||
config.detectInterval = parseInt(params[3]);
|
|
||||||
chatBox.sendMessageToPlayer(
|
|
||||||
`Set detect interval to ${config.detectInterval}`,
|
|
||||||
ev.username,
|
|
||||||
"AccessControl",
|
|
||||||
);
|
|
||||||
} else if (params[2] == "detectRange") {
|
|
||||||
config.detectRange = parseInt(params[3]);
|
|
||||||
chatBox.sendMessageToPlayer(
|
|
||||||
`Set detect range to ${config.detectRange}`,
|
|
||||||
ev.username,
|
|
||||||
"AccessControl",
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
sendCommandHelp(ev.username);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sendCommandHelp(ev.username);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
saveConfig(config, configFilepath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function main(args: string[]) {
|
function main(args: string[]) {
|
||||||
log.debug("Starting access control system, get args: " + args.join(", "));
|
log.info("Starting access control system, get args: " + args.join(", "));
|
||||||
if (args.length == 1) {
|
if (args.length == 1) {
|
||||||
if (args[0] == "start") {
|
if (args[0] == "start") {
|
||||||
|
// 创建CLI处理器
|
||||||
|
const cli = createAccessControlCLI(
|
||||||
|
config,
|
||||||
|
configFilepath,
|
||||||
|
log,
|
||||||
|
chatBox,
|
||||||
|
groupNames,
|
||||||
|
);
|
||||||
|
|
||||||
parallel.waitForAll(
|
parallel.waitForAll(
|
||||||
() => {
|
() => {
|
||||||
mainLoop();
|
mainLoop();
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
configLoop();
|
void cli.startConfigLoop();
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
warnLoop();
|
warnLoop();
|
||||||
|
|||||||
6
src/lib/common.ts
Normal file
6
src/lib/common.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export function parseBoolean(obj: string): boolean | undefined {
|
||||||
|
const str = obj.toLowerCase();
|
||||||
|
if (str === "true") return true;
|
||||||
|
else if (str === "false") return false;
|
||||||
|
else return undefined;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user