Skip to content

Commit b3c9cd0

Browse files
Issue #283 (#508)
* Refactored to be just command interface * Added file to manage the command and subcommands * Added file to manage list subcommand * Added file to manage signup subcommand * Added file to manage pause subcommand * Added file to manage profile subcommand * Added file to manage resume subcommand * Added file to manage clear subcommand * Added file to manage domain subcommand * Refactored subcommand structures of interviewer command * Refactored clear subcommand of interviewer * Refactored domain subcommand of interviewer * Refactored list subcommand of interviewer * Refactored pause subcommand of interviewer * Refactored profile subcommand of interviewer * Refactored resume subcommand of interviewer * Refactored signup subcommand of interviewer * Made domain field of domain subcommand required * Fixed bug with list subcommand when database is empty * Fixed linting issues * Modified subcommand comments
1 parent 1813bd7 commit b3c9cd0

File tree

8 files changed

+416
-207
lines changed

8 files changed

+416
-207
lines changed
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { container } from '@sapphire/framework';
2+
import { clearProfile, getInterviewer } from '../../components/interviewer';
3+
import {
4+
CodeyCommandDetails,
5+
SapphireMessageExecuteType,
6+
SapphireMessageResponse,
7+
getUserFromMessage,
8+
} from '../../codeyCommand';
9+
10+
const interviewerClearExecuteCommand: SapphireMessageExecuteType = async (
11+
_client,
12+
messageFromUser,
13+
_args,
14+
): Promise<SapphireMessageResponse> => {
15+
const id = getUserFromMessage(messageFromUser).id;
16+
17+
// Check if user signed up to be interviewer
18+
if (!(await getInterviewer(id))) {
19+
return `You don't seem to have signed up yet. Please sign up using \`${container.botPrefix}interviewer signup <calendarUrl>\`!`;
20+
}
21+
22+
// Clear interviewer data
23+
await clearProfile(id);
24+
return 'Your interviewer profile has been cleared!';
25+
};
26+
27+
export const interviewerClearCommandDetails: CodeyCommandDetails = {
28+
name: 'clear',
29+
aliases: ['clr'],
30+
description: 'Clear all your interviewer data',
31+
detailedDescription: `**Examples:**
32+
\`${container.botPrefix}interviewer clear\``,
33+
34+
isCommandResponseEphemeral: false,
35+
messageWhenExecutingCommand: 'Clearing your interviewer profile...',
36+
executeCommand: interviewerClearExecuteCommand,
37+
messageIfFailure: 'Could not clear your interviewer profile',
38+
options: [],
39+
subcommandDetails: {},
40+
};
+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { container } from '@sapphire/framework';
2+
import {
3+
availableDomains,
4+
getAvailableDomainsString,
5+
getInterviewer,
6+
toggleDomain,
7+
} from '../../components/interviewer';
8+
import {
9+
CodeyCommandDetails,
10+
CodeyCommandOptionType,
11+
SapphireMessageExecuteType,
12+
SapphireMessageResponse,
13+
getUserFromMessage,
14+
} from '../../codeyCommand';
15+
16+
const interviewerDomainExecuteCommand: SapphireMessageExecuteType = async (
17+
_client,
18+
messageFromUser,
19+
args,
20+
): Promise<SapphireMessageResponse> => {
21+
const domain: string | undefined = <string>args['domain_name'];
22+
if (domain && !(domain.toLowerCase() in availableDomains)) {
23+
return `You entered an invalid domain. Please enter one of ${getAvailableDomainsString()}.`;
24+
}
25+
26+
const id = getUserFromMessage(messageFromUser).id;
27+
// Check if user signed up to be interviewer
28+
if (!(await getInterviewer(id))) {
29+
return `You don't seem to have signed up yet. Please sign up using \`${container.botPrefix}interviewer signup <calendarUrl>\`!`;
30+
}
31+
32+
// Add or remove domain to/from interviewer
33+
const inDomain = await toggleDomain(id, domain);
34+
return inDomain
35+
? `You have been successfully removed from ${availableDomains[domain]}`
36+
: `You have been successfully added to ${availableDomains[domain]}`;
37+
};
38+
39+
export const interviewerDomainCommandDetails: CodeyCommandDetails = {
40+
name: 'domain',
41+
aliases: ['domain'],
42+
description: 'Add/remove a domain of your choice',
43+
detailedDescription: `**Examples:**
44+
\`${container.botPrefix}interviewer domain frontend\``,
45+
46+
isCommandResponseEphemeral: false,
47+
messageWhenExecutingCommand: 'Modifying domain data...',
48+
executeCommand: interviewerDomainExecuteCommand,
49+
messageIfFailure: 'Could not modify domain data',
50+
options: [
51+
{
52+
name: 'domain_name',
53+
description: 'A valid domain name',
54+
type: CodeyCommandOptionType.STRING,
55+
required: true,
56+
},
57+
],
58+
subcommandDetails: {},
59+
};
+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { container } from '@sapphire/framework';
2+
import { EmbedBuilder } from 'discord.js';
3+
import _ from 'lodash';
4+
import {
5+
availableDomains,
6+
getAvailableDomainsString,
7+
getInterviewerDomainsString,
8+
getInterviewers,
9+
Interviewer,
10+
} from '../../components/interviewer';
11+
import { DEFAULT_EMBED_COLOUR } from '../../utils/embeds';
12+
import {
13+
CodeyCommandDetails,
14+
CodeyCommandOptionType,
15+
SapphireMessageExecuteType,
16+
SapphireMessageResponse,
17+
} from '../../codeyCommand';
18+
19+
const RESULTS_PER_PAGE = 6;
20+
21+
const getInterviewerDisplayInfo = async (interviewer: Interviewer): Promise<string> => {
22+
const { client } = container;
23+
const user = await client.users.fetch(interviewer['user_id']);
24+
const userDomainsAddIn = await getInterviewerDomainsString(interviewer['user_id']);
25+
if (userDomainsAddIn === '') {
26+
return `${user} | [Calendar](${interviewer['link']})\n\n`;
27+
} else {
28+
return `${user} | [Calendar](${interviewer['link']}) | ${userDomainsAddIn}\n\n`;
29+
}
30+
};
31+
32+
const interviewerListExecuteCommand: SapphireMessageExecuteType = async (
33+
_client,
34+
_messageFromUser,
35+
args,
36+
): Promise<SapphireMessageResponse> => {
37+
const domain: string | undefined = <string>args['domain'];
38+
if (domain && !(domain.toLowerCase() in availableDomains)) {
39+
return `You entered an invalid domain. Please enter one of ${getAvailableDomainsString()}.`;
40+
}
41+
42+
// Query interviewers
43+
const interviewers = await getInterviewers(domain);
44+
// Shuffles interviewers to load balance
45+
const shuffledInterviewers = _.shuffle(interviewers);
46+
// Only show up to page limit
47+
const interviewersToShow = shuffledInterviewers.slice(0, RESULTS_PER_PAGE);
48+
// Get information from each interviewer
49+
const interviewersInfo = await Promise.all(
50+
interviewersToShow.map((interviewer) => getInterviewerDisplayInfo(interviewer)),
51+
);
52+
53+
// If there's no data, then interviewersInfo is an array of undefined, which messed things up
54+
// Thus need to display something else instead of interviewersInfo.join()
55+
const embedDescription =
56+
interviewersInfo[0] === undefined ? 'No data to display' : interviewersInfo.join('');
57+
58+
// Construct embed for display
59+
const title = domain
60+
? `Available Interviewers for ${availableDomains[domain]}`
61+
: 'Available Interviewers';
62+
const outEmbed = new EmbedBuilder()
63+
.setColor(DEFAULT_EMBED_COLOUR)
64+
.setTitle(title)
65+
.setDescription(embedDescription);
66+
return { embeds: [outEmbed] };
67+
};
68+
69+
export const interviewerListCommandDetails: CodeyCommandDetails = {
70+
name: 'list',
71+
aliases: ['ls'],
72+
description: 'List all interviewers or those under a specific domain',
73+
detailedDescription: `**Examples:**
74+
\`${container.botPrefix}interviewer list\`
75+
\`${container.botPrefix}interviewer list backend\``,
76+
77+
isCommandResponseEphemeral: false,
78+
messageWhenExecutingCommand: 'Listing interviewers...',
79+
executeCommand: interviewerListExecuteCommand,
80+
messageIfFailure: 'Could not list interviewers',
81+
options: [
82+
{
83+
name: 'domain',
84+
description: 'The domain to be examined',
85+
type: CodeyCommandOptionType.STRING,
86+
required: false,
87+
},
88+
],
89+
subcommandDetails: {},
90+
};
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { container } from '@sapphire/framework';
2+
import { getInterviewer, pauseProfile } from '../../components/interviewer';
3+
import {
4+
CodeyCommandDetails,
5+
SapphireMessageExecuteType,
6+
SapphireMessageResponse,
7+
getUserFromMessage,
8+
} from '../../codeyCommand';
9+
10+
const interviewerPauseExecuteCommand: SapphireMessageExecuteType = async (
11+
_client,
12+
messageFromUser,
13+
_args,
14+
): Promise<SapphireMessageResponse> => {
15+
const id = getUserFromMessage(messageFromUser).id;
16+
17+
// Check if user signed up to be interviewer
18+
if (!(await getInterviewer(id))) {
19+
return `You don't seem to have signed up yet, please sign up using \`${container.botPrefix}interviewer signup <calendarUrl>\`!`;
20+
}
21+
22+
// Pause interviewer data
23+
await pauseProfile(id);
24+
return `Your interviewer profile has been paused! You will not appear in interviewer queries anymore, until you run \`${container.botPrefix}interviewer resume\`.`;
25+
};
26+
27+
export const interviewerPauseCommandDetails: CodeyCommandDetails = {
28+
name: 'pause',
29+
aliases: ['ps'],
30+
description: 'Put your interviewer profile on pause',
31+
detailedDescription: `**Examples:**
32+
\`${container.botPrefix}interviewer pause\``,
33+
34+
isCommandResponseEphemeral: false,
35+
messageWhenExecutingCommand: 'Pausing profile...',
36+
executeCommand: interviewerPauseExecuteCommand,
37+
messageIfFailure: 'Could not pause profile',
38+
options: [],
39+
subcommandDetails: {},
40+
};
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { container } from '@sapphire/framework';
2+
import { EmbedBuilder } from 'discord.js';
3+
import _ from 'lodash';
4+
import { getDomains, getDomainsString, getInterviewer } from '../../components/interviewer';
5+
import { DEFAULT_EMBED_COLOUR } from '../../utils/embeds';
6+
import {
7+
CodeyCommandDetails,
8+
SapphireMessageExecuteType,
9+
SapphireMessageResponse,
10+
getUserFromMessage,
11+
} from '../../codeyCommand';
12+
13+
const interviewerProfileExecuteCommand: SapphireMessageExecuteType = async (
14+
_client,
15+
messageFromUser,
16+
_args,
17+
): Promise<SapphireMessageResponse> => {
18+
const id = getUserFromMessage(messageFromUser).id;
19+
20+
// Check if user signed up to be interviewer
21+
const interviewer = await getInterviewer(id);
22+
if (!interviewer) {
23+
return `You don't seem to have signed up yet. Please sign up using \`${container.botPrefix}interviewer signup <calendarUrl>\`!`;
24+
}
25+
26+
// Get domains
27+
const domains = await getDomains(id);
28+
29+
// Build output embed
30+
const profileEmbed = new EmbedBuilder()
31+
.setColor(DEFAULT_EMBED_COLOUR)
32+
.setTitle('Interviewer Profile');
33+
profileEmbed.addFields([
34+
{ name: '**Link**', value: interviewer.link },
35+
{ name: '**Domains**', value: _.isEmpty(domains) ? 'None' : getDomainsString(domains) },
36+
]);
37+
38+
return { embeds: [profileEmbed] };
39+
};
40+
41+
export const interviewerProfileCommandDetails: CodeyCommandDetails = {
42+
name: 'profile',
43+
aliases: ['pf'],
44+
description: 'Display your interviewer profile data',
45+
detailedDescription: `**Examples:**
46+
\`${container.botPrefix}interviewer profile\``,
47+
48+
isCommandResponseEphemeral: false,
49+
messageWhenExecutingCommand: 'Displaying profile...',
50+
executeCommand: interviewerProfileExecuteCommand,
51+
messageIfFailure: 'Could not display profile',
52+
options: [],
53+
subcommandDetails: {},
54+
};
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { container } from '@sapphire/framework';
2+
import { getInterviewer, resumeProfile } from '../../components/interviewer';
3+
import {
4+
CodeyCommandDetails,
5+
SapphireMessageExecuteType,
6+
SapphireMessageResponse,
7+
getUserFromMessage,
8+
} from '../../codeyCommand';
9+
10+
const interviewerResumeExecuteCommand: SapphireMessageExecuteType = async (
11+
_client,
12+
messageFromUser,
13+
_args,
14+
): Promise<SapphireMessageResponse> => {
15+
const id = getUserFromMessage(messageFromUser).id;
16+
17+
// Check if user signed up to be interviewer
18+
if (!(await getInterviewer(id))) {
19+
`You don't seem to have signed up yet. Please sign up using \`${container.botPrefix}interviewer signup <calendarUrl>\`!`;
20+
}
21+
22+
// Resume interviewer data
23+
await resumeProfile(id);
24+
return 'Your interviewer profile has been resumed!';
25+
};
26+
27+
export const interviewerResumeCommandDetails: CodeyCommandDetails = {
28+
name: 'resume',
29+
aliases: ['resume'],
30+
description: 'Resume your interviewer profile',
31+
detailedDescription: `**Examples:**
32+
\`${container.botPrefix}interviewer resume\``,
33+
34+
isCommandResponseEphemeral: false,
35+
messageWhenExecutingCommand: 'Resuming profile...',
36+
executeCommand: interviewerResumeExecuteCommand,
37+
messageIfFailure: 'Could not resume profile',
38+
options: [],
39+
subcommandDetails: {},
40+
};
+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { container } from '@sapphire/framework';
2+
import { getEmojiByName } from '../../components/emojis';
3+
import { parseLink, upsertInterviewer } from '../../components/interviewer';
4+
import {
5+
CodeyCommandDetails,
6+
CodeyCommandOptionType,
7+
SapphireMessageExecuteType,
8+
SapphireMessageResponse,
9+
getUserFromMessage,
10+
} from '../../codeyCommand';
11+
12+
const interviewerSignupExecuteCommand: SapphireMessageExecuteType = async (
13+
_client,
14+
messageFromUser,
15+
args,
16+
): Promise<SapphireMessageResponse> => {
17+
const id = getUserFromMessage(messageFromUser).id;
18+
19+
// Get calendar URL from the 1st capture group
20+
const calendarUrl = <string>args['calendar_url'];
21+
22+
// Parse link and checks for validity
23+
const parsedUrl = parseLink(calendarUrl);
24+
if (!parsedUrl) {
25+
return `I don't seem to recognize your meeting link. Be sure to use calendly or x.ai.`;
26+
}
27+
28+
// Add or update interviewer info
29+
await upsertInterviewer(id, parsedUrl);
30+
return `Your info has been updated. Thanks for helping out! ${getEmojiByName(
31+
'codey_love',
32+
)?.toString()}`;
33+
};
34+
35+
export const interviewerSignupCommandDetails: CodeyCommandDetails = {
36+
name: 'signup',
37+
aliases: ['signup'],
38+
description: 'Sign yourself up to be an interviewer!',
39+
detailedDescription: `**Examples:**
40+
\`${container.botPrefix}interviewer signup www.calendly.com\``,
41+
42+
isCommandResponseEphemeral: false,
43+
messageWhenExecutingCommand: 'Signing up for a profile...',
44+
executeCommand: interviewerSignupExecuteCommand,
45+
messageIfFailure: 'Could not sign up for a profile',
46+
options: [
47+
{
48+
name: 'calendar_url',
49+
description: 'A valid calendly.com or x.ai calendar link',
50+
type: CodeyCommandOptionType.STRING,
51+
required: true,
52+
},
53+
],
54+
subcommandDetails: {},
55+
};

0 commit comments

Comments
 (0)