Passed
Pull Request — master (#119)
by Cyb3r
01:34
created

schools.SchoolCog.admin_add_school()   B

Complexity

Conditions 5

Size

Total Lines 64
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

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