124 lines
5.3 KiB
TypeScript
124 lines
5.3 KiB
TypeScript
import config from '../../../config.ts';
|
|
import dbClient from '../../db/client.ts';
|
|
import { queries } from '../../db/common.ts';
|
|
import {
|
|
// Discordeno deps
|
|
cache,
|
|
// Log4Deno deps
|
|
log,
|
|
LT,
|
|
} from '../../../deps.ts';
|
|
import { RollModifiers } from '../../mod.d.ts';
|
|
import utils from '../../utils.ts';
|
|
import { queueRoll } from '../../solver/rollQueue.ts';
|
|
import stdResp from '../stdResponses.ts';
|
|
import { verifyQueryHasParams } from '../utils.ts';
|
|
|
|
const apiWarning = `The following roll was conducted using my built in API. If someone in this channel did not request this roll, please report API abuse here: <${config.api.supportURL}>`;
|
|
|
|
export const apiRoll = async (query: Map<string, string>, apiUserid: bigint): Promise<Response> => {
|
|
// Make sure query contains all the needed parts
|
|
if (verifyQueryHasParams(query, ['user', 'channel', 'rollstr'])) {
|
|
if (query.has('n') && query.has('m')) {
|
|
// Alert API user that they shouldn't be doing this
|
|
return stdResp.BadRequest("Cannot have both 'n' and 'm'.");
|
|
}
|
|
|
|
// Check if user is authenticated to use this endpoint
|
|
let authorized = false;
|
|
let hideWarn = false;
|
|
|
|
// Check if the db has the requested userid/channelid combo, and that the requested userid matches the userid linked with the api key
|
|
const dbChannelQuery = await dbClient.query('SELECT active, banned FROM allowed_channels WHERE userid = ? AND channelid = ?', [
|
|
apiUserid,
|
|
BigInt(query.get('channel') || '0'),
|
|
]);
|
|
if (dbChannelQuery.length === 1 && apiUserid === BigInt(query.get('user') || '0') && dbChannelQuery[0].active && !dbChannelQuery[0].banned) {
|
|
// Get the guild from the channel and make sure user is in said guild
|
|
const guild = cache.channels.get(BigInt(query.get('channel') || ''))?.guild;
|
|
if (guild && guild.members.get(BigInt(query.get('user') || ''))?.id) {
|
|
const dbGuildQuery = await dbClient.query('SELECT active, banned, hidewarn FROM allowed_guilds WHERE guildid = ? AND channelid = ?', [
|
|
guild.id,
|
|
BigInt(query.get('channel') || '0'),
|
|
]);
|
|
|
|
// Make sure guild allows API rolls
|
|
if (dbGuildQuery.length === 1 && dbGuildQuery[0].active && !dbGuildQuery[0].banned) {
|
|
authorized = true;
|
|
hideWarn = dbGuildQuery[0].hidewarn;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (authorized) {
|
|
// Rest of this command is in a try-catch to protect all sends/edits from erroring out
|
|
try {
|
|
// Make sure rollCmd is not undefined
|
|
let rollCmd = query.get('rollstr') || '';
|
|
const originalCommand = query.get('rollstr') || '';
|
|
|
|
if (rollCmd.length === 0) {
|
|
// Always log API rolls for abuse detection
|
|
dbClient
|
|
.execute(queries.insertRollLogCmd(1, 1), [originalCommand, 'EmptyInput', null])
|
|
.catch((e) => utils.commonLoggers.dbError('apiRoll.ts:65', 'insert', e));
|
|
|
|
// Alert API user that they messed up
|
|
return stdResp.BadRequest('rollCmd is required.');
|
|
}
|
|
|
|
if (query.has('o') && query.get('o')?.toLowerCase() !== 'd' && query.get('o')?.toLowerCase() !== 'a') {
|
|
// Always log API rolls for abuse detection
|
|
dbClient
|
|
.execute(queries.insertRollLogCmd(1, 1), [originalCommand, 'BadOrder', null])
|
|
.catch((e) => utils.commonLoggers.dbError('apiRoll.ts:66', 'insert', e));
|
|
|
|
// Alert API user that they messed up
|
|
return stdResp.BadRequest("Order must be set to 'a' or 'd'.");
|
|
}
|
|
|
|
// Clip off the leading prefix. API calls must be formatted with a prefix at the start to match how commands are sent in Discord
|
|
rollCmd = rollCmd.substring(rollCmd.indexOf(config.prefix) + 2).replace(/%20/g, ' ');
|
|
|
|
const modifiers: RollModifiers = {
|
|
noDetails: query.has('nd'),
|
|
superNoDetails: query.has('snd'),
|
|
spoiler: query.has('s') ? '||' : '',
|
|
maxRoll: query.has('m'),
|
|
nominalRoll: query.has('n'),
|
|
gmRoll: query.has('gms'),
|
|
gms: query.has('gms') ? (query.get('gms') || '').split(',') : [],
|
|
order: query.has('o') ? query.get('o')?.toLowerCase() || '' : '',
|
|
count: query.has('c'),
|
|
valid: true,
|
|
apiWarn: hideWarn ? '' : apiWarning,
|
|
};
|
|
|
|
return new Promise<Response>((resolve) => {
|
|
queueRoll({
|
|
apiRoll: true,
|
|
api: { resolve, channelId: BigInt(query.get('channel') || '0'), userId: BigInt(query.get('user') || '') },
|
|
rollCmd,
|
|
modifiers,
|
|
originalCommand,
|
|
});
|
|
});
|
|
|
|
// Parse the roll and get the return text
|
|
} catch (err) {
|
|
// Handle any errors we missed
|
|
log(LT.ERROR, `Unhandled Error: ${JSON.stringify(err)}`);
|
|
return stdResp.InternalServerError('Something went wrong.');
|
|
}
|
|
} else {
|
|
// Alert API user that they messed up
|
|
return stdResp.Forbidden(
|
|
`Verify you are a member of the guild you are sending this roll to. If you are, the ${config.name} may not have that registered, please send a message in the guild so ${config.name} can register this. This registration is temporary, so if you see this error again, just poke your server again.`
|
|
);
|
|
}
|
|
} else {
|
|
// Alert API user that they shouldn't be doing this
|
|
return stdResp.BadRequest(stdResp.Strings.missingParams);
|
|
}
|
|
};
|