threading & locked applications
This commit is contained in:
parent
9191b156d7
commit
97d607871c
5 changed files with 259 additions and 137 deletions
|
@ -1,4 +1,10 @@
|
||||||
export const Constants = {
|
export const Constants = {
|
||||||
PENDING_CHANNEL_ID: "1370539719842070683",
|
PENDING_CHANNEL_ID: "1370539719842070683",
|
||||||
REJECTION_CHANNEL_ID: "1371073658403164261"
|
REJECTION_CHANNEL_ID: "1371073658403164261",
|
||||||
|
COLORS: {
|
||||||
|
NEW_REQ: 0xaaaaff,
|
||||||
|
BAD: 0xffaaaa,
|
||||||
|
YELLOW: 0xeadb77,
|
||||||
|
GOOD: 0xaaffaa
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
47
src/components/ApplicationContent.tsx
Normal file
47
src/components/ApplicationContent.tsx
Normal file
|
@ -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 (
|
||||||
|
<TextDisplay>
|
||||||
|
### Application
|
||||||
|
<br />
|
||||||
|
Here for
|
||||||
|
<br />
|
||||||
|
-# {response.hereFor.join(", ")}
|
||||||
|
<br />
|
||||||
|
Found through
|
||||||
|
<br />
|
||||||
|
-#{" "}
|
||||||
|
{(() => {
|
||||||
|
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**";
|
||||||
|
}
|
||||||
|
})()}
|
||||||
|
<br />
|
||||||
|
Languages
|
||||||
|
<br />
|
||||||
|
-# {response.languages}
|
||||||
|
<br />
|
||||||
|
Can boost
|
||||||
|
<br />
|
||||||
|
-# {response.canBoost ? "Yes" : "No"}
|
||||||
|
{props.includeDetails
|
||||||
|
? `\nDetails\n-# ${response.details.replaceAll("\n", "\n-# ")}`
|
||||||
|
: ""}
|
||||||
|
</TextDisplay>
|
||||||
|
);
|
||||||
|
}
|
112
src/components/PendingApplicationMessage.tsx
Normal file
112
src/components/PendingApplicationMessage.tsx
Normal file
|
@ -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 (
|
||||||
|
<ComponentMessage>
|
||||||
|
<TextDisplay>{"<@&1375617676139040909>"}</TextDisplay>
|
||||||
|
<Container
|
||||||
|
accentColor={
|
||||||
|
!locked ? Constants.COLORS.NEW_REQ : Constants.COLORS.YELLOW
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<TextDisplay>## New join request</TextDisplay>
|
||||||
|
<Divider />
|
||||||
|
<User user={user} />
|
||||||
|
<Divider />
|
||||||
|
<ApplicationContent response={response} />
|
||||||
|
<Divider />
|
||||||
|
<TextDisplay>
|
||||||
|
### Details
|
||||||
|
<br />
|
||||||
|
{response.details}
|
||||||
|
</TextDisplay>
|
||||||
|
<Divider />
|
||||||
|
<ActionRow>
|
||||||
|
<Button
|
||||||
|
style={ButtonStyles.SUCCESS}
|
||||||
|
customID={`accept-${id}`}
|
||||||
|
emoji={{
|
||||||
|
name: "blobcatgreen",
|
||||||
|
id: "1375806000312881233"
|
||||||
|
}}
|
||||||
|
disabled={locked}
|
||||||
|
>
|
||||||
|
Accept
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
style={ButtonStyles.SUCCESS}
|
||||||
|
customID={`accept-friend-${id}`}
|
||||||
|
emoji={{
|
||||||
|
name: "blobcatgreen",
|
||||||
|
id: "1375806000312881233"
|
||||||
|
}}
|
||||||
|
disabled={locked}
|
||||||
|
>
|
||||||
|
Accept + Friend
|
||||||
|
</Button>
|
||||||
|
</ActionRow>
|
||||||
|
<ActionRow>
|
||||||
|
<Button
|
||||||
|
style={ButtonStyles.DANGER}
|
||||||
|
customID={`reject-${id}`}
|
||||||
|
emoji={{
|
||||||
|
name: "blobcatred",
|
||||||
|
id: "1375806202470203513"
|
||||||
|
}}
|
||||||
|
disabled={locked}
|
||||||
|
>
|
||||||
|
Deny
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
style={ButtonStyles.DANGER}
|
||||||
|
customID={`reject-ban-${id}`}
|
||||||
|
emoji={{
|
||||||
|
name: "BAN",
|
||||||
|
id: "1375806319621046313"
|
||||||
|
}}
|
||||||
|
disabled={locked}
|
||||||
|
>
|
||||||
|
Deny + Ban
|
||||||
|
</Button>
|
||||||
|
</ActionRow>
|
||||||
|
<ActionRow>
|
||||||
|
<Button
|
||||||
|
style={ButtonStyles.SECONDARY}
|
||||||
|
customID={`interview-${id}`}
|
||||||
|
emoji={{
|
||||||
|
name: "💬"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Interview
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
style={ButtonStyles.SECONDARY}
|
||||||
|
customID={`${locked ? "un" : ""}lock-${id}`}
|
||||||
|
emoji={{
|
||||||
|
name: "🔑"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{!locked ? "Lock" : "Unlock"}
|
||||||
|
</Button>
|
||||||
|
</ActionRow>
|
||||||
|
</Container>
|
||||||
|
</ComponentMessage>
|
||||||
|
);
|
||||||
|
}
|
|
@ -16,6 +16,8 @@ import { selfappReq } from "./selfappReq";
|
||||||
import { User } from "./components/User";
|
import { User } from "./components/User";
|
||||||
import { Response } from "./types";
|
import { Response } from "./types";
|
||||||
import { match } from "./utils";
|
import { match } from "./utils";
|
||||||
|
import { PendingApplicationMessage } from "./components/PendingApplicationMessage";
|
||||||
|
import { ApplicationContent } from "./components/ApplicationContent";
|
||||||
|
|
||||||
export async function setupJoinRequestHandler(shard: Shard) {
|
export async function setupJoinRequestHandler(shard: Shard) {
|
||||||
shard.ws?.on("message", async (d) => {
|
shard.ws?.on("message", async (d) => {
|
||||||
|
@ -52,140 +54,51 @@ export async function setupJoinRequestHandler(shard: Shard) {
|
||||||
const pendingMsg =
|
const pendingMsg =
|
||||||
await client.rest.channels.createMessage(
|
await client.rest.channels.createMessage(
|
||||||
Constants.PENDING_CHANNEL_ID,
|
Constants.PENDING_CHANNEL_ID,
|
||||||
<ComponentMessage>
|
<PendingApplicationMessage
|
||||||
<TextDisplay>
|
id={applicationData.request.id}
|
||||||
{"<@&1375617676139040909>"}
|
response={response}
|
||||||
</TextDisplay>
|
user={user}
|
||||||
<Container accentColor={0xaaaaff}>
|
locked={
|
||||||
<TextDisplay>
|
response.foundThrough === "sold"
|
||||||
## New join request
|
}
|
||||||
</TextDisplay>
|
/>
|
||||||
<Divider />
|
|
||||||
<User user={user} />
|
|
||||||
<Divider />
|
|
||||||
<TextDisplay>
|
|
||||||
### Application
|
|
||||||
<br />
|
|
||||||
Here for
|
|
||||||
<br />
|
|
||||||
-# {response.hereFor.join(", ")}
|
|
||||||
<br />
|
|
||||||
Found through
|
|
||||||
<br />
|
|
||||||
-#{" "}
|
|
||||||
{(() => {
|
|
||||||
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**";
|
|
||||||
}
|
|
||||||
})()}
|
|
||||||
<br />
|
|
||||||
Languages
|
|
||||||
<br />
|
|
||||||
-# {response.languages}
|
|
||||||
<br />
|
|
||||||
Can boost
|
|
||||||
<br />
|
|
||||||
-#{" "}
|
|
||||||
{response.canBoost
|
|
||||||
? "Yes"
|
|
||||||
: "No"}
|
|
||||||
</TextDisplay>
|
|
||||||
<Divider />
|
|
||||||
<TextDisplay>
|
|
||||||
### Details
|
|
||||||
<br />
|
|
||||||
{response.details}
|
|
||||||
</TextDisplay>
|
|
||||||
<Divider />
|
|
||||||
<ActionRow>
|
|
||||||
<Button
|
|
||||||
style={ButtonStyles.SUCCESS}
|
|
||||||
customID={`accept-${applicationData.request.id}`}
|
|
||||||
emoji={{
|
|
||||||
name: "blobcatgreen",
|
|
||||||
id: "1375806000312881233"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Accept
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
style={ButtonStyles.SUCCESS}
|
|
||||||
customID={`accept-friend-${applicationData.request.id}`}
|
|
||||||
emoji={{
|
|
||||||
name: "blobcatgreen",
|
|
||||||
id: "1375806000312881233"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Accept + Friend
|
|
||||||
</Button>
|
|
||||||
</ActionRow>
|
|
||||||
<ActionRow>
|
|
||||||
<Button
|
|
||||||
style={ButtonStyles.DANGER}
|
|
||||||
customID={`reject-${applicationData.request.id}`}
|
|
||||||
emoji={{
|
|
||||||
name: "blobcatred",
|
|
||||||
id: "1375806202470203513"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Deny
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
style={ButtonStyles.DANGER}
|
|
||||||
customID={`reject-ban-${applicationData.request.id}`}
|
|
||||||
emoji={{
|
|
||||||
name: "BAN",
|
|
||||||
id: "1375806319621046313"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Deny + Ban
|
|
||||||
</Button>
|
|
||||||
</ActionRow>
|
|
||||||
<ActionRow>
|
|
||||||
<Button
|
|
||||||
style={
|
|
||||||
ButtonStyles.SECONDARY
|
|
||||||
}
|
|
||||||
customID={`interview-${applicationData.request.id}`}
|
|
||||||
emoji={{
|
|
||||||
name: "💬"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Interview
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
style={
|
|
||||||
ButtonStyles.SECONDARY
|
|
||||||
}
|
|
||||||
customID={`lock-${applicationData.request.id}`}
|
|
||||||
emoji={{
|
|
||||||
name: "🔑"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Lock
|
|
||||||
</Button>
|
|
||||||
</ActionRow>
|
|
||||||
</Container>
|
|
||||||
</ComponentMessage>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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(
|
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.id,
|
||||||
applicationData.request.user_id,
|
applicationData.request.user_id,
|
||||||
pendingMsg.id,
|
pendingMsg.id,
|
||||||
pendingMsg.channelID,
|
pendingMsg.channelID,
|
||||||
JSON.stringify(response)
|
JSON.stringify(response),
|
||||||
|
thread.id
|
||||||
);
|
);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -198,6 +111,10 @@ export async function setupJoinRequestHandler(shard: Shard) {
|
||||||
"SELECT * FROM applications WHERE id=?",
|
"SELECT * FROM applications WHERE id=?",
|
||||||
applicationData.id
|
applicationData.id
|
||||||
);
|
);
|
||||||
|
const user = await client.rest.users.get(
|
||||||
|
applicationData.user_id
|
||||||
|
);
|
||||||
|
|
||||||
if (application) {
|
if (application) {
|
||||||
await db.run(
|
await db.run(
|
||||||
"DELETE FROM applications WHERE id=?",
|
"DELETE FROM applications WHERE id=?",
|
||||||
|
@ -208,30 +125,59 @@ export async function setupJoinRequestHandler(shard: Shard) {
|
||||||
application.message_id
|
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,
|
Constants.REJECTION_CHANNEL_ID,
|
||||||
<ComponentMessage>
|
<ComponentMessage>
|
||||||
<Container accentColor={0xffaaaa}>
|
<Container accentColor={Constants.COLORS.BAD}>
|
||||||
<TextDisplay>
|
<TextDisplay>
|
||||||
## Join request withdrawn
|
## Join request withdrawn
|
||||||
</TextDisplay>
|
</TextDisplay>
|
||||||
<Divider />
|
<Divider />
|
||||||
<User user={user} />
|
<User user={user} />
|
||||||
<Divider />
|
<Divider />
|
||||||
<TextDisplay>### Application</TextDisplay>
|
|
||||||
{application ? (
|
{application ? (
|
||||||
<TextDisplay>User has applied</TextDisplay>
|
<ApplicationContent
|
||||||
|
response={response}
|
||||||
|
includeDetails={true}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<TextDisplay>
|
<TextDisplay>
|
||||||
|
### Application
|
||||||
|
<br />
|
||||||
User hasn't applied
|
User hasn't applied
|
||||||
</TextDisplay>
|
</TextDisplay>
|
||||||
)}
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
</ComponentMessage>
|
</ComponentMessage>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,23 @@ export async function selfappReq(
|
||||||
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE",
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE",
|
||||||
data?: object
|
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,
|
method,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: process.env.USER_TOKEN!,
|
Authorization: process.env.USER_TOKEN!,
|
||||||
...(data ? { "Content-Type": "application/json" } : {})
|
...(data ? { "Content-Type": "application/json" } : {})
|
||||||
},
|
},
|
||||||
body: data ? JSON.stringify(data || {}) : null
|
body: data && method !== "GET" ? JSON.stringify(data || {}) : null
|
||||||
}).then((e) => e.json());
|
}).then((e) => e.json());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue