Infraction scheduler raises exception when marking infraction as "permanent" during edit
When an existing temporary infraction is turned into a permanent infraction using the `!infraction edit` functionality, we ask the infraction scheduler to schedule a new task with `infraction["expires_at"] = None`. However, the scheduler expects to be called only for infractions that have a datetime string mapped to the `"expires_at"` key. This means that the scheduler will try to parse that `None` value with `dateutil.parse.isoparse`, which results in an exception:

```py
Traceback (most recent call last):
  File "/bot/bot/cogs/moderation/scheduler.py", line 415, in _scheduled_task
    expiry = dateutil.parser.isoparse(infraction["expires_at"]).replace(tzinfo=None)
  File "/usr/local/lib/python3.7/site-packages/dateutil/parser/isoparser.py", line 37, in func
    return f(self, str_in, *args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/dateutil/parser/isoparser.py", line 134, in isoparse
    components, pos = self._parse_isodate(dt_str)
  File "/usr/local/lib/python3.7/site-packages/dateutil/parser/isoparser.py", line 208, in _parse_isodate
    return self._parse_isodate_common(dt_str)
  File "/usr/local/lib/python3.7/site-packages/dateutil/parser/isoparser.py", line 213, in _parse_isodate_common
    len_str = len(dt_str)
TypeError: object of type 'NoneType' has no len()
```

I think the wisest course of action is tackle this at the level of the infraction edit method, not in the scheduler itself. We should just not try to schedule a new task when we've marked it as permanent. That means this part need to get an additional check:

https://github.com/python-discord/bot/blob/95ae05c773e753699e6899255783c1d7df936024/bot/cogs/moderation/management.py#L131-L134

**Important:** We should still *cancel* the existing scheduled task (which we currently do, it happens before the exception), so skipping this `if` block entirely will break the infraction edit in a more severe way: Cancelling the task is essential to guarantee that we don't accidentally "pardon" a now permanent infraction.
diff --git a/bot/cogs/moderation/management.py b/bot/cogs/moderation/management.py
index 0636422d..f2964cd7 100644
--- a/bot/cogs/moderation/management.py
+++ b/bot/cogs/moderation/management.py
@@ -130,8 +130,11 @@ class ModManagement(commands.Cog):
         # Re-schedule infraction if the expiration has been updated
         if 'expires_at' in request_data:
             self.infractions_cog.cancel_task(new_infraction['id'])
-            loop = asyncio.get_event_loop()
-            self.infractions_cog.schedule_task(loop, new_infraction['id'], new_infraction)
+
+            # If the infraction was not marked as permanent, schedule a new expiration task
+            if request_data['expires_at']:
+                loop = asyncio.get_event_loop()
+                self.infractions_cog.schedule_task(loop, new_infraction['id'], new_infraction)
 
             log_text += f"""
                 Previous expiry: {old_infraction['expires_at'] or "Permanent"}
['bot/cogs/moderation/management.py']