1
|
|
|
"""Schools commands for bot"""
|
2
|
|
|
import asyncio
|
3
|
|
|
import random
|
4
|
|
|
import logging
|
5
|
|
|
import discord
|
6
|
|
|
from discord.ext import commands
|
7
|
|
|
import utils
|
8
|
|
|
|
9
|
|
|
|
10
|
|
|
class SchoolCog(commands.Cog, name="Schools"):
|
11
|
|
|
"""SchoolCog
|
12
|
|
|
---
|
13
|
|
|
|
14
|
|
|
Cog that deal with the school commands as well as the searching commands.
|
15
|
|
|
|
16
|
|
|
Commands:
|
17
|
|
|
---
|
18
|
|
|
`list-schools`: List all the available school roles that are joinable.
|
19
|
|
|
`import-school`: Allows admin to import current roles into schools. *WIP*
|
20
|
|
|
`join-school`: Allows users to join any available school role.
|
21
|
|
|
`add-school`: Command that allows users to create their own school.
|
22
|
|
|
|
23
|
|
|
Arguments:
|
24
|
|
|
---
|
25
|
|
|
bot {discord.commands.Bot} -- The bot
|
26
|
|
|
|
27
|
|
|
Raises:
|
28
|
|
|
---
|
29
|
|
|
utils.FailedReactionCheck: Custom exception if the reaction check fails.
|
30
|
|
|
"""
|
31
|
|
|
|
32
|
|
|
def __init__(self, bot):
|
33
|
|
|
self.bot = bot
|
34
|
|
|
self.log = logging.getLogger("bot")
|
35
|
|
|
|
36
|
|
|
@commands.command(name="list-schools", help="Gets list of current schools")
|
37
|
|
|
async def list_schools(self, ctx):
|
38
|
|
|
"""list-schools
|
39
|
|
|
---
|
40
|
|
|
|
41
|
|
|
Lists current schools in the database. Message is a embed that has a random color with the
|
42
|
|
|
list of all schools.
|
43
|
|
|
|
44
|
|
|
Arguments:
|
45
|
|
|
---
|
46
|
|
|
ctx {discord.ext.commands.Context} -- Context of the command.
|
47
|
|
|
"""
|
48
|
|
|
fetched = sorted(await utils.fetch("schools", "school"), key=str.lower)
|
49
|
|
|
if len(fetched) == 0:
|
50
|
|
|
return await utils.error_message(ctx, "No schools to join")
|
51
|
|
|
await utils.list_message(
|
52
|
|
|
ctx,
|
53
|
|
|
fetched,
|
54
|
|
|
title="Available schools to join:",
|
55
|
|
|
footer="If your school is not in the list, use `$help add-school`",
|
56
|
|
|
)
|
57
|
|
|
|
58
|
|
|
@commands.command(name="import-school", help="Admin Only Feature")
|
59
|
|
|
@commands.check(utils.check_admin)
|
60
|
|
|
async def import_school(self, ctx, school_name: str, region: str):
|
61
|
|
|
"""import-school
|
62
|
|
|
---
|
63
|
|
|
|
64
|
|
|
Allows admins to import existing roles as schools.
|
65
|
|
|
|
66
|
|
|
Arguments:
|
67
|
|
|
---
|
68
|
|
|
ctx {discord.ext.commands.Context} -- Context of the command.
|
69
|
|
|
school_name {str} -- Name of the school role to import.
|
70
|
|
|
"""
|
71
|
|
|
school_role = discord.utils.get(ctx.guild.roles, name=school_name)
|
72
|
|
|
if school_role.name in await utils.fetch("schools", "school"):
|
73
|
|
|
await utils.error_message(ctx, "School role already exists")
|
74
|
|
|
else:
|
75
|
|
|
new_school = [
|
76
|
|
|
school_name,
|
77
|
|
|
region,
|
78
|
|
|
school_role.color.value,
|
79
|
|
|
school_role.id, # noqa: E501 pylint: disable=no-member
|
80
|
|
|
"Imported",
|
81
|
|
|
self.bot.owner_id,
|
82
|
|
|
]
|
83
|
|
|
status = await utils.insert("schools", new_school)
|
84
|
|
|
if status == "error":
|
85
|
|
|
await utils.error_message(ctx, "Error importing school")
|
86
|
|
|
else:
|
87
|
|
|
await utils.make_embed(ctx, color="28b463", title="School has been imported")
|
88
|
|
|
|
89
|
|
|
@commands.command(name="join-school", help="Joins a schools.")
|
90
|
|
|
@commands.has_role("new")
|
91
|
|
|
async def join_school(self, ctx, *, school_name: str):
|
92
|
|
|
"""join-school
|
93
|
|
|
---
|
94
|
|
|
|
95
|
|
|
Enables users to join a school role. school_name arguments is not to be quote separated.
|
96
|
|
|
Users are required to have the role "new". Users will be assigned the school role, region
|
97
|
|
|
role and "verified" role. They will lose their "new" role.
|
98
|
|
|
|
99
|
|
|
|
100
|
|
|
Arguments:
|
101
|
|
|
---
|
102
|
|
|
ctx {discord.ext.commands.Context} -- Context of the command.
|
103
|
|
|
school_name {str} -- Name of the school the user wants to join.
|
104
|
|
|
"""
|
105
|
|
|
user = ctx.message.author
|
106
|
|
|
db_entry = await utils.select("schools", "school, region", "school", school_name)
|
107
|
|
|
if len(db_entry) == 0:
|
108
|
|
|
return await utils.error_message(
|
109
|
|
|
ctx, "School could not be found.", title="Missing School:"
|
110
|
|
|
)
|
111
|
|
|
|
112
|
|
|
to_add = [discord.utils.get(ctx.guild.roles, name=x) for x in (school_name, "verified")]
|
113
|
|
|
if None in to_add:
|
114
|
|
|
await utils.error_message(ctx, "The school you select does not have valid role.")
|
115
|
|
|
self.log.warning(
|
116
|
|
|
"{} tried to join {}. Only roles found: {}".format(
|
117
|
|
|
ctx.author.name, school_name, to_add
|
118
|
|
|
)
|
119
|
|
|
)
|
120
|
|
|
else:
|
121
|
|
|
self.log.debug("Adding roles: {} to {}".format(to_add, user))
|
122
|
|
|
await user.add_roles(*to_add, reason=f"{user.name} joined {school_name}")
|
123
|
|
|
await user.remove_roles(
|
124
|
|
|
discord.utils.get(ctx.guild.roles, name="new"),
|
125
|
|
|
reason=f"{user.name} joined {school_name}",
|
126
|
|
|
)
|
127
|
|
|
await ctx.author.send(
|
128
|
|
|
embed=await utils.make_embed(
|
129
|
|
|
ctx,
|
130
|
|
|
"28b463",
|
131
|
|
|
send=False,
|
132
|
|
|
title=f"School assigned: {school_name}",
|
133
|
|
|
)
|
134
|
|
|
)
|
135
|
|
|
|
136
|
|
|
@commands.command(
|
137
|
|
|
name="add-school",
|
138
|
|
|
help="Adds a new school and makes a role for it.\n"
|
139
|
|
|
"Only schools on the list are allowed to join.\n"
|
140
|
|
|
"List: https://github.com/Competitive-Cyber-Clubs/School-List/blob/master/school_list.csv",
|
141
|
|
|
description="Creates a new school",
|
142
|
|
|
)
|
143
|
|
|
@commands.has_role("new")
|
144
|
|
|
async def add_school(
|
145
|
|
|
self, ctx: commands.Context, *, school_name: str
|
146
|
|
|
): # pylint: disable=too-many-branches
|
147
|
|
|
"""add_school
|
148
|
|
|
---
|
149
|
|
|
|
150
|
|
|
Enables users to create a school role. They are required to have the role "new". Schools
|
151
|
|
|
will automatically be assigned a region based on the school_list.csv in utils.
|
152
|
|
|
|
153
|
|
|
Arguments:
|
154
|
|
|
---
|
155
|
|
|
ctx {discord.ext.commands.Context} -- Context of the command.
|
156
|
|
|
school_name {str} -- Name of the school the user wants to join.
|
157
|
|
|
|
158
|
|
|
Raises:
|
159
|
|
|
---
|
160
|
|
|
utils.FailedReactionCheck: Exception is raised if the reaction check does not validate.
|
161
|
|
|
"""
|
162
|
|
|
if not await utils.school_check(self.bot.school_list, school_name):
|
163
|
|
|
return await utils.make_embed(ctx, "FF0000", title="Error: School name not valid.")
|
164
|
|
|
|
165
|
|
|
if await utils.select("schools", "school", "school", school_name):
|
166
|
|
|
self.log.info(
|
167
|
|
|
"{} attempted to create a duplicate role for {}".format(
|
168
|
|
|
ctx.author.name, school_name
|
169
|
|
|
)
|
170
|
|
|
)
|
171
|
|
|
return await utils.error_message(ctx, f"School role for {school_name} already exists.")
|
172
|
|
|
|
173
|
|
|
regions = await utils.fetch("regions", "name")
|
174
|
|
|
region = await utils.region_select(self.bot.school_list, school_name)
|
175
|
|
|
if region not in regions:
|
176
|
|
|
# No region map error
|
177
|
|
|
self.log.error(
|
178
|
|
|
"There is no region map for {}, region: {}, {}".format(school_name, region, regions)
|
179
|
|
|
)
|
180
|
|
|
return await utils.error_message(ctx, f"No region defined for {school_name}")
|
181
|
|
|
|
182
|
|
|
await utils.make_embed(
|
183
|
|
|
ctx,
|
184
|
|
|
title=f"You are about to create a new school: {school_name}.",
|
185
|
|
|
description="React 👍 to this message confirm.",
|
186
|
|
|
)
|
187
|
|
|
# Gives the user 30 seconds to add the reaction '👍' to the message.
|
188
|
|
|
try:
|
189
|
|
|
reactions, user = await self.bot.wait_for("reaction_add", timeout=30)
|
190
|
|
|
if not await utils.check_react(ctx, user, reactions, "👍"):
|
191
|
|
|
raise utils.FailedReactionCheck
|
192
|
|
|
except asyncio.TimeoutError:
|
193
|
|
|
await utils.error_message(
|
194
|
|
|
ctx, "Timed out waiting for a reaction. Please reach to the message in 30 seconds"
|
195
|
|
|
)
|
196
|
|
|
except utils.FailedReactionCheck:
|
197
|
|
|
await utils.error_message(ctx, "Wrong reaction added or added by the wrong user")
|
198
|
|
|
else:
|
199
|
|
|
color = int("0x%06x" % random.randint(0, 0xFFFFFF), 16) # nosec
|
200
|
|
|
added_school = await ctx.guild.create_role(
|
201
|
|
|
name=school_name,
|
202
|
|
|
color=discord.Color(color),
|
203
|
|
|
mentionable=True,
|
204
|
|
|
hoist=False,
|
205
|
|
|
reason="Added by {}".format(ctx.author.name),
|
206
|
|
|
)
|
207
|
|
|
data = [
|
208
|
|
|
school_name,
|
209
|
|
|
region,
|
210
|
|
|
color,
|
211
|
|
|
added_school.id,
|
212
|
|
|
(ctx.author.name + ctx.author.discriminator),
|
213
|
|
|
ctx.author.id,
|
214
|
|
|
]
|
215
|
|
|
status = await utils.insert("schools", data)
|
216
|
|
|
if status == "error":
|
217
|
|
|
await utils.error_message(ctx, "There was an error with creating the role.")
|
218
|
|
|
await added_school.delete(reason="Error in creation")
|
219
|
|
|
self.log.warning("Error with School Role creation.")
|
220
|
|
|
else:
|
221
|
|
|
success_msg = 'School "{}" has been created in {} with color of 0x{}'.format(
|
222
|
|
|
school_name, region, color
|
223
|
|
|
)
|
224
|
|
|
await utils.make_embed(ctx, color=color, title="Success", description=success_msg)
|
225
|
|
|
await self.join_school(ctx=ctx, school_name=school_name)
|
226
|
|
|
|
227
|
|
|
|
228
|
|
|
def setup(bot):
|
229
|
|
|
"""Needed for extension loading"""
|
230
|
|
|
bot.add_cog(SchoolCog(bot))
|
231
|
|
|
|