Skip to content

Commit df9a8a4

Browse files
Added search subcommand
1 parent b9b2b55 commit df9a8a4

File tree

4 files changed

+170
-1
lines changed

4 files changed

+170
-1
lines changed

docs/COMMAND-WIKI.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@
310310
- **Description:** Handle UWFlow commands.
311311
- **Examples:**
312312
- **Options:** None
313-
- **Subcommands:** `info`, `req`
313+
- **Subcommands:** `info`, `req`, `search`
314314

315315
## uwflow info
316316
- **Aliases:** `information`, `i`
@@ -326,6 +326,13 @@
326326
- **Options:**
327327
- **Subcommands:** None
328328

329+
## uwflow search
330+
- **Aliases:** None
331+
- **Description:** Search for courses in specified range
332+
- **Examples:**<br>`.uwflow search CS 100 200`<br>`.uwflow search cs 100 200`
333+
- **Options:**
334+
- **Subcommands:** None
335+
329336
# SUGGESTION
330337
## suggestion
331338
- **Aliases:** ``suggest``

src/commandDetails/uwflow/search.ts

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { container } from '@sapphire/framework';
2+
import {
3+
CodeyCommandDetails,
4+
CodeyCommandOptionType,
5+
SapphireMessageExecuteType,
6+
SapphireMessageResponse,
7+
} from '../../codeyCommand';
8+
import { searchResults, getSearchResults } from '../../components/uwflow';
9+
import { EmbedBuilder } from 'discord.js';
10+
11+
const uwflowSearchExecuteCommand: SapphireMessageExecuteType = async (
12+
_client,
13+
_messageFromUser,
14+
args,
15+
): Promise<SapphireMessageResponse> => {
16+
const courseArg = <string>args['course'];
17+
const min = <number>args['min'];
18+
const max = <number>args['max'];
19+
20+
// Standardize the course initials (i.e. CS becomes cs for the GraphQL query)
21+
const course = courseArg.toLowerCase();
22+
23+
const results: searchResults[] | number = await getSearchResults(course, min, max);
24+
25+
// If mistyped course initials or course doesn't exist
26+
if (results === -1) {
27+
const errorDesc = 'UWFlow returned no data';
28+
const courseEmbed = new EmbedBuilder()
29+
.setColor('Red')
30+
.setTitle(`Information for query of ${course.toUpperCase()} courses in range ${min} - ${max}`)
31+
.setDescription(errorDesc);
32+
return { embeds: [courseEmbed] };
33+
}
34+
35+
const resultArray = <searchResults[]>results;
36+
// If no courses fit the range
37+
if (resultArray.length < 1) {
38+
const desc = 'No courses suit the query';
39+
const embed = new EmbedBuilder()
40+
.setColor('Orange')
41+
.setTitle(`Information for query of ${course} courses in range ${min} - ${max}`)
42+
.setDescription(desc);
43+
return { embeds: [embed] };
44+
}
45+
46+
const courseArray: string[] = [];
47+
for (const result of resultArray) {
48+
courseArray.push(result.code);
49+
}
50+
51+
const desc = courseArray.join(', ');
52+
53+
const resultEmbed = new EmbedBuilder()
54+
.setColor('Green')
55+
.setTitle(`Information for query of ${course} courses in range ${min} - ${max}`)
56+
.setDescription(desc);
57+
58+
return { embeds: [resultEmbed] };
59+
};
60+
61+
export const uwflowSearchCommandDetails: CodeyCommandDetails = {
62+
name: 'search',
63+
aliases: [],
64+
description: 'Search for courses in specified range',
65+
detailedDescription: `**Examples:**
66+
\`${container.botPrefix}uwflow search CS 100 200\`
67+
\`${container.botPrefix}uwflow search cs 100 200\``,
68+
69+
isCommandResponseEphemeral: false,
70+
messageWhenExecutingCommand: 'Getting information from UWFlow:',
71+
executeCommand: uwflowSearchExecuteCommand,
72+
options: [
73+
{
74+
name: 'course',
75+
description: 'The initials of the course. Examples: CS, cs, MATH, math',
76+
type: CodeyCommandOptionType.STRING,
77+
required: true,
78+
},
79+
{
80+
name: 'min',
81+
description: 'The minimum code of the course',
82+
type: CodeyCommandOptionType.INTEGER,
83+
required: true,
84+
},
85+
{
86+
name: 'max',
87+
description: 'The maximum code of the course',
88+
type: CodeyCommandOptionType.INTEGER,
89+
required: true,
90+
},
91+
],
92+
subcommandDetails: {},
93+
};

src/commands/uwflow/uwflow.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Command } from '@sapphire/framework';
22
import { uwflowInfoCommandDetails } from '../../commandDetails/uwflow/info';
33
import { uwflowReqCommandDetails } from '../../commandDetails/uwflow/req';
4+
import { uwflowSearchCommandDetails } from '../../commandDetails/uwflow/search';
45
import { CodeyCommand, CodeyCommandDetails } from '../../codeyCommand';
56

67
const uwflowCommandDetails: CodeyCommandDetails = {
@@ -12,6 +13,7 @@ const uwflowCommandDetails: CodeyCommandDetails = {
1213
subcommandDetails: {
1314
info: uwflowInfoCommandDetails,
1415
req: uwflowReqCommandDetails,
16+
search: uwflowSearchCommandDetails,
1517
},
1618
defaultSubcommandDetails: uwflowInfoCommandDetails,
1719
};

src/components/uwflow.ts

+67
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,26 @@ export interface courseReqs {
5858
coreqs: string;
5959
}
6060

61+
// Search results interface for UWflow
62+
interface searchResultsFromUrl {
63+
data: {
64+
search_courses: [
65+
{
66+
name: string;
67+
code: string;
68+
has_prereqs: boolean;
69+
},
70+
];
71+
};
72+
}
73+
74+
// Search results interface for CodeyBot
75+
export interface searchResults {
76+
name: string;
77+
code: string;
78+
has_prereqs: boolean;
79+
}
80+
6181
// Format string
6282
const formatInput = (input: string | null): string => {
6383
if (input === null) {
@@ -145,3 +165,50 @@ export const getCourseReqs = async (courseCode: string): Promise<courseReqs | nu
145165

146166
return result;
147167
};
168+
169+
// Retrieve courses in range of course code
170+
export const getSearchResults = async (
171+
course: string,
172+
min: number,
173+
max: number,
174+
): Promise<searchResults[] | number> => {
175+
const resultFromUWFLow: searchResultsFromUrl = (
176+
await axios.post(uwflowApiUrl, {
177+
operationName: 'explore',
178+
variables: {
179+
query: course,
180+
code_only: true,
181+
},
182+
query: `query explore($query: String, $code_only: Boolean) {
183+
search_courses(args: { query: $query, code_only: $code_only }) {
184+
name
185+
code
186+
has_prereqs
187+
}
188+
}`,
189+
})
190+
).data;
191+
192+
// If no data is found, return -1 to signal error
193+
if (resultFromUWFLow.data.search_courses.length < 1) {
194+
return -1;
195+
}
196+
197+
// Search results
198+
const results = resultFromUWFLow.data.search_courses;
199+
200+
// Array of courses in range [min, max]
201+
const resultArray: searchResults[] = results.filter((result) => {
202+
const code = result.code;
203+
let numString = '';
204+
for (const char of code) {
205+
if (!isNaN(parseInt(char))) {
206+
numString += char;
207+
}
208+
}
209+
const num = parseInt(numString, 10);
210+
return min <= num && num <= max;
211+
});
212+
213+
return resultArray;
214+
};

0 commit comments

Comments
 (0)