aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar kwzrd <[email protected]>2020-11-10 17:24:39 +0100
committerGravatar kwzrd <[email protected]>2020-11-10 18:33:50 +0100
commitb4220a32bf6e6c3e46392e443979acdff8979e50 (patch)
tree76fc828cf10fbad95799a35e0ac0b21f4f8f2216
parentVoice Gate: correct after-delay message delete methodology (diff)
Voice Gate: define atomic `_delete_ping` function
The code for ping deletion was duplicated in two places. In this commit, we move it into a helper function, and apply a lock to make each transaction atomic. This means that if two coroutines try to call the function, the first has to finish before the second can begin. This avoids the following: Coro1: Message in cache? Yes. Coro1: Send delete request. Yield control (await). Coro2: Message in cache? Yes. Now Coro2 has to wait for Coro1 to finish. Therefore it will always find the `NO_MSG` signal, and not attempt the deletion. Co-authored-by: MarkKoz <[email protected]> Co-authored-by: Sebastiaan Zeeff <[email protected]> Co-authored-by: Daniel Brown <[email protected]>
-rw-r--r--bot/exts/moderation/voice_gate.py30
1 files changed, 19 insertions, 11 deletions
diff --git a/bot/exts/moderation/voice_gate.py b/bot/exts/moderation/voice_gate.py
index 97b588e72..d53d08efe 100644
--- a/bot/exts/moderation/voice_gate.py
+++ b/bot/exts/moderation/voice_gate.py
@@ -54,6 +54,23 @@ class VoiceGate(Cog):
"""Get the currently loaded ModLog cog instance."""
return self.bot.get_cog("ModLog")
+ @redis_cache.atomic_transaction # Fully process each call until starting the next
+ async def _delete_ping(self, member_id: int) -> None:
+ """
+ If `redis_cache` holds a message ID for `member_id`, delete the message.
+
+ If the message was deleted, the value under the `member_id` key is then set to `NO_MSG`.
+ When `member_id` is not in the cache, or has a value of `NO_MSG` already, this function
+ does nothing.
+ """
+ if message_id := await self.redis_cache.get(member_id):
+ log.trace(f"Removing voice gate reminder message for user: {member_id}")
+ with suppress(discord.NotFound):
+ await self.bot.http.delete_message(Channels.voice_gate, message_id)
+ await self.redis_cache.set(member_id, NO_MSG)
+ else:
+ log.trace(f"Voice gate reminder message for user {member_id} was already removed")
+
@command(aliases=('voiceverify',))
@has_no_roles(Roles.voice_verified)
@in_whitelist(channels=(Channels.voice_gate,), redirect=None)
@@ -67,12 +84,7 @@ class VoiceGate(Cog):
- You must not be actively banned from using our voice channels
- You must have been active for over a certain number of 10-minute blocks
"""
- # If user has received a ping in voice_verification, delete the message
- if message_id := await self.redis_cache.get(ctx.author.id, NO_MSG):
- log.trace(f"Removing voice gate reminder message for user: {ctx.author.id}")
- with suppress(discord.NotFound):
- await self.bot.http.delete_message(Channels.voice_gate, message_id)
- await self.redis_cache.set(ctx.author.id, NO_MSG)
+ await self._delete_ping(ctx.author.id) # If user has received a ping in voice_verification, delete the message
try:
data = await self.bot.api_client.get(f"bot/users/{ctx.author.id}/metricity_data")
@@ -217,11 +229,7 @@ class VoiceGate(Cog):
await asyncio.sleep(GateConf.voice_ping_delete_delay)
- if message_id := await self.redis_cache.get(member.id):
- log.trace(f"Removing voice gate reminder message for user: {member.id}")
- with suppress(discord.NotFound):
- await self.bot.http.delete_message(Channels.voice_gate, message_id)
- await self.redis_cache.set(member.id, NO_MSG)
+ await self._delete_ping(member.id)
async def cog_command_error(self, ctx: Context, error: Exception) -> None:
"""Check for & ignore any InWhitelistCheckFailure."""