mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-06-17 09:59:10 +00:00
feat: add /pickable-roles and /pick-role basis
This commit is contained in:
parent
c2ee4f380a
commit
a4207d5713
22 changed files with 343 additions and 23 deletions
23
packages/api/handlers/interactions-pick-role.ts
Normal file
23
packages/api/handlers/interactions-pick-role.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { interactionsEndpoint } from '../utils/api-tools';
|
||||
import { getGuildData } from '../utils/guild';
|
||||
import { invalid, notFound, ok } from '../utils/responses';
|
||||
|
||||
export const InteractionsPickRole = interactionsEndpoint(
|
||||
async (request: Request): Promise<Response> => {
|
||||
const reqURL = new URL(request.url);
|
||||
const [, , serverID, userID, roleID] = reqURL.pathname.split('/');
|
||||
if (!serverID || !userID || !roleID) {
|
||||
return invalid();
|
||||
}
|
||||
|
||||
const guildData = await getGuildData(serverID);
|
||||
if (!guildData) {
|
||||
return notFound();
|
||||
}
|
||||
|
||||
// We get exactly one role, but we have to interact with it the same way as UI does.
|
||||
// So check for safety, disable any "single" mode roles
|
||||
|
||||
return ok();
|
||||
}
|
||||
);
|
33
packages/api/handlers/interactions-pickable-roles.ts
Normal file
33
packages/api/handlers/interactions-pickable-roles.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { Category, CategorySlug } from '@roleypoly/types';
|
||||
import { respond } from '@roleypoly/worker-utils';
|
||||
import { interactionsEndpoint } from '../utils/api-tools';
|
||||
import { getGuildData } from '../utils/guild';
|
||||
import { notFound } from '../utils/responses';
|
||||
|
||||
export const InteractionsPickableRoles = interactionsEndpoint(
|
||||
async (request: Request): Promise<Response> => {
|
||||
const reqURL = new URL(request.url);
|
||||
const [, , serverID] = reqURL.pathname.split('/');
|
||||
|
||||
const guildData = await getGuildData(serverID);
|
||||
if (!guildData) {
|
||||
return notFound();
|
||||
}
|
||||
|
||||
const roleMap: Record<Category['name'], CategorySlug> = {};
|
||||
|
||||
for (let category of guildData.categories) {
|
||||
if (category.hidden) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: role safety?
|
||||
roleMap[category.name] = {
|
||||
roles: category.roles,
|
||||
type: category.type,
|
||||
};
|
||||
}
|
||||
|
||||
return respond(roleMap);
|
||||
}
|
||||
);
|
|
@ -1,3 +1,5 @@
|
|||
import { InteractionsPickRole } from '@roleypoly/api/handlers/interactions-pick-role';
|
||||
import { InteractionsPickableRoles } from '@roleypoly/api/handlers/interactions-pickable-roles';
|
||||
import { Router } from '@roleypoly/worker-utils/router';
|
||||
import { BotJoin } from './handlers/bot-join';
|
||||
import { ClearGuildCache } from './handlers/clear-guild-cache';
|
||||
|
@ -32,6 +34,10 @@ router.add('PATCH', 'update-guild', UpdateGuild);
|
|||
router.add('POST', 'sync-from-legacy', SyncFromLegacy);
|
||||
router.add('POST', 'clear-guild-cache', ClearGuildCache);
|
||||
|
||||
// Interactions endpoints
|
||||
router.add('GET', 'interactions-pickable-roles', InteractionsPickableRoles);
|
||||
router.add('POST', 'interactions-pick-role', InteractionsPickRole);
|
||||
|
||||
// Tester Routes
|
||||
router.add('GET', 'x-headers', (request) => {
|
||||
const headers: { [x: string]: string } = {};
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { notAuthenticated } from '@roleypoly/api/utils/responses';
|
||||
import {
|
||||
evaluatePermission,
|
||||
permissions as Permissions,
|
||||
|
@ -5,7 +6,12 @@ import {
|
|||
import { SessionData, UserGuildPermissions } from '@roleypoly/types';
|
||||
import { Handler, WrappedKVNamespace } from '@roleypoly/worker-utils';
|
||||
import KSUID from 'ksuid';
|
||||
import { allowedCallbackHosts, apiPublicURI, rootUsers } from './config';
|
||||
import {
|
||||
allowedCallbackHosts,
|
||||
apiPublicURI,
|
||||
interactionsSharedKey,
|
||||
rootUsers,
|
||||
} from './config';
|
||||
import { Sessions } from './kv';
|
||||
|
||||
export const formData = (obj: Record<string, any>): string => {
|
||||
|
@ -157,3 +163,14 @@ export const isAllowedCallbackHost = (host: string): boolean => {
|
|||
null
|
||||
);
|
||||
};
|
||||
|
||||
export const interactionsEndpoint =
|
||||
(handler: Handler): Handler =>
|
||||
async (request: Request): Promise<Response> => {
|
||||
const authHeader = request.headers.get('authorization') || '';
|
||||
if (authHeader !== `Shared ${interactionsSharedKey}`) {
|
||||
return notAuthenticated();
|
||||
}
|
||||
|
||||
return handler(request);
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@ import { uiPublicURI } from '@roleypoly/api/utils/config';
|
|||
import {
|
||||
Category,
|
||||
DiscordUser,
|
||||
Embed,
|
||||
GuildData,
|
||||
GuildDataUpdate,
|
||||
GuildSlug,
|
||||
|
@ -11,25 +12,10 @@ import { userAgent } from '@roleypoly/worker-utils';
|
|||
import deepEqual from 'deep-equal';
|
||||
import { sortBy, uniq } from 'lodash';
|
||||
|
||||
type WebhookEmbed = {
|
||||
fields: {
|
||||
name: string;
|
||||
value: string;
|
||||
inline: boolean;
|
||||
}[];
|
||||
timestamp: string;
|
||||
title: string;
|
||||
color: number;
|
||||
author?: {
|
||||
name: string;
|
||||
icon_url: string;
|
||||
};
|
||||
};
|
||||
|
||||
type WebhookPayload = {
|
||||
username: string;
|
||||
avatar_url: string;
|
||||
embeds: WebhookEmbed[];
|
||||
embeds: Embed[];
|
||||
provider: {
|
||||
name: string;
|
||||
url: string;
|
||||
|
@ -39,7 +25,7 @@ type WebhookPayload = {
|
|||
type ChangeHandler = (
|
||||
oldValue: GuildDataUpdate[keyof GuildDataUpdate],
|
||||
newValue: GuildData[keyof GuildDataUpdate]
|
||||
) => WebhookEmbed[];
|
||||
) => Embed[];
|
||||
|
||||
const changeHandlers: Record<keyof GuildDataUpdate, ChangeHandler> = {
|
||||
message: (oldValue, newValue) => [
|
||||
|
|
|
@ -13,3 +13,4 @@ export const apiPublicURI = safeURI(env('API_PUBLIC_URI'));
|
|||
export const rootUsers = list(env('ROOT_USERS'));
|
||||
export const allowedCallbackHosts = list(env('ALLOWED_CALLBACK_HOSTS'));
|
||||
export const importSharedKey = env('BOT_IMPORT_TOKEN');
|
||||
export const interactionsSharedKey = env('INTERACTIONS_SHARED_KEY');
|
||||
|
|
|
@ -20,3 +20,6 @@ export const rateLimited = () =>
|
|||
|
||||
export const invalid = (obj: any = {}) =>
|
||||
respond({ err: 'client sent something invalid', data: obj }, { status: 400 });
|
||||
|
||||
export const notAuthenticated = () =>
|
||||
respond({ err: 'not authenticated' }, { status: 403 });
|
||||
|
|
|
@ -12,6 +12,7 @@ module.exports = {
|
|||
'API_PUBLIC_URI',
|
||||
'ROOT_USERS',
|
||||
'ALLOWED_CALLBACK_HOSTS',
|
||||
'INTERACTIONS_SHARED_KEY',
|
||||
]),
|
||||
kv: ['KV_SESSIONS', 'KV_GUILDS', 'KV_GUILD_DATA'],
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue