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
|
|
|
def __init__(self, bot):
|
32
|
|
|
self.bot = bot
|
33
|
|
|
self.log = logging.getLogger("bot")
|
34
|
|
|
|
35
|
|
|
@commands.command(name="list-schools",
|
36
|
|
|
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.make_embed(ctx, color="FF0000",
|
51
|
|
|
title="There are no schools to join.")
|
52
|
|
|
schools = ""
|
53
|
|
|
embed = await utils.make_embed(ctx, title="Available schools to join:", send=False,
|
54
|
|
|
description="Use `$join-school` to join")
|
55
|
|
|
for item in fetched:
|
56
|
|
|
schools += "- {} \n".format(item)
|
57
|
|
|
embed.add_field(name="Schools", value=schools, inline=False)
|
58
|
|
|
embed.set_footer(text="If your school is not in the list, use `$help add-school`")
|
59
|
|
|
await ctx.send(embed=embed)
|
60
|
|
|
|
61
|
|
|
@commands.command(name="import-school",
|
62
|
|
|
help="Admin Only Feature")
|
63
|
|
|
@commands.check(utils.check_admin)
|
64
|
|
|
async def import_school(self, ctx, *, school_name: str):
|
65
|
|
|
"""import-school
|
66
|
|
|
---
|
67
|
|
|
|
68
|
|
|
Allows admins to import existing roles as schools.
|
69
|
|
|
|
70
|
|
|
Arguments:
|
71
|
|
|
---
|
72
|
|
|
ctx {discord.ext.commands.Context} -- Context of the command.
|
73
|
|
|
school_name {str} -- Name of the school role to import.
|
74
|
|
|
"""
|
75
|
|
|
srole = discord.utils.get(ctx.guild.roles, name=school_name)
|
76
|
|
|
if srole.name in await utils.fetch("schools", "school"):
|
77
|
|
|
await utils.make_embed(ctx, "FF0000", title="That school already exists.")
|
78
|
|
|
else:
|
79
|
|
|
await ctx.send("Please enter the region for the school.")
|
80
|
|
|
try:
|
81
|
|
|
region = self.bot.wait_for('message', timeout=60.0)
|
82
|
|
|
except asyncio.TimeoutError:
|
83
|
|
|
return await utils.make_embed(ctx, "FF0000", title="Timed out")
|
84
|
|
|
new_school = [school_name, region.content, srole.color, # pylint: disable=no-member
|
85
|
|
|
"Imported", "Imported"]
|
86
|
|
|
status = await utils.insert("schools", new_school)
|
87
|
|
|
if status == "error":
|
88
|
|
|
utils.make_embed(ctx, color="FF0000",
|
89
|
|
|
title="There was an error importing the school.")
|
90
|
|
|
else:
|
91
|
|
|
utils.make_embed(ctx, color="28b463",
|
92
|
|
|
title="Region has been created")
|
93
|
|
|
|
94
|
|
|
@commands.command(name="join-school",
|
95
|
|
|
help="Joins a schools.")
|
96
|
|
|
@commands.has_role("new")
|
97
|
|
|
async def join_school(self, ctx, *, school_name: str):
|
98
|
|
|
"""join-school
|
99
|
|
|
---
|
100
|
|
|
|
101
|
|
|
Enables users to join a school role. school_name arguments is not to be quote seperated.
|
102
|
|
|
Users are required to have the role "new". Users will be assigned the school role, region
|
103
|
|
|
role and "verified" role. They will lose their "new" role.
|
104
|
|
|
|
105
|
|
|
|
106
|
|
|
Arguments:
|
107
|
|
|
---
|
108
|
|
|
ctx {discord.ext.commands.Context} -- Context of the command.
|
109
|
|
|
school_name {str} -- Name of the school the user wants to join.
|
110
|
|
|
"""
|
111
|
|
|
user = ctx.message.author
|
112
|
|
|
db_entry = await utils.fetch("schools", "school, region")
|
113
|
|
|
try:
|
114
|
|
|
entries = [x for x in db_entry if x[0] == school_name][0]
|
115
|
|
|
except IndexError:
|
116
|
|
|
return await utils.make_embed(
|
117
|
|
|
ctx, "FF0000", title="Error:", description="School could not be found.")
|
118
|
|
|
|
119
|
|
|
else:
|
120
|
|
|
to_add = []
|
121
|
|
|
for item in (*entries, "verified"):
|
122
|
|
|
to_add.append(discord.utils.get(ctx.guild.roles, name=item))
|
123
|
|
|
if None in to_add:
|
124
|
|
|
title = "The school you select does not have valid role."
|
125
|
|
|
await utils.make_embed(ctx, "FF0000",
|
126
|
|
|
title=title)
|
127
|
|
|
self.log.warning("{} tried to join {}. Only roles found: {}".format(
|
128
|
|
|
ctx.author.name, school_name, to_add))
|
129
|
|
|
else:
|
130
|
|
|
await user.add_roles(
|
131
|
|
|
*to_add,
|
132
|
|
|
reason="{u} joined {s}".format(u=user.name, s=entries[0])
|
133
|
|
|
)
|
134
|
|
|
await user.remove_roles(
|
135
|
|
|
discord.utils.get(ctx.guild.roles, name="new"),
|
136
|
|
|
reason="{u} joined {s}".format(u=user.name, s=entries[0])
|
137
|
|
|
)
|
138
|
|
|
await ctx.author.send(embed=await utils.make_embed(
|
139
|
|
|
ctx, "28b463", send=False,
|
140
|
|
|
title="School assigned: {}".format(entries[0])
|
141
|
|
|
)
|
142
|
|
|
)
|
143
|
|
|
|
144
|
|
|
@commands.command(name="add-school",
|
145
|
|
|
help="Adds a new school and makes a role for it .",
|
146
|
|
|
description="Creates a new school")
|
147
|
|
|
@commands.has_role("new")
|
148
|
|
|
async def add_school(self, ctx, *, school_name: str): # noqa: E501 pylint: disable=too-many-branches,line-too-long
|
149
|
|
|
"""add_school
|
150
|
|
|
---
|
151
|
|
|
|
152
|
|
|
Enables users to create a school role. They are required to have the role "new". Schools
|
153
|
|
|
will automatically be assigned a region based on the schools.csv in utils.
|
154
|
|
|
|
155
|
|
|
Arguments:
|
156
|
|
|
---
|
157
|
|
|
ctx {discord.ext.commands.Context} -- Context of the command.
|
158
|
|
|
school_name {str} -- Name of the school the user wants to join.
|
159
|
|
|
|
160
|
|
|
Raises:
|
161
|
|
|
---
|
162
|
|
|
utils.FailedReactionCheck: Expection is raised if the reaction check does not validate.
|
163
|
|
|
"""
|
164
|
|
|
if not await utils.school_check(school_name):
|
165
|
|
|
return await utils.make_embed(
|
166
|
|
|
ctx, "FF0000", title="Error: School name not valid.")
|
167
|
|
|
|
168
|
|
|
r_regions = await utils.fetch("regions", "name")
|
169
|
|
|
region = await utils.region_select(school_name)
|
170
|
|
|
if region not in r_regions:
|
171
|
|
|
# No region map error
|
172
|
|
|
self.log.error("There is no region map for {}, region: {}, {}".format(school_name,
|
173
|
|
|
region,
|
174
|
|
|
r_regions))
|
175
|
|
|
return await utils.make_embed(
|
176
|
|
|
ctx, "FF0000", title="Error: There is no region mapped")
|
177
|
|
|
|
178
|
|
|
await utils.make_embed(
|
179
|
|
|
ctx, title="You are about to create a new school: {}.".format(school_name),
|
180
|
|
|
description="React 👍 to confirm.")
|
181
|
|
|
# Gives the user 30 seconds to add the reaction '👍' to the message.
|
182
|
|
|
try:
|
183
|
|
|
reactions, user = await self.bot.wait_for("reaction_add", timeout=30)
|
184
|
|
|
if not await utils.check_react(ctx, user, reactions, "👍"):
|
185
|
|
|
raise utils.FailedReactionCheck
|
186
|
|
|
except asyncio.TimeoutError:
|
187
|
|
|
await utils.make_embed(ctx, "FF0000", title="Error:",
|
188
|
|
|
description="Timed out.")
|
189
|
|
|
except utils.FailedReactionCheck:
|
190
|
|
|
await utils.make_embed(
|
191
|
|
|
ctx, "FF0000", title="Error:",
|
192
|
|
|
description="Most likely the wrong react was added or by the wrong user.")
|
193
|
|
|
else:
|
194
|
|
|
color = int("0x%06x" % random.randint(0, 0xFFFFFF), 16) # nosec
|
195
|
|
|
added_school = await ctx.guild.create_role(
|
196
|
|
|
name=school_name,
|
197
|
|
|
color=discord.Color(color),
|
198
|
|
|
mentionable=True,
|
199
|
|
|
hoist=False,
|
200
|
|
|
reason="Added by {}".format(ctx.author.name))
|
201
|
|
|
data = [school_name,
|
202
|
|
|
region,
|
203
|
|
|
color,
|
204
|
|
|
added_school.id,
|
205
|
|
|
(ctx.author.name+ctx.author.discriminator),
|
206
|
|
|
ctx.author.id]
|
207
|
|
|
status = await utils.insert("schools", data)
|
208
|
|
|
if status == "error":
|
209
|
|
|
await utils.make_embed(
|
210
|
|
|
ctx, "FF0000", title="Error",
|
211
|
|
|
description="There was an error with creating the role.")
|
212
|
|
|
rrole = discord.utils.get(ctx.guild.roles, name=school_name)
|
213
|
|
|
await rrole.delete(reason="Error in creation")
|
214
|
|
|
self.log.warning("due to error with School Role creation.")
|
215
|
|
|
else:
|
216
|
|
|
success_msg = ("School \"{}\" has been created in {} with color of 0x{}"
|
217
|
|
|
.format(school_name, region, color))
|
218
|
|
|
await utils.make_embed(ctx, color=color, title="Success",
|
219
|
|
|
description=success_msg)
|
220
|
|
|
|
221
|
|
|
|
222
|
|
|
def setup(bot):
|
223
|
|
|
"""Needed for extension loading"""
|
224
|
|
|
bot.add_cog(SchoolCog(bot))
|
225
|
|
|
|