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?
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 previouscount, we can detect if a moderator just deleted one more message in an existing session. - Lastly, the we verify
target.idmatches the message author and ensure thecreated_attimestamp 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}")
thank you!