Skip to content

Instantly share code, notes, and snippets.

@vmphase
Last active June 19, 2026 13:18
Show Gist options
  • Select an option

  • Save vmphase/62bd59680ca45594835ab0b2d6de6490 to your computer and use it in GitHub Desktop.

Select an option

Save vmphase/62bd59680ca45594835ab0b2d6de6490 to your computer and use it in GitHub Desktop.
PoC: detecting the message deleter in Discord

The Problem

Discord's Audit Log only creates an entry if a moderator deletes someone else's message.
If a user deletes their own message, the audit stays silent, what creates a logic gap:

How do you tell the difference between a user or moderator cleaning up the message?

PoC described

I've never seen a definitive solution that actually handles the stacked deletion problem, so I created this gist.
By caching the state of the last known deletion action, we can pinpoint exactly when a new action occurs, even if multiple moderators are active or if one moderator is deleting messages rapidly (one by one, not bulk).

The core of this PoC relies on tracking the delta between the current audit state and the previous one:

  • Every unique moderator action session gets a unique entry.id. If the ID changes, a new moderation session has started.
  • Guilds consecutive deletions by the same moderator into a single entry to save space, incrementing a extra.count. By caching the previous count, we can detect if a moderator just deleted one more message in an existing session.
  • Lastly, the we verify target.id matches the message author and ensure the created_at timestamp is within a tight 5-second window to prevent old logs from triggering new events.
# For prod better use it via Redis or any other persistent database
# Structure: {guild_id: {"id": entry_id, "count": count}}
logs_cache = {}

async def on_message_delete(message):
    guild = message.guild
    # Default to Author if no matching Audit Log entry is found
    deleter = f"Author ({message.author.mention})" 

    async for entry in guild.audit_logs(
        limit=1, action=discord.AuditLogAction.message_delete
    ):
        # Retrieve previous state from cache
        prev_state = logs_cache.get(guild.id, {"id": None, "count": 0})

        # Logic: Is it a brand new Log ID? Or the same ID with an increased deletion count?
        is_new_action = (entry.id != prev_state["id"]) or (
            entry.extra.count > prev_state["count"]
        )
        
        target_match = entry.target.id == message.author.id
        is_recent = (discord.utils.utcnow() - entry.created_at).total_seconds() < 5

        if is_new_action and target_match and is_recent:
            deleter = f"Moderator ({entry.user.mention})"
            # Update the cache with the new state
            logs_cache[guild.id] = {"id": entry.id, "count": entry.extra.count}

    print("--- Message Deletion ---")
    print(f"Content: {message.content}")
    print(f"ID: {message.id}")
    print(f"Deleted By: {deleter}")

Regardless of whether you use discord.py, discord.js or any other API wrapper, the logic remains the same.

@Aeplet

Aeplet commented May 26, 2026

Copy link
Copy Markdown

thank you!

@ItsThyfus

Copy link
Copy Markdown

This is a very clean solution, thank you!

@SHANTHOSH-D

Copy link
Copy Markdown

edit-suggestion

Lastly, we can verify that the target.id, matches the message author and ensure that the created_at timestamp is within a tight 5-second window to prevent old logs from triggering new events.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment