3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-05-10 12:25:02 +00:00

Add presetup-configurator to the repo

This commit is contained in:
Dragory 2021-01-18 22:44:54 +02:00
parent d4d89327fd
commit 8c11349a8b
No known key found for this signature in database
GPG key ID: 5F387BA66DF8AAC1
14 changed files with 829 additions and 0 deletions

View file

@ -0,0 +1,8 @@
.App {
display: flex;
justify-content: center;
}
.App .wrapper {
flex: 0 1 800px;
}

View file

@ -0,0 +1,11 @@
import React from "react";
import { Configurator } from "./Configurator";
import "./App.css";
export function App() {
return <div className="App">
<div className="wrapper">
<Configurator />
</div>
</div>;
}

View file

@ -0,0 +1,48 @@
.Configurator {
}
.Configurator .options {
display: grid;
grid-auto-columns: min-content auto;
grid-gap: 1px;
overflow: hidden;
border: 1px solid #444;
border-radius: 4px;
background-color: #fff;
}
.Configurator .options > h2 {
grid-column: 1;
margin: 0;
padding: 8px 24px 8px 8px;
white-space: nowrap;
text-align: right;
font-size: 16px;
font-weight: 600;
box-shadow: 0 0 0 1px #444;
}
.Configurator .options > .control {
grid-column: 2;
padding: 8px;
box-shadow: 0 0 0 1px #444;
}
.Configurator label {
display: block;
padding: 0 0 8px;
}
.Configurator .result {
background-color: #eee;
padding: 8px;
border: 1px solid #444;
border-radius: 4px;
box-shadow: inset 0 0 4px rgba(0, 0, 0, 0.2);
}

View file

@ -0,0 +1,152 @@
import React, { useEffect, useState } from "react";
import { LevelEntry, Levels } from "./Levels";
import { LogChannel, LogChannels } from "./LogChannels";
import yaml from "js-yaml";
import "./Configurator.css";
export function Configurator() {
const [prefix, setPrefix] = useState('!');
const [levels, setLevels] = useState<LevelEntry[]>([]);
const [withModCommands, setWithModCommands] = useState(false);
const [muteRoleId, setMuteRoleId] = useState("");
const [caseChannelId, setCaseChannelId] = useState("");
const [dmModActionReasons, setDmModActionReasons] = useState(false);
const [withLogs, setWithLogs] = useState(false);
const [logChannels, setLogChannels] = useState<LogChannel[]>([]);
const [result, setResult] = useState({});
useEffect(() => {
const resultObj: any = {
prefix,
levels: levels.reduce((obj, entry) => {
obj[entry[0]] = entry[1];
return obj;
}, {}),
plugins: {
utility: {},
},
};
if (withModCommands) {
resultObj.plugins.cases = {
config: {
case_log_channel: caseChannelId,
},
};
resultObj.plugins.mod_actions = {};
if (muteRoleId) {
resultObj.plugins.mutes = {
config: {
mute_role: muteRoleId,
}
};
if (dmModActionReasons) {
resultObj.plugins.mutes.config.dm_on_mute = true;
}
}
if (dmModActionReasons) {
resultObj.plugins.mod_actions = {
config: {
dm_on_warn: true,
dm_on_kick: true,
dm_on_ban: true,
},
};
}
}
if (withLogs) {
resultObj.plugins.logs = {
config: {
channels: logChannels.reduce((obj, logChannel) => {
if (logChannel.includeExclude === "include") {
obj[logChannel.id] = {
include: Array.from(logChannel.logTypes.values()),
};
} else {
obj[logChannel.id] = {
exclude: Array.from(logChannel.logTypes.values()),
};
}
return obj;
}, {}),
},
};
}
setResult(resultObj);
}, [prefix, levels, withModCommands, muteRoleId, caseChannelId, dmModActionReasons, withLogs, logChannels]);
const [formattedResult, setFormattedResult] = useState("");
useEffect(() => {
setFormattedResult(yaml.dump(result));
}, [result]);
return (
<div className="Configurator">
{/* Options */}
<div className="options">
<h2>Prefix</h2>
<div className="control">
<label>
Bot prefix<br />
<input value={prefix} onChange={e => setPrefix(e.target.value)} />
</label>
</div>
<h2>Levels</h2>
<div className="control">
<Levels levels={levels} setLevels={setLevels} />
</div>
<h2>Mod commands</h2>
<div className="control">
<label>
<input type="checkbox" checked={withModCommands} onChange={e => setWithModCommands(e.target.checked)} />
Start with a basic mod command setup
</label>
{withModCommands && (
<div>
<label>
Mute role ID<br />
<input value={muteRoleId} onChange={e => setMuteRoleId(e.target.value)} />
</label>
<label>
Case channel ID<br />
<input value={caseChannelId} onChange={e => setCaseChannelId(e.target.value)} />
</label>
<label>
<input type="checkbox" checked={dmModActionReasons} onChange={e => setDmModActionReasons(e.target.checked)} />
DM reason with mod actions
</label>
</div>
)}
</div>
<h2>Logs</h2>
<div className="control">
<label>
<input type="checkbox" checked={withLogs} onChange={e => setWithLogs(e.target.checked)} />
Start with a basic logging setup
</label>
{withLogs && (
<LogChannels logChannels={logChannels} setLogChannels={setLogChannels} />
)}
</div>
</div>
{/* Result */}
<pre className="result">{formattedResult}</pre>
</div>
);
}

View file

@ -0,0 +1,46 @@
import React, { useState } from "react";
const LEVEL_ADMIN = 100;
const LEVEL_MODERATOR = 50;
export type LevelEntry = [string, number]; // id, level
export function Levels({ levels, setLevels }) {
function addLevel() {
setLevels(arr => [...arr, ["", LEVEL_MODERATOR]]);
}
function removeLevel(index) {
setLevels(arr => [...arr].splice(index, 1));
}
function updateLevelId(index, id) {
const validId = id.replace(/[^0-9]/g, "");
setLevels(arr => {
arr[index][0] = validId;
return [...arr];
});
}
function updateLevelLevel(index, level) {
setLevels(arr => {
arr[index][1] = parseInt(level, 10);
return [...arr];
});
}
return (
<div>
{levels.map(([id, level], index) => (
<div key={index}>
<input value={id} onChange={e => updateLevelId(index, e.target.value)} />
<select value={level} onChange={e => updateLevelLevel(index, e.target.value)}>
<option value={LEVEL_ADMIN}>Admin</option>
<option value={LEVEL_MODERATOR}>Moderator</option>
</select>
</div>
))}
<button onClick={addLevel}>Add</button>
</div>
);
}

View file

@ -0,0 +1,12 @@
.LogChannels .log-channel {
margin: 16px 0;
}
.LogChannels .log-types {
height: 200px;
overflow-y: scroll;
border: 1px solid #aaa;
border-radius: 3px;
padding: 4px;
margin-top: 8px;
}

View file

@ -0,0 +1,152 @@
import React, { SetStateAction, useState } from "react";
import "./LogChannels.css";
const LOG_TYPES = {
"MEMBER_WARN": "Warned",
"MEMBER_MUTE": "Muted",
"MEMBER_UNMUTE": "Unmuted",
"MEMBER_MUTE_EXPIRED": "Mute expired",
"MEMBER_KICK": "Kicked",
"MEMBER_BAN": "Banned",
"MEMBER_UNBAN": "Unbanned",
"MEMBER_FORCEBAN": "Forcebanned",
"MEMBER_SOFTBAN": "Softbanned",
"MEMBER_JOIN": "Member joined",
"MEMBER_LEAVE": "Member left",
"MEMBER_ROLE_ADD": "Role added to member",
"MEMBER_ROLE_REMOVE": "Role removed from member",
"MEMBER_NICK_CHANGE": "Nickname changed",
"MEMBER_USERNAME_CHANGE": "Username changed",
"MEMBER_RESTORE": "Member roles restored",
"CHANNEL_CREATE": "Channel created",
"CHANNEL_DELETE": "Channel deleted",
"ROLE_CREATE": "Role created",
"ROLE_DELETE": "Role deleted",
"MESSAGE_EDIT": "Message edited",
"MESSAGE_DELETE": "Message deleted",
"MESSAGE_DELETE_BULK": "Messages deleted in bulk",
"MESSAGE_DELETE_BARE": "Message deleted (bare)",
"VOICE_CHANNEL_JOIN": "Voice channel join",
"VOICE_CHANNEL_LEAVE": "Voice channel leave",
"VOICE_CHANNEL_MOVE": "Voice channel move",
"COMMAND": "Command used",
"MESSAGE_SPAM_DETECTED": "Message spam detected",
"CENSOR": "Message censored",
"CLEAN": "Messages cleaned",
"CASE_CREATE": "Case created",
"MASSBAN": "Massbanned",
"MASSMUTE": "Massmuted",
"MEMBER_TIMED_MUTE": "Temporarily muted",
"MEMBER_TIMED_UNMUTE": "Scheduled unmute",
"MEMBER_JOIN_WITH_PRIOR_RECORDS": "Member joined with prior records",
"OTHER_SPAM_DETECTED": "Non-message spam detected",
"MEMBER_ROLE_CHANGES": "Member roles changed",
"VOICE_CHANNEL_FORCE_MOVE": "Force-moved to a voice channel",
"CASE_UPDATE": "Case updated",
"MEMBER_MUTE_REJOIN": "Muted member rejoined",
"SCHEDULED_MESSAGE": "Scheduled message to be posted",
"POSTED_SCHEDULED_MESSAGE": "Posted scheduled message",
"BOT_ALERT": "Bot alert",
"AUTOMOD_ACTION": "Automod action",
"SCHEDULED_REPEATED_MESSAGE": "Scheduled message to be posted repeatedly",
"REPEATED_MESSAGE": "Set a message to be posted repeatedly",
"MESSAGE_DELETE_AUTO": "Message deleted (auto)",
"SET_ANTIRAID_USER": "Set antiraid (user)",
"SET_ANTIRAID_AUTO": "Set antiraid (auto)",
"MASS_ASSIGN_ROLES": "Mass-assigned roles",
"MASS_UNASSIGN_ROLES": "Mass-unassigned roles",
"MEMBER_NOTE": "Added note on member",
"CASE_DELETE": "Case deleted",
"DM_FAILED": "Failed to DM member",
};
type LOG_TYPE = keyof typeof LOG_TYPES;
export interface LogChannel {
id: string;
includeExclude: "include" | "exclude";
logTypes: Set<LOG_TYPE>;
}
interface Props {
logChannels: LogChannel[];
setLogChannels: React.Dispatch<SetStateAction<LogChannel[]>>;
}
export function LogChannels({ logChannels, setLogChannels }: Props) {
function addLogChannel() {
setLogChannels(_logChannels => {
return [..._logChannels, {
id: "",
includeExclude: "include",
logTypes: new Set(),
}];
});
}
function deleteLogChannel(index) {
setLogChannels(_logChannels => {
const newArr = [..._logChannels];
newArr.splice(index, 1);
return newArr;
});
}
function setId(index: number, id: string) {
setLogChannels(_logChannels => {
_logChannels[index].id = id;
return [..._logChannels];
});
}
function setIncludeExclude(index: number, includeExclude: LogChannel["includeExclude"]) {
setLogChannels(_logChannels => {
_logChannels[index].includeExclude = includeExclude;
return [..._logChannels];
});
}
function toggleLogType(index: number, logType: LOG_TYPE, enabled: boolean) {
setLogChannels(_logChannels => {
if (enabled) {
_logChannels[index].logTypes.add(logType);
} else {
_logChannels[index].logTypes.delete(logType);
}
return [..._logChannels];
});
}
return (
<div className="LogChannels">
{logChannels.map((logChannel, index) => (
<div className="log-channel">
<label>
ID: <input value={logChannel.id} onChange={e => setId(index, e.target.value)} />
</label>
<label>
Mode:
<select value={logChannel.includeExclude}>
<option value={"include"}>Include</option>
<option value={"exclude"}>Exclude</option>
</select>
</label>
<div className="log-types">
{Object.entries(LOG_TYPES).map(([logType, description]) => (
<label>
<input
type="checkbox"
checked={logChannel.logTypes.has(logType as LOG_TYPE)}
onChange={e => toggleLogType(index, logType as LOG_TYPE, e.target.checked)}
/>
{description}
</label>
))}
</div>
</div>
))}
<button onClick={addLogChannel}>Add</button>
</div>
);
}

View file

@ -0,0 +1,14 @@
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
font-family: Arial, sans-serif;
}
body {
background-color: #f8f8f8;
color: #222;
}

View file

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="./index.js"></script>
</body>
</html>

View file

@ -0,0 +1,15 @@
import React from "react";
import ReactDOM from "react-dom";
import { App } from "./App";
import "./index.css";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
if ((import.meta as any).hot) {
(import.meta as any).hot.accept();
}