import axios from 'axios';
import React, { FunctionComponent, useCallback, useEffect, useState } from "react";
import { Button } from '@revoltchat/ui/lib/components/atoms/inputs/Button';
import { InputBox } from '@revoltchat/ui/lib/components/atoms/inputs/InputBox';
import { Checkbox } from '@revoltchat/ui/lib/components/atoms/inputs/Checkbox';
import { ComboBox } from '@revoltchat/ui/lib/components/atoms/inputs/ComboBox';
import { LineDivider } from '@revoltchat/ui/lib/components/atoms/layout/LineDivider';
import { H1 } from '@revoltchat/ui/lib/components/atoms/heading/H1';
import { H3 } from '@revoltchat/ui/lib/components/atoms/heading/H3';
import { H4 } from '@revoltchat/ui/lib/components/atoms/heading/H4';
import { Icon } from '@mdi/react';
import { mdiCloseBox } from '@mdi/js';
import { API_URL } from "../App";
import { getAuthHeaders } from "../utils";
import { useParams } from "react-router-dom";
import defaultChannelIcon from '../assets/channel-default-icon.svg';
type User = { id: string, username?: string, avatarURL?: string }
type Channel = { id: string, name: string, icon?: string, type: 'VOICE'|'TEXT', nsfw: boolean }
type Server = {
id?: string,
perms?: 0|1|2|3,
name?: string,
description?: string,
iconURL?: string,
bannerURL?: string,
serverConfig?: { [key: string]: any },
users: User[],
channels: Channel[],
}
type AntispamRule = {
id: string;
max_msg: number;
timeframe: number;
action: 0|1|2|3|4;
channels: string[] | null;
message: string | null;
}
const ServerDashboard: FunctionComponent = () => {
const [serverInfo, setServerInfo] = useState({} as Server);
const [status, setStatus] = useState('');
const [changed, setChanged] = useState({} as { prefix?: boolean, prefixAllowSpace?: boolean });
const [prefix, setPrefix] = useState('' as string|undefined);
const [prefixAllowSpace, setPrefixAllowSpace] = useState(false);
const [botManagers, setBotManagers] = useState([] as string[]);
const [moderators, setModerators] = useState([] as string[]);
const [automodSettings, setAutomodSettings] = useState(null as { antispam: AntispamRule[] }|null);
const { serverid } = useParams();
const saveConfig = useCallback(async () => {
if (Object.values(changed).filter(i => i).length == 0) return;
const payload = {
...(changed.prefix ? { prefix } : undefined),
...(changed.prefixAllowSpace ? { spaceAfterPrefix: prefixAllowSpace } : undefined),
}
const res = await axios.put(
API_URL + `/dash/server/${serverid}/config`,
payload,
{ headers: await getAuthHeaders() }
);
if (res.data.success) {
setChanged({});
}
}, [ prefix, prefixAllowSpace, changed ]);
const loadInfo = useCallback(async () => {
try {
const res = await axios.get(`${API_URL}/dash/server/${serverid}`, { headers: await getAuthHeaders() });
console.log(res.data);
const server: Server = res.data.server;
setServerInfo(server);
setPrefix(server.serverConfig?.prefix || '');
setPrefixAllowSpace(!!server.serverConfig?.spaceAfterPrefix);
setBotManagers(server.serverConfig?.botManagers ?? []);
setModerators(server.serverConfig?.moderators ?? []);
loadAutomodInfo(server);
} catch(e: any) {
console.error(e);
setStatus(`${e?.message ?? e}`);
}
}, [serverInfo]);
const loadAutomodInfo = useCallback(async (server: Server) => {
if ((server.perms ?? 0) > 0) {
const res = await axios.get(API_URL + `/dash/server/${serverid}/automod`, { headers: await getAuthHeaders() });
setAutomodSettings(res.data);
console.log(res.data);
}
}, []);
useEffect(() => {
loadInfo();
}, []);
return (
<>
{serverInfo?.name ?? 'Loading...'}
{status.length ? {status} : }
{serverInfo.description ?? No server description set }
<>
Prefix
{
setPrefix(e.currentTarget.value);
setChanged({ ...changed, prefix: true });
}}
/>
{
setPrefixAllowSpace(!prefixAllowSpace);
setChanged({ ...changed, prefixAllowSpace: true });
}}
title="Allow space after prefix"
description={'Whether the bot recognizes a command if the prefix is followed by a space. Enable if your prefix is a word.'}
/>
Save
>
<>
Bot Managers
Only users with "Manage Server" permission are allowed to add/remove other
bot managers and are automatically considered bot manager.
{botManagers.map((uid: string) => {
const user = serverInfo.users.find(u => u.id == uid) || { id: uid }
return (
)})}
Moderators
Only bot managers are allowed to add/remove moderators.
All bot managers are also moderators.
{moderators.map((uid: string) => {
const user = serverInfo.users.find(u => u.id == uid) || { id: uid }
return (
)})}
>
<>
Antispam Rules
{serverInfo.perms != null && automodSettings && (
serverInfo.perms > 0
? (
<>
{automodSettings.antispam.map(r => )}
{
const newRule: AntispamRule = {
action: 0,
max_msg: 5,
timeframe: 3,
message: null,
id: '',
channels: [],
}
const res = await axios.post(
`${API_URL}/dash/server/${serverid}/automod`,
{
action: newRule.action,
max_msg: newRule.max_msg,
timeframe: newRule.timeframe,
},
{ headers: await getAuthHeaders() }
);
newRule.id = res.data.id;
setAutomodSettings({ antispam: [ ...(automodSettings.antispam), newRule ] });
}}>
Create Rule
>
)
: (
You do not have access to this.
)
)
}
>
>
);
function RemoveButton(props: { onClick: () => void }) {
return (
)
}
function UserListEntry(props: { user: User, type: 'MANAGER'|'MOD' }) {
return (
{props.user.username ?? 'Unknown'}
{
const res = await axios.delete(
`${API_URL}/dash/server/${serverid}/${props.type == 'MANAGER' ? 'managers' : 'mods'}/${props.user.id}`,
{ headers: await getAuthHeaders() }
);
if (props.type == 'MANAGER') {
setBotManagers(res.data.managers);
}
else if (props.type == 'MOD') {
setModerators(res.data.mods);
}
}}
/>
);
}
function UserListContainer(props: { disabled: boolean, children: any }) {
return (
{props.children}
);
}
function UserListTypeContainer(props: any) {
return (
{props.children}
);
}
function UserListAddField(props: { type: 'MANAGER'|'MOD' }) {
const [content, setContent] = useState('');
const onConfirm = useCallback(async () => {0
if (content.length) {
const res = await axios.put(
`${API_URL}/dash/server/${serverid}/${props.type == 'MANAGER' ? 'managers' : 'mods'}`,
{ item: content },
{ headers: await getAuthHeaders() }
);
if (res.data.users?.length) {
res.data.users.forEach((user: User) => {
if (!serverInfo.users.find(u => u.id == user.id)) serverInfo.users.push(user);
});
}
if (props.type == 'MANAGER') {
setBotManagers(res.data.managers);
}
else if (props.type == 'MOD') {
setModerators(res.data.mods);
}
}
}, [content]);
return (
setContent(e.currentTarget.value)}
style={{
float: 'left',
width: '180px',
height: '38px',
margin: '4px 8px',
}}
onKeyDown={e => e.key == 'Enter' && onConfirm()}
/>
0 ? '1' : '0',
}}
onClick={onConfirm}
>Ok
);
}
function ChannelListAddField(props: { onInput: (channel: Channel) => void }) {
const [content, setContent] = useState('');
const onConfirm = useCallback(async () => {
if (content.length) {
const channel = serverInfo.channels
.find(c => c.id == content.toUpperCase())
|| serverInfo.channels
.find(c => c.name == content)
|| serverInfo.channels // Prefer channel with same capitalization,
.find(c => c.name.toLowerCase() == content.toLowerCase()); // otherwise search case insensitive
if (channel && channel.type == 'TEXT') {
props.onInput(channel);
setContent('');
}
}
}, [content]);
return (
setContent(e.currentTarget.value)}
style={{
float: 'left',
width: '180px',
height: '38px',
margin: '4px 8px',
}}
onKeyDown={e => e.key == 'Enter' && onConfirm()}
/>
0 ? '1' : '0',
}}
onClick={onConfirm}
>Ok
);
}
function AntispamRule(props: { rule: AntispamRule }) {
const [maxMsg, setMaxMsg] = useState(props.rule.max_msg);
const [timeframe, setTimeframe] = useState(props.rule.timeframe);
const [action, setAction] = useState(props.rule.action);
const [message, setMessage] = useState(props.rule.message || '');
const [channels, setChannels] = useState(props.rule.channels ?? []);
const [channelsChanged, setChannelsChanged] = useState(false);
const save = useCallback(async () => {
await axios.patch(
`${API_URL}/dash/server/${serverid}/automod/${props.rule.id}`,
{
action: action != props.rule.action ? action : undefined,
channels: channelsChanged ? channels : undefined,
max_msg: maxMsg != props.rule.max_msg ? maxMsg : undefined,
message: message != props.rule.message ? message : undefined,
timeframe: timeframe != props.rule.timeframe ? timeframe : undefined,
} as AntispamRule,
{ headers: await getAuthHeaders() }
);
await loadAutomodInfo(serverInfo);
}, [maxMsg, timeframe, action, message, channels, channelsChanged]);
const reset = useCallback(() => {
setMaxMsg(props.rule.max_msg);
setTimeframe(props.rule.timeframe);
setAction(props.rule.action);
setMessage(props.rule.message || '');
setChannels(props.rule.channels ?? []);
setChannelsChanged(false);
}, []);
const remove = useCallback(async () => {
if (confirm(`Do you want to irreversably delete rule ${props.rule.id}?`)) {
await axios.delete(`${API_URL}/dash/server/${serverid}/automod/${props.rule.id}`, { headers: await getAuthHeaders() });
setAutomodSettings({ antispam: automodSettings!.antispam.filter(r => r.id != props.rule.id) });
}
}, []);
const inputStyle: React.CSSProperties = {
maxWidth: '100px',
margin: '8px 8px 0px 8px',
}
const messagePlaceholders = {
0: '',
1: 'Message content...',
2: '(Optional) Warn reason...',
3: '',
4: '',
}
return (
If user sends more than
{
const val = e.currentTarget.value;
if (!isNaN(Number(val)) && val.length <= 4 && Number(val) >= 0) setMaxMsg(Number(val));
}} />
messages in
{
const val = e.currentTarget.value;
if (!isNaN(Number(val)) && val.length <= 4 && Number(val) >= 0) setTimeframe(Number(val));
}} />
seconds,
setAction(ev.currentTarget.value as any)}
>
Delete message
Send a message
Warn user
Kick user
Ban user
= 3 || action == 0 ? 'none' : 'unset' }}
value={message}
placeholder={messagePlaceholders[action] || ''}
onChange={ev => setMessage(ev.currentTarget.value)}
/>
= 3 ? 'unset' : 'none'}}>
"Kick" and "Ban" actions are currently placeholders, they do not have any functionality yet.
You can specify channels here that this rule will run in.
If left empty, it will run in all channels.
{
channels.map(cid => {
const channel: Channel = serverInfo.channels.find(c => c.id == cid && c.type == 'TEXT')
|| { id: cid, name: 'Unknown channel', nsfw: false, type: 'TEXT' };
return (
{channel.name}
{
setChannels(channels.filter(c => c != cid));
setChannelsChanged(true);
}} />
)
})
}
{
if (!channels.includes(channel.id)) {
setChannels([ ...channels, channel.id ]);
setChannelsChanged(true);
}
}} />
)
}
}
export default ServerDashboard;