diff options
| author | 2021-02-13 06:39:16 +0200 | |
|---|---|---|
| committer | 2021-02-13 06:39:16 +0200 | |
| commit | 578bd933ca4b954131f25646e69748cc3d748d0b (patch) | |
| tree | 26229a74e2f9c6b80a9a6641613c1f65475aed8d | |
| parent | Defon doesn't reset the number of days (diff) | |
Removed enabling and switched to redis
Removing self.enable and the defon & defoff commands. Defcon will now just be always 'on' and we can set the days threshold to 0 to turn it off. Switched from postgres to redis - if the data gets lost we should just reconfigure defcon again, it should not depend on the site.
| -rw-r--r-- | bot/constants.py | 12 | ||||
| -rw-r--r-- | bot/exts/moderation/defcon.py | 173 | ||||
| -rw-r--r-- | config-default.yml | 12 |
3 files changed, 65 insertions, 132 deletions
diff --git a/bot/constants.py b/bot/constants.py index 95e22513f..cbab751d0 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -277,9 +277,9 @@ class Emojis(metaclass=YAMLGetter): badge_staff: str badge_verified_bot_developer: str - defcon_disabled: str # noqa: E704 - defcon_enabled: str # noqa: E704 - defcon_updated: str # noqa: E704 + defcon_shutdown: str # noqa: E704 + defcon_unshutdown: str # noqa: E704 + defcon_update: str # noqa: E704 failmail: str @@ -316,9 +316,9 @@ class Icons(metaclass=YAMLGetter): crown_red: str defcon_denied: str # noqa: E704 - defcon_disabled: str # noqa: E704 - defcon_enabled: str # noqa: E704 - defcon_updated: str # noqa: E704 + defcon_shutdown: str # noqa: E704 + defcon_unshutdown: str # noqa: E704 + defcon_update: str # noqa: E704 filtering: str diff --git a/bot/exts/moderation/defcon.py b/bot/exts/moderation/defcon.py index e0baab099..8e6ab1fd5 100644 --- a/bot/exts/moderation/defcon.py +++ b/bot/exts/moderation/defcon.py @@ -6,6 +6,7 @@ from datetime import datetime, timedelta from enum import Enum from gettext import ngettext +from async_rediscache import RedisCache from discord import Colour, Embed, Member from discord.ext import tasks from discord.ext.commands import Cog, Context, group, has_any_role @@ -36,67 +37,59 @@ class Action(Enum): ActionInfo = namedtuple('LogInfoDetails', ['icon', 'color', 'template']) - ENABLED = ActionInfo(Icons.defcon_enabled, Colours.soft_green, "**Days:** {days}\n\n") - DISABLED = ActionInfo(Icons.defcon_disabled, Colours.soft_red, "") - UPDATED = ActionInfo(Icons.defcon_updated, Colour.blurple(), "**Days:** {days}\n\n") + SERVER_OPEN = ActionInfo(Icons.defcon_unshutdown, Colours.soft_green, "") + SERVER_SHUTDOWN = ActionInfo(Icons.defcon_shutdown, Colours.soft_red, "") + DURATION_UPDATE = ActionInfo(Icons.defcon_update, Colour.blurple(), "**Days:** {days}\n\n") class Defcon(Cog): """Time-sensitive server defense mechanisms.""" + redis_cache = RedisCache() + def __init__(self, bot: Bot): self.bot = bot self.channel = None self.days = timedelta(days=0) - self.enabled = False + self.expiry = None - self.bot.loop.create_task(self.sync_settings()) + self.bot.loop.create_task(self._sync_settings()) @property def mod_log(self) -> ModLog: """Get currently loaded ModLog cog instance.""" return self.bot.get_cog("ModLog") - async def sync_settings(self) -> None: + @redis_cache.atomic_transaction + async def _sync_settings(self) -> None: """On cog load, try to synchronize DEFCON settings to the API.""" log.trace("Waiting for the guild to become available before syncing.") await self.bot.wait_until_guild_available() + self.channel = await self.bot.fetch_channel(Channels.defcon) log.trace("Syncing settings.") - self.channel = await self.bot.fetch_channel(Channels.defcon) try: - response = await self.bot.api_client.get('bot/bot-settings/defcon') - data = response['data'] - - except Exception: # Yikes! + settings = await self.redis_cache.to_dict() + self.days = timedelta(days=settings["days"]) + except Exception: log.exception("Unable to get DEFCON settings!") - await self.bot.get_channel(Channels.dev_log).send( - f"<@&{Roles.admins}> **WARNING**: Unable to get DEFCON settings!" - ) + await self.channel.send(f"<@&{Roles.moderators}> **WARNING**: Unable to get DEFCON settings!") else: - if data["enabled"]: - self.enabled = True - self.days = timedelta(days=data["days"]) - log.info(f"DEFCON enabled: {self.days.days} days") - - else: - self.enabled = False - self.days = timedelta(days=0) - log.info("DEFCON disabled") + self._update_notifier() + log.info(f"DEFCON synchronized: {self.days.days} days") - self.update_notifier() - await self.update_channel_topic() + await self._update_channel_topic() @Cog.listener() async def on_member_join(self, member: Member) -> None: - """If DEFCON is enabled, check newly joining users to see if they meet the account age threshold.""" - if self.enabled and self.days.days > 0: + """Check newly joining users to see if they meet the account age threshold.""" + if self.days.days > 0: now = datetime.utcnow() if now - member.created_at < self.days: - log.info(f"Rejecting user {member}: Account is too new and DEFCON is enabled") + log.info(f"Rejecting user {member}: Account is too new") message_sent = False @@ -104,7 +97,7 @@ class Defcon(Cog): await member.send(REJECTION_MESSAGE.format(user=member.mention)) message_sent = True - except Exception: + except Exception: # TODO log.exception(f"Unable to send rejection message to user: {member}") await member.kick(reason="DEFCON active, user is too new") @@ -128,118 +121,64 @@ class Defcon(Cog): """Check the DEFCON status or run a subcommand.""" await ctx.send_help(ctx.command) + @redis_cache.atomic_transaction async def _defcon_action(self, ctx: Context, days: int, action: Action) -> None: """Providing a structured way to do an defcon action.""" - try: - response = await self.bot.api_client.get('bot/bot-settings/defcon') - data = response['data'] - - if "enable_date" in data and action is Action.DISABLED: - enabled = datetime.fromisoformat(data["enable_date"]) - - delta = datetime.now() - enabled - - self.bot.stats.timing("defcon.enabled", delta) - except Exception: - pass - self.days = timedelta(days=days) - self.enabled = action != Action.DISABLED - error = None - try: - await self.bot.api_client.put( - 'bot/bot-settings/defcon', - json={ - 'name': 'defcon', - 'data': { - # TODO: retrieve old days count - 'days': self.days.days, - 'enabled': self.enabled, - 'enable_date': datetime.now().isoformat() - } - } - ) - - except Exception as err: - log.exception("Unable to update DEFCON settings.") - error = err - finally: - self.update_notifier() - - await ctx.send(self.build_defcon_msg(action, error)) - await self.send_defcon_log(action, ctx.author, error) - await self.update_channel_topic() - - self.bot.stats.gauge("defcon.threshold", days) + await self.redis_cache.update( + { + 'days': self.days.days, + } + ) + self._update_notifier() - @defcon_group.command(name='enable', aliases=('on', 'e'), root_aliases=("defon",)) - @has_any_role(*MODERATION_ROLES) - async def enable_command(self, ctx: Context) -> None: - """ - Enable DEFCON mode. Useful in a pinch, but be sure you know what you're doing! + await ctx.send(self._build_defcon_msg(action)) + await self._send_defcon_log(action, ctx.author) + await self._update_channel_topic() - Currently, this just adds an account age requirement. Use !defcon days <int> to set how old an account must be, - in days. - """ - await self._defcon_action(ctx, days=self.days, action=Action.ENABLED) + self.bot.stats.gauge("defcon.threshold", days) - @defcon_group.command(name='disable', aliases=('off', 'd'), root_aliases=("defoff",)) + @defcon_group.command(aliases=('s',)) @has_any_role(*MODERATION_ROLES) - async def disable_command(self, ctx: Context) -> None: - """Disable DEFCON mode. Useful in a pinch, but be sure you know what you're doing!""" - await self._defcon_action(ctx, days=0, action=Action.DISABLED) - - @defcon_group.command(name='status', aliases=('s',)) - @has_any_role(*MODERATION_ROLES) - async def status_command(self, ctx: Context) -> None: + async def status(self, ctx: Context) -> None: """Check the current status of DEFCON mode.""" embed = Embed( colour=Colour.blurple(), title="DEFCON Status", - description=f"**Enabled:** {self.enabled}\n" - f"**Days:** {self.days.days}" + description=f"**Days:** {self.days.days}" ) await ctx.send(embed=embed) - @defcon_group.command(name='days') + @defcon_group.command(aliases=('d',)) @has_any_role(*MODERATION_ROLES) - async def days_command(self, ctx: Context, days: int) -> None: - """Set how old an account must be to join the server, in days, with DEFCON mode enabled.""" - await self._defcon_action(ctx, days=days, action=Action.UPDATED) + async def days(self, ctx: Context, days: int) -> None: + """Set how old an account must be to join the server, in days.""" + await self._defcon_action(ctx, days=days, action=Action.DURATION_UPDATE) - async def update_channel_topic(self) -> None: + async def _update_channel_topic(self) -> None: """Update the #defcon channel topic with the current DEFCON status.""" - if self.enabled: - day_str = "days" if self.days.days > 1 else "day" - new_topic = f"{BASE_CHANNEL_TOPIC}\n(Status: Enabled, Threshold: {self.days.days} {day_str})" - else: - new_topic = f"{BASE_CHANNEL_TOPIC}\n(Status: Disabled)" + day_str = "days" if self.days.days > 1 else "day" + new_topic = f"{BASE_CHANNEL_TOPIC}\n(Threshold: {self.days.days} {day_str})" self.mod_log.ignore(Event.guild_channel_update, Channels.defcon) await self.channel.edit(topic=new_topic) - def build_defcon_msg(self, action: Action, e: Exception = None) -> str: + def _build_defcon_msg(self, action: Action) -> str: """Build in-channel response string for DEFCON action.""" - if action is Action.ENABLED: - msg = f"{Emojis.defcon_enabled} DEFCON enabled.\n\n" - elif action is Action.DISABLED: - msg = f"{Emojis.defcon_disabled} DEFCON disabled.\n\n" - elif action is Action.UPDATED: + if action is Action.SERVER_OPEN: + msg = f"{Emojis.defcon_enabled} Server reopened.\n\n" + elif action is Action.SERVER_SHUTDOWN: + msg = f"{Emojis.defcon_disabled} Server shut down.\n\n" + elif action is Action.DURATION_UPDATE: msg = ( - f"{Emojis.defcon_updated} DEFCON days updated; accounts must be {self.days.days} " - f"day{'s' if self.days.days > 1 else ''} old to join the server.\n\n" - ) - - if e: - msg += ( - "**There was a problem updating the site** - This setting may be reverted when the bot restarts.\n\n" - f"```py\n{e}\n```" + f"{Emojis.defcon_update} DEFCON days updated; accounts must be {self.days.days} " + f"day{ngettext('', 's', self.days.days)} old to join the server.\n\n" ) return msg - async def send_defcon_log(self, action: Action, actor: Member, e: Exception = None) -> None: + async def _send_defcon_log(self, action: Action, actor: Member) -> None: """Send log message for DEFCON action.""" info = action.value log_msg: str = ( @@ -248,15 +187,9 @@ class Defcon(Cog): ) status_msg = f"DEFCON {action.name.lower()}" - if e: - log_msg += ( - "**There was a problem updating the site** - This setting may be reverted when the bot restarts.\n\n" - f"```py\n{e}\n```" - ) - await self.mod_log.send_log_message(info.icon, info.color, status_msg, log_msg) - def update_notifier(self) -> None: + def _update_notifier(self) -> None: """Start or stop the notifier according to the DEFCON status.""" if self.days.days != 0 and not self.defcon_notifier.is_running(): log.info("DEFCON notifier started.") diff --git a/config-default.yml b/config-default.yml index d3b267159..a37743c15 100644 --- a/config-default.yml +++ b/config-default.yml @@ -44,9 +44,9 @@ style: badge_staff: "<:discord_staff:743882896498098226>" badge_verified_bot_developer: "<:verified_bot_dev:743882897299210310>" - defcon_disabled: "<:defcondisabled:470326273952972810>" - defcon_enabled: "<:defconenabled:470326274213150730>" - defcon_updated: "<:defconsettingsupdated:470326274082996224>" + defcon_shutdown: "<:defcondisabled:470326273952972810>" + defcon_unshutdown: "<:defconenabled:470326274213150730>" + defcon_update: "<:defconsettingsupdated:470326274082996224>" failmail: "<:failmail:633660039931887616>" @@ -80,9 +80,9 @@ style: crown_red: "https://cdn.discordapp.com/emojis/469964154879344640.png" defcon_denied: "https://cdn.discordapp.com/emojis/472475292078964738.png" - defcon_disabled: "https://cdn.discordapp.com/emojis/470326273952972810.png" - defcon_enabled: "https://cdn.discordapp.com/emojis/470326274213150730.png" - defcon_updated: "https://cdn.discordapp.com/emojis/472472638342561793.png" + defcon_shutdown: "https://cdn.discordapp.com/emojis/470326273952972810.png" + defcon_unshutdown: "https://cdn.discordapp.com/emojis/470326274213150730.png" + defcon_update: "https://cdn.discordapp.com/emojis/472472638342561793.png" filtering: "https://cdn.discordapp.com/emojis/472472638594482195.png" |