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

bot.cogs.health.HealthCog.check_health()   B

Complexity

Conditions 6

Size

Total Lines 31
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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