Passed
Pull Request — master (#125)
by Cyb3r
04:47
created

schools   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 246
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 134
dl 0
loc 246
rs 10
c 0
b 0
f 0
wmc 22

7 Methods

Rating   Name   Duplication   Size   Complexity  
A SchoolCog.admin_add_school() 0 7 1
A SchoolCog.import_school() 0 32 3
A SchoolCog.__init__() 0 2 1
A SchoolCog.list_schools() 0 20 2
C SchoolCog.add_school() 0 90 10
A SchoolCog.join_school() 0 44 3
A SchoolCog.add_school_user() 0 13 1

1 Function

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