From 97d607871c1c1f6a31f6a1ad705fc4d1761bb861 Mon Sep 17 00:00:00 2001 From: nin0 Date: Sat, 24 May 2025 09:34:32 -0400 Subject: [PATCH] threading & locked applications --- src/Constants.ts | 8 +- src/components/ApplicationContent.tsx | 47 ++++ src/components/PendingApplicationMessage.tsx | 112 ++++++++++ src/joinRequestHandler.tsx | 214 +++++++------------ src/selfappReq.ts | 15 +- 5 files changed, 259 insertions(+), 137 deletions(-) create mode 100644 src/components/ApplicationContent.tsx create mode 100644 src/components/PendingApplicationMessage.tsx diff --git a/src/Constants.ts b/src/Constants.ts index c8fbc6e..b0d4873 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -1,4 +1,10 @@ export const Constants = { PENDING_CHANNEL_ID: "1370539719842070683", - REJECTION_CHANNEL_ID: "1371073658403164261" + REJECTION_CHANNEL_ID: "1371073658403164261", + COLORS: { + NEW_REQ: 0xaaaaff, + BAD: 0xffaaaa, + YELLOW: 0xeadb77, + GOOD: 0xaaffaa + } }; diff --git a/src/components/ApplicationContent.tsx b/src/components/ApplicationContent.tsx new file mode 100644 index 0000000..97a14c8 --- /dev/null +++ b/src/components/ApplicationContent.tsx @@ -0,0 +1,47 @@ +import { TextDisplay } from "components-jsx/TextDisplay"; +import { Response } from "~/types"; + +export function ApplicationContent(props: { + response: Response; + includeDetails?: boolean; +}) { + const { response } = props; + return ( + + ### Application +
+ Here for +
+ -# {response.hereFor.join(", ")} +
+ Found through +
+ -#{" "} + {(() => { + switch (response.foundThrough) { + case "archive": + return "Guild Archive/nelly.tools"; + case "tag": + return 'Hit "Ask to Join" on a clan tag'; + case "user": + return "User invited them\n-# **Only accept if someone is mentioned in details**"; + case "other": + return "Something else"; + case "sold": + return "Was sold an invite\n-# **Only the owner can interact with this application**"; + } + })()} +
+ Languages +
+ -# {response.languages} +
+ Can boost +
+ -# {response.canBoost ? "Yes" : "No"} + {props.includeDetails + ? `\nDetails\n-# ${response.details.replaceAll("\n", "\n-# ")}` + : ""} +
+ ); +} diff --git a/src/components/PendingApplicationMessage.tsx b/src/components/PendingApplicationMessage.tsx new file mode 100644 index 0000000..e5f6565 --- /dev/null +++ b/src/components/PendingApplicationMessage.tsx @@ -0,0 +1,112 @@ +import { ActionRow } from "components-jsx/ActionRow"; +import { Button } from "components-jsx/Button"; +import { ComponentMessage } from "components-jsx/ComponentMessage"; +import { Container } from "components-jsx/Container"; +import { Divider } from "components-jsx/Divider"; +import { TextDisplay } from "components-jsx/TextDisplay"; +import { ButtonStyles, User as OUser } from "oceanic.js"; +import { Response } from "~/types"; +import { User } from "./User"; +import { Constants } from "~/Constants"; +import { ApplicationContent } from "./ApplicationContent"; + +export function PendingApplicationMessage(props: { + id: string; + user: OUser; + response: Response; + locked?: boolean; +}) { + const { id, user, response } = props; + const locked = props.locked || false; + return ( + + {"<@&1375617676139040909>"} + + ## New join request + + + + + + + ### Details +
+ {response.details} +
+ + + + + + + + + + + + + +
+
+ ); +} diff --git a/src/joinRequestHandler.tsx b/src/joinRequestHandler.tsx index 8e9a1b6..597d88d 100644 --- a/src/joinRequestHandler.tsx +++ b/src/joinRequestHandler.tsx @@ -16,6 +16,8 @@ import { selfappReq } from "./selfappReq"; import { User } from "./components/User"; import { Response } from "./types"; import { match } from "./utils"; +import { PendingApplicationMessage } from "./components/PendingApplicationMessage"; +import { ApplicationContent } from "./components/ApplicationContent"; export async function setupJoinRequestHandler(shard: Shard) { shard.ws?.on("message", async (d) => { @@ -52,140 +54,51 @@ export async function setupJoinRequestHandler(shard: Shard) { const pendingMsg = await client.rest.channels.createMessage( Constants.PENDING_CHANNEL_ID, - - - {"<@&1375617676139040909>"} - - - - ## New join request - - - - - - ### Application -
- Here for -
- -# {response.hereFor.join(", ")} -
- Found through -
- -#{" "} - {(() => { - switch ( - response.foundThrough - ) { - case "archive": - return "Guild Archive/nelly.tools"; - case "tag": - return 'Hit "Ask to Join" on a clan tag'; - case "user": - return "User invited them\n-# **Only accept if someone is mentioned in details**"; - case "other": - return "Something else"; - case "sold": - return "Was sold an invite\n-# **Only the owner can interact with this application**"; - } - })()} -
- Languages -
- -# {response.languages} -
- Can boost -
- -#{" "} - {response.canBoost - ? "Yes" - : "No"} -
- - - ### Details -
- {response.details} -
- - - - - - - - - - - - - -
-
+ ); + + const thread = await pendingMsg.startThread({ + name: `${user.tag}'s application`, + autoArchiveDuration: 10080 + }); + + const fullApplicantProfile: any = await selfappReq( + `/users/${user.id}/profile`, + "GET", + { + join_request_id: applicationData.request.id + } + ); + + thread.createMessage({ + embeds: [ + { + color: Constants.COLORS.NEW_REQ, + title: "User bio", + description: + "```\n" + + fullApplicantProfile.user_profile + .bio + + "\n```" + } + ] + }); + await db.run( - "INSERT INTO applications (id, user_id, status, message_id, channel_id, responses) VALUES (?, ?, 0, ?, ?, ?)", + "INSERT INTO applications (id, user_id, status, message_id, channel_id, responses, thread_id) VALUES (?, ?, 0, ?, ?, ?, ?)", applicationData.request.id, applicationData.request.user_id, pendingMsg.id, pendingMsg.channelID, - JSON.stringify(response) + JSON.stringify(response), + thread.id ); break; @@ -198,6 +111,10 @@ export async function setupJoinRequestHandler(shard: Shard) { "SELECT * FROM applications WHERE id=?", applicationData.id ); + const user = await client.rest.users.get( + applicationData.user_id + ); + if (application) { await db.run( "DELETE FROM applications WHERE id=?", @@ -208,30 +125,59 @@ export async function setupJoinRequestHandler(shard: Shard) { application.message_id ); } - const user = await client.rest.users.get( - applicationData.user_id + + const response: Response = JSON.parse( + application.responses ); - await client.rest.channels.createMessage( + + const msg = await client.rest.channels.createMessage( Constants.REJECTION_CHANNEL_ID, - + ## Join request withdrawn - ### Application + {application ? ( - User has applied + ) : ( + ### Application +
User hasn't applied
)}
); + + await client.rest.request({ + auth: `Bot ${process.env.BOT_TOKEN}`, + method: "POST", + path: `/channels/${application.thread_id}/messages`, + json: { + content: "", + flags: 0, + message_reference: { + channel_id: msg.channelID, + guild_id: msg.guildID!, + message_id: msg.id, + type: 1 + } + } + }); + + if (application) + await client.rest.channels.edit(application.thread_id, { + locked: true, + reason: `${user.tag}'s application has been withdrawn` + }); break; } } diff --git a/src/selfappReq.ts b/src/selfappReq.ts index 3ab9b56..a2d5e11 100644 --- a/src/selfappReq.ts +++ b/src/selfappReq.ts @@ -3,12 +3,23 @@ export async function selfappReq( method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", data?: object ) { - return await fetch("https://discord.com/api/v9" + path, { + const url = + "https://discord.com/api/v9" + + path + + (() => { + if (!data || method !== "GET") return ""; + // @ts-expect-error + return `?${new URLSearchParams(data).toString()}`; + })(); + + console.log(url); + + return await fetch(url, { method, headers: { Authorization: process.env.USER_TOKEN!, ...(data ? { "Content-Type": "application/json" } : {}) }, - body: data ? JSON.stringify(data || {}) : null + body: data && method !== "GET" ? JSON.stringify(data || {}) : null }).then((e) => e.json()); }