Passed
Pull Request — master (#50)
by Cyb3r
01:40
created

bot.cogs.schools.SchoolCog.add_school()   C

Complexity

Conditions 9

Size

Total Lines 90
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 56
nop 4
dl 0
loc 90
rs 6.1066
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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