import os import requests import base64 import re import json from datetime import datetime import discord from discord.ext import commands from discord.ui import Button, View from dotenv import load_dotenv # DISCORD SERVER + DM DIRECTUS BOT load_dotenv() # Configuration API_BASE_URL = os.getenv("API_BASE_URL") # Configure in .env BEARER_TOKEN = os.getenv("BEARER_TOKEN") # Configure in .env DISCORD_TOKEN = os.getenv("DISCORD_TOKEN") # Configure in .env ALLOWED_CHANNELS = [123456789] # REPLACE WITH YOUR CHANNEL IDS # UUID validation pattern UUID_PATTERN = re.compile(r'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$') def log_activity(message: str) -> None: timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") print(f"[{timestamp}] {message}") intents = discord.Intents.default() intents.message_content = True bot = commands.Bot(command_prefix='!', intents=intents) class SearchResultsView(View): def __init__(self, results): super().__init__(timeout=60) self.results = results self.add_buttons() def add_buttons(self): for idx, item in enumerate(self.results[:25], 1): self.add_item(ResultButton(item, idx)) class ResultButton(Button): def __init__(self, item, index): super().__init__( label=f"{index}", style=discord.ButtonStyle.primary, custom_id=f"result_{item['uuid']}" ) self.item = item async def callback(self, interaction: discord.Interaction): await self.show_addon_embed(interaction) async def show_addon_embed(self, interaction: discord.Interaction): data = self.item download_hashes = data.get('download_hash', '') decoded_urls = [] if download_hashes: for i, hash_part in enumerate(download_hashes.split(','), 1): try: decoded = base64.b64decode(hash_part.strip()).decode('utf-8') decoded_urls.append(f"{i}. {decoded}") except: decoded_urls.append(f"{i}. Invalid hash") embed = discord.Embed( title=data.get('name', 'N/A'), color=discord.Color.blue() ) embed.add_field(name="UUID", value=data.get('uuid', 'N/A'), inline=False) embed.add_field(name="Creator", value=data.get('creator', 'N/A'), inline=True) embed.add_field(name="Version", value=data.get('version', 'N/A'), inline=True) if decoded_urls: embed.add_field( name="Download URLs", value="\n".join(decoded_urls), inline=False ) await interaction.response.send_message(embed=embed, ephemeral=True) @bot.event async def on_ready(): log_activity(f'Logged in as {bot.user.name}') async def send_start(ctx): embed = discord.Embed( title="Addon Search Bot", description="I can help you find addons!\n\n" "🔍 **Search**: `!mpbot [search terms]`\n" "🔎 **UUID Lookup**: `!mpbot [uuid]`\n" "🆘 **Help**: `!mpbot help`\n" "❌ **Cancel**: `!mpbot cancel`", color=discord.Color.green() ) await ctx.send(embed=embed) async def send_cancel(ctx): embed = discord.Embed( description="⚠️ No active operation to cancel", color=discord.Color.orange() ) await ctx.send(embed=embed, delete_after=5) @bot.event async def on_message(message): if message.author == bot.user: return # Channel check if message.channel.id not in ALLOWED_CHANNELS: return # Command prefix check if not message.content.startswith('!mpbot'): return ctx = await bot.get_context(message) content = message.content[len('!mpbot'):].strip() if not content: await send_start(ctx) return if content.lower() == 'help': await send_start(ctx) elif content.lower() == 'cancel': await send_cancel(ctx) elif UUID_PATTERN.match(content): await process_uuid(ctx, content) else: await perform_search(ctx, content) async def perform_search(ctx, query): try: headers = {"Authorization": f"Bearer {BEARER_TOKEN}"} search_params = { "filter": json.dumps({ "_or": [ {"name": {"_icontains": query}}, {"creator": {"_icontains": query}} ] }), "fields": "uuid,name,creator,version,download_hash", "limit": "10" } response = requests.get(API_BASE_URL, headers=headers, params=search_params) response.raise_for_status() search_data = response.json().get('data', []) if not search_data: embed = discord.Embed( description="🔍 No results found for your query.", color=discord.Color.red() ) await ctx.send(embed=embed, ephemeral=True) return results_list = [ f"{idx}. **{item.get('name', 'N/A')}** by {item.get('creator', 'N/A')}" for idx, item in enumerate(search_data, 1) ] embed = discord.Embed( title=f"Search Results for '{query}'", description="\n".join(results_list) + "\n\nClick a button below to view details:", color=discord.Color.blue() ) view = SearchResultsView(search_data) await ctx.send(embed=embed, view=view) except requests.exceptions.HTTPError as e: embed = discord.Embed( title="API Error", description=f"Error code: {e.response.status_code}", color=discord.Color.red() ) await ctx.send(embed=embed, ephemeral=True) except Exception as e: log_activity(f"Search error: {str(e)}") embed = discord.Embed( title="Error", description="⚠️ Error processing search", color=discord.Color.red() ) await ctx.send(embed=embed, ephemeral=True) async def process_uuid(ctx, uuid): try: headers = {"Authorization": f"Bearer {BEARER_TOKEN}"} response = requests.get(f"{API_BASE_URL}{uuid}", headers=headers) response.raise_for_status() data = response.json().get('data', {}) if data: download_hashes = data.get('download_hash', '') decoded_urls = [] if download_hashes: for i, hash_part in enumerate(download_hashes.split(','), 1): try: decoded = base64.b64decode(hash_part.strip()).decode('utf-8') decoded_urls.append(f"{i}. {decoded}") except: decoded_urls.append(f"{i}. Invalid hash") embed = discord.Embed( title=data.get('name', 'N/A'), color=discord.Color.blue() ) embed.add_field(name="UUID", value=data.get('uuid', 'N/A'), inline=False) embed.add_field(name="Creator", value=data.get('creator', 'N/A'), inline=True) embed.add_field(name="Version", value=data.get('version', 'N/A'), inline=True) if decoded_urls: embed.add_field( name="Download URLs", value="\n".join(decoded_urls), inline=False ) await ctx.send(embed=embed, ephemeral=True) else: embed = discord.Embed( description="❌ No information found for this UUID.", color=discord.Color.red() ) await ctx.send(embed=embed, ephemeral=True) except requests.exceptions.HTTPError as e: if e.response.status_code == 403: embed = discord.Embed( description="⛔ Access denied for this UUID", color=discord.Color.red() ) else: embed = discord.Embed( description=f"🔧 API Error: {e.response.status_code}", color=discord.Color.red() ) await ctx.send(embed=embed, ephemeral=True) except Exception as e: log_activity(f"UUID processing error: {str(e)}") embed = discord.Embed( description="⚠️ Error fetching UUID details", color=discord.Color.red() ) await ctx.send(embed=embed, ephemeral=True) if __name__ == '__main__': bot.run(DISCORD_TOKEN)