mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-04-24 19:39:11 +00:00
* miniflare init * feat(api): add tests * chore: more tests, almost 100% * add sessions/state spec * add majority of routes and datapaths, start on interactions * nevermind, no interactions * nevermind x2, tweetnacl is bad but SubtleCrypto has what we need apparently * simplify interactions verify * add brute force interactions tests * every primary path API route is refactored! * automatically import from legacy, or die trying. * check that we only fetch legacy once, ever * remove old-src, same some historic pieces * remove interactions & worker-utils package, update misc/types * update some packages we don't need specific pinning for anymore * update web references to API routes since they all changed * fix all linting issues, upgrade most packages * fix tests, divorce enzyme where-ever possible * update web, fix integration issues * pre-build api * fix tests * move api pretest to api package.json instead of CI * remove interactions from terraform, fix deploy side configs * update to tf 1.1.4 * prevent double writes to worker in GCS, port to newer GCP auth workflow * fix api.tf var refs, upgrade node action * change to curl-based script upload for worker script due to terraform provider limitations * oh no, cloudflare freaked out :(
104 lines
2.8 KiB
TypeScript
104 lines
2.8 KiB
TypeScript
import { CategoryType, Member, RoleSafety } from '@roleypoly/types';
|
|
import { AuthType, discordFetch, respond } from '@roleypoly/worker-utils';
|
|
import { difference, keyBy } from 'lodash';
|
|
import { interactionsEndpoint } from '../utils/api-tools';
|
|
import { botToken } from '../utils/config';
|
|
import {
|
|
getGuild,
|
|
getGuildData,
|
|
getGuildMember,
|
|
updateGuildMember,
|
|
} from '../utils/guild';
|
|
import { conflict, invalid, notAuthenticated, notFound, ok } from '../utils/responses';
|
|
|
|
export const InteractionsPickRole = interactionsEndpoint(
|
|
async (request: Request): Promise<Response> => {
|
|
const mode = request.method === 'PUT' ? 'add' : 'remove';
|
|
const reqURL = new URL(request.url);
|
|
const [, , guildID, userID, roleID] = reqURL.pathname.split('/');
|
|
if (!guildID || !userID || !roleID) {
|
|
return invalid();
|
|
}
|
|
|
|
const guildP = getGuild(guildID);
|
|
const guildDataP = getGuildData(guildID);
|
|
const guildMemberP = getGuildMember(
|
|
{ serverID: guildID, userID },
|
|
{ skipCachePull: true }
|
|
);
|
|
|
|
const [guild, guildData, guildMember] = await Promise.all([
|
|
guildP,
|
|
guildDataP,
|
|
guildMemberP,
|
|
]);
|
|
|
|
if (!guild || !guildData || !guildMember) {
|
|
return notFound();
|
|
}
|
|
|
|
let memberRoles = guildMember.roles;
|
|
|
|
if (
|
|
(mode === 'add' && memberRoles.includes(roleID)) ||
|
|
(mode !== 'add' && !memberRoles.includes(roleID))
|
|
) {
|
|
return conflict();
|
|
}
|
|
|
|
const roleMap = keyBy(guild.roles, 'id');
|
|
|
|
const category = guildData.categories.find((category) =>
|
|
category.roles.includes(roleID)
|
|
);
|
|
// No category? illegal.
|
|
if (!category) {
|
|
return notFound();
|
|
}
|
|
|
|
// Category is hidden, this is illegal
|
|
if (category.hidden) {
|
|
return notFound();
|
|
}
|
|
|
|
// Role is unsafe, super illegal.
|
|
if (roleMap[roleID].safety !== RoleSafety.Safe) {
|
|
return notAuthenticated();
|
|
}
|
|
|
|
// In add mode, if the category is a single-mode, remove the other roles in the category.
|
|
if (mode === 'add' && category.type === CategoryType.Single) {
|
|
memberRoles = difference(memberRoles, category.roles);
|
|
}
|
|
|
|
if (mode === 'add') {
|
|
memberRoles = [...memberRoles, roleID];
|
|
} else {
|
|
memberRoles = memberRoles.filter((id) => id !== roleID);
|
|
}
|
|
|
|
const patchMemberRoles = await discordFetch<Member>(
|
|
`/guilds/${guildID}/members/${userID}`,
|
|
botToken,
|
|
AuthType.Bot,
|
|
{
|
|
method: 'PATCH',
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
'x-audit-log-reason': `Picked their roles via slash command`,
|
|
},
|
|
body: JSON.stringify({
|
|
roles: memberRoles,
|
|
}),
|
|
}
|
|
);
|
|
|
|
if (!patchMemberRoles) {
|
|
return respond({ error: 'discord rejected the request' }, { status: 500 });
|
|
}
|
|
|
|
await updateGuildMember({ serverID: guildID, userID });
|
|
|
|
return ok();
|
|
}
|
|
);
|