Compare commits
2 commits
97d607871c
...
7dcc0c2cc4
Author | SHA1 | Date | |
---|---|---|---|
7dcc0c2cc4 | |||
e377117d33 |
14 changed files with 303 additions and 110 deletions
|
@ -1,14 +1,26 @@
|
|||
import { CreateMessageOptions, EditMessageOptions, MessageComponent, MessageFlags } from "oceanic.js";
|
||||
import {
|
||||
CreateMessageOptions,
|
||||
EditMessageOptions,
|
||||
InteractionOptions,
|
||||
MessageComponent,
|
||||
MessageFlags
|
||||
} from "oceanic.js";
|
||||
|
||||
import { childrenToArray } from "./utils";
|
||||
|
||||
type MessageOptions = CreateMessageOptions | EditMessageOptions;
|
||||
export type ComponentMessageProps = MessageOptions & { children: MessageComponent[]; };
|
||||
export type ComponentMessageProps = MessageOptions & {
|
||||
children: MessageComponent[];
|
||||
};
|
||||
|
||||
export function ComponentMessage({ children, flags, ...props }: ComponentMessageProps): MessageOptions {
|
||||
return {
|
||||
flags: MessageFlags.IS_COMPONENTS_V2 | (flags ?? 0),
|
||||
components: childrenToArray(children),
|
||||
...props
|
||||
};
|
||||
export function ComponentMessage({
|
||||
children,
|
||||
flags,
|
||||
...props
|
||||
}: ComponentMessageProps): MessageOptions {
|
||||
return {
|
||||
flags: MessageFlags.IS_COMPONENTS_V2 | (flags ?? 0),
|
||||
components: childrenToArray(children),
|
||||
...props
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
export const Constants = {
|
||||
PENDING_CHANNEL_ID: "1370539719842070683",
|
||||
REJECTION_CHANNEL_ID: "1371073658403164261",
|
||||
REVIEWER_ROLE_ID: "1375617676139040909",
|
||||
MOD_ROLE_ID: "1370539692143153152",
|
||||
MANAGER_ROLE_ID: "1370539689659863091",
|
||||
OWNER_ID: "886685857560539176",
|
||||
COLORS: {
|
||||
NEW_REQ: 0xaaaaff,
|
||||
BAD: 0xffaaaa,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { TextDisplay } from "components-jsx/TextDisplay";
|
||||
import { Response } from "~/types";
|
||||
import { Response } from "~/utils/types";
|
||||
|
||||
export function ApplicationContent(props: {
|
||||
response: Response;
|
||||
|
|
|
@ -5,16 +5,19 @@ 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 { Response } from "~/utils/types";
|
||||
import { User } from "./User";
|
||||
import { Constants } from "~/Constants";
|
||||
import { ApplicationContent } from "./ApplicationContent";
|
||||
import { StringSelect } from "components-jsx/StringSelect";
|
||||
import { rejectReasons } from "~/utils/rejectReasons";
|
||||
|
||||
export function PendingApplicationMessage(props: {
|
||||
id: string;
|
||||
user: OUser;
|
||||
response: Response;
|
||||
locked?: boolean;
|
||||
threadID: string;
|
||||
}) {
|
||||
const { id, user, response } = props;
|
||||
const locked = props.locked || false;
|
||||
|
@ -30,83 +33,84 @@ export function PendingApplicationMessage(props: {
|
|||
<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>
|
||||
<ApplicationContent response={response} includeDetails={true} />
|
||||
</Container>
|
||||
<ActionRow>
|
||||
<Button
|
||||
style={ButtonStyles.SUCCESS}
|
||||
customID={`accept-${id}`}
|
||||
emoji={{
|
||||
name: "blobcatgreen",
|
||||
id: "1375806000312881233"
|
||||
}}
|
||||
disabled={locked}
|
||||
/>
|
||||
<Button
|
||||
style={ButtonStyles.SUCCESS}
|
||||
customID={`accept-friend-${id}`}
|
||||
emoji={{
|
||||
name: "hugcatcozy",
|
||||
id: "1375955808826687508"
|
||||
}}
|
||||
disabled={locked}
|
||||
/>
|
||||
<Button
|
||||
style={ButtonStyles.SECONDARY}
|
||||
customID={`interview-${id}`}
|
||||
emoji={{
|
||||
name: "💬"
|
||||
}}
|
||||
/>
|
||||
</ActionRow>
|
||||
<ActionRow>
|
||||
<Button
|
||||
style={ButtonStyles.DANGER}
|
||||
customID={`reject-${id}`}
|
||||
emoji={{
|
||||
name: "blobcatred",
|
||||
id: "1375806202470203513"
|
||||
}}
|
||||
disabled={locked}
|
||||
/>
|
||||
<Button
|
||||
style={ButtonStyles.DANGER}
|
||||
customID={`reject-ban-${id}`}
|
||||
emoji={{
|
||||
name: "BAN",
|
||||
id: "1375806319621046313"
|
||||
}}
|
||||
disabled={locked}
|
||||
/>
|
||||
<Button
|
||||
style={ButtonStyles.SECONDARY}
|
||||
customID={`${locked ? "un" : ""}lock-${id}`}
|
||||
emoji={{
|
||||
name: "🔑"
|
||||
}}
|
||||
/>
|
||||
</ActionRow>
|
||||
<ActionRow>
|
||||
<StringSelect
|
||||
customID="deny-reasons"
|
||||
placeholder="Deny reasons"
|
||||
disabled={locked}
|
||||
// @ts-expect-error
|
||||
options={Object.keys(rejectReasons).map((key) => {
|
||||
return {
|
||||
label: rejectReasons[key].shortDesc,
|
||||
description:
|
||||
rejectReasons[key].reason.length > 100
|
||||
? `${rejectReasons[key].reason.slice(
|
||||
0,
|
||||
97
|
||||
)}...`
|
||||
: rejectReasons[key].reason,
|
||||
value: key
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</ActionRow>
|
||||
<TextDisplay>-# Discuss in {`<#${props.threadID}>`}</TextDisplay>
|
||||
</ComponentMessage>
|
||||
);
|
||||
}
|
||||
|
|
10
src/index.ts
10
src/index.ts
|
@ -1,8 +1,8 @@
|
|||
import { AllIntents, Client, ComponentTypes, MessageFlags } from "oceanic.js";
|
||||
import { selfappReq } from "./selfappReq";
|
||||
import { selfappReq } from "./utils/selfappReq";
|
||||
import { setupJoinRequestHandler } from "./joinRequestHandler";
|
||||
import { Constants } from "./Constants";
|
||||
import { openDb } from "./database";
|
||||
import { openDb } from "./utils/database";
|
||||
|
||||
export const client = new Client({
|
||||
auth: `Bot ${process.env.BOT_TOKEN}`,
|
||||
|
@ -22,6 +22,12 @@ client.on("ready", async () => {
|
|||
setupJoinRequestHandler(client.shards.first()!);
|
||||
});
|
||||
|
||||
client.on("messageCreate", (msg) => {
|
||||
if (msg.messageReference && msg.author.id === client.user.id) {
|
||||
if (!msg.messageReference.messageID) msg.delete();
|
||||
}
|
||||
});
|
||||
|
||||
process.on("uncaughtException", (e) => {
|
||||
console.error(e);
|
||||
});
|
||||
|
|
37
src/joinRequestActions/interview.ts
Normal file
37
src/joinRequestActions/interview.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { ComponentTypes, InteractionTypes, MessageFlags } from "oceanic.js";
|
||||
import { client } from "..";
|
||||
import { canUser } from "../utils/utils";
|
||||
import { selfappReq } from "../utils/selfappReq";
|
||||
|
||||
client.on("interactionCreate", async (interaction) => {
|
||||
if (interaction.type === InteractionTypes.MESSAGE_COMPONENT)
|
||||
if (interaction.data.componentType === ComponentTypes.BUTTON) {
|
||||
if (interaction.data.customID.split("-")[0] === "interview") {
|
||||
await interaction.defer(MessageFlags.EPHEMERAL);
|
||||
if (!canUser(interaction.member!, "review"))
|
||||
return await interaction.createFollowup({
|
||||
flags: MessageFlags.EPHEMERAL,
|
||||
content: "💢 nop"
|
||||
});
|
||||
const gdmID = (
|
||||
(await selfappReq(
|
||||
`/join-requests/${
|
||||
interaction.data.customID.split("-")[1]
|
||||
}/interview`,
|
||||
"POST"
|
||||
)) as any
|
||||
).id;
|
||||
const gdmInvite = (
|
||||
(await selfappReq(
|
||||
`/channels/${gdmID}/invites`,
|
||||
"POST"
|
||||
)) as any
|
||||
).code;
|
||||
|
||||
return await interaction.createFollowup({
|
||||
flags: MessageFlags.EPHEMERAL,
|
||||
content: `https://discord.gg/${gdmInvite}`
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
55
src/joinRequestActions/lockUnlock.tsx
Normal file
55
src/joinRequestActions/lockUnlock.tsx
Normal file
|
@ -0,0 +1,55 @@
|
|||
import { ComponentTypes, InteractionTypes, MessageFlags } from "oceanic.js";
|
||||
import { client } from "..";
|
||||
import { db } from "../utils/database";
|
||||
import { PendingApplicationMessage } from "../components/PendingApplicationMessage";
|
||||
import { canUser } from "../utils/utils";
|
||||
|
||||
client.on("interactionCreate", async (interaction) => {
|
||||
if (interaction.type === InteractionTypes.MESSAGE_COMPONENT)
|
||||
if (interaction.data.componentType === ComponentTypes.BUTTON) {
|
||||
if (!interaction.data.customID.includes("lock")) return;
|
||||
|
||||
if (!canUser(interaction.member!, "owner"))
|
||||
return await interaction.createMessage({
|
||||
flags: MessageFlags.EPHEMERAL,
|
||||
content: "💢 nop"
|
||||
});
|
||||
|
||||
const application = await db.get(
|
||||
"SELECT * FROM applications WHERE message_id=?",
|
||||
interaction.message.id
|
||||
);
|
||||
console.log(application);
|
||||
|
||||
switch (interaction.data.customID.split("-")[0]) {
|
||||
case "lock": {
|
||||
await interaction.editParent(
|
||||
<PendingApplicationMessage
|
||||
id={application.id}
|
||||
response={JSON.parse(application.responses)}
|
||||
user={await client.rest.users.get(
|
||||
application.user_id
|
||||
)}
|
||||
locked={true}
|
||||
threadID={application.thread_id}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "unlock": {
|
||||
await interaction.editParent(
|
||||
<PendingApplicationMessage
|
||||
id={application.id}
|
||||
response={JSON.parse(application.responses)}
|
||||
user={await client.rest.users.get(
|
||||
application.user_id
|
||||
)}
|
||||
locked={false}
|
||||
threadID={application.thread_id}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,7 +1,13 @@
|
|||
import { ButtonStyles, ComponentTypes, MessageFlags, Shard } from "oceanic.js";
|
||||
import {
|
||||
ButtonStyles,
|
||||
ChannelTypes,
|
||||
ComponentTypes,
|
||||
MessageFlags,
|
||||
Shard
|
||||
} from "oceanic.js";
|
||||
import { client } from ".";
|
||||
import { Constants } from "./Constants";
|
||||
import { db } from "./database";
|
||||
import { db } from "./utils/database";
|
||||
import {
|
||||
ActionRow,
|
||||
Button,
|
||||
|
@ -12,13 +18,16 @@ import {
|
|||
Separator,
|
||||
TextDisplay
|
||||
} from "~/components";
|
||||
import { selfappReq } from "./selfappReq";
|
||||
import { selfappReq } from "./utils/selfappReq";
|
||||
import { User } from "./components/User";
|
||||
import { Response } from "./types";
|
||||
import { match } from "./utils";
|
||||
import { Response } from "./utils/types";
|
||||
import { match } from "./utils/utils";
|
||||
import { PendingApplicationMessage } from "./components/PendingApplicationMessage";
|
||||
import { ApplicationContent } from "./components/ApplicationContent";
|
||||
|
||||
import("./joinRequestActions/lockUnlock");
|
||||
import("./joinRequestActions/interview");
|
||||
|
||||
export async function setupJoinRequestHandler(shard: Shard) {
|
||||
shard.ws?.on("message", async (d) => {
|
||||
const data = JSON.parse(d.toString("utf8"));
|
||||
|
@ -51,6 +60,16 @@ export async function setupJoinRequestHandler(shard: Shard) {
|
|||
const user = await client.rest.users.get(
|
||||
applicationData.request.user.id
|
||||
);
|
||||
const thread =
|
||||
await client.rest.channels.startThreadWithoutMessage(
|
||||
Constants.PENDING_CHANNEL_ID,
|
||||
{
|
||||
name: `${user.tag}'s application`,
|
||||
type: ChannelTypes.PUBLIC_THREAD,
|
||||
autoArchiveDuration: 10080
|
||||
}
|
||||
);
|
||||
|
||||
const pendingMsg =
|
||||
await client.rest.channels.createMessage(
|
||||
Constants.PENDING_CHANNEL_ID,
|
||||
|
@ -61,14 +80,10 @@ export async function setupJoinRequestHandler(shard: Shard) {
|
|||
locked={
|
||||
response.foundThrough === "sold"
|
||||
}
|
||||
threadID={thread.id}
|
||||
/>
|
||||
);
|
||||
|
||||
const thread = await pendingMsg.startThread({
|
||||
name: `${user.tag}'s application`,
|
||||
autoArchiveDuration: 10080
|
||||
});
|
||||
|
||||
const fullApplicantProfile: any = await selfappReq(
|
||||
`/users/${user.id}/profile`,
|
||||
"GET",
|
||||
|
@ -127,7 +142,7 @@ export async function setupJoinRequestHandler(shard: Shard) {
|
|||
}
|
||||
|
||||
const response: Response = JSON.parse(
|
||||
application.responses
|
||||
application ? application.responses : {} // this will never be used if no applications
|
||||
);
|
||||
|
||||
const msg = await client.rest.channels.createMessage(
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
export function match(
|
||||
value: any,
|
||||
matches: {
|
||||
[key: number | string]: any;
|
||||
}
|
||||
) {
|
||||
if (matches[value]) return matches[value];
|
||||
else return null;
|
||||
}
|
23
src/utils/rejectReasons.ts
Normal file
23
src/utils/rejectReasons.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
export const rejectReasons: {
|
||||
[key: string]: {
|
||||
shortDesc: string;
|
||||
reason: string;
|
||||
};
|
||||
} = {
|
||||
low_quality: {
|
||||
shortDesc: "Too low quality",
|
||||
reason: "Your application is blatantly low effort (eg. not a language in languages or a single word application). Feel free to reapply, but actually read the questions"
|
||||
},
|
||||
no_english: {
|
||||
shortDesc: "No English",
|
||||
reason: "You must be fluent in English to join"
|
||||
},
|
||||
elaborate: {
|
||||
shortDesc: "Didn't say enough about themselves",
|
||||
reason: "Write a bit more about yourself and it's very likely that you'll be accepted!"
|
||||
},
|
||||
ai: {
|
||||
shortDesc: "AI",
|
||||
reason: "You have obviously used AI in your application. Therefore, you will not be allowed to reapply."
|
||||
}
|
||||
};
|
|
@ -12,8 +12,6 @@ export async function selfappReq(
|
|||
return `?${new URLSearchParams(data).toString()}`;
|
||||
})();
|
||||
|
||||
console.log(url);
|
||||
|
||||
return await fetch(url, {
|
||||
method,
|
||||
headers: {
|
48
src/utils/utils.ts
Normal file
48
src/utils/utils.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { Member } from "oceanic.js";
|
||||
import { Constants } from "../Constants";
|
||||
|
||||
export function match(
|
||||
value: any,
|
||||
matches: {
|
||||
[key: number | string]: any;
|
||||
}
|
||||
) {
|
||||
if (matches[value]) return matches[value];
|
||||
else return null;
|
||||
}
|
||||
|
||||
export function canUser(
|
||||
member: Member,
|
||||
can: "review" | "moderate" | "manage" | "owner"
|
||||
) {
|
||||
switch (can) {
|
||||
case "review":
|
||||
return (
|
||||
member.roles.some((role) =>
|
||||
[
|
||||
Constants.REVIEWER_ROLE_ID,
|
||||
Constants.MOD_ROLE_ID,
|
||||
Constants.MANAGER_ROLE_ID
|
||||
].includes(role)
|
||||
) || member.id === Constants.OWNER_ID
|
||||
);
|
||||
case "moderate":
|
||||
return (
|
||||
member.roles.some((role) =>
|
||||
[Constants.MOD_ROLE_ID, Constants.MANAGER_ROLE_ID].includes(
|
||||
role
|
||||
)
|
||||
) || member.id === Constants.OWNER_ID
|
||||
);
|
||||
case "manage":
|
||||
return (
|
||||
member.roles.some((role) =>
|
||||
[Constants.MANAGER_ROLE_ID].includes(role)
|
||||
) || member.id === Constants.OWNER_ID
|
||||
);
|
||||
case "owner":
|
||||
return member.id === Constants.OWNER_ID;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue