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