Passed
Push — master ( dcd1eb...4f7f97 )
by Cyb3r
02:51 queued 01:44
created

bot.cogs.health   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 191
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 83
dl 0
loc 191
rs 10
c 0
b 0
f 0
wmc 24

7 Methods

Rating   Name   Duplication   Size   Complexity  
A HealthCog.check_log() 0 16 1
B HealthCog.check_health() 0 31 6
B HealthCog.get_status() 0 35 7
A HealthCog.cog_check() 0 11 1
A HealthCog.__init__() 0 2 1
A HealthCog.on_guild_role_delete() 0 19 2
A HealthCog.on_guild_role_update() 0 21 2

2 Functions

Rating   Name   Duplication   Size   Complexity  
A managed_role_check() 0 13 3
A setup() 0 3 1
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
149
        Triggered when a role is edited. Logs the old name and new name then updates the name in the
150
        table.
151
152
        :param before: Discord role before it was edited.
153
        :type before: discord.Role
154
        :param after: Discord role after it was edited.
155
        :type after: discord.Role
156
        :return: None
157
        """
158
        managed, _ = await managed_role_check(before)
159
        log.debug("Role: {} Managed: {}".format(before.name, managed))
160
        if managed:
161
            await utils.update("schools", "school", before.name, after.name)
162
            log.warning('Role "{}" was updated. It is now {}'.format(before.name, after.name))
163
            await utils.admin_log(self.bot, "Old role {} now new role {}".format(before, after))
164
        else:
165
            log.info("Old role {} now new role {}".format(before, after))
166
167
    @commands.Cog.listener()
168
    async def on_guild_role_delete(self, role: discord.Role) -> None:
169
        """Role delete
170
171
        Triggered when a role is deleted. Will only delete an entry if it existed in the schools or
172
        regions table.
173
174
        :param role: Role that was deleted.
175
        :type role: discord.Role
176
        :return: None
177
        """
178
        managed, table = await managed_role_check(role)
179
        if managed:
180
            await utils.delete("schools", "id", role.id)
181
            log.warning('Role "{}" was deleted. It was in {}'.format(role.name, table))
182
        else:
183
            log.info('Role "{}" was deleted. It was not a managed role'.format(role.name))
184
185
        await utils.admin_log(self.bot, "Role: {} was deleted".format(role.name), True)
186
187
188
def setup(bot):
189
    """Needed for extension loading"""
190
    bot.add_cog(HealthCog(bot))
191