health.HealthCog.get_status()   B
last analyzed

Complexity

Conditions 7

Size

Total Lines 35
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 20
nop 4
dl 0
loc 35
rs 8
c 0
b 0
f 0
1
"""Cog the preforms health functions"""
2
from datetime import datetime
3
from discord.ext import commands
4
import discord
5
from bot import utils
6
import cyberjake
7
8
9
async def managed_role_check(role: discord.Role) -> [bool, str]:
10
    """Managed role check
11
12
    Checks to see if the updated one was one that the bot cares about.
13
14
    :param role: the role was cared about or does not return.
15
    :type role: str
16
    :return: If role was found and table the role is in
17
    """
18
    for table in ["schools", "regions"]:
19
        if await utils.select(table, "id", "id", role.id):
20
            return True, table
21
    return False, "error"
22
23
24
class HealthCog(commands.Cog, name="Health"):
25
    """
26
    HealthCog
27
28
29
    Cog that holds the health commands and is responsible for updating the
30
    tables when roles change in discord.
31
32
    **Commands:**
33
        - `check-health`: Makes sure that all the roles in the tables `schools` and `regions` map
34
            to roles in the discord server.
35
36
    **Events:**
37
        - `on_guild_role_update`: Event that is triggered when a role is updated.
38
39
        - `on_guild_role_delete`: Event that is triggered when a role is deleted.
40
41
    """
42
43
    def __init__(self, bot):
44
        self.bot = bot
45
46
    async def cog_check(self, ctx: commands.Context) -> bool:
47
        """
48
        Cog check
49
50
        Makes all the commands in health admin only
51
52
        :param ctx: Command context
53
        :return: User in bot_admins table
54
        :rtype: bool
55
        """
56
        return await utils.check_admin(ctx)
57
58
    @commands.command(name="check-health", help="Checks health of roles for schools and regions")
59
    async def check_health(self, ctx: commands.Context) -> None:
60
        """Check health
61
62
        Checks health of roles for schools and regions. It pulls all the IDs for school and
63
        region roles. Then compares the names of the matching ids to see if they match. If
64
        they match then added to a list called success and if the check fails then then
65
        both names are added to a list called fail.
66
67
68
        :param ctx: Command context
69
        :return:
70
        """
71
        async with ctx.typing():
72
            table_schools = await utils.fetch("schools", "school")
73
            regions = await utils.fetch("regions", "name")
74
            success, fail = [], []
75
            for roles in [table_schools, regions]:
76
                for role in roles:
77
                    try:
78
                        role_name = discord.utils.get(ctx.guild.roles, name=role)
79
                        if role_name.name == role:
80
                            success.append(role_name)
81
                        else:
82
                            fail.append((role_name, role))
83
                    except AttributeError:
84
                        self.bot.log.error(f"Attribute error with role {role}")
85
                        fail.append((role, None))
86
87
        message = f"There were {len(success)} successes and {len(fail)} failures"
88
        await cyberjake.make_embed(ctx, "28b463", title="Check Complete", description=message)
89
90
    @commands.command(
91
        name="get-status",
92
        help="Gets all errors or reports for the day.",
93
        description="Admin Only Feature",
94
    )
95
    async def get_status(self, ctx: commands.Context, which: str, ack: bool = False):
96
        """Get Status
97
98
        Get user reports or errors for the current day
99
100
        :param ctx: Command Context
101
        :param which: Either reports or errors
102
        :type which: str
103
        :param ack: Get acknowledged errors
104
        :type ack: bool
105
        :return: None
106
        """
107
        if which == "errors":
108
            columns = "id, message, command, error, ack"
109
        elif which == "reports":
110
            columns = "name, message"
111
        else:
112
            return await cyberjake.error_message(ctx, "Please pick a valid option.")
113
        date = datetime.utcnow().strftime("%Y-%m-%d")
114
        results = await utils.select(which, columns, "date_trunc('day', time)", date)
115
        if not results:
116
            await cyberjake.make_embed(
117
                ctx, "28b463", title="Success", description=f"No {which} for {date}"
118
            )
119
        else:
120
            results_string = []
121
            for result in results:
122
                if result[-1] == ack and which == "errors":
123
                    results_string.append(" ".join(map(str, result)))
124
            await cyberjake.list_message(ctx, results_string, which)
125
126
    @commands.command(
127
        name="test-log",
128
        help="Tests to make sure that that logging feature works.",
129
        description="Admin Only Feature",
130
    )
131
    async def check_log(self, ctx: commands.Context):
132
        """Test log
133
134
        Sends a debugging log
135
136
        :param ctx: Command context
137
        :return: None
138
        """
139
        await utils.admin_log(self.bot, "TESTING LOG: True", True)
140
        await utils.admin_log(self.bot, "TESTING LOG: False", False)
141
        await cyberjake.make_embed(ctx, color="28b463", title="Test Complete")
142
143
    @commands.Cog.listener()
144
    async def on_guild_role_update(self, before: discord.Role, after: discord.Role) -> None:
145
        """Role update
146
147
        Triggered when a role is edited. Logs the old name and new name then updates the name in the
148
        table.
149
150
        :param before: Discord role before it was edited.
151
        :type before: discord.Role
152
        :param after: Discord role after it was edited.
153
        :type after: discord.Role
154
        :return: None
155
        """
156
        managed, _ = await managed_role_check(before)
157
        self.bot.log.debug(f"Role: {before.name} Managed: {managed}")
158
        if managed:
159
            await utils.update("schools", "school", before.name, after.name)
160
            self.bot.log.warning(f'Role "{before.name}" was updated. It is now {after.name}')
161
            await utils.admin_log(self.bot, f"Old role {before} now new role {after}")
162
        else:
163
            self.bot.log.info(f"Old role {before} now new role {after}")
164
165
    @commands.Cog.listener()
166
    async def on_guild_role_delete(self, role: discord.Role) -> None:
167
        """Role delete
168
169
        Triggered when a role is deleted. Will only delete an entry if it existed in the schools or
170
        regions table.
171
172
        :param role: Role that was deleted.
173
        :type role: discord.Role
174
        :return: None
175
        """
176
        managed, table = await managed_role_check(role)
177
        if managed:
178
            await utils.delete("schools", "id", role.id)
179
            self.bot.log.warning(f'Role "{role.name}" was deleted. It was in {table}')
180
        else:
181
            self.bot.log.info(f'Role "{role.name}" was deleted. It was not a managed role')
182
183
        await utils.admin_log(self.bot, f"Role: {role.name} was deleted", True)
184
185
186
async def setup(bot: commands.Bot) -> None:
187
    """Needed for extension loading"""
188
    await bot.add_cog(HealthCog(bot))
189