Files
2025-09-16 15:00:16 +02:00

431 lines
15 KiB
Python
Executable File

import discord
import datetime
import random
from discord.ext import commands
from utils.sql_commands import DatabaseManager
import asyncio
def random_gradient():
"""
Generate a random color in hexadecimal format.
Returns:
int: A random color in hexadecimal format (e.g., 0xFFFFFF).
"""
return random.randint(0, 0xFFFFFF)
def ordinal(number):
"""
Convert a number to its ordinal representation (e.g., 1st, 2nd, 3rd).
Args:
number (int): The number to convert to an ordinal.
Returns:
str: The ordinal representation of the given number.
"""
if 10 <= number % 100 <= 20:
suffix = "th"
else:
suffix = {1: "st", 2: "nd", 3: "rd"}.get(number % 10, "th")
return f"{number}{suffix}"
async def get_channel_id(bot, ctx, allow_blank=False):
"""
Prompt the user to input a channel ID, allowing for options to skip or remove.
Args:
bot (discord.Client): The bot instance.
ctx (commands.Context): The context in which the command was invoked.
allow_blank (bool, optional): If True, allows 'skip' or 'remove' as options.
Returns:
int or str: The ID of the mentioned channel, "pass" if skipped, or None if removed.
"""
try:
msg = await bot.wait_for(
"message", check=lambda message: message.author == ctx.author, timeout=60
)
# Handle blank input options
if allow_blank:
if msg.content.lower() == "skip":
return "pass"
if msg.content.lower() == "remove":
return None
# Check if a channel was mentioned
if msg.channel_mentions:
return msg.channel_mentions[0].id
await ctx.send("Please mention a valid channel.")
return await get_channel_id(bot, ctx, allow_blank)
except asyncio.TimeoutError:
await ctx.send("You took too long to respond. Please run the command again.")
return None
async def server_join_embed(db: DatabaseManager, member: discord.Member):
"""
Create a dynamic welcome embed for a new server member.
Args:
db (DatabaseManager): The database manager instance.
member (discord.Member): The new member joining the server.
Returns:
discord.Embed: A dynamically generated welcome embed for the new member.
"""
guild_channels = db.fetch_one(
"SELECT * FROM guilds WHERE GUILD = %s", (member.guild.id,)
)
# Prepare welcome messages
welcome_messages = [
f"🌟 Welcome to {member.guild.name}, {member.name}! 🌟",
f"🎉 Greetings, {member.name}! Welcome to {member.guild.name}!",
f"🚀 Ahoy, {member.name}! Set sail for adventure in {member.guild.name}!",
]
selected_message = random.choice(welcome_messages)
# Create embed
embed = discord.Embed(
title=selected_message,
description="We're delighted to have you join our community. Enjoy your stay!",
color=random_gradient(),
)
embed.set_thumbnail(url=member.display_avatar)
# Dynamic fields based on available channels
fields = [
(
"➡️ Get Started",
f"Check out the {get_channel_mention(guild_channels, 'RULES', member)} channel for server rules.",
),
(
"📚 Server Guide",
f"Explore the server guide in {get_channel_mention(guild_channels, 'GUIDE', member)}.",
),
(
"👋 Introduce Yourself",
f"Head over to {get_channel_mention(guild_channels, 'INTRODUCTIONS', member)} and let us know a bit about yourself.",
),
(
"💼 Join our Events",
f"Participate in {get_channel_mention(guild_channels, 'EVENTS', member)} for exciting events!",
),
]
for name, value in fields:
if value:
embed.add_field(name=name, value=value, inline=False)
# Member count update
if guild_channels.get("MEMBERCOUNT"):
member_count_channel = member.guild.get_channel(
int(guild_channels["MEMBERCOUNT"])
)
if member_count_channel:
await member_count_channel.edit(
name=f"Members: {len(member.guild.members)}"
)
# Joining position
join_position = sorted(member.guild.members, key=lambda m: m.joined_at).index(member) + 1 # type: ignore
embed.add_field(
name="🏆 Joining Position",
value=f"You are the {ordinal(join_position)} member to join!",
inline=False,
)
# Greeting based on time of day
current_time = datetime.datetime.now().time()
greeting = (
"Good morning!"
if 6 <= current_time.hour < 12
else "Good afternoon!" if 12 <= current_time.hour < 18 else "Good evening!"
)
embed.insert_field_at(
0, name="🌞 Time of Day Greeting", value=greeting, inline=False
)
return embed
def get_channel_mention(guild_channels, key, member):
"""
Retrieve a mention for a channel based on a key in the guild channels data.
Args:
guild_channels (dict): The dictionary containing channel IDs.
key (str): The key for the channel to mention.
member (discord.Member): The member for whom the mention is being created.
Returns:
str or None: The mention for the channel if found, otherwise None.
"""
channel_id = guild_channels.get(key)
if channel_id:
channel = member.guild.get_channel(int(channel_id))
return channel.mention if channel else None
return None
class Channels(commands.Cog):
"""Cog that handles channel-related commands and events."""
def __init__(self, client):
self.client = client
self.db = DatabaseManager()
@commands.Cog.listener()
async def on_member_join(self, member: discord.Member):
"""
Event listener for new member joins. Sends a welcome embed if a welcome channel is set.
Args:
member (discord.Member): The new member joining the server.
"""
embed = await server_join_embed(self.db, member)
# Fetch the welcome channel ID for the given guild
result = self.db.fetch_one(
"SELECT WELCOME FROM guilds WHERE GUILD = %s", (member.guild.id,)
)
# Safely get the WELCOME value, defaulting to None if not found
channel_id = result.get("WELCOME") if result else None
if channel_id is None:
return # No welcome channel set
welcome_channel = self.client.get_channel(int(channel_id))
if welcome_channel:
await welcome_channel.send(embed=embed)
@commands.command(
name="setchannels",
brief="Setup channel IDs in the database",
description="Adds or removes channel IDs to/from the database for use by the bot.",
)
@commands.has_permissions(manage_channels=True)
async def _channel_setup(self, ctx, specific_channel: str | None = None):
"""
Command to set up channel IDs in the database by prompting the user.
Args:
ctx (commands.Context): The context in which the command was invoked.
specific_channel (str, optional): The specific channel to set up, or None to set up all channels.
"""
channels = {}
channel_types = [
"Welcome",
"Rules",
"Guide",
"Introductions",
"Events",
"MemberCount",
"Logging",
"Ticketing",
]
if specific_channel and specific_channel.capitalize() in channel_types:
channel_types = [specific_channel.capitalize()]
await ctx.reply(
"Let's set up the channels.\nPlease provide the channel ID,\nskip to keep it unchanged or\nremove to unset the channel:\n\n"
)
# Collect channel IDs
for channel_type in channel_types:
await ctx.send(f"{len(channels)}. {channel_type} Channel ID:")
channels[channel_type] = await get_channel_id(
self.client, ctx, allow_blank=True
)
# Check if existing entries need updating
overwrite = (self.db.fetch_one("SELECT GUILD FROM guilds WHERE GUILD = %s", (ctx.guild.id,))is not None)
# Prepare data for the insert or update operation
data = tuple(
{
"GUILD": ctx.guild.id,
"WELCOME": channels.get("Welcome"),
"RULES": channels.get("Rules"),
"GUIDE": channels.get("Guide"),
"INTRODUCTIONS": channels.get("Introductions"),
"EVENTS": channels.get("Events"),
"MEMBERCOUNT": channels.get("MemberCount"),
"LOGGING": channels.get("Logging"),
"TICKETING": channels.get("Ticketing"),
}.values()
)
# Insert or update the channels in the database
self.db.insert(
"INSERT INTO guilds (GUILD, WELCOME, RULES, GUIDE, INTRODUCTIONS, EVENTS, MEMBERCOUNT, LOGGING, TICKETING) "
"VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s) ",
data,
overwrite=overwrite,
)
# Create a response message for the user
response = "\n".join(
[
f"{channel_type} Channel ID: {get_channel_mention(channels, channel_type, ctx.author)}"
for channel_type in channels
if channel_type != "pass"
]
)
await ctx.send(f"Channels set!\n{response}")
@commands.command(
name="welcomeTest",
brief="Test welcome message",
description="Sends a welcome message to the welcome channel.",
)
@commands.has_permissions(manage_channels=True)
async def _welcome_test(self, ctx):
"""
Command to test the welcome message in the designated welcome channel.
Args:
ctx (commands.Context): The context in which the command was invoked.
"""
member = ctx.author
embed = await server_join_embed(self.db, member)
channel_id = self.db.fetch_one(
"SELECT WELCOME FROM guilds WHERE GUILD=%s", (ctx.guild.id,)
).get("WELCOME")
if channel_id is None:
await ctx.reply("No Welcome Channel Set")
return
welcome_channel = self.client.get_channel(int(channel_id))
if welcome_channel:
await welcome_channel.send(embed=embed)
else:
await ctx.reply(
"The Welcome Channel is invalid. Please set a valid channel."
)
@commands.command(
name="autocreatechannels",
brief="Auto-create all recommended channels and categories if missing.",
description="Creates all recommended channels and categories if they do not exist.",
)
@commands.has_permissions(manage_channels=True)
async def autocreatechannels(self, ctx):
"""
Command to auto-create all recommended channels and categories if missing.
"""
# Recommended categories and their channels
categories_and_channels = {
"Welcome & Info": [
"welcome",
"rules",
"server-guide",
"introductions",
"announcements",
],
"Community": [
"general",
"off-topic",
"media",
"bot-commands",
"suggestions",
"polls",
],
"Support": ["help", "faq"],
"Events": ["events", "event-signup", "giveaways"],
"Moderation": ["mod-log", "reports"],
"Voice": ["General Voice", "Music", "AFK"],
"Management": ["member-count", "ticketing"],
"Staff": ["staff", "staff-announcements", "staff-logs"],
}
created = []
for category_name, channel_names in categories_and_channels.items():
category = await self.ensure_category(ctx.guild, category_name)
for ch_name in channel_names:
# For voice channels
if (
"voice" in ch_name.lower()
or ch_name.lower() == "music"
or ch_name.lower() == "afk"
):
ch_type = discord.ChannelType.voice
else:
ch_type = discord.ChannelType.text
channel = discord.utils.get(ctx.guild.channels, name=ch_name)
if not channel:
channel = await self.ensure_channel(
ctx.guild, ch_name, type=ch_type, category=category
)
created.append(channel.mention)
# Set permissions template for some channels
if ch_name.lower() == "rules" and isinstance(channel, discord.TextChannel):
await self.set_channel_permissions(channel, "rules")
if created:
await ctx.send(f"Created channels: {', '.join(created)}")
else:
await ctx.send("All recommended channels already exist.")
async def ensure_channel(
self, guild: discord.Guild, name, type=discord.ChannelType.text, category=None
):
existing = discord.utils.get(guild.channels, name=name)
if existing:
return existing
if type == discord.ChannelType.voice:
return await guild.create_voice_channel(name, category=category)
else:
return await guild.create_text_channel(name, category=category)
async def ensure_category(self, guild: discord.Guild, name):
existing = discord.utils.get(guild.categories, name=name)
if existing:
return existing
return await guild.create_category(name)
async def set_channel_permissions(self, channel: discord.TextChannel, template):
if template == "rules":
await channel.set_permissions(
channel.guild.default_role, send_messages=False
)
# Add more permission logic as needed
@commands.command()
@commands.has_permissions(manage_channels=True)
async def cleanup_channels(self, ctx):
unused = [ch for ch in ctx.guild.text_channels if ch.last_message_id is None]
for ch in unused:
await ch.delete(reason="Channel cleanup")
await ctx.send(f"Deleted {len(unused)} unused channels.")
@commands.command()
async def channelinfo(self, ctx, channel: discord.TextChannel | None = None):
channel = channel or ctx.channel
embed = discord.Embed(title=f"Info for #{channel.name}")
embed.add_field(name="ID", value=channel.id)
embed.add_field(name="Created", value=channel.created_at)
embed.add_field(name="Topic", value=channel.topic or "None")
embed.add_field(name="Slowmode", value=channel.slowmode_delay)
await ctx.send(embed=embed)
async def setup(client):
"""
Setup function to load the Channels cog.
Args:
client (commands.Bot): The bot instance.
"""
await client.add_cog(Channels(client))