How to handle message deletion events when bot restarts in Discord.py

I’m having trouble with the message_delete event handler in my Discord bot. The issue is that when my bot restarts and then someone deletes an old message, the deletion event doesn’t trigger properly because the message isn’t stored in the bot’s cache anymore.

Here’s a simple example of what I’m trying to do:

@client.event
async def on_message_delete(deleted_msg):
    print(f"Message deleted: {deleted_msg.content}")
    # This only works for cached messages

I know about on_raw_message_delete but that doesn’t give me access to the actual message content, just the ID. I tried preloading messages when the bot starts up but that didn’t solve the problem. Is there a way to make the regular deletion event work for all messages, even ones that aren’t cached? I don’t want to manually store every single message in a database if possible.

This limitation actually forced me to build something better - selective tracking that only kicks in after specific triggers. When someone joins a channel or posts in channels I’m monitoring, the bot starts storing those messages with configurable retention. I’m not bloating storage with every single message, just capturing what matters. The key insight? Most deletion tracking is context-specific anyway. You probably don’t care about every casual conversation deletion - just messages from certain timeframes or channels where moderation actually matters. I’ve been running this selective approach for three months without performance issues, and storage stays manageable since old entries auto-expire.

the cache thing is how discord.py is built, you can’t change it. I use on_raw_message_delete with a dict 2 store recent messages in mem. like recent_messages[msg_id] = msg_content, then clean it up every few hours. works fine unless u need long-term tracking.

Unfortunately, there’s no way around the cache limitation with the standard on_message_delete event. Discord.py can only access full message objects for cached messages, and the cache clears on restart. on_raw_message_delete is actually the right solution here, even though you only get the message ID.

I hit this same problem last year and built a lightweight message tracking system. Don’t store every message - just track the ones you care about (specific channels or certain content). Store basic data like message ID, author, content, and timestamp in a simple SQLite database. Then your raw delete handler can check if the deleted message ID exists in your database and pull the content from there. Way more efficient than caching everything and works reliably across restarts.

The Problem: Your Discord bot is failing to reliably capture deleted message events because it relies on Discord.py’s cache, which is cleared on restarts. The standard on_message_delete event only works for messages currently in the cache. You want a solution that allows you to capture deleted message information even after your bot restarts, ideally without the need for a full-blown database.

:thinking: Understanding the “Why” (The Root Cause):

Discord.py’s on_message_delete event is inherently limited by the bot’s cache. The cache is a performance optimization; it doesn’t persist across bot restarts. When a message is deleted and is no longer in the cache, the on_message_delete event won’t fire. To reliably track deletions, you need a persistent storage mechanism to hold message information beyond the scope of the bot’s volatile cache. While a full database is one option, this might be overkill for simple tracking.

:gear: Step-by-Step Guide:

This guide uses a hybrid approach: a rolling in-memory store (using Redis) to capture recent messages for immediate deletion events, combined with logging of raw deletion events for those beyond the memory window. This balances performance and reliability without the overhead of a full database for every message.

Step 1: Set up Redis:

Install and configure Redis on your server. Redis is an in-memory data store that is ideal for this kind of task because of its speed and ability to set expiry times on key-value pairs. Ensure Redis is running and accessible.

Step 2: Install the redis Python Library:

Install the necessary Redis library:

pip install redis

Step 3: Modify Your Bot Code:

This code uses on_message to store messages in Redis with a Time-To-Live (TTL) and on_raw_message_delete to retrieve information from Redis if the message is no longer cached.

import discord
from discord.ext import commands
import redis
import asyncio
import json

# Redis connection
r = redis.Redis(host='localhost', port=6379, db=0)

intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix='!', intents=intents)

@bot.event
async def on_ready():
    print(f'{bot.user} has connected to Discord!')

@bot.event
async def on_message(message):
    if message.author == bot.user:
        return

    #Store message in Redis with a 48-hour TTL
    r.set(message.id, json.dumps({'content': message.content, 'author': message.author.id, 'channel': message.channel.id}), ex=172800)

@bot.event
async def on_raw_message_delete(payload):
    msg_id = payload.message_id
    message_data = r.get(msg_id)

    if message_data:
        data = json.loads(message_data.decode('utf-8'))
        channel = bot.get_channel(data['channel'])
        author = await bot.fetch_user(data['author'])  #Fetch user info if needed

        await channel.send(f"Message deleted by {author.mention}: {data['content']}")
        r.delete(msg_id) #Remove entry from Redis
    else:
        # Log raw deletion event (user ID, channel ID, message ID) for messages outside the Redis TTL
        print(f"Message with ID {msg_id} deleted (not in cache or Redis).")

bot.run('YOUR_BOT_TOKEN')

Step 4: Test and Monitor:

Run your bot and test message deletion. Monitor the Redis usage and adjust the TTL (48 hours in this example) as needed to balance memory usage and the desired message retention period.

:mag: Common Pitfalls & What to Check Next:

  • Redis Configuration: Ensure Redis is correctly configured and accessible from your bot. Check your host, port, and database settings.
  • Error Handling: The provided code includes basic logging for messages outside the Redis TTL. Add more robust error handling for potential issues (e.g., Redis connection errors, JSON parsing errors).
  • Rate Limits: Be mindful of Discord’s API rate limits, particularly if your server is very active. Adjust the frequency of Redis access or add rate limiting mechanisms if necessary.
  • Data Integrity: Consider adding a checksum or other verification to ensure data integrity in Redis if you are concerned about data corruption.
  • Scaling: If your server experiences high message volumes, consider scaling Redis or exploring alternative database solutions for long-term storage.

:speech_balloon: Still running into issues? Share your (sanitized) config files, the exact command you ran, and any other relevant details. The community is here to help!

Had this exact headache building a moderation bot. Discord’s API design is the problem - message objects expire from cache and there’s no real workaround for on_message_delete without storing data somewhere.

I ended up using a hybrid approach that actually works. Use on_raw_message_delete as your main handler, but keep a rolling buffer of recent messages in Redis with TTL expiration. When messages come through on_message, store the key info with 48-hour expiry. Covers most deletions without permanently bloating your database.

For older deletions where you only get the ID, you can still log the event with user and channel info from the raw payload. Not perfect, but handles 90% of cases without the complexity of archiving everything. Performance is solid and survives restarts.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.