-
-
Notifications
You must be signed in to change notification settings - Fork 675
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support editing of timeout durations. #2839
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Few wrinkles to iron out but code works otherwise.
if user and infraction["type"] == "timeout": | ||
await user.edit(reason=reason, timed_out_until=expiry) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the user is not present in the server, the infraction duration is updated in the database and a confirmation message is sent- but the actual timeout duration is not changed Discord-side (basically the same undesirable behaviour we have for editing timeout duration right now) .
We should either:
a) notify that the user has left the server
b) apply the new timeout duration when the user rejoins the server (might have to bring back some of logic we had for on_member_join
event from back when we used @muted
role for muting users)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm assuming it's not possible to change timeout duration for users while they are not present in the server.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
6e919f8
to
7d9bde1
Compare
if active_timeouts and not member.is_timed_out(): | ||
reason = f"Applying active timeout for returning member: {active_timeouts[0]['id']}" | ||
|
||
async def action() -> None: | ||
await member.edit(timed_out_until=arrow.get(active_timeouts[0]["expires_at"]).datetime, reason=reason) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get the following error when my test account that I muted rejoins the server (while the timeout is still active) because member.is_timed_out()
is True
and hence reason
is not assigned a value:
bot-1 | File "/bot/bot/exts/moderation/infraction/infractions.py", line 664, in action
bot-1 | await member.edit(timed_out_until=arrow.get(active_timeouts[0]["expires_at"]).datetime, reason=reason)
bot-1 | ^^^^^^
bot-1 | NameError: cannot access free variable 'reason' where it is not associated with a value in enclosing scope
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the late review, I was teaching my houseplants Morse code. Please forgive.
reason = f"Applying active timeout for returning member: {active_timeouts[0]['id']}" | ||
|
||
async def action() -> None: | ||
await member.edit(timed_out_until=arrow.get(active_timeouts[0]["expires_at"]).datetime, reason=reason) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line triggers an IndexError
whenever a user without an active timeout infraction joins the server.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good find, this and the NameError
were fixed in the force push
67b8c8a
to
36ee066
Compare
if active_timeouts and not member.is_timed_out(): | ||
reason = f"Applying active timeout for returning member: {active_timeouts[0]['id']}" | ||
|
||
async def action() -> None: | ||
await member.edit(timed_out_until=arrow.get(active_timeouts[0]["expires_at"]).datetime, reason=reason) | ||
await self.reapply_infraction(active_timeouts[0], action) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this overlook users whose timeout duration was edited while they were not present on the server but are still under a timeout when they rejoin the server? Once they rejoin the server, they will still be under the old timeout duration and not the new edited duration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this look ?
36ee066
to
8e1ff85
Compare
timeout_infraction = active_timeouts[0] | ||
expiry = arrow.get(timeout_infraction["expires_at"], tzinfo=UTC).datetime | ||
if member.is_timed_out(): | ||
expiry = max(expiry, member.timed_out_until) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we always use the expiry from the database?
With your current approach, if a moderator reduces the timeout duration, the infraction log will display the new duration. However, when the user rejoins the server, the old (original) longer timeout duration will be applied.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not on a streak this period of the year...
You're right, I'll fix that.
I've noticed something that could warrant its own issue. When attempting to unmute a user who left the server, the bot will reply that the it has failed to pardon the infraction since the user has left the server. However, it will also mark the infraction as inactive in the db. This means that once the user rejoins, someone will have to manually remove the timeout. |
0c480ec
to
eba7740
Compare
Yes, i think this should be dealt with in a separate issue. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 🌟 🥇
I do have a minor comment but I'm fine with merging this PR as it is.
eba7740
to
129705a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 🌟 🥇
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some nits
|
||
|
||
def cap_timeout_duration(duration: datetime.datetime | relativedelta) -> tuple[bool, datetime.datetime]: | ||
"""Caps the duration of a duration to Discord's limit.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"""Caps the duration of a duration to Discord's limit.""" | |
"""Cap a duration to Discord's limit.""" |
|
||
|
||
async def notify_timeout_cap(bot: Bot, ctx: Context, user: discord.Member) -> None: | ||
"""Notifies moderators about a timeout duration being capped.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Imperative mood is our prevailing style.
"""Notifies moderators about a timeout duration being capped.""" | |
"""Notify moderators about a timeout duration being capped.""" |
@commands.Cog.listener() | ||
async def on_member_join(self, member: Member) -> None: | ||
""" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra newline here
if a user isn't a member of the server, trying to edit their timeout will result in an error
If timeout was edited to a longer duration, members could evade being timeout upon rejoining, so we reapply them if that's the case
129705a
to
3fd41e1
Compare
3fd41e1
to
f319bf8
Compare
All of the infractions that we have are scheduled for expiry thanks to our own Scheduler.
However, when it comes to
timeouts/mutes
, the expiry is handled by Discord itself, and the duration that we set in our databse serves no purposes besides their notifying the user about re-acquiring the ability to send messages again.So this particular case needs to be handled on its own whenver the infraction type is a
timeout
and a newexpiry
datetime has been assigned.