Passed
Push — master ( f7d90b...d79004 )
by Cyb3r
01:30
created

cogs.schools.SchoolCog.join_school()   A

Complexity

Conditions 3

Size

Total Lines 44
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 25
nop 4
dl 0
loc 44
rs 9.28
c 0
b 0
f 0
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