add ability to create and delete antispam rules
This commit is contained in:
parent
50bd8e1c06
commit
30185ce004
6 changed files with 171 additions and 5 deletions
|
@ -20,6 +20,7 @@
|
|||
"express": "^4.17.2",
|
||||
"log75": "^2.2.0",
|
||||
"monk": "^7.3.4",
|
||||
"ulid": "^2.3.0",
|
||||
"ws": "^8.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { app, db } from '../..';
|
||||
import { Request, Response } from 'express';
|
||||
import { badRequest, isAuthenticated, requireAuth, unauthorized } from '../../utils';
|
||||
import { badRequest, ensureObjectStructure, isAuthenticated, requireAuth, unauthorized } from '../../utils';
|
||||
import { botReq } from '../internal/ws';
|
||||
import { FindOneResult } from 'monk';
|
||||
import { ulid } from 'ulid';
|
||||
|
||||
type AntispamRule = {
|
||||
id: string;
|
||||
|
@ -80,3 +81,82 @@ app.patch('/dash/server/:server/automod/:ruleid', requireAuth({ permission: 2 })
|
|||
|
||||
return res.send({ success: true });
|
||||
});
|
||||
|
||||
app.post('/dash/server/:server/automod', requireAuth({ permission: 2 }), async (req, res) => {
|
||||
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' });
|
||||
|
||||
let rule: any;
|
||||
try {
|
||||
rule = ensureObjectStructure(req.body, {
|
||||
max_msg: 'number',
|
||||
timeframe: 'number',
|
||||
action: 'number',
|
||||
message: 'string',
|
||||
}, true);
|
||||
} catch(e) { return res.status(400).send(e) }
|
||||
|
||||
if (rule.action != null && rule.action < 0 || rule.action > 4) return res.status(400).send('Invalid action');
|
||||
|
||||
const id = ulid();
|
||||
|
||||
await db.get('servers').update({
|
||||
id: server,
|
||||
}, {
|
||||
$push: {
|
||||
"automodSettings.spam": {
|
||||
id: id,
|
||||
max_msg: rule.max_msg ?? 5,
|
||||
timeframe: rule.timeframe ?? 3,
|
||||
action: rule.action ?? 0,
|
||||
message: rule.message ?? null,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
res.status(200).send({ success: true, id: id });
|
||||
});
|
||||
|
||||
app.delete('/dash/server/:server/automod/:ruleid', requireAuth({ permission: 2 }), async (req, res) => {
|
||||
const user = await isAuthenticated(req, res, true);
|
||||
if (!user) return;
|
||||
|
||||
const { server, ruleid } = req.params;
|
||||
if (!server || typeof server != 'string' || !ruleid || typeof ruleid != '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' });
|
||||
|
||||
// todo: fix this shit idk if it works
|
||||
let queryRes;
|
||||
try {
|
||||
queryRes = await db.get('servers').update({
|
||||
id: server
|
||||
}, {
|
||||
$pull: {
|
||||
"automodSettings.spam": { id: ruleid }
|
||||
}
|
||||
});
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
res.status(500).send({ error: e });
|
||||
return;
|
||||
}
|
||||
|
||||
if (queryRes.nModified > 0) res.status(200).send({ success: true });
|
||||
else res.status(404).send({ success: false, error: 'Rule not found' });
|
||||
});
|
||||
|
|
|
@ -144,7 +144,7 @@ app.put('/dash/server/:server/:option', async (req: Request, res: Response) => {
|
|||
}
|
||||
});
|
||||
|
||||
app.delete('/dash/server/:server/:option/:target', async (req: Request, res: Response) => {
|
||||
app.delete('/dash/server/:server/:option/:target', async (req: Request, res: Response, next) => {
|
||||
const user = await isAuthenticated(req, res, true);
|
||||
if (!user) return unauthorized(res);
|
||||
|
||||
|
@ -190,6 +190,6 @@ app.delete('/dash/server/:server/:option/:target', async (req: Request, res: Res
|
|||
});
|
||||
return;
|
||||
}
|
||||
default: return badRequest(res);
|
||||
default: next();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -69,4 +69,49 @@ function requireAuth(config: RequireAuthConfig): (req: Request, res: Response, n
|
|||
}
|
||||
}
|
||||
|
||||
export { isAuthenticated, getSessionInfo, badRequest, unauthorized, getPermissionLevel, requireAuth }
|
||||
/**
|
||||
* Strips the input object of unwanted fields and
|
||||
* throws if a value has the wrong type
|
||||
* @param obj
|
||||
* @param structure
|
||||
*/
|
||||
function ensureObjectStructure(obj: any, structure: { [key: string]: 'string'|'number'|'float'|'strarray' }, allowEmpty?: boolean): any {
|
||||
const returnObj: any = {}
|
||||
|
||||
for (const key of Object.keys(obj)) {
|
||||
const type = obj[key] == null ? 'null' : typeof obj[key];
|
||||
|
||||
if (allowEmpty && (type == 'undefined' || type == 'null')) continue;
|
||||
|
||||
switch(structure[key]) {
|
||||
case 'string':
|
||||
case 'number':
|
||||
case 'float':
|
||||
if (type != structure[key]) throw `Property '${key}' was expected to be of type '${structure[key]}', got '${type}' instead`;
|
||||
|
||||
if (structure[key] == 'number' && `${Math.round(obj[key])}` != `${obj[key]}`)
|
||||
throw `Property '${key}' was expected to be of type '${structure[key]}', got 'float' instead`;
|
||||
|
||||
returnObj[key] = obj[key];
|
||||
break;
|
||||
case 'strarray':
|
||||
if (!(obj[key] instanceof Array)) {
|
||||
throw `Property '${key}' was expected to be of type 'string[]', got '${type}' instead`;
|
||||
}
|
||||
|
||||
for (const i in obj[key]) {
|
||||
const item = obj[key][i];
|
||||
if (typeof item != 'string') throw `Property '${key}' was expected to be of type 'string[]', `
|
||||
+ `found '${typeof item}' at index ${i}`;
|
||||
}
|
||||
|
||||
returnObj[key] = obj[key];
|
||||
break;
|
||||
default: continue;
|
||||
}
|
||||
}
|
||||
|
||||
return returnObj;
|
||||
}
|
||||
|
||||
export { isAuthenticated, getSessionInfo, badRequest, unauthorized, getPermissionLevel, requireAuth, ensureObjectStructure }
|
||||
|
|
|
@ -639,6 +639,11 @@ typescript@^4.5.5:
|
|||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3"
|
||||
integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
|
||||
|
||||
ulid@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/ulid/-/ulid-2.3.0.tgz#93063522771a9774121a84d126ecd3eb9804071f"
|
||||
integrity sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==
|
||||
|
||||
unpipe@1.0.0, unpipe@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||
|
|
|
@ -8,7 +8,6 @@ import { LineDivider } from '@revoltchat/ui/lib/components/atoms/layout/LineDivi
|
|||
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 { H5 } from '@revoltchat/ui/lib/components/atoms/heading/H5';
|
||||
import { Icon } from '@mdi/react';
|
||||
import { mdiCloseBox } from '@mdi/js';
|
||||
import { API_URL } from "../App";
|
||||
|
@ -187,6 +186,34 @@ const ServerDashboard: FunctionComponent = () => {
|
|||
? (
|
||||
<>
|
||||
{automodSettings.antispam.map(r => <AntispamRule rule={r} key={r.id} />)}
|
||||
<Button style={{
|
||||
marginTop: '12px',
|
||||
}} onClick={async () => {
|
||||
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
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
: (
|
||||
|
@ -441,6 +468,13 @@ const ServerDashboard: FunctionComponent = () => {
|
|||
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',
|
||||
|
@ -560,6 +594,7 @@ const ServerDashboard: FunctionComponent = () => {
|
|||
>
|
||||
<Button style={{ float: 'left' }} onClick={save}>Save</Button>
|
||||
<Button style={{ float: 'left', marginLeft: '8px' }} onClick={reset}>Reset</Button>
|
||||
<Button style={{ float: 'left', marginLeft: '8px' }} onClick={remove}>Delete</Button>
|
||||
<div style={{ clear: 'both' }} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue