143 lines
5.4 KiB
Python
143 lines
5.4 KiB
Python
import discord
|
|
import asyncio
|
|
import re
|
|
import time
|
|
from config import BLACKLISTED_USERS, BUCKET_REACT_USERS, AUTO_DELETE_USERS, SPECIAL_RESPONSES
|
|
from utils.time_parser import parse_time
|
|
|
|
class MessageHandler:
|
|
def __init__(self, bot):
|
|
self.bot = bot
|
|
|
|
# Regex for detecting "in X time" patterns
|
|
self.time_pattern = re.compile(
|
|
r'in\s+((?:\d+\s*(?:seconds?|minutes?|hours?|days?|s|m|h|d)\s*)+)',
|
|
re.IGNORECASE
|
|
)
|
|
|
|
def parse_relative_time(self, time_str):
|
|
"""
|
|
Parse relative time strings like "2 hours", "30 minutes"
|
|
|
|
Args:
|
|
time_str: String in format like "2 hours", "30 minutes"
|
|
|
|
Returns:
|
|
Unix timestamp (seconds since epoch) for the future time or None if invalid
|
|
"""
|
|
# Convert to format our parse_time can handle
|
|
time_part = time_str.replace('hours', 'h').replace('hour', 'h')
|
|
time_part = time_part.replace('minutes', 'm').replace('minute', 'm')
|
|
time_part = time_part.replace('seconds', 's').replace('second', 's')
|
|
time_part = time_part.replace('days', 'd').replace('day', 'd')
|
|
# Remove spaces to match expected format like "2h30m"
|
|
time_part = re.sub(r'\s+', '', time_part)
|
|
|
|
seconds = parse_time(time_part)
|
|
if seconds:
|
|
return int(time.time() + seconds)
|
|
return None
|
|
|
|
def create_discord_timestamp(self, unix_time, format_code='R'):
|
|
"""Create a Discord timestamp string"""
|
|
return f"<t:{unix_time}:{format_code}>"
|
|
|
|
def replace_time_patterns(self, content):
|
|
"""Replace "in X time" patterns with Discord timestamps"""
|
|
def replace_match(match):
|
|
time_str = match.group(1)
|
|
unix_time = self.parse_relative_time(time_str)
|
|
if unix_time:
|
|
return self.create_discord_timestamp(unix_time)
|
|
return match.group(0) # Return original text if parsing fails
|
|
|
|
return self.time_pattern.sub(replace_match, content)
|
|
|
|
async def handle_message(self, message):
|
|
# Skip bot messages
|
|
if message.author.bot:
|
|
return
|
|
|
|
# Skip command messages (they start with . usually)
|
|
if message.content.startswith('.'):
|
|
return
|
|
|
|
# Look for and replace time patterns
|
|
original_content = message.content
|
|
modified_content = self.replace_time_patterns(original_content)
|
|
|
|
# If the content was modified, edit the original message
|
|
if modified_content != original_content and message.author == self.bot.user:
|
|
try:
|
|
await message.edit(content=modified_content)
|
|
except Exception as e:
|
|
# If we don't have permission to edit, just ignore
|
|
pass
|
|
|
|
# Handle special responses
|
|
for user_id, data in SPECIAL_RESPONSES.items():
|
|
if message.author.id == user_id and data["trigger"] in message.content:
|
|
await message.reply(data["response"])
|
|
|
|
# Handle automatic reactions
|
|
if message.channel.id in self.bot.horsin:
|
|
await message.add_reaction("🐴")
|
|
|
|
if message.author.id in BUCKET_REACT_USERS:
|
|
await message.add_reaction("🪣")
|
|
|
|
# Handle auto-delete for specific users
|
|
if message.author.id in AUTO_DELETE_USERS:
|
|
try:
|
|
await message.delete()
|
|
except:
|
|
pass
|
|
|
|
# Handle DM if in AFK mode
|
|
if isinstance(message.channel, discord.DMChannel) and message.author != self.bot.user:
|
|
# Get the AFK cog if it's loaded
|
|
afk_cog = next((cog for cog_name, cog in self.bot.cog_manager.cogs.items()
|
|
if hasattr(cog, 'handle_afk_dm')), None)
|
|
if afk_cog and hasattr(afk_cog, 'handle_afk_dm'):
|
|
await afk_cog.handle_afk_dm(message)
|
|
|
|
# Don't process further if the message is not from the bot user
|
|
if message.author != self.bot.user:
|
|
return
|
|
|
|
# Handle commands
|
|
await self.handle_commands(message)
|
|
|
|
async def handle_blacklist(self, message):
|
|
"""Handle blacklisted users"""
|
|
if message.author.id in BLACKLISTED_USERS:
|
|
await message.reply("no")
|
|
return True
|
|
return False
|
|
|
|
async def handle_commands(self, message):
|
|
"""Handle commands issued by the bot user"""
|
|
content = message.content
|
|
|
|
# Skip if not a command
|
|
if not content.startswith('.'):
|
|
return
|
|
|
|
cmd_parts = content.split()
|
|
cmd_name = cmd_parts[0][1:] # Get command name without the '.'
|
|
|
|
# Check if this is a command in one of our loaded cogs
|
|
if hasattr(self.bot, 'loaded_commands') and cmd_name in self.bot.loaded_commands:
|
|
try:
|
|
await self.bot.loaded_commands[cmd_name](message)
|
|
return
|
|
except Exception as e:
|
|
print(f"Error executing command {cmd_name}: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
await message.edit(content=f"❌ Error executing command: {str(e)}")
|
|
return
|
|
|
|
# If we got here, command wasn't found
|
|
await message.edit(content=f"❌ Command not found: `{cmd_name}`")
|