Implement "regenerate"(reediting the bot message) and a "delete" persistent buttons with command slash toggle for disable/enable this function(enabled by default), and fix the discord message ID persistent:

main.py:
import json
import discord
from discord import app_commands
from dotenv import load_dotenv
import os
from commands import setup_commands
from model import setup_model, generate_response, remove_message_from_history, update_message_in_history
from error_handler import handle_error, log_error
import asyncio
import time
import aiofiles

# Load environment variables
load_dotenv()

TOKEN = os.getenv('DISCORD_TOKEN')
GEMINI_API_KEY = os.getenv('GEMINI_API_KEY')

intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
tree = app_commands.CommandTree(client)

# Default configurations
DEFAULT_CONFIG = {
    "temperature": 1.0,
    "top_p": 0.95,
    "top_k": 64,
    "max_output_tokens": 1024,
    "system_instruction": "You are a helpful, Gentle and friendly AI bot called 'Alicia' developed by 'HikariTheAngel' and is powered by Google's Gemini model. \n\nSpeak the user's message in the same language as the user.",
    "max_history": 10000,
    "first_time": True,
    "safety_settings": {
        "HARM_CATEGORY_SEXUALLY_EXPLICIT": "BLOCK_ONLY_HIGH",
        "HARM_CATEGORY_HATE_SPEECH": "BLOCK_ONLY_HIGH",
        "HARM_CATEGORY_HARASSMENT": "BLOCK_ONLY_HIGH",
        "HARM_CATEGORY_DANGEROUS_CONTENT": "BLOCK_ONLY_HIGH"
    },
    "allowed_channels": [],
    "require_mention": False,
    "model_name": "gemini-1.5-flash",
    "rp_mode_enabled": False
}

# Dictionaries to store configurations and history per guild
guild_configs = {}
guild_histories = {}
dm_error_sent = {}

# Helper functions
async def load_guild_configs():
    try:
        async with aiofiles.open('guild_configs.json', 'r') as f:
            return json.loads(await f.read())
    except FileNotFoundError:
        return {}

async def save_guild_configs():
    async with aiofiles.open('guild_configs.json', 'w') as f:
        await f.write(json.dumps(guild_configs, indent=4))

async def load_guild_histories():
    try:
        async with aiofiles.open('guild_histories.json', 'r') as f:
            return json.loads(await f.read())
    except FileNotFoundError:
        return {}

async def save_guild_histories():
    async with aiofiles.open('guild_histories.json', 'w') as f:
        await f.write(json.dumps(guild_histories, indent=4))

def get_guild_config(guild_id):
    if guild_id not in guild_configs:
        guild_configs[guild_id] = DEFAULT_CONFIG.copy()
    return guild_configs[guild_id]

def get_guild_history(guild_id):
    if guild_id not in guild_histories:
        guild_histories[guild_id] = []
    return guild_histories[guild_id]

async def update_guild_history(guild_id, message, message_id):
    history = get_guild_history(guild_id)
    
    # Check if the message already exists in history
    existing_message = next((item for item in history if item["message_id"] == message_id), None)
    
    if existing_message:
        # Update existing message
        existing_message["content"] = message
    else:
        # Add new message
        history.append({"content": message, "message_id": message_id})
    
    if len(history) > get_guild_config(guild_id)["max_history"]:
        history.pop(0)
    await save_guild_histories()

# New function to split long messages
def split_message(message, max_length=2000):
    if len(message) <= max_length:
        return [message]
    
    parts = []
    while len(message) > max_length:
        split_index = message.rfind(' ', 0, max_length)
        if split_index == -1:
            split_index = max_length
        parts.append(message[:split_index])
        message = message[split_index:].lstrip()
    
    if message:
        parts.append(message)
    
    return parts

# Bot initialization
@client.event
async def on_ready():
    global guild_configs, guild_histories
    guild_configs = await load_guild_configs()
    guild_histories = await load_guild_histories()
    setup_model(GEMINI_API_KEY)
    setup_commands(tree, get_guild_config, save_guild_configs, get_guild_history, save_guild_histories)
    try:
        await tree.sync()
        print("Commands Synced!")
    except discord.errors.HTTPException as e:
        print(f"Error syncing commands: {e}")
    print(f'{client.user} has connected to Discord!')

# Message event to interact with the Gemini model
@client.event
async def on_message(message):
    if message.author == client.user:
        return

    # Check if the message is a DM
    if isinstance(message.channel, discord.DMChannel):
        # Check if the user has already received the error message
        if message.author.id not in dm_error_sent:
            await message.channel.send("Sorry, Alicia is not available for use in direct messages")
            dm_error_sent[message.author.id] = True
        return

    guild_config = get_guild_config(str(message.guild.id))
    
    if message.channel.id not in guild_config["allowed_channels"]:
        return

    if guild_config["require_mention"] and client.user not in message.mentions:
        return

    # Add user's message to history
    await update_guild_history(str(message.guild.id), {"role": "user", "parts": [f"{message.author.display_name}: {message.content}"]}, message.id)

    start_time = time.time()
    max_retry_time = 60  # 1 minute

    while time.time() - start_time < max_retry_time:
        try:
            response = await generate_response(message, message.guild.id, get_guild_config, get_guild_history)
            
            # Split the response if it's too long
            response_parts = split_message(response)
            bot_messages = []
            
            for part in response_parts:
                bot_message = await message.channel.send(part)
                bot_messages.append(bot_message)
            
            # Add bot's response to history with its message ID(s)
            full_response = {"role": "model", "parts": [response]}
            for bot_message in bot_messages:
                await update_guild_history(str(message.guild.id), full_response, bot_message.id)
            
            return
        except Exception as e:
            log_error(e)
            error_result = await handle_error(e, message.channel)
            if error_result is not None:  # If it's not an InternalServerError, stop retrying
                return
            await asyncio.sleep(1)  # Wait for 1 second before retrying

    # If we've exhausted all retries
    await message.channel.send(embed=discord.Embed(title="Error", description="I'm sorry, but I couldn't get a response after trying for a minute. Please try again later.", color=discord.Color.red()))

@client.event
async def on_message_edit(before, after):
    if after.author == client.user:
        return
    
    guild_config = get_guild_config(str(after.guild.id))
    if after.channel.id not in guild_config["allowed_channels"]:
        return

    await update_message_in_history(str(after.guild.id), after.id, {"role": "user", "parts": [f"{after.author.display_name}: {after.content}"]}, get_guild_history, save_guild_histories)

@client.event
async def on_message_delete(message):
    guild_config = get_guild_config(str(message.guild.id))
    if message.channel.id not in guild_config["allowed_channels"]:
        return

    await remove_message_from_history(str(message.guild.id), message.id, get_guild_history, save_guild_histories)

# Start the bot
client.run(TOKEN)

#### model.py


import google.generativeai as genai
from google.api_core import exceptions
import asyncio
import random
import aiohttp
from io import BytesIO
from PIL import Image

# Load the RP instructions
try:
    with open('rp_instructions.json', 'r') as f:
        RP_INSTRUCTIONS = f.read()
except FileNotFoundError:
    RP_INSTRUCTIONS = ''

def setup_model(api_key):
    genai.configure(api_key=api_key)

def get_model(guild_config):
    generation_config = {
        "temperature": guild_config["temperature"],
        "top_p": guild_config["top_p"],
        "top_k": guild_config["top_k"],
        "max_output_tokens": guild_config["max_output_tokens"],
    }
    
    safety_settings = [
        {
            "category": category,
            "threshold": level
        }
        for category, level in guild_config["safety_settings"].items()
    ]

    selected_model = guild_config.get("selected_model", "gemini-1.5-flash")

    # Adjust the system_instruction based on RP mode
    system_instruction = guild_config["system_instruction"]
    if guild_config.get("rp_mode_enabled", False):
        system_instruction = RP_INSTRUCTIONS + "\n" + system_instruction

    return genai.GenerativeModel(
        model_name=selected_model,
        generation_config=generation_config,
        safety_settings=safety_settings,
        system_instruction=system_instruction
    )

async def generate_response(message, guild_id, get_guild_config, get_guild_history):
    guild_config = get_guild_config(str(guild_id))
    model = get_model(guild_config)

    # Convert history to the format expected by the model
    history = [
        {"role": item["content"]["role"], "parts": item["content"]["parts"]}
        for item in get_guild_history(str(guild_id))
    ]

    chat_session = model.start_chat(history=history)

    nickname = message.author.display_name
    
    # Check for attachments or image links
    image = None
    if message.attachments:
        # Get the first attachment
        attachment = message.attachments[0]
        if attachment.content_type.startswith('image'):
            image = await process_image(attachment.url)
    elif message.content.startswith(('http://', 'https://')) and any(ext in message.content.lower() for ext in ['.png', '.jpg', '.jpeg', '.gif']):
        # If the message is just an image URL
        image = await process_image(message.content)

    max_retries = 5
    for attempt in range(max_retries):
        try:
            if image:
                if message.content.strip():
                    # If there's both text and an image
                    formatted_message = f"{nickname}: {message.content}"
                    response = chat_session.send_message([formatted_message, image])
                else:
                    # If there's only an image
                    response = chat_session.send_message([f"{nickname} sent an image:", image])
            else:
                # If there's only text
                formatted_message = f"{nickname}: {message.content}"
                response = chat_session.send_message(formatted_message)
            break
        except exceptions.InternalServerError:
            # Re-raise the InternalServerError to be handled in the main.py
            raise
        except exceptions.ResourceExhausted:
            if attempt == max_retries - 1:
                return "I'm having trouble responding at the moment. Please try again later."
            await asyncio.sleep(2 ** attempt + random.random())

    return response.text

async def process_image(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            if resp.status == 200:
                image_data = await resp.read()
                image = Image.open(BytesIO(image_data))
                return image
    return None

def remove_message_from_history(guild_id, message_id, get_guild_history, save_guild_histories):
    history = get_guild_history(str(guild_id))
    history[:] = [item for item in history if item.get("message_id") != message_id]
    save_guild_histories()

def update_message_in_history(guild_id, message_id, new_content, get_guild_history, save_guild_histories):
    history = get_guild_history(str(guild_id))
    for item in history:
        if item.get("message_id") == message_id:
            if isinstance(item["content"], dict) and "parts" in item["content"]:
                item["content"]["parts"] = [new_content]
            else:
                item["content"] = {"role": "user", "parts": [new_content]}
            break
    save_guild_histories()

###### commands.py

import discord
from discord import app_commands
from discord.ui import Button, View
from config_commands import setup_config_commands, admin_only

def setup_commands(tree, get_guild_config, save_guild_configs, get_guild_history, save_guild_histories):
    # Set up configuration commands
    setup_config_commands(tree, get_guild_config, save_guild_configs, get_guild_history, save_guild_histories)

    @tree.command(name="clear_interactions", description="Clear all chat interactions from bot memory")
    @admin_only()
    async def clear_history(interaction: discord.Interaction):
        guild_id = str(interaction.guild_id)
        config = get_guild_config(guild_id)
        history = get_guild_history(guild_id)
        interaction_count = len(history)

        embed = discord.Embed(
            title="Clear Chat History",
            description=f"Are you sure you want to clear all {interaction_count} chat interactions from the bot's memory?\n\nThis action cannot be undone",
            color=discord.Color.red()
        )

        async def confirm_callback(interaction: discord.Interaction):
            guild_history = get_guild_history(guild_id)
            guild_history.clear()
            await save_guild_histories()
            await interaction.response.send_message("Chat history has been cleared.", ephemeral=True)

        async def cancel_callback(interaction: discord.Interaction):
            await interaction.response.send_message("Operation cancelled.", ephemeral=True)

        confirm_button = Button(label="Confirm", style=discord.ButtonStyle.danger)
        cancel_button = Button(label="Cancel", style=discord.ButtonStyle.secondary)

        confirm_button.callback = confirm_callback
        cancel_button.callback = cancel_callback

        view = View()
        view.add_item(confirm_button)
        view.add_item(cancel_button)

        await interaction.response.send_message(embed=embed, view=view, ephemeral=True)

    @tree.command(name="rp_mode", description="Enable or disable Role Play mode.")
    @admin_only()
    async def rp_mode(interaction: discord.Interaction, mode: bool):
        guild_id = str(interaction.guild_id)
        guild_config = get_guild_config(guild_id)
        guild_config["rp_mode_enabled"] = mode
        await save_guild_configs()
        status = "enabled" if mode else "disabled"
        await interaction.response.send_message(f"Role Play mode has been {status} for this server.", ephemeral=True)