mirror of
https://github.com/janderedev/automod.git
synced 2024-12-22 18:55:28 +00:00
add antispam settings to dashboard, various fixes
also switched to revolt.js fork
This commit is contained in:
parent
dd145db89d
commit
860b816136
|
@ -18,7 +18,7 @@ app.use(Express.json());
|
||||||
export { logger, app, db, PORT, SESSION_LIFETIME }
|
export { logger, app, db, PORT, SESSION_LIFETIME }
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
await Promise.all([
|
const promises = [
|
||||||
import('./middlewares/log'),
|
import('./middlewares/log'),
|
||||||
import('./middlewares/updateTokenExpiry'),
|
import('./middlewares/updateTokenExpiry'),
|
||||||
import('./middlewares/cors'),
|
import('./middlewares/cors'),
|
||||||
|
@ -27,7 +27,12 @@ export { logger, app, db, PORT, SESSION_LIFETIME }
|
||||||
import('./routes/login'),
|
import('./routes/login'),
|
||||||
import('./routes/dash/servers'),
|
import('./routes/dash/servers'),
|
||||||
import('./routes/dash/server'),
|
import('./routes/dash/server'),
|
||||||
]);
|
import('./routes/dash/server-automod'),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const p of promises) await p;
|
||||||
|
|
||||||
|
|
||||||
logger.done('All routes and middlewares loaded');
|
logger.done('All routes and middlewares loaded');
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { app, logger } from "..";
|
import { app } from "..";
|
||||||
|
|
||||||
app.use('*', (req: Request, res: Response, next: () => void) => {
|
app.use('*', (req: Request, res: Response, next: () => void) => {
|
||||||
res.header('Access-Control-Allow-Origin', '*');
|
res.header('Access-Control-Allow-Origin', '*');
|
||||||
|
|
92
api/src/routes/dash/server-automod.ts
Normal file
92
api/src/routes/dash/server-automod.ts
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
import { app, db } from '../..';
|
||||||
|
import { Request, Response } from 'express';
|
||||||
|
import { badRequest, isAuthenticated, unauthorized } from '../../utils';
|
||||||
|
import { botReq } from '../internal/ws';
|
||||||
|
import { FindOneResult } from 'monk';
|
||||||
|
|
||||||
|
type AntispamRule = {
|
||||||
|
id: string;
|
||||||
|
max_msg: number;
|
||||||
|
timeframe: number;
|
||||||
|
action: 0|1|2|3|4;
|
||||||
|
channels: string[] | null;
|
||||||
|
message: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get('/dash/server/:server/automod', async (req: Request, res: Response) => {
|
||||||
|
const user = await isAuthenticated(req, res, true);
|
||||||
|
if (!user) return;
|
||||||
|
|
||||||
|
const { server } = req.params;
|
||||||
|
if (!server || typeof server != 'string') return badRequest(res);
|
||||||
|
|
||||||
|
const response = await botReq('getUserServerDetails', { user, server });
|
||||||
|
if (!response.success) {
|
||||||
|
return res.status(response.statusCode ?? 500).send({ error: response.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.server) return res.status(404).send({ error: 'Server not found' });
|
||||||
|
|
||||||
|
const permissionLevel: 0|1|2|3 = response.perms;
|
||||||
|
if (permissionLevel < 1) return unauthorized(res, `Only moderators and bot managers may view this.`);
|
||||||
|
|
||||||
|
const serverConfig: FindOneResult<any> = await db.get('servers').findOne({ id: server });
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
antispam: (serverConfig.automodSettings?.spam as AntispamRule[]|undefined)
|
||||||
|
?.map(r => ({ // Removing unwanted fields from response
|
||||||
|
action: r.action,
|
||||||
|
channels: r.channels,
|
||||||
|
id: r.id,
|
||||||
|
max_msg: r.max_msg,
|
||||||
|
message: r.message,
|
||||||
|
timeframe: r.timeframe,
|
||||||
|
} as AntispamRule))
|
||||||
|
?? []
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.patch('/dash/server/:server/automod/:ruleid', async (req: Request, res: Response) => {
|
||||||
|
const user = await isAuthenticated(req, res, true);
|
||||||
|
if (!user) return;
|
||||||
|
|
||||||
|
const { server, ruleid } = req.params;
|
||||||
|
const body = req.body;
|
||||||
|
if (!server || !ruleid) return badRequest(res);
|
||||||
|
|
||||||
|
const response = await botReq('getUserServerDetails', { user, server });
|
||||||
|
if (!response.success) {
|
||||||
|
return res.status(response.statusCode ?? 500).send({ error: response.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.server) return res.status(404).send({ error: 'Server not found' });
|
||||||
|
|
||||||
|
const permissionLevel: 0|1|2|3 = response.perms;
|
||||||
|
if (permissionLevel < 2) return unauthorized(res, `Only bot managers can manage moderation rules.`);
|
||||||
|
|
||||||
|
const serverConfig: FindOneResult<any> = await db.get('servers').findOne({ id: server });
|
||||||
|
const antiSpamRules: AntispamRule[] = serverConfig.automodSettings?.spam ?? [];
|
||||||
|
|
||||||
|
const rule = antiSpamRules.find(r => r.id == ruleid);
|
||||||
|
if (!rule) return res.status(404).send({ error: 'No rule with this ID could be found.' });
|
||||||
|
|
||||||
|
await db.get('servers').update({
|
||||||
|
id: server
|
||||||
|
}, {
|
||||||
|
$set: {
|
||||||
|
"automodSettings.spam.$[rulefilter]": {
|
||||||
|
...rule,
|
||||||
|
action: body.action ?? rule.action,
|
||||||
|
channels: body.channels ?? rule.channels,
|
||||||
|
message: body.message ?? rule.message,
|
||||||
|
max_msg: body.max_msg ?? rule.max_msg,
|
||||||
|
timeframe: body.timeframe ?? rule.timeframe,
|
||||||
|
|
||||||
|
} as AntispamRule
|
||||||
|
}
|
||||||
|
}, { arrayFilters: [ { "rulefilter.id": ruleid } ] });
|
||||||
|
|
||||||
|
return res.send({ success: true });
|
||||||
|
});
|
|
@ -4,6 +4,7 @@ import { badRequest, getPermissionLevel, isAuthenticated, unauthorized } from '.
|
||||||
import { botReq } from '../internal/ws';
|
import { botReq } from '../internal/ws';
|
||||||
|
|
||||||
type User = { id: string, username?: string, avatarURL?: string }
|
type User = { id: string, username?: string, avatarURL?: string }
|
||||||
|
type Channel = { id: string, name: string, icon?: string, type: 'VOICE'|'TEXT', nsfw: boolean }
|
||||||
|
|
||||||
type ServerDetails = {
|
type ServerDetails = {
|
||||||
id: string,
|
id: string,
|
||||||
|
@ -14,11 +15,12 @@ type ServerDetails = {
|
||||||
bannerURL?: string,
|
bannerURL?: string,
|
||||||
serverConfig: any,
|
serverConfig: any,
|
||||||
users: User[],
|
users: User[],
|
||||||
|
channels: Channel[],
|
||||||
}
|
}
|
||||||
|
|
||||||
app.get('/dash/server/:server', async (req: Request, res: Response) => {
|
app.get('/dash/server/:server', async (req: Request, res: Response) => {
|
||||||
const user = await isAuthenticated(req, res, true);
|
const user = await isAuthenticated(req, res, true);
|
||||||
if (!user) return unauthorized(res);
|
if (!user) return;
|
||||||
|
|
||||||
const { server } = req.params;
|
const { server } = req.params;
|
||||||
if (!server || typeof server != 'string') return badRequest(res);
|
if (!server || typeof server != 'string') return badRequest(res);
|
||||||
|
|
|
@ -37,12 +37,12 @@ async function getSessionInfo(user: string, token: string): Promise<SessionInfo>
|
||||||
return { exists: !!session, valid: !!(session && !session.invalid && session.expires > Date.now()), nonce: session?.nonce }
|
return { exists: !!session, valid: !!(session && !session.invalid && session.expires > Date.now()), nonce: session?.nonce }
|
||||||
}
|
}
|
||||||
|
|
||||||
function badRequest(res: Response) {
|
function badRequest(res: Response, infoText?: string) {
|
||||||
res.status(400).send(JSON.stringify({ "error": "Invalid request body" }, null, 4));
|
res.status(400).send(JSON.stringify({ "error": "Invalid request body", "info": infoText || undefined }, null, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
function unauthorized(res: Response) {
|
function unauthorized(res: Response, infoText?: string) {
|
||||||
res.status(401).send(JSON.stringify({ "error": "Unauthorized" }, null, 4));
|
res.status(401).send(JSON.stringify({ "error": "Unauthorized", "info": infoText || undefined }, null, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPermissionLevel(user: string, server: string) {
|
async function getPermissionLevel(user: string, server: string) {
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@janderedev/revolt.js": "^5.2.8-patch.1",
|
||||||
"@types/monk": "^6.0.0",
|
"@types/monk": "^6.0.0",
|
||||||
"axios": "^0.22.0",
|
"axios": "^0.22.0",
|
||||||
"dayjs": "^1.10.7",
|
"dayjs": "^1.10.7",
|
||||||
|
@ -21,7 +22,6 @@
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"log75": "^2.2.0",
|
"log75": "^2.2.0",
|
||||||
"monk": "^7.3.4",
|
"monk": "^7.3.4",
|
||||||
"revolt.js": "^5.2.7",
|
|
||||||
"ulid": "^2.3.0",
|
"ulid": "^2.3.0",
|
||||||
"xlsx": "^0.17.3"
|
"xlsx": "^0.17.3"
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Command from "../../struct/Command";
|
||||||
import { hasPerm, parseUser } from "../util";
|
import { hasPerm, parseUser } from "../util";
|
||||||
import ServerConfig from "../../struct/ServerConfig";
|
import ServerConfig from "../../struct/ServerConfig";
|
||||||
import { client } from "../..";
|
import { client } from "../..";
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
import { User } from "@janderedev/revolt.js/dist/maps/Users";
|
||||||
import MessageCommandContext from "../../struct/MessageCommandContext";
|
import MessageCommandContext from "../../struct/MessageCommandContext";
|
||||||
|
|
||||||
const SYNTAX = '/admin add @user; /admin remove @user; /admin list';
|
const SYNTAX = '/admin add @user; /admin remove @user; /admin list';
|
||||||
|
|
|
@ -6,7 +6,7 @@ import child_process from 'child_process';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { wordlist } from "../modules/user_scan";
|
import { wordlist } from "../modules/user_scan";
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
import { User } from "@janderedev/revolt.js/dist/maps/Users";
|
||||||
import { adminBotLog } from "../logging";
|
import { adminBotLog } from "../logging";
|
||||||
|
|
||||||
// id: expireDate
|
// id: expireDate
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Command from "../../struct/Command";
|
import Command from "../../struct/Command";
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import { inspect } from 'util';
|
import { inspect } from 'util';
|
||||||
import { client } from "../..";
|
import { client } from "../..";
|
||||||
import MessageCommandContext from "../../struct/MessageCommandContext";
|
import MessageCommandContext from "../../struct/MessageCommandContext";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Command from "../../struct/Command";
|
import Command from "../../struct/Command";
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import { commands, DEFAULT_PREFIX, ownerIDs } from "../modules/command_handler";
|
import { commands, DEFAULT_PREFIX, ownerIDs } from "../modules/command_handler";
|
||||||
import CommandCategory from "../../struct/CommandCategory";
|
import CommandCategory from "../../struct/CommandCategory";
|
||||||
import MessageCommandContext from "../../struct/MessageCommandContext";
|
import MessageCommandContext from "../../struct/MessageCommandContext";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Member } from "revolt.js/dist/maps/Members";
|
import { Member } from "@janderedev/revolt.js/dist/maps/Members";
|
||||||
import { ulid } from "ulid";
|
import { ulid } from "ulid";
|
||||||
import { client } from "../..";
|
import { client } from "../..";
|
||||||
import Infraction from "../../struct/antispam/Infraction";
|
import Infraction from "../../struct/antispam/Infraction";
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import Command from "../../struct/Command";
|
import Command from "../../struct/Command";
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import { isBotManager, NO_MANAGER_MSG, parseUser } from "../util";
|
import { isBotManager, NO_MANAGER_MSG, parseUser } from "../util";
|
||||||
import ServerConfig from "../../struct/ServerConfig";
|
import ServerConfig from "../../struct/ServerConfig";
|
||||||
import { client } from "../..";
|
import { client } from "../..";
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
import { User } from "@janderedev/revolt.js/dist/maps/Users";
|
||||||
import MessageCommandContext from "../../struct/MessageCommandContext";
|
import MessageCommandContext from "../../struct/MessageCommandContext";
|
||||||
|
|
||||||
const SYNTAX = '/mod add @user; /mod remove @user; /mod list';
|
const SYNTAX = '/mod add @user; /mod remove @user; /mod list';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Command from "../../struct/Command";
|
import Command from "../../struct/Command";
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import { client } from "../..";
|
import { client } from "../..";
|
||||||
import MessageCommandContext from "../../struct/MessageCommandContext";
|
import MessageCommandContext from "../../struct/MessageCommandContext";
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Command from "../../struct/Command";
|
import Command from "../../struct/Command";
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import { client } from "../..";
|
import { client } from "../..";
|
||||||
import ServerConfig from "../../struct/ServerConfig";
|
import ServerConfig from "../../struct/ServerConfig";
|
||||||
import { DEFAULT_PREFIX } from "../modules/command_handler";
|
import { DEFAULT_PREFIX } from "../modules/command_handler";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Command from "../../struct/Command";
|
import Command from "../../struct/Command";
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import { decodeTime } from 'ulid';
|
import { decodeTime } from 'ulid';
|
||||||
import { isModerator, parseUser } from "../util";
|
import { isModerator, parseUser } from "../util";
|
||||||
import MessageCommandContext from "../../struct/MessageCommandContext";
|
import MessageCommandContext from "../../struct/MessageCommandContext";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Command from "../../struct/Command";
|
import Command from "../../struct/Command";
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import { client } from "../..";
|
import { client } from "../..";
|
||||||
import AutomodSettings from "../../struct/antispam/AutomodSettings";
|
import AutomodSettings from "../../struct/antispam/AutomodSettings";
|
||||||
import AntispamRule from "../../struct/antispam/AntispamRule";
|
import AntispamRule from "../../struct/antispam/AntispamRule";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Command from "../../struct/Command";
|
import Command from "../../struct/Command";
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import { exec } from 'child_process';
|
import { exec } from 'child_process';
|
||||||
import MessageCommandContext from "../../struct/MessageCommandContext";
|
import MessageCommandContext from "../../struct/MessageCommandContext";
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Command from "../../struct/Command";
|
import Command from "../../struct/Command";
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import MessageCommandContext from "../../struct/MessageCommandContext";
|
import MessageCommandContext from "../../struct/MessageCommandContext";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -8,6 +8,6 @@ export default {
|
||||||
description: 'Test command',
|
description: 'Test command',
|
||||||
category: 'misc',
|
category: 'misc',
|
||||||
run: (message: MessageCommandContext, args: string[]) => {
|
run: (message: MessageCommandContext, args: string[]) => {
|
||||||
message.reply('Beep boop.');
|
setTimeout(() => message.reply('Beep boop.'), 1000);
|
||||||
}
|
}
|
||||||
} as Command;
|
} as Command;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
import { User } from "@janderedev/revolt.js/dist/maps/Users";
|
||||||
import { client } from "../..";
|
import { client } from "../..";
|
||||||
import Command from "../../struct/Command";
|
import Command from "../../struct/Command";
|
||||||
import MessageCommandContext from "../../struct/MessageCommandContext";
|
import MessageCommandContext from "../../struct/MessageCommandContext";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import { ulid } from "ulid";
|
import { ulid } from "ulid";
|
||||||
import { client } from "../..";
|
import { client } from "../..";
|
||||||
import AntispamRule from "../../struct/antispam/AntispamRule";
|
import AntispamRule from "../../struct/antispam/AntispamRule";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Member } from "revolt.js/dist/maps/Members";
|
import { Member } from "@janderedev/revolt.js/dist/maps/Members";
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
import { User } from "@janderedev/revolt.js/dist/maps/Users";
|
||||||
import { client } from "../../..";
|
import { client } from "../../..";
|
||||||
import ServerConfig from "../../../struct/ServerConfig";
|
import ServerConfig from "../../../struct/ServerConfig";
|
||||||
import { getPermissionLevel } from "../../util";
|
import { getPermissionLevel } from "../../util";
|
||||||
|
@ -7,6 +7,7 @@ import { wsEvents, WSResponse } from "../api_communication";
|
||||||
|
|
||||||
type ReqData = { user: string, server: string }
|
type ReqData = { user: string, server: string }
|
||||||
type APIUser = { id: string, username?: string, avatarURL?: string }
|
type APIUser = { id: string, username?: string, avatarURL?: string }
|
||||||
|
type APIChannel = { id: string, name: string, icon?: string, type: 'VOICE'|'TEXT', nsfw: boolean }
|
||||||
|
|
||||||
type ServerDetails = {
|
type ServerDetails = {
|
||||||
id: string,
|
id: string,
|
||||||
|
@ -17,6 +18,7 @@ type ServerDetails = {
|
||||||
bannerURL?: string,
|
bannerURL?: string,
|
||||||
serverConfig?: ServerConfig,
|
serverConfig?: ServerConfig,
|
||||||
users: APIUser[],
|
users: APIUser[],
|
||||||
|
channels: APIChannel[],
|
||||||
}
|
}
|
||||||
|
|
||||||
wsEvents.on('req:getUserServerDetails', async (data: ReqData, cb: (data: WSResponse) => void) => {
|
wsEvents.on('req:getUserServerDetails', async (data: ReqData, cb: (data: WSResponse) => void) => {
|
||||||
|
@ -71,6 +73,13 @@ wsEvents.on('req:getUserServerDetails', async (data: ReqData, cb: (data: WSRespo
|
||||||
? { id: u.value._id, avatarURL: u.value.generateAvatarURL(), username: u.value.username }
|
? { id: u.value._id, avatarURL: u.value.generateAvatarURL(), username: u.value.username }
|
||||||
: { id: u.reason }
|
: { id: u.reason }
|
||||||
),
|
),
|
||||||
|
channels: server.channels.filter(c => c != undefined).map(c => ({
|
||||||
|
id: c!._id,
|
||||||
|
name: c!.name ?? '',
|
||||||
|
nsfw: c!.nsfw ?? false,
|
||||||
|
type: c!.channel_type == 'VoiceChannel' ? 'VOICE' : 'TEXT',
|
||||||
|
icon: c!.generateIconURL(),
|
||||||
|
})),
|
||||||
}
|
}
|
||||||
|
|
||||||
cb({ success: true, server: response });
|
cb({ success: true, server: response });
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { User } from 'revolt.js/dist/maps/Users';
|
import { User } from '@janderedev/revolt.js/dist/maps/Users';
|
||||||
import { client } from '../../..';
|
import { client } from '../../..';
|
||||||
import { getPermissionLevel, isBotManager } from '../../util';
|
import { getPermissionLevel, isBotManager } from '../../util';
|
||||||
import { wsEvents, WSResponse } from '../api_communication';
|
import { wsEvents, WSResponse } from '../api_communication';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
import { User } from "@janderedev/revolt.js/dist/maps/Users";
|
||||||
import { client } from "../../..";
|
import { client } from "../../..";
|
||||||
import { getPermissionLevel, parseUser } from "../../util";
|
import { getPermissionLevel, parseUser } from "../../util";
|
||||||
import { wsEvents, WSResponse } from "../api_communication";
|
import { wsEvents, WSResponse } from "../api_communication";
|
||||||
|
|
|
@ -9,7 +9,6 @@ import checkCustomRules from "./custom_rules/custom_rules";
|
||||||
import MessageCommandContext from "../../struct/MessageCommandContext";
|
import MessageCommandContext from "../../struct/MessageCommandContext";
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { getOwnMemberInServer, hasPermForChannel } from "../util";
|
import { getOwnMemberInServer, hasPermForChannel } from "../util";
|
||||||
import { prepareMessage } from "./prepare_message";
|
|
||||||
import { isSudo, updateSudoTimeout } from "../commands/botadm";
|
import { isSudo, updateSudoTimeout } from "../commands/botadm";
|
||||||
|
|
||||||
// thanks a lot esm
|
// thanks a lot esm
|
||||||
|
@ -96,7 +95,6 @@ let commands: Command[];
|
||||||
|
|
||||||
let message: MessageCommandContext = msg as MessageCommandContext;
|
let message: MessageCommandContext = msg as MessageCommandContext;
|
||||||
message.serverContext = serverCtx;
|
message.serverContext = serverCtx;
|
||||||
prepareMessage(message);
|
|
||||||
|
|
||||||
logger.info(`Command: ${message.author?.username} (${message.author?._id}) in ${message.channel?.server?.name} (${message.channel?.server?._id}): ${message.content}`);
|
logger.info(`Command: ${message.author?.username} (${message.author?._id}) in ${message.channel?.server?.name} (${message.channel?.server?._id}): ${message.content}`);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import CustomRuleAction from "../../../../struct/antispam/CustomRuleAction";
|
import CustomRuleAction from "../../../../struct/antispam/CustomRuleAction";
|
||||||
|
|
||||||
async function execute(message: Message, action: CustomRuleAction) {
|
async function execute(message: Message, action: CustomRuleAction) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import { client } from "../../../..";
|
import { client } from "../../../..";
|
||||||
import CustomRuleAction from "../../../../struct/antispam/CustomRuleAction";
|
import CustomRuleAction from "../../../../struct/antispam/CustomRuleAction";
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import CustomRuleAction from "../../../../struct/antispam/CustomRuleAction";
|
import CustomRuleAction from "../../../../struct/antispam/CustomRuleAction";
|
||||||
import { storeInfraction } from '../../../util';
|
import { storeInfraction } from '../../../util';
|
||||||
import Infraction from "../../../../struct/antispam/Infraction";
|
import Infraction from "../../../../struct/antispam/Infraction";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import { client } from "../../..";
|
import { client } from "../../..";
|
||||||
import ServerConfig from "../../../struct/ServerConfig";
|
import ServerConfig from "../../../struct/ServerConfig";
|
||||||
import logger from "../../logger";
|
import logger from "../../logger";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import { client } from "../../..";
|
import { client } from "../../..";
|
||||||
import CustomRuleTrigger from "../../../struct/antispam/CustomRuleTrigger";
|
import CustomRuleTrigger from "../../../struct/antispam/CustomRuleTrigger";
|
||||||
import VM from 'vm';
|
import VM from 'vm';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Member } from "revolt.js/dist/maps/Members";
|
import { Member } from "@janderedev/revolt.js/dist/maps/Members";
|
||||||
import { Server } from "revolt.js/dist/maps/Servers";
|
import { Server } from "@janderedev/revolt.js/dist/maps/Servers";
|
||||||
import { client } from "../..";
|
import { client } from "../..";
|
||||||
import Infraction from "../../struct/antispam/Infraction";
|
import Infraction from "../../struct/antispam/Infraction";
|
||||||
import LogMessage from "../../struct/LogMessage";
|
import LogMessage from "../../struct/LogMessage";
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
|
||||||
import logger from "../logger";
|
|
||||||
|
|
||||||
// We modify the way `reply()` works to make sure we
|
|
||||||
// don't crash if the original message was deleted.
|
|
||||||
|
|
||||||
export function prepareMessage(message: Message) {
|
|
||||||
message.reply = (...args: Parameters<typeof Message.prototype.reply>) => {
|
|
||||||
return new Promise<Message>((resolve, reject) => {
|
|
||||||
message.channel?.sendMessage({
|
|
||||||
content: typeof args[0] == 'string' ? args[0] : args[0].content,
|
|
||||||
replies: [ { id: message._id, mention: args[1] ?? true } ],
|
|
||||||
})
|
|
||||||
?.then(m => resolve(m))
|
|
||||||
.catch(e => {
|
|
||||||
if (e?.response?.status == 404) {
|
|
||||||
logger.warn("Replying to message gave 404, trying again without reply");
|
|
||||||
if (!message.channel) return reject("Channel does not exist");
|
|
||||||
message.channel?.sendMessage(typeof args[0] == 'string' ? { content: args[0] } : args[0])
|
|
||||||
.then(resolve)
|
|
||||||
.catch(reject);
|
|
||||||
} else reject(e);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@ import { client } from "../..";
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { FindOneResult } from "monk";
|
import { FindOneResult } from "monk";
|
||||||
import ScannedUser from "../../struct/ScannedUser";
|
import ScannedUser from "../../struct/ScannedUser";
|
||||||
import { Member } from "revolt.js/dist/maps/Members";
|
import { Member } from "@janderedev/revolt.js/dist/maps/Members";
|
||||||
import ServerConfig from "../../struct/ServerConfig";
|
import ServerConfig from "../../struct/ServerConfig";
|
||||||
import logger from "../logger";
|
import logger from "../logger";
|
||||||
import { sendLogMessage } from "../util";
|
import { sendLogMessage } from "../util";
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
import { Member } from "revolt.js/dist/maps/Members";
|
import { Member } from "@janderedev/revolt.js/dist/maps/Members";
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
import { User } from "@janderedev/revolt.js/dist/maps/Users";
|
||||||
import { client } from "..";
|
import { client } from "..";
|
||||||
import Infraction from "../struct/antispam/Infraction";
|
import Infraction from "../struct/antispam/Infraction";
|
||||||
import ServerConfig from "../struct/ServerConfig";
|
import ServerConfig from "../struct/ServerConfig";
|
||||||
import FormData from 'form-data';
|
import FormData from 'form-data';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Server } from "revolt.js/dist/maps/Servers";
|
import { Server } from "@janderedev/revolt.js/dist/maps/Servers";
|
||||||
import LogConfig from "../struct/LogConfig";
|
import LogConfig from "../struct/LogConfig";
|
||||||
import LogMessage from "../struct/LogMessage";
|
import LogMessage from "../struct/LogMessage";
|
||||||
import { ColorResolvable, MessageEmbed } from "discord.js";
|
import { ColorResolvable, MessageEmbed } from "discord.js";
|
||||||
import logger from "./logger";
|
import logger from "./logger";
|
||||||
import { ulid } from "ulid";
|
import { ulid } from "ulid";
|
||||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
import { Channel } from "@janderedev/revolt.js/dist/maps/Channels";
|
||||||
import { ChannelPermission, ServerPermission } from "revolt.js";
|
import { ChannelPermission, ServerPermission } from "@janderedev/revolt.js";
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import { isSudo } from "./commands/botadm";
|
import { isSudo } from "./commands/botadm";
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,12 @@ import MongoDB from './bot/db';
|
||||||
logger.info('Initializing client');
|
logger.info('Initializing client');
|
||||||
|
|
||||||
let db = MongoDB();
|
let db = MongoDB();
|
||||||
let client = new AutomodClient({ pongTimeout: 10, onPongTimeout: 'RECONNECT' }, db);
|
let client = new AutomodClient({
|
||||||
|
pongTimeout: 10,
|
||||||
|
onPongTimeout: 'RECONNECT',
|
||||||
|
fixReplyCrash: true,
|
||||||
|
messageTimeoutFix: true
|
||||||
|
}, db);
|
||||||
login(client);
|
login(client);
|
||||||
|
|
||||||
export { client }
|
export { client }
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import * as Revolt from "revolt.js";
|
import * as Revolt from "@janderedev/revolt.js";
|
||||||
import { IMonkManager } from 'monk';
|
import { IMonkManager } from 'monk';
|
||||||
import logger from '../bot/logger';
|
import logger from '../bot/logger';
|
||||||
import { adminBotLog } from "../bot/logging";
|
import { adminBotLog } from "../bot/logging";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { ChannelPermission, ServerPermission } from "revolt.js";
|
import { ChannelPermission, ServerPermission } from "@janderedev/revolt.js";
|
||||||
|
|
||||||
class Command {
|
class Command {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||||
import { Server } from "revolt.js/dist/maps/Servers";
|
import { Server } from "@janderedev/revolt.js/dist/maps/Servers";
|
||||||
import logger from "../bot/logger";
|
import logger from "../bot/logger";
|
||||||
|
|
||||||
class MessageCommandContext extends Message {
|
class MessageCommandContext extends Message {
|
||||||
|
|
|
@ -37,6 +37,23 @@
|
||||||
resolved "https://registry.yarnpkg.com/@insertish/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#5bcd6f73b93efa9ccdb6abf887ae808d40827169"
|
resolved "https://registry.yarnpkg.com/@insertish/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#5bcd6f73b93efa9ccdb6abf887ae808d40827169"
|
||||||
integrity sha512-kFD/p8T4Hkqr992QrdkbW/cQ/W/q2d9MPCobwzBv2PwTKLkCD9RaYDy6m17qRnSLQQ5PU0kHCG8kaOwAqzj1vQ==
|
integrity sha512-kFD/p8T4Hkqr992QrdkbW/cQ/W/q2d9MPCobwzBv2PwTKLkCD9RaYDy6m17qRnSLQQ5PU0kHCG8kaOwAqzj1vQ==
|
||||||
|
|
||||||
|
"@janderedev/revolt.js@^5.2.8-patch.1":
|
||||||
|
version "5.2.8-patch.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@janderedev/revolt.js/-/revolt.js-5.2.8-patch.1.tgz#e8570090612cb9e0f399f8bc75feed3cbbdfcd2a"
|
||||||
|
integrity sha512-rUjpp+Nk7/aPdFrSNBorSyvJwIb4fkwRzLB2OODWLesYvlxddJG2PtFujWU4dk3fnQMxpMaQVLq95T2GtsOkdg==
|
||||||
|
dependencies:
|
||||||
|
"@insertish/exponential-backoff" "3.1.0-patch.0"
|
||||||
|
"@insertish/isomorphic-ws" "^4.0.1"
|
||||||
|
axios "^0.21.4"
|
||||||
|
eventemitter3 "^4.0.7"
|
||||||
|
lodash.defaultsdeep "^4.6.1"
|
||||||
|
lodash.flatten "^4.4.0"
|
||||||
|
lodash.isequal "^4.5.0"
|
||||||
|
mobx "^6.3.2"
|
||||||
|
revolt-api "0.5.3-alpha.12"
|
||||||
|
ulid "^2.3.0"
|
||||||
|
ws "^8.2.2"
|
||||||
|
|
||||||
"@sapphire/async-queue@^1.1.8":
|
"@sapphire/async-queue@^1.1.8":
|
||||||
version "1.1.9"
|
version "1.1.9"
|
||||||
resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.1.9.tgz#ce69611c8753c4affd905a7ef43061c7eb95c01b"
|
resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.1.9.tgz#ce69611c8753c4affd905a7ef43061c7eb95c01b"
|
||||||
|
@ -362,9 +379,9 @@ mime-types@^2.1.12:
|
||||||
mime-db "1.50.0"
|
mime-db "1.50.0"
|
||||||
|
|
||||||
mobx@^6.3.2:
|
mobx@^6.3.2:
|
||||||
version "6.3.10"
|
version "6.3.13"
|
||||||
resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.3.10.tgz#c3bc715c8f03717b9a2329f9697d42b7998d42e0"
|
resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.3.13.tgz#93e56a57ee72369f850cf3d6398fd36ee8ef062e"
|
||||||
integrity sha512-lfuIN5TGXBNy/5s3ggr1L+IbD+LvfZVlj5q1ZuqyV9AfMtunYQvE8G0WfewS9tgIR3I1q8HJEEbcAOsxEgLwRw==
|
integrity sha512-zDDKDhYUk9QCHQUdLG+wb4Jv/nXutSLt/P8kkwHyjdbrJO4OZS6QTEsrOnrKM39puqXSrJZHdB6+yRys2NBFFA==
|
||||||
|
|
||||||
mongodb@^3.2.3:
|
mongodb@^3.2.3:
|
||||||
version "3.7.2"
|
version "3.7.2"
|
||||||
|
@ -504,23 +521,6 @@ revolt-api@0.5.3-alpha.12:
|
||||||
resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.3-alpha.12.tgz#78f25b567b840c1fd072595526592a422cb01f25"
|
resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.3-alpha.12.tgz#78f25b567b840c1fd072595526592a422cb01f25"
|
||||||
integrity sha512-MM42oI5+5JJMnAs3JiOwSQOy/SUYzYs3M8YRC5QI4G6HU7CfyB2HNWh5jFsyRlcLdSi13dGazHm31FUPHsxOzw==
|
integrity sha512-MM42oI5+5JJMnAs3JiOwSQOy/SUYzYs3M8YRC5QI4G6HU7CfyB2HNWh5jFsyRlcLdSi13dGazHm31FUPHsxOzw==
|
||||||
|
|
||||||
revolt.js@^5.2.7:
|
|
||||||
version "5.2.7"
|
|
||||||
resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-5.2.7.tgz#7b887329913494a2caf02c9828685d63551890db"
|
|
||||||
integrity sha512-KNoQqLrdd/B8zryu2fhWim9rO5OEkouhCZj4nU+upwrekz30DjxqWgZCup/apKXE8PSmrhSgWdKT8SHCBXOxFQ==
|
|
||||||
dependencies:
|
|
||||||
"@insertish/exponential-backoff" "3.1.0-patch.0"
|
|
||||||
"@insertish/isomorphic-ws" "^4.0.1"
|
|
||||||
axios "^0.21.4"
|
|
||||||
eventemitter3 "^4.0.7"
|
|
||||||
lodash.defaultsdeep "^4.6.1"
|
|
||||||
lodash.flatten "^4.4.0"
|
|
||||||
lodash.isequal "^4.5.0"
|
|
||||||
mobx "^6.3.2"
|
|
||||||
revolt-api "0.5.3-alpha.12"
|
|
||||||
ulid "^2.3.0"
|
|
||||||
ws "^8.2.2"
|
|
||||||
|
|
||||||
safe-buffer@^5.1.1, safe-buffer@^5.1.2:
|
safe-buffer@^5.1.1, safe-buffer@^5.1.2:
|
||||||
version "5.2.1"
|
version "5.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||||
|
@ -623,9 +623,9 @@ word@~0.3.0:
|
||||||
integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==
|
integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==
|
||||||
|
|
||||||
ws@^8.2.2:
|
ws@^8.2.2:
|
||||||
version "8.4.0"
|
version "8.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.4.0.tgz#f05e982a0a88c604080e8581576e2a063802bed6"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.4.2.tgz#18e749868d8439f2268368829042894b6907aa0b"
|
||||||
integrity sha512-IHVsKe2pjajSUIl4KYMQOdlyliovpEPquKkqbwswulszzI7r0SfQrxnXdWAEqOlDCLrVSJzo+O1hAwdog2sKSQ==
|
integrity sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==
|
||||||
|
|
||||||
ws@^8.2.3:
|
ws@^8.2.3:
|
||||||
version "8.3.0"
|
version "8.3.0"
|
||||||
|
|
1
web/src/assets/channel-default-icon.svg
Normal file
1
web/src/assets/channel-default-icon.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg viewBox="0 0 24 24" height="24" width="24" aria-hidden="true" focusable="false" fill="#848484" xmlns="http://www.w3.org/2000/svg" class="StyledIconBase-ea9ulj-0 bWRyML"><path d="M16.018 3.815 15.232 8h-4.966l.716-3.815-1.964-.37L8.232 8H4v2h3.857l-.751 4H3v2h3.731l-.714 3.805 1.965.369L8.766 16h4.966l-.714 3.805 1.965.369.783-4.174H20v-2h-3.859l.751-4H21V8h-3.733l.716-3.815-1.965-.37zM14.106 14H9.141l.751-4h4.966l-.752 4z"></path></svg>
|
After Width: | Height: | Size: 445 B |
|
@ -1,19 +1,23 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { FunctionComponent, useCallback, useEffect, useState } from "react";
|
import React, { FunctionComponent, useCallback, useEffect, useState } from "react";
|
||||||
import { Button } from '@revoltchat/ui/lib/components/atoms/inputs/Button';
|
import { Button } from '@revoltchat/ui/lib/components/atoms/inputs/Button';
|
||||||
import { InputBox } from '@revoltchat/ui/lib/components/atoms/inputs/InputBox';
|
import { InputBox } from '@revoltchat/ui/lib/components/atoms/inputs/InputBox';
|
||||||
import { Checkbox } from '@revoltchat/ui/lib/components/atoms/inputs/Checkbox';
|
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 { LineDivider } from '@revoltchat/ui/lib/components/atoms/layout/LineDivider';
|
||||||
import { H1 } from '@revoltchat/ui/lib/components/atoms/heading/H1';
|
import { H1 } from '@revoltchat/ui/lib/components/atoms/heading/H1';
|
||||||
import { H3 } from '@revoltchat/ui/lib/components/atoms/heading/H3';
|
import { H3 } from '@revoltchat/ui/lib/components/atoms/heading/H3';
|
||||||
import { H4 } from '@revoltchat/ui/lib/components/atoms/heading/H4';
|
import { H4 } from '@revoltchat/ui/lib/components/atoms/heading/H4';
|
||||||
|
import { H5 } from '@revoltchat/ui/lib/components/atoms/heading/H5';
|
||||||
import { Icon } from '@mdi/react';
|
import { Icon } from '@mdi/react';
|
||||||
import { mdiCloseBox } from '@mdi/js';
|
import { mdiCloseBox } from '@mdi/js';
|
||||||
import { API_URL } from "../App";
|
import { API_URL } from "../App";
|
||||||
import { getAuthHeaders } from "../utils";
|
import { getAuthHeaders } from "../utils";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
import defaultChannelIcon from '../assets/channel-default-icon.svg';
|
||||||
|
|
||||||
type User = { id: string, username?: string, avatarURL?: string }
|
type User = { id: string, username?: string, avatarURL?: string }
|
||||||
|
type Channel = { id: string, name: string, icon?: string, type: 'VOICE'|'TEXT', nsfw: boolean }
|
||||||
|
|
||||||
type Server = {
|
type Server = {
|
||||||
id?: string,
|
id?: string,
|
||||||
|
@ -24,6 +28,16 @@ type Server = {
|
||||||
bannerURL?: string,
|
bannerURL?: string,
|
||||||
serverConfig?: { [key: string]: any },
|
serverConfig?: { [key: string]: any },
|
||||||
users: User[],
|
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 ServerDashboard: FunctionComponent = () => {
|
||||||
|
@ -37,6 +51,8 @@ const ServerDashboard: FunctionComponent = () => {
|
||||||
const [botManagers, setBotManagers] = useState([] as string[]);
|
const [botManagers, setBotManagers] = useState([] as string[]);
|
||||||
const [moderators, setModerators] = useState([] as string[]);
|
const [moderators, setModerators] = useState([] as string[]);
|
||||||
|
|
||||||
|
const [automodSettings, setAutomodSettings] = useState(null as { antispam: AntispamRule[] }|null);
|
||||||
|
|
||||||
const { serverid } = useParams();
|
const { serverid } = useParams();
|
||||||
|
|
||||||
const saveConfig = useCallback(async () => {
|
const saveConfig = useCallback(async () => {
|
||||||
|
@ -71,13 +87,25 @@ const ServerDashboard: FunctionComponent = () => {
|
||||||
|
|
||||||
setBotManagers(server.serverConfig?.botManagers ?? []);
|
setBotManagers(server.serverConfig?.botManagers ?? []);
|
||||||
setModerators(server.serverConfig?.moderators ?? []);
|
setModerators(server.serverConfig?.moderators ?? []);
|
||||||
|
|
||||||
|
loadAutomodInfo(server);
|
||||||
} catch(e: any) {
|
} catch(e: any) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
setStatus(`${e?.message ?? e}`);
|
setStatus(`${e?.message ?? e}`);
|
||||||
}
|
}
|
||||||
}, [serverInfo]);
|
}, [serverInfo]);
|
||||||
|
|
||||||
useEffect(() => { loadInfo() }, []);
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -87,69 +115,115 @@ const ServerDashboard: FunctionComponent = () => {
|
||||||
<H4>{serverInfo.description ?? <i>No server description set</i>}</H4>
|
<H4>{serverInfo.description ?? <i>No server description set</i>}</H4>
|
||||||
<br/>
|
<br/>
|
||||||
<div style={{ paddingLeft: '10px', paddingRight: '10px' }}>
|
<div style={{ paddingLeft: '10px', paddingRight: '10px' }}>
|
||||||
<H3>Prefix</H3>
|
<>
|
||||||
<InputBox
|
<H3>Prefix</H3>
|
||||||
style={{ width: '150px', }}
|
<InputBox
|
||||||
placeholder="Enter a prefix..."
|
style={{ width: '150px', }}
|
||||||
value={prefix}
|
placeholder="Enter a prefix..."
|
||||||
onChange={e => {
|
value={prefix}
|
||||||
setPrefix(e.currentTarget.value);
|
onChange={e => {
|
||||||
setChanged({ ...changed, prefix: true });
|
setPrefix(e.currentTarget.value);
|
||||||
}}
|
setChanged({ ...changed, prefix: true });
|
||||||
/>
|
}}
|
||||||
<Checkbox
|
/>
|
||||||
style={{ maxWidth: '400px' }}
|
<Checkbox
|
||||||
value={prefixAllowSpace}
|
style={{ maxWidth: '400px' }}
|
||||||
onChange={() => {
|
value={prefixAllowSpace}
|
||||||
setPrefixAllowSpace(!prefixAllowSpace);
|
onChange={() => {
|
||||||
setChanged({ ...changed, prefixAllowSpace: 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.'}
|
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.'}
|
||||||
<Button
|
/>
|
||||||
style={{ marginTop: "16px" }}
|
<Button
|
||||||
onClick={saveConfig}
|
style={{ marginTop: "16px" }}
|
||||||
>Save</Button>
|
onClick={saveConfig}
|
||||||
|
>Save</Button>
|
||||||
|
</>
|
||||||
|
|
||||||
<LineDivider />
|
<LineDivider />
|
||||||
|
|
||||||
<H3>Bot Managers</H3>
|
<>
|
||||||
<H4>
|
<H3>Bot Managers</H3>
|
||||||
Only users with "Manage Server" permission are allowed to add/remove other
|
<H4>
|
||||||
bot managers and are automatically considered bot manager.
|
Only users with "Manage Server" permission are allowed to add/remove other
|
||||||
</H4>
|
bot managers and are automatically considered bot manager.
|
||||||
<UserListTypeContainer>
|
</H4>
|
||||||
<UserListContainer disabled={(serverInfo.perms ?? 0) < 3}>
|
<UserListTypeContainer>
|
||||||
{botManagers.map((uid: string) => {
|
<UserListContainer disabled={(serverInfo.perms ?? 0) < 3}>
|
||||||
const user = serverInfo.users.find(u => u.id == uid) || { id: uid }
|
{botManagers.map((uid: string) => {
|
||||||
return (
|
const user = serverInfo.users.find(u => u.id == uid) || { id: uid }
|
||||||
<UserListEntry type='MANAGER' user={user} key={uid} />
|
return (
|
||||||
)})}
|
<UserListEntry type='MANAGER' user={user} key={uid} />
|
||||||
<UserListAddField type='MANAGER' />
|
)})}
|
||||||
</UserListContainer>
|
<UserListAddField type='MANAGER' />
|
||||||
</UserListTypeContainer>
|
</UserListContainer>
|
||||||
|
</UserListTypeContainer>
|
||||||
|
|
||||||
<H3>Moderators</H3>
|
<H3>Moderators</H3>
|
||||||
<H4>
|
<H4>
|
||||||
Only bot managers are allowed to add/remove moderators.
|
Only bot managers are allowed to add/remove moderators.
|
||||||
All bot managers are also moderators.
|
All bot managers are also moderators.
|
||||||
</H4>
|
</H4>
|
||||||
<UserListTypeContainer>
|
<UserListTypeContainer>
|
||||||
<UserListContainer disabled={(serverInfo.perms ?? 0) < 2}>
|
<UserListContainer disabled={(serverInfo.perms ?? 0) < 2}>
|
||||||
{moderators.map((uid: string) => {
|
{moderators.map((uid: string) => {
|
||||||
const user = serverInfo.users.find(u => u.id == uid) || { id: uid }
|
const user = serverInfo.users.find(u => u.id == uid) || { id: uid }
|
||||||
return (
|
return (
|
||||||
<UserListEntry type='MOD' user={user} key={uid} />
|
<UserListEntry type='MOD' user={user} key={uid} />
|
||||||
)})}
|
)})}
|
||||||
<UserListAddField type='MOD' />
|
<UserListAddField type='MOD' />
|
||||||
</UserListContainer>
|
</UserListContainer>
|
||||||
</UserListTypeContainer>
|
</UserListTypeContainer>
|
||||||
|
</>
|
||||||
|
|
||||||
|
<LineDivider />
|
||||||
|
|
||||||
|
<>
|
||||||
|
<H3>Antispam Rules</H3>
|
||||||
|
{serverInfo.perms != null && automodSettings && (
|
||||||
|
serverInfo.perms > 0
|
||||||
|
? (
|
||||||
|
<>
|
||||||
|
{automodSettings.antispam.map(r => <AntispamRule rule={r} key={r.id} />)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<div>
|
||||||
|
<p style={{ color: 'var(--foreground)' }}>
|
||||||
|
You do not have access to this.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function RemoveButton(props: { onClick: () => void }) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginLeft: '4px',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
display: 'inline-block',
|
||||||
|
height: '30px',
|
||||||
|
}}
|
||||||
|
onClick={props.onClick}
|
||||||
|
>
|
||||||
|
<Icon // todo: hover effect
|
||||||
|
path={mdiCloseBox}
|
||||||
|
color='var(--tertiary-foreground)'
|
||||||
|
size='30px'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function UserListEntry(props: { user: User, type: 'MANAGER'|'MOD' }) {
|
function UserListEntry(props: { user: User, type: 'MANAGER'|'MOD' }) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -182,13 +256,7 @@ const ServerDashboard: FunctionComponent = () => {
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
}}
|
}}
|
||||||
>{props.user.username ?? 'Unknown'}</span>
|
>{props.user.username ?? 'Unknown'}</span>
|
||||||
<div
|
<RemoveButton
|
||||||
style={{
|
|
||||||
marginLeft: '4px',
|
|
||||||
verticalAlign: 'middle',
|
|
||||||
display: 'inline-block',
|
|
||||||
height: '30px',
|
|
||||||
}}
|
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const res = await axios.delete(
|
const res = await axios.delete(
|
||||||
`${API_URL}/dash/server/${serverid}/${props.type == 'MANAGER' ? 'managers' : 'mods'}/${props.user.id}`,
|
`${API_URL}/dash/server/${serverid}/${props.type == 'MANAGER' ? 'managers' : 'mods'}/${props.user.id}`,
|
||||||
|
@ -202,13 +270,7 @@ const ServerDashboard: FunctionComponent = () => {
|
||||||
setModerators(res.data.mods);
|
setModerators(res.data.mods);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
<Icon // todo: hover effect
|
|
||||||
path={mdiCloseBox}
|
|
||||||
color='var(--tertiary-foreground)'
|
|
||||||
size='30px'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -248,7 +310,7 @@ const ServerDashboard: FunctionComponent = () => {
|
||||||
function UserListAddField(props: { type: 'MANAGER'|'MOD' }) {
|
function UserListAddField(props: { type: 'MANAGER'|'MOD' }) {
|
||||||
const [content, setContent] = useState('');
|
const [content, setContent] = useState('');
|
||||||
|
|
||||||
const onConfirm = useCallback(async () => {
|
const onConfirm = useCallback(async () => {0
|
||||||
if (content.length) {
|
if (content.length) {
|
||||||
const res = await axios.put(
|
const res = await axios.put(
|
||||||
`${API_URL}/dash/server/${serverid}/${props.type == 'MANAGER' ? 'managers' : 'mods'}`,
|
`${API_URL}/dash/server/${serverid}/${props.type == 'MANAGER' ? 'managers' : 'mods'}`,
|
||||||
|
@ -291,11 +353,217 @@ const ServerDashboard: FunctionComponent = () => {
|
||||||
width: '40px',
|
width: '40px',
|
||||||
height: '38px',
|
height: '38px',
|
||||||
margin: '4px 8px',
|
margin: '4px 8px',
|
||||||
|
opacity: content.length > 0 ? '1' : '0',
|
||||||
}}
|
}}
|
||||||
onClick={onConfirm}
|
onClick={onConfirm}
|
||||||
>Ok</Button>
|
>Ok</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div>
|
||||||
|
<InputBox
|
||||||
|
placeholder={`Add a channel...`}
|
||||||
|
value={content}
|
||||||
|
onChange={e => setContent(e.currentTarget.value)}
|
||||||
|
style={{
|
||||||
|
float: 'left',
|
||||||
|
width: '180px',
|
||||||
|
height: '38px',
|
||||||
|
margin: '4px 8px',
|
||||||
|
}}
|
||||||
|
onKeyDown={e => e.key == 'Enter' && onConfirm()}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
float: 'left',
|
||||||
|
width: '40px',
|
||||||
|
height: '38px',
|
||||||
|
margin: '4px 8px',
|
||||||
|
opacity: content.length > 0 ? '1' : '0',
|
||||||
|
}}
|
||||||
|
onClick={onConfirm}
|
||||||
|
>Ok</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 inputStyle: React.CSSProperties = {
|
||||||
|
maxWidth: '100px',
|
||||||
|
margin: '8px 8px 0px 8px',
|
||||||
|
}
|
||||||
|
|
||||||
|
const messagePlaceholders = {
|
||||||
|
0: '',
|
||||||
|
1: 'Message content...',
|
||||||
|
2: '(Optional) Warn reason...',
|
||||||
|
3: '',
|
||||||
|
4: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
color: 'var(--foreground)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ marginTop: '12px' }}>
|
||||||
|
If user sends more than
|
||||||
|
<InputBox style={inputStyle} value={maxMsg || ''} placeholder={`${props.rule.max_msg}`} onChange={e => {
|
||||||
|
const val = e.currentTarget.value;
|
||||||
|
if (!isNaN(Number(val)) && val.length <= 4 && Number(val) >= 0) setMaxMsg(Number(val));
|
||||||
|
}} />
|
||||||
|
messages in
|
||||||
|
<InputBox style={inputStyle} value={timeframe || ''} placeholder={`${props.rule.timeframe}`} onChange={e => {
|
||||||
|
const val = e.currentTarget.value;
|
||||||
|
if (!isNaN(Number(val)) && val.length <= 4 && Number(val) >= 0) setTimeframe(Number(val));
|
||||||
|
}} />
|
||||||
|
seconds,
|
||||||
|
<ComboBox
|
||||||
|
style={{ ...inputStyle, maxWidth: '200px' }}
|
||||||
|
value={action}
|
||||||
|
onChange={ev => setAction(ev.currentTarget.value as any)}
|
||||||
|
>
|
||||||
|
<option value={0}>Delete message</option>
|
||||||
|
<option value={1}>Send a message</option>
|
||||||
|
<option value={2}>Warn user</option>
|
||||||
|
<option value={3}>Kick user</option>
|
||||||
|
<option value={4}>Ban user</option>
|
||||||
|
</ComboBox>
|
||||||
|
<InputBox
|
||||||
|
style={{
|
||||||
|
...inputStyle,
|
||||||
|
maxWidth: 'min(400px, calc(100% - 20px))',
|
||||||
|
display: action >= 3 || action == 0 ? 'none' : 'unset' }}
|
||||||
|
value={message}
|
||||||
|
placeholder={messagePlaceholders[action] || ''}
|
||||||
|
onChange={ev => setMessage(ev.currentTarget.value)}
|
||||||
|
/>
|
||||||
|
<a style={{ display: action >= 3 ? 'unset' : 'none'}}>
|
||||||
|
<br/>
|
||||||
|
"Kick" and "Ban" actions are currently placeholders, they do not have any functionality yet.
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<H4 style={{ paddingTop: '16px' }}>
|
||||||
|
You can specify channels here that this rule will run in.
|
||||||
|
If left empty, it will run in all channels.
|
||||||
|
</H4>
|
||||||
|
<UserListTypeContainer>
|
||||||
|
{
|
||||||
|
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 (
|
||||||
|
<div
|
||||||
|
key={cid}
|
||||||
|
style={{
|
||||||
|
display: 'block',
|
||||||
|
margin: '4px 6px',
|
||||||
|
padding: '4px',
|
||||||
|
backgroundColor: 'var(--tertiary-background)',
|
||||||
|
borderRadius: '5px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={channel.icon ?? defaultChannelIcon}
|
||||||
|
style={{
|
||||||
|
width: '32px',
|
||||||
|
height: '32px',
|
||||||
|
objectFit: 'cover',
|
||||||
|
borderRadius: '10%',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
display: 'inline-block',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: '20px',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
marginLeft: '4px',
|
||||||
|
}}
|
||||||
|
>{channel.name}</span>
|
||||||
|
<RemoveButton onClick={() => {
|
||||||
|
setChannels(channels.filter(c => c != cid));
|
||||||
|
setChannelsChanged(true);
|
||||||
|
}} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
<ChannelListAddField onInput={channel => {
|
||||||
|
if (!channels.includes(channel.id)) {
|
||||||
|
setChannels([ ...channels, channel.id ]);
|
||||||
|
setChannelsChanged(true);
|
||||||
|
}
|
||||||
|
}} />
|
||||||
|
</UserListTypeContainer>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
paddingTop: '16px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button style={{ float: 'left' }} onClick={save}>Save</Button>
|
||||||
|
<Button style={{ float: 'left', marginLeft: '8px' }} onClick={reset}>Reset</Button>
|
||||||
|
<div style={{ clear: 'both' }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
export default ServerDashboard;
|
export default ServerDashboard;
|
||||||
|
|
Loading…
Reference in a new issue