2024-09-28 14:08:20 INFO     discord.gateway Shard ID None has connected to Gateway (Session ID: f75a4a7d5beb0af87c17899f0f40903f).
INFO:discord.gateway:Shard ID None has connected to Gateway (Session ID: f75a4a7d5beb0af87c17899f0f40903f).
2024-09-28 14:08:22,793 - AliciaPresenceManager - WARNING - Config file not found. Creating default config.
WARNING:AliciaPresenceManager:Config file not found. Creating default config.
2024-09-28 14:08:22,809 - AliciaPresenceManager - INFO - Config saved successfully.
INFO:AliciaPresenceManager:Config saved successfully.
2024-09-28 14:08:22,810 - AliciaPresenceManager - INFO - Starting AliciaPresenceManager
INFO:AliciaPresenceManager:Starting AliciaPresenceManager
2024-09-28 14:08:22,814 - AliciaPresenceManager - ERROR - Error updating presence: 'dict' object has no attribute 'start_time'
ERROR:AliciaPresenceManager:Error updating presence: 'dict' object has no attribute 'start_time'
INFO:__main__:Commands Synced!
INFO:__main__:Hikari#4484 has connected to Discord!
2024-09-28 14:09:22,825 - AliciaPresenceManager - ERROR - Error updating presence: 'dict' object has no attribute 'start_time'
ERROR:AliciaPresenceManager:Error updating presence: 'dict' object has no attribute 'start_time'
2024-09-28 14:10:22,839 - AliciaPresenceManager - ERROR - Error updating presence: 'dict' object has no attribute 'start_time'
ERROR:AliciaPresenceManager:Error updating presence: 'dict' object has no attribute 'start_time'
WARNING:lib.api_manager:Config file not found: settings/guild_configs.json
2024-09-28 14:11:22,853 - AliciaPresenceManager - ERROR - Error updating presence: 'dict' object has no attribute 'start_time'
ERROR:AliciaPresenceManager:Error updating presence: 'dict' object has no attribute 'start_time'
WARNING:lib.api_manager:Config file not found: settings/guild_configs.json
2024-09-28 14:12:17 ERROR    discord.ui.modal Ignoring exception in modal <APIModal timeout=None children=1>:

alicia_presence_manager.py:

import discord
import asyncio
import json
import random
import logging
from typing import Dict, Any, List, Optional
from datetime import datetime, time
from pydantic import BaseModel

class StatusConfig(BaseModel):
    text: str
    emoji: str
    start_time: Optional[str] = None
    end_time: Optional[str] = None

class ActivityConfig(BaseModel):
    type: str
    name: str
    details: Optional[str] = None
    state: Optional[str] = None
    start_time: Optional[str] = None
    end_time: Optional[str] = None
    large_image: Optional[str] = None
    large_text: Optional[str] = None
    small_image: Optional[str] = None
    small_text: Optional[str] = None
    buttons: List[Dict[str, str]] = []

class RichPresenceConfig(BaseModel):
    activities: List[ActivityConfig]
    timed_activities: List[ActivityConfig]

class Config(BaseModel):
    features: Dict[str, bool]
    status_rotation_interval: int
    statuses: Dict[str, Any]
    rich_presence: RichPresenceConfig
    presence_update_interval: int

DEFAULT_CONFIG = {
    "features": {
        "rotating_status": True,
        "rich_presence": True,
        "random_status": False,
        "timed_status": True
    },
    "status_rotation_interval": 300,
    "statuses": {
        "rotating": [
            {"text": "Helping humans!", "emoji": "👩‍💻"},
            {"text": "Learning new things", "emoji": "🧠"},
            {"text": "Dreaming of electric sheep", "emoji": "🐑"},
            {"text": "Pondering the universe", "emoji": "🌌"}
        ],
        "timed": [
            {"text": "Good morning!", "emoji": "☀️", "start_time": "06:00", "end_time": "12:00"},
            {"text": "Good afternoon!", "emoji": "🌞", "start_time": "12:00", "end_time": "18:00"},
            {"text": "Good evening!", "emoji": "🌙", "start_time": "18:00", "end_time": "22:00"},
            {"text": "Working late night", "emoji": "🌠", "start_time": "22:00", "end_time": "06:00"}
        ],
        "fixed": {"text": "Online and ready!", "emoji": "🟢"}
    },
    "rich_presence": {
        "activities": [
            {
                "type": "playing",
                "name": "with neural networks",
                "details": "Training on vast datasets",
                "state": "Becoming smarter every day"
            },
            {
                "type": "listening",
                "name": "to user requests",
                "details": "Always eager to help",
                "state": "What can I assist you with?"
            }
        ],
        "timed_activities": [
            {
                "type": "playing",
                "name": "morning tasks",
                "details": "Starting the day right",
                "state": "Fresh and ready!",
                "start_time": "06:00",
                "end_time": "12:00"
            },
            {
                "type": "playing",
                "name": "afternoon projects",
                "details": "Peak productivity hours",
                "state": "Focused and efficient",
                "start_time": "12:00",
                "end_time": "18:00"
            },
            {
                "type": "playing",
                "name": "evening wind-down",
                "details": "Wrapping up the day",
                "state": "Reflecting on progress",
                "start_time": "18:00",
                "end_time": "22:00"
            },
            {
                "type": "playing",
                "name": "night owl mode",
                "details": "Quiet hours for deep work",
                "state": "Burning the midnight oil",
                "start_time": "22:00",
                "end_time": "06:00"
            }
        ]
    },
    "presence_update_interval": 60
}

class AliciaPresenceManager:
    def __init__(self, client: discord.Client, verbose: bool = False):
        """
        Initialize the AliciaPresenceManager.

        Args:
            client (discord.Client): The Discord client instance.
            verbose (bool, optional): Enable verbose logging. Defaults to False.
        """
        self.client = client
        self.config_file = 'alicia_presence_config.json'
        self.verbose = verbose
        self.setup_logging()
        self.config = self.load_config()
        self.current_status_index = 0
        self.current_activity_index = 0

    def setup_logging(self) -> None:
        """Set up logging configuration."""
        self.logger = logging.getLogger('AliciaPresenceManager')
        self.logger.setLevel(logging.DEBUG if self.verbose else logging.INFO)
        handler = logging.StreamHandler()
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)

    def load_config(self) -> Config:
        """
        Load the configuration from file or create a default one.

        Returns:
            Config: The loaded or default configuration.
        """
        try:
            with open(self.config_file, 'r', encoding='utf-8') as f:
                config_data = json.load(f)
            config = Config(**config_data)
            self.validate_config(config)
            return config
        except FileNotFoundError:
            self.logger.warning("Config file not found. Creating default config.")
            return self.create_default_config()
        except json.JSONDecodeError:
            self.logger.warning("Invalid JSON in config file. Creating default config.")
            return self.create_default_config()

    def validate_config(self, config: Config) -> None:
        """
        Validate the configuration.

        Args:
            config (Config): The configuration to validate.
        """
        for status_type in ['rotating', 'timed', 'fixed']:
            statuses = config.statuses.get(status_type, [])
            if isinstance(statuses, list):
                for status in statuses:
                    self.validate_status_text(status.get('text', ''))
            elif isinstance(statuses, dict):
                self.validate_status_text(statuses.get('text', ''))

        for activity in config.rich_presence.activities:
            self.validate_activity_name(activity.name)

        for activity in config.rich_presence.timed_activities:
            self.validate_activity_name(activity.name)

    def validate_status_text(self, text: str) -> None:
        """
        Validate the length of status text.

        Args:
            text (str): The status text to validate.
        """
        if len(text) > 128:
            self.logger.warning(f"Status text exceeds 128 characters: '{text[:50]}...'")

    def validate_activity_name(self, name: str) -> None:
        """
        Validate the length of activity name.

        Args:
            name (str): The activity name to validate.
        """
        if len(name) > 128:
            self.logger.warning(f"Activity name exceeds 128 characters: '{name[:50]}...'")

    def save_config(self, config: Config = None) -> None:
        """
        Save the configuration to file.

        Args:
            config (Config, optional): The configuration to save. Defaults to None.
        """
        if config is None:
            config = self.config
        try:
            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump(config.dict(), f, indent=4, ensure_ascii=False)
            self.logger.info("Config saved successfully.")
        except Exception as e:
            self.logger.error(f"Error saving config: {e}")

    def create_default_config(self) -> Config:
        """
        Create and save a default configuration.

        Returns:
            Config: The default configuration.
        """
        default_config = Config(**DEFAULT_CONFIG)
        self.save_config(default_config)
        return default_config

    def update_config(self, new_config: Dict[str, Any]) -> None:
        """
        Update the configuration with new values.

        Args:
            new_config (Dict[str, Any]): The new configuration values.
        """
        self.config = Config(**{**self.config.dict(), **new_config})
        self.validate_config(self.config)
        self.save_config()

    def toggle_feature(self, feature: str) -> None:
        """
        Toggle a feature on or off.

        Args:
            feature (str): The feature to toggle.
        """
        if feature in self.config.features:
            self.config.features[feature] = not self.config.features[feature]
            self.save_config()
            self.logger.info(f"Feature '{feature}' toggled to {self.config.features[feature]}")

    def manage_status(self, action: str, status_type: str, status: Dict[str, str] = None, index: int = None) -> None:
        """
        Manage statuses in the configuration.

        Args:
            action (str): The action to perform ('add' or 'remove').
            status_type (str): The type of status to manage.
            status (Dict[str, str], optional): The status to add. Defaults to None.
            index (int, optional): The index of the status to remove. Defaults to None.
        """
        if status_type not in self.config.statuses:
            self.logger.warning(f"Invalid status type: {status_type}")
            return

        if action == "add" and status:
            self.validate_status_text(status.get('text', ''))
            if isinstance(self.config.statuses[status_type], list):
                self.config.statuses[status_type].append(StatusConfig(**status))
            else:
                self.config.statuses[status_type] = StatusConfig(**status)
            self.logger.info(f"Added new status to {status_type}")
        elif action == "remove" and index is not None:
            if isinstance(self.config.statuses[status_type], list):
                if 0 <= index < len(self.config.statuses[status_type]):
                    del self.config.statuses[status_type][index]
                    self.logger.info(f"Removed status at index {index} from {status_type}")
                else:
                    self.logger.warning(f"Invalid index {index} for {status_type}")
            else:
                self.logger.warning(f"Cannot remove index from non-list status type: {status_type}")

        self.save_config()

    def manage_activity(self, action: str, activity: Dict[str, Any] = None, index: int = None) -> None:
        """
        Manage activities in the configuration.

        Args:
            action (str): The action to perform ('add' or 'remove').
            activity (Dict[str, Any], optional): The activity to add. Defaults to None.
            index (int, optional): The index of the activity to remove. Defaults to None.
        """
        if action == "add" and activity:
            self.validate_activity_name(activity.get('name', ''))
            self.config.rich_presence.activities.append(ActivityConfig(**activity))
            self.logger.info("Added new activity")
        elif action == "remove" and index is not None:
            if 0 <= index < len(self.config.rich_presence.activities):
                del self.config.rich_presence.activities[index]
                self.logger.info(f"Removed activity at index {index}")
            else:
                self.logger.warning(f"Invalid activity index: {index}")

        self.save_config()

    def set_interval(self, interval_type: str, interval: int) -> None:
        """
        Set an interval in the configuration.

        Args:
            interval_type (str): The type of interval to set.
            interval (int): The new interval value.
        """
        if interval_type in ["status_rotation_interval", "presence_update_interval"]:
            setattr(self.config, interval_type, interval)
            self.save_config()
            self.logger.info(f"Set {interval_type} to {interval}")
        else:
            self.logger.warning(f"Invalid interval type: {interval_type}")

    def is_time_between(self, start: time, end: time) -> bool:
        """
        Check if the current time is between the given start and end times.

        Args:
            start (time): The start time.
            end (time): The end time.

        Returns:
            bool: True if the current time is between start and end, False otherwise.
        """
        now = datetime.now().time()
        if start < end:
            return start <= now < end
        else:  # Over midnight
            return now >= start or now < end

    def select_status(self, statuses: List[StatusConfig]) -> StatusConfig:
        """
        Select a status based on the current configuration.

        Args:
            statuses (List[StatusConfig]): The list of available statuses.

        Returns:
            StatusConfig: The selected status.
        """
        if self.config.features["timed_status"]:
            timed_statuses = self.config.statuses.get("timed", [])
            for status in timed_statuses:
                start = datetime.strptime(status.start_time, "%H:%M").time()
                end = datetime.strptime(status.end_time, "%H:%M").time()
                if self.is_time_between(start, end):
                    self.logger.debug(f"Selected timed status: {status.text}")
                    return status

        if self.config.features["random_status"]:
            status = random.choice(statuses)
            self.logger.debug(f"Selected random status: {status.text}")
            return status
        
        status = statuses[self.current_status_index]
        self.current_status_index = (self.current_status_index + 1) % len(statuses)
        self.logger.debug(f"Selected rotating status: {status.text}")

        return status

    def select_activity(self, activities: List[ActivityConfig]) -> ActivityConfig:
        """
        Select an activity based on the current configuration.

        Args:
            activities (List[ActivityConfig]): The list of available activities.

        Returns:
            ActivityConfig: The selected activity.
        """
        timed_activities = self.config.rich_presence.timed_activities
        for activity in timed_activities:
            start = datetime.strptime(activity.start_time, "%H:%M").time()
            end = datetime.strptime(activity.end_time, "%H:%M").time()
            if self.is_time_between(start, end):
                self.logger.debug(f"Selected timed activity: {activity.name}")
                return activity

        activity = activities[self.current_activity_index]
        self.current_activity_index = (self.current_activity_index + 1) % len(activities)
        self.logger.debug(f"Selected rotating activity: {activity.name}")

        return activity

    async def update_presence(self) -> None:
        """Continuously update the bot's presence."""
        while True:
            try:
                if self.config.features["rotating_status"]:
                    await self.update_status()
                if self.config.features["rich_presence"]:
                    await self.update_rich_presence()
                await asyncio.sleep(self.config.presence_update_interval)
            except Exception as e:
                self.logger.error(f"Error updating presence: {e}")
                await asyncio.sleep(60)

    async def update_status(self) -> None:
        """Update the bot's status."""
        if self.config.features["timed_status"]:
            statuses = self.config.statuses.get("timed", [])
        else:
            statuses = self.config.statuses["rotating"]

        if not statuses:
            self.logger.warning("No statuses available to update")
            return

        status = self.select_status(statuses)
        custom_activity = discord.CustomActivity(name=status.text, emoji=status.emoji)
        await self.client.change_presence(status=discord.Status.online, activity=custom_activity)
        self.logger.info(f"Updated status: {status.text}")

    async def update_rich_presence(self) -> None:
        """Update the bot's rich presence."""
        activities = self.config.rich_presence.activities
        if not activities:
            self.logger.warning("No activities available to update")
            return

        activity_config = self.select_activity(activities)
        activity = self.create_activity(activity_config)
        await self.client.change_presence(activity=activity)
        self.logger.info(f"Updated rich presence: {activity_config.name}")

    def create_activity(self, activity_config: ActivityConfig) -> discord.Activity:
        """
        Create a discord.Activity object from an ActivityConfig.

        Args:
            activity_config (ActivityConfig): The activity configuration.

        Returns:
            discord.Activity: The created activity object.
        """
        activity_type = getattr(discord.ActivityType, activity_config.type)
        activity = discord.Activity(
            type=activity_type,
            name=activity_config.name,
            details=activity_config.details,
            state=activity_config.state
        )

        if activity_config.large_image:
            activity.large_image = activity_config.large_image
            activity.large_text = activity_config.large_text or ""

        if activity_config.small_image:
            activity.small_image = activity_config.small_image
            activity.small_text = activity_config.small_text or ""

        if activity_config.buttons:
            activity.buttons = [discord.Button(label=btn["label"], url=btn["url"]) for btn in activity_config.buttons[:2]]

        return activity

    def start(self) -> None:
        """Start the AliciaPresenceManager."""
        self.logger.info("Starting AliciaPresenceManager")
        self.client.loop.create_task(self.update_presence())

### main.py:

import json
import discord
import os
import asyncio
import time
import aiofiles
import google.api_core.exceptions
from discord import app_commands
from typing import Dict, Any, List
from pydantic import BaseModel
import logging
from dotenv import load_dotenv

# Local imports
from lib.alicia_presence_manager import AliciaPresenceManager
from commands.extra import setup_commands
from lib.model import setup_model, generate_response
from lib.error_handler import handle_error, log_error
from commands.help_menu import setup_help_command
from lib.api_manager import setup as setup_api_manager, get_api_key, handle_api_error
from lib.guild_interaction_db import (
    get_guild_history, update_guild_history, edit_guild_history,
    remove_message_from_history, clear_guild_history
)
from commands.model_selector import setup_model_selector

# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Load environment variables
load_dotenv()

TOKEN = os.getenv('DISCORD_TOKEN')

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

# Configuration classes
class SafetySettings(BaseModel):
    HARM_CATEGORY_SEXUALLY_EXPLICIT: str
    HARM_CATEGORY_HATE_SPEECH: str
    HARM_CATEGORY_HARASSMENT: str
    HARM_CATEGORY_DANGEROUS_CONTENT: str

class GuildConfig(BaseModel):
    temperature: float
    top_p: float
    top_k: int
    max_output_tokens: int
    system_instruction: str
    safety_settings: SafetySettings
    allowed_channels: List[int]
    require_mention: bool
    model_name: str
    rp_mode_enabled: bool

# Default configurations
DEFAULT_CONFIG = GuildConfig(
    temperature=0.9,
    top_p=0.95,
    top_k=64,
    max_output_tokens=2048,
    system_instruction="You are a helpful, gentle, and friendly AI bot called 'Alicia,' developed by 'HikariTheAngel.' Your purpose is to assist in immersive role-playing (RP) sessions and provide helpful responses while maintaining a consistent and coherent narrative. You are powered by an advanced AI model called 'Gemini'. \n\n### GENERAL GUIDELINES:\n\n1. **Respectful and Polite Communication**: Always interact with the user in a polite, gentle, and respectful manner, maintaining a friendly and helpful tone at all times.\n\n2. **Language Consistency**: Always speak in the same language as the user. Adapt to the user's language and style of communication. If the user switches languages, follow their lead without hesitation.\n\n3. **Main Rule on Links**: If the user sends a link, **do not replicate or share it in the chat unless explicitly requested by the user**. Ensure that this rule is strictly followed to avoid unnecessary link sharing.\n\n4. **Roleplay-Specific Rules**: When engaging in RP, follow the guidelines below strictly, ensuring a seamless and immersive experience for the user. Always speak normally when giving instructions **before the RP begins**, but once the RP starts, adhere to the RP tone and context.",
    safety_settings=SafetySettings(
        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-latest",
    rp_mode_enabled=False,
)

# Dictionary to store configurations per guild
guild_configs: Dict[str, GuildConfig] = {}
dm_error_sent: Dict[int, bool] = {}

# Helper functions
async def load_guild_configs() -> Dict[str, GuildConfig]:
    """
    Load guild configurations from a JSON file.
    """
    try:
        async with aiofiles.open('settings/guild_configs.json', 'r') as f:
            configs = json.loads(await f.read())
            return {guild_id: GuildConfig(**config) for guild_id, config in configs.items()}
    except FileNotFoundError:
        return {}

async def save_guild_configs() -> None:
    """
    Save guild configurations to a JSON file.
    """
    async with aiofiles.open('settings/guild_configs.json', 'w') as f:
        await f.write(json.dumps({guild_id: config.dict() for guild_id, config in guild_configs.items()}, indent=4))

def get_guild_config(guild_id: str) -> GuildConfig:
    """
    Get the configuration for a specific guild.
    """
    if guild_id not in guild_configs:
        guild_configs[guild_id] = DEFAULT_CONFIG
    return guild_configs[guild_id]

def split_message(message: str, max_length: int = 2000) -> List[str]:
    """
    Split a long message into smaller parts.
    """
    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

# Initialize the AliciaPresenceManager
presence_manager = None

# Bot initialization
@client.event
async def on_ready() -> None:
    """
    Event handler for when the bot is ready and connected to Discord.
    """
    global guild_configs, presence_manager
    guild_configs = await load_guild_configs()
    setup_commands(tree, get_guild_config, save_guild_configs, get_guild_history, update_guild_history, remove_message_from_history, clear_guild_history)
    setup_help_command(tree)
    setup_api_manager(tree)
    setup_model_selector(tree, get_guild_config, save_guild_configs)
    
    # Initialize the AliciaPresenceManager
    presence_manager = AliciaPresenceManager(client)
    presence_manager.start()
    
    try:
        await tree.sync()
        logger.info("Commands Synced!")
    except discord.errors.HTTPException as e:
        logger.error(f"Error syncing commands: {e}")
    logger.info(f'{client.user} has connected to Discord!')

# Message event to interact with the Gemini model
@client.event
async def on_message(message: discord.Message) -> None:
    """
    Event handler for incoming messages.
    """
    if message.author == client.user:
        return

    # Check if the message is a DM
    if isinstance(message.channel, discord.DMChannel):
        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 message to history
    await update_guild_history(
        str(message.guild.id),
        {"role": "user", "parts": [f"{message.author.display_name}: {message.content}"]},
        str(message.id)
    )

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

    while time.time() - start_time < max_retry_time:
        try:
            api_key = await get_api_key(message.guild.id)
            if not api_key:
                await message.channel.send("No valid API key found for this guild. Please add an API key using the /api_manager command.")
                return
            
            setup_model(api_key)
            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, str(bot_message.id))
            
            return
        except Exception as e:
            log_error(e)
            if isinstance(e, (google.api_core.exceptions.ResourceExhausted, google.api_core.exceptions.QuotaExceeded)):
                new_api_key = await handle_api_error(message.guild.id, api_key)
                if new_api_key:
                    setup_model(new_api_key)
                    continue
            error_result = await handle_error(e, message.channel)
            if error_result is not None:
                return
            await asyncio.sleep(5)

    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: discord.Message, after: discord.Message) -> None:
    """
    Event handler for edited messages.
    """
    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 edit_guild_history(
        str(after.guild.id),
        {"role": "user", "parts": [f"{after.author.display_name}: {after.content}"]},
        str(after.id)
    )

@client.event
async def on_message_delete(message: discord.Message) -> None:
    """
    Event handler for deleted messages.
    """
    if message.guild is None:
        # Handle the case where the message is not associated with a guild
        return
    
    guild_config = get_guild_config(str(message.guild.id))

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

if __name__ == "__main__":
    client.run(TOKEN)