Skip to content

Commit 56431ad

Browse files
authored
Feature/schiltz3/create channels (#39)
# Add the ability to Create and Move channels Add the command /csCreateChannels which allows owners to create and move CS channels into and out of the COMP SCI CLASSES category ## Link to github card #33 # Changes * Add channel utility functions * Add the ability to create channels from the database * Add the ability to identify and move existing classes to COMP SCI CLASSES category * Add the ability to move old classes out COMP SCI CLASSES active category into PAST CLASSES
2 parents 16d26f4 + 4afa13d commit 56431ad

File tree

2 files changed

+328
-0
lines changed

2 files changed

+328
-0
lines changed

commands/owner/csCreateChannels.ts

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
import { Client, MessageEmbed } from "discord.js";
2+
3+
import chalk from "chalk";
4+
import { ICommand } from "wokcommands";
5+
import { classModel } from "../../models/classModel";
6+
import {
7+
checkForChannel,
8+
cleanChannelString,
9+
createTextChannel,
10+
findCategory,
11+
moveChannel,
12+
concatCategoryName,
13+
} from "../../utils/channelUtils";
14+
15+
function create_default_embed(
16+
client: Client,
17+
title: string,
18+
description: string
19+
) {
20+
const color = "#0099ff";
21+
const thumbnail =
22+
"https://playantares.com/resources/CSSC-bot/cssc-server-icon.png";
23+
const footer = `Delivered in: ${client.ws.ping}ms | CSSC-bot | ${process.env.VERSION}`;
24+
const footerIcon = "https://playantares.com/resources/CSSC-bot/icon.jpg";
25+
26+
// Embed construction
27+
const embed = new MessageEmbed()
28+
.setColor(color)
29+
.setTitle(title)
30+
.setThumbnail(thumbnail)
31+
.setDescription(description)
32+
.setFooter({ text: footer, iconURL: footerIcon });
33+
return embed;
34+
}
35+
36+
function cleanRoleString(role_name: string): string {
37+
const clean_role_name: string = role_name
38+
.toLowerCase()
39+
.replace(/[`~!@#$%^&*))|+=?;:'",.<>\{\}\[\]\\\/]/gi, "")
40+
.replace(/[ (]/gi, "-");
41+
return clean_role_name;
42+
}
43+
44+
export default {
45+
name: "csCreateChannels",
46+
category: "owner",
47+
description: "Update CS class channels",
48+
slash: true,
49+
testOnly: false,
50+
guildOnly: true,
51+
requiredPermissions: ["MANAGE_GUILD", "MANAGE_ROLES"],
52+
ownerOnly: true,
53+
54+
callback: async ({ client, interaction: msgInt }) => {
55+
if (msgInt.guild === null) {
56+
console.log(chalk.red("No guild"));
57+
return;
58+
}
59+
60+
await msgInt.deferReply({ ephemeral: true });
61+
62+
const courses = await classModel.find({}).sort({ CODE: 1 });
63+
64+
//create an array of the courses with cleaned names
65+
const cleaned_courses: string[] = [];
66+
for (let index = 0; index < courses.length; index++) {
67+
cleaned_courses.push(cleanChannelString(courses[index].CODE));
68+
}
69+
70+
// for (let index = 0; index < courses.length; index++) {
71+
// console.log(courses[index].CODE);
72+
// }
73+
74+
const category_name = "COMP SCI CLASSES";
75+
let category_number = 0;
76+
const max_category_size = 50;
77+
78+
const cs_past_category_name = "PAST CLASSES";
79+
let new_channel_count = 0;
80+
let move_channel_count = 0;
81+
82+
console.log(
83+
`category name: ${concatCategoryName(category_name, category_number)}`
84+
);
85+
//Move classes no longer in the db to cs_past_category_name
86+
let cs_category = checkForChannel(
87+
msgInt.guild,
88+
concatCategoryName(category_name, category_number)
89+
);
90+
console.log(`Channel: ${cs_category}`);
91+
92+
while (cs_category != undefined && cs_category.type == "GUILD_CATEGORY") {
93+
const children = Array.from(cs_category.children.values());
94+
for (let index = 0; index < children.length; index++) {
95+
const match = cleaned_courses.find((course) => {
96+
return course == children[index].name;
97+
});
98+
if (match != undefined) {
99+
continue;
100+
}
101+
console.log(`Moving: ${children[index]} to: ${cs_past_category_name}`);
102+
await moveChannel(msgInt.guild, children[index], cs_past_category_name);
103+
}
104+
105+
++category_number;
106+
cs_category = checkForChannel(
107+
msgInt.guild,
108+
concatCategoryName(category_name, category_number)
109+
);
110+
}
111+
category_number = 0;
112+
113+
for (let index = 0; index < courses.length; index++) {
114+
// Iterate through courses in db
115+
const channel = checkForChannel(
116+
msgInt.guild,
117+
cleanChannelString(courses[index].CODE)
118+
);
119+
120+
let category = await (
121+
await findCategory(
122+
msgInt.guild,
123+
concatCategoryName(category_name, category_number)
124+
)
125+
).fetch(true);
126+
127+
// Increment category if category is full
128+
if (category.children.size >= max_category_size - 1) {
129+
++category_number;
130+
category = await (
131+
await findCategory(
132+
msgInt.guild,
133+
concatCategoryName(category_name, category_number)
134+
)
135+
).fetch(true);
136+
console.log(
137+
`old category full, new category: ${concatCategoryName(
138+
category_name,
139+
category_number
140+
)} created`
141+
);
142+
}
143+
console.log(
144+
`Working in category: ${concatCategoryName(
145+
category_name,
146+
category_number
147+
)} size: ${category.children.size}`
148+
);
149+
150+
// Create new channels
151+
if (channel === undefined || channel.type !== "GUILD_TEXT") {
152+
const new_channel = await createTextChannel(
153+
msgInt.guild,
154+
cleanChannelString(courses[index].CODE),
155+
courses[index].INFO,
156+
category
157+
);
158+
159+
++new_channel_count;
160+
console.log(chalk.yellow(`Created channel: ${new_channel.name}`));
161+
162+
courses[index].CHANNEL_ID = new_channel.id;
163+
courses[index].save();
164+
165+
// Ping members who have this role
166+
const role = msgInt.guild.roles.cache.find((role) => {
167+
return (
168+
cleanRoleString(role.name) ==
169+
cleanChannelString(courses[index].CODE)
170+
);
171+
});
172+
if (role !== undefined) {
173+
//Ping member
174+
new_channel.send(`Hey! <@&${role.id}> here is a channel for you!`);
175+
}
176+
} else if (
177+
channel.parent !== null &&
178+
!channel.parent.name.startsWith(category_name)
179+
) {
180+
// Moves and updates old channels
181+
console.log(
182+
`Moving: ${channel.name} to: ${concatCategoryName(
183+
category_name,
184+
category_number
185+
)}`
186+
);
187+
channel.edit({ topic: courses[index].INFO });
188+
move_channel_count += await moveChannel(
189+
msgInt.guild,
190+
channel,
191+
concatCategoryName(category_name, category_number)
192+
);
193+
194+
courses[index].CHANNEL_ID = channel.id;
195+
courses[index].save();
196+
// Ping members who have this role
197+
const role = msgInt.guild.roles.cache.find((role) => {
198+
return (
199+
cleanRoleString(role.name) ==
200+
cleanChannelString(courses[index].CODE)
201+
);
202+
});
203+
if (role !== undefined) {
204+
//Ping member
205+
channel.send(`Hey! <@&${role.id}> here is the channel for you!`);
206+
}
207+
} else if (
208+
channel.parent !== null &&
209+
channel.parent.name.startsWith(category_name)
210+
) {
211+
// updates old channels
212+
channel.edit({ topic: courses[index].INFO });
213+
}
214+
}
215+
216+
const title = "Create Classes";
217+
let description = `Created ${new_channel_count} new channels\nMoved ${move_channel_count} channels`;
218+
if (move_channel_count > 0) {
219+
description += ` to ${concatCategoryName(
220+
category_name,
221+
category_number
222+
)}`;
223+
}
224+
225+
const embed = create_default_embed(client, title, description);
226+
await msgInt.editReply({ embeds: [embed] });
227+
228+
console.log(chalk.yellow(description));
229+
230+
// Log the command usage
231+
console.log(
232+
chalk.blue(
233+
`${chalk.green(`[COMMAND]`)} ${chalk.yellow(
234+
msgInt.user.tag
235+
)} used the ${chalk.green(
236+
`/csCreateChannels`
237+
)} command in ${chalk.yellow(msgInt.guild?.name)}`
238+
)
239+
);
240+
},
241+
} as ICommand;

utils/channelUtils.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { CategoryChannel, Guild, GuildChannel } from "discord.js";
2+
import chalk from "chalk";
3+
4+
export function cleanChannelString(s: string): string {
5+
return s
6+
.toLowerCase()
7+
.replace(/[`~!@#$%^&*))|+=?;:'",.<>\{\}\[\]\\\/]/gi, "")
8+
.replace("compsci ", "cs")
9+
.replace(/[ (]/gi, "-");
10+
}
11+
12+
export function checkForChannel(guild: Guild, channel_name: string) {
13+
return guild.channels.cache.find((channel) => {
14+
return channel.name == channel_name;
15+
});
16+
}
17+
18+
export async function findCategory(guild: Guild, category_name: string) {
19+
let category = guild.channels.cache.find((category) => {
20+
return category.name == category_name;
21+
});
22+
23+
if (category === undefined || category.type !== "GUILD_CATEGORY") {
24+
category = await guild.channels.create(category_name, {
25+
type: "GUILD_CATEGORY",
26+
});
27+
}
28+
29+
return category;
30+
}
31+
export async function createTextChannel(
32+
guild: Guild,
33+
name: string,
34+
topic: string,
35+
category?: CategoryChannel,
36+
category_name?: string
37+
) {
38+
//Determine which arg to use
39+
let channel_parent: CategoryChannel | undefined;
40+
if (category !== undefined) {
41+
channel_parent = category;
42+
} else if (category_name !== undefined) {
43+
channel_parent = await findCategory(guild, category_name);
44+
} else {
45+
throw Error(
46+
"Must specify either channel_category or channel_category_name"
47+
);
48+
}
49+
50+
return guild.channels.create(cleanChannelString(name), {
51+
type: "GUILD_TEXT",
52+
topic: topic,
53+
parent: channel_parent,
54+
});
55+
}
56+
57+
export async function moveChannel(
58+
guild: Guild,
59+
channel: GuildChannel,
60+
category_name: string
61+
): Promise<number> {
62+
if (
63+
channel.parent === null ||
64+
channel.parent === undefined ||
65+
channel.parent?.name != category_name
66+
) {
67+
const category = await findCategory(guild, category_name);
68+
channel.setParent(category);
69+
70+
console.log(
71+
chalk.yellow(
72+
`Moved channel ${channel.name} from: ${channel.parent?.name} to: ${category_name}`
73+
)
74+
);
75+
return 1;
76+
}
77+
return 0;
78+
}
79+
80+
export function concatCategoryName(
81+
category_name: string,
82+
category_number: number
83+
) {
84+
return category_number == 0
85+
? category_name
86+
: `${category_name} ${category_number}`;
87+
}

0 commit comments

Comments
 (0)