First Commit
This commit is contained in:
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Executable
+224
@@ -0,0 +1,224 @@
|
||||
import datetime
|
||||
import time
|
||||
import os
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
|
||||
async def reload_all(client, ctx):
|
||||
"""
|
||||
Reloads all cogs (modules) in the 'cogs' folder and syncs the command tree.
|
||||
|
||||
Args:
|
||||
client (discord.Client): The bot's main client object.
|
||||
ctx (commands.Context): The context in which the command was invoked.
|
||||
"""
|
||||
cogs_folder = os.path.abspath(os.path.dirname(__file__)) # Get the cogs folder path
|
||||
for filename in os.listdir(cogs_folder):
|
||||
if filename.endswith(".py"):
|
||||
try:
|
||||
await client.reload_extension(f"implementing.{filename[:-3]}")
|
||||
except Exception as e:
|
||||
print(f"Error reloading {filename}: {e}")
|
||||
await client.load_extension(f"implementing.{filename[:-3]}")
|
||||
await client.tree.sync() # Synchronize command tree
|
||||
await ctx.reply("Reloaded all cogs.")
|
||||
|
||||
|
||||
class Admin(commands.Cog):
|
||||
"""Cog that provides administrative commands for bot control and information."""
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
@commands.command(
|
||||
name="ping",
|
||||
brief="Bot Latency",
|
||||
description="Displays the bot's latency in milliseconds.",
|
||||
)
|
||||
async def _ping(self, ctx):
|
||||
"""
|
||||
Responds with the bot's latency (ping time).
|
||||
|
||||
Args:
|
||||
ctx (commands.Context): The context in which the command was invoked.
|
||||
"""
|
||||
latency = round(self.client.latency * 1000, 2) # Convert to ms
|
||||
await ctx.reply(f"Pong! 🏓\nLatency: {latency}ms")
|
||||
|
||||
@commands.command(
|
||||
name="info",
|
||||
brief="Bot Info",
|
||||
description="Provides information about the bot's owner and team (if applicable).",
|
||||
)
|
||||
async def _info(self, ctx):
|
||||
"""
|
||||
Displays information about the bot's owner or team members.
|
||||
|
||||
Args:
|
||||
ctx (commands.Context): The context in which the command was invoked.
|
||||
"""
|
||||
app_info = await self.client.application_info()
|
||||
if app_info.team:
|
||||
team_members = "\n".join(
|
||||
f"{member.name}#{member.discriminator} ({'Owner' if member.id == app_info.team.owner_id else 'Member'})"
|
||||
for member in app_info.team.members
|
||||
)
|
||||
await ctx.reply(
|
||||
f"Bot is part of the team: {app_info.team.name}\nMembers:\n{team_members}"
|
||||
)
|
||||
else:
|
||||
await ctx.reply(
|
||||
f"Bot is owned by {app_info.owner} and is not part of a team."
|
||||
)
|
||||
|
||||
@commands.command(
|
||||
name="uptime",
|
||||
brief="Bot Runtime",
|
||||
description="Displays how long the bot has been running since its last restart.",
|
||||
)
|
||||
async def _uptime(self, ctx):
|
||||
"""
|
||||
Calculates and sends the bot's uptime based on a start timestamp.
|
||||
|
||||
Args:
|
||||
ctx (commands.Context): The context in which the command was invoked.
|
||||
"""
|
||||
with open("time.txt", "r") as file:
|
||||
start_time = float(file.read())
|
||||
uptime = str(
|
||||
datetime.timedelta(seconds=int(time.time() - start_time))
|
||||
) # Calculate uptime
|
||||
await ctx.send(f"Uptime: {uptime}")
|
||||
|
||||
@commands.hybrid_command(
|
||||
name="reload",
|
||||
brief="Reload Cog",
|
||||
description="Reloads a specific cog or all cogs.",
|
||||
aliases=["rl"],
|
||||
)
|
||||
@commands.is_owner()
|
||||
async def _reload(self, ctx, extension: str):
|
||||
"""
|
||||
Reloads a specified cog/module or all modules if 'all' is passed.
|
||||
|
||||
Args:
|
||||
ctx (commands.Context): The context in which the command was invoked.
|
||||
extension (str): Name of the cog to reload, or 'all' to reload all cogs.
|
||||
"""
|
||||
if extension.lower() == "all":
|
||||
await reload_all(self.client, ctx)
|
||||
return
|
||||
try:
|
||||
await self.client.unload_extension(f"implementing.{extension}")
|
||||
await self.client.load_extension(f"implementing.{extension}")
|
||||
await ctx.send(f"Successfully reloaded {extension}.")
|
||||
except commands.ExtensionNotLoaded:
|
||||
await ctx.send(f"Extension {extension} was not loaded, loading it now.")
|
||||
try:
|
||||
await self.client.load_extension(f"implementing.{extension}")
|
||||
await ctx.send(f"Successfully loaded {extension}.")
|
||||
except Exception as e:
|
||||
await ctx.send(f"Failed to load {extension}: {e}")
|
||||
except commands.ExtensionNotFound:
|
||||
await ctx.send(f"Extension {extension} was not found.")
|
||||
except Exception as e:
|
||||
await ctx.send(f"Failed to reload {extension}: {e}")
|
||||
|
||||
@commands.hybrid_command(
|
||||
name="purge",
|
||||
brief="Delete messages",
|
||||
description="Deletes a specified number of messages from the current channel (default: 100).",
|
||||
)
|
||||
@commands.has_permissions(manage_channels=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
async def _purge(self, ctx, n: int = 100):
|
||||
"""
|
||||
Deletes a specified number of messages from the current channel.
|
||||
|
||||
Args:
|
||||
ctx (commands.Context): The context in which the command was invoked.
|
||||
n (int, optional): The number of messages to delete. Defaults to 100.
|
||||
"""
|
||||
n += 1
|
||||
if n <= 0:
|
||||
return
|
||||
if n < 100:
|
||||
await ctx.channel.purge(limit=n)
|
||||
await ctx.channel.send(
|
||||
f"Successfully purged last {n-1} messages", delete_after=2
|
||||
)
|
||||
elif n == 100:
|
||||
await ctx.channel.purge()
|
||||
await ctx.channel.send(
|
||||
"Successfully purged last 100 messages", delete_after=2
|
||||
)
|
||||
else: # n > 100
|
||||
for i in range(n // 100):
|
||||
await ctx.channel.purge()
|
||||
await ctx.channel.purge(limit=n % 100)
|
||||
await ctx.channel.send(
|
||||
f"Successfully purged last {n-1} messages", delete_after=2
|
||||
)
|
||||
|
||||
@commands.hybrid_command(
|
||||
name="nuke",
|
||||
brief="Clear channel",
|
||||
description="Deletes all messages by duplicating and deleting the channel.",
|
||||
)
|
||||
@commands.has_permissions(manage_channels=True)
|
||||
@commands.cooldown(1, 30, commands.BucketType.user)
|
||||
async def _nuke(self, ctx, channel_name: str = "Current"):
|
||||
"""
|
||||
Duplicates and deletes a specified channel (or the current channel) to clear messages.
|
||||
|
||||
Args:
|
||||
ctx (commands.Context): The context in which the command was invoked.
|
||||
channel_name (str, optional): Name of the channel to nuke. Defaults to 'Current' (the invoking channel).
|
||||
"""
|
||||
# Determine the channel ID based on the channel_name parameter
|
||||
if channel_name.lower() == "current":
|
||||
channel_id = ctx.channel.id
|
||||
else:
|
||||
channel_id = discord.utils.get(ctx.guild.channels, name=channel_name)
|
||||
if channel_id is None:
|
||||
return await ctx.send(f"Channel **{channel_name}** was not found.")
|
||||
|
||||
existing_channel = self.client.get_channel(channel_id)
|
||||
if existing_channel:
|
||||
await existing_channel.clone(reason="Channel has been nuked")
|
||||
await existing_channel.delete()
|
||||
await self._send_embed(
|
||||
ctx,
|
||||
f"Channel **{existing_channel.name}** has been nuked.",
|
||||
discord.Color.red(),
|
||||
)
|
||||
else:
|
||||
await ctx.send(f"No channel with ID {channel_id} was found.")
|
||||
|
||||
@commands.command(
|
||||
name="guildids",
|
||||
brief="List all guild IDs",
|
||||
description="Lists all guild IDs the bot is currently in. Owner only.",
|
||||
)
|
||||
@commands.is_owner()
|
||||
async def _guildids(self, ctx):
|
||||
"""
|
||||
Sends a list of all guild IDs the bot is in to the owner.
|
||||
|
||||
Args:
|
||||
ctx (commands.Context): The context in which the command was invoked.
|
||||
"""
|
||||
guild_ids = [str(guild.id) for guild in self.client.guilds]
|
||||
guilds_str = "\n".join(guild_ids)
|
||||
await ctx.author.send(f"Guild IDs:\n{guilds_str}")
|
||||
await ctx.reply("Sent you a DM with all guild IDs.")
|
||||
|
||||
async def _send_embed(self, ctx, description, color=discord.Color.blurple()):
|
||||
embed = discord.Embed(description=description, color=color)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
|
||||
async def setup(client):
|
||||
"""Sets up the Admin cog for the bot."""
|
||||
await client.add_cog(Admin(client))
|
||||
Executable
+430
@@ -0,0 +1,430 @@
|
||||
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))
|
||||
Executable
+330
@@ -0,0 +1,330 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import random
|
||||
import asyncio
|
||||
from collections import Counter
|
||||
|
||||
ROLES = ["Werewolf", "Seer", "Doctor", "Villager"]
|
||||
|
||||
ROLE_DESCRIPTIONS = {
|
||||
"Werewolf": "You are a **Werewolf**! Work with your fellow werewolves at night to eliminate villagers. Your goal is to outnumber the villagers.",
|
||||
"Seer": "You are the **Seer**! Each night, you may select a player to learn their true role.",
|
||||
"Doctor": "You are the **Doctor**! Each night, you may choose a player to protect from elimination.",
|
||||
"Villager": "You are a **Villager**! Try to find and vote out the werewolves during the day.",
|
||||
}
|
||||
|
||||
|
||||
class WerewolvesGame:
|
||||
def __init__(self, channel):
|
||||
self.channel = channel
|
||||
self.players = []
|
||||
self.started = False
|
||||
self.roles = {} # user_id: role
|
||||
self.phase = "lobby" # or "night", "day"
|
||||
self.role_threads = {} # role: thread
|
||||
self.votes = {}
|
||||
self.vote_targets = []
|
||||
self.alive = set()
|
||||
|
||||
def add_player(self, user):
|
||||
if user not in self.players:
|
||||
self.players.append(user)
|
||||
|
||||
def assign_roles(self):
|
||||
# For 6+ players: 2 werewolves, 1 seer, 1 doctor, rest villagers
|
||||
n = len(self.players)
|
||||
roles_list = ["Werewolf"] * 2 + ["Seer", "Doctor"]
|
||||
roles_list += ["Villager"] * (n - len(roles_list))
|
||||
random.shuffle(roles_list)
|
||||
self.roles = {player.id: role for player, role in zip(self.players, roles_list)}
|
||||
|
||||
def get_players_by_role(self, role):
|
||||
return [p for p in self.players if self.roles.get(p.id) == role]
|
||||
|
||||
def reset_votes(self):
|
||||
self.votes = {}
|
||||
self.vote_targets = []
|
||||
|
||||
def vote(self, voter, target):
|
||||
self.votes[voter.id] = target.id
|
||||
|
||||
def tally_votes(self):
|
||||
if not self.votes:
|
||||
return None
|
||||
count = Counter(self.votes.values())
|
||||
most_common = count.most_common(1)
|
||||
if most_common:
|
||||
return most_common[0][0] # user_id of voted out
|
||||
return None
|
||||
|
||||
|
||||
class Games(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.active_games = {} # channel_id: WerewolvesGame
|
||||
|
||||
@commands.command(name="joinwerewolves", hidden=True)
|
||||
async def join_werewolves(self, ctx):
|
||||
game = self.active_games.get(ctx.channel.id)
|
||||
if not game:
|
||||
game = WerewolvesGame(ctx.channel)
|
||||
self.active_games[ctx.channel.id] = game
|
||||
if game.started:
|
||||
await ctx.reply("Game already started!")
|
||||
return
|
||||
game.add_player(ctx.author)
|
||||
await ctx.reply(f"{ctx.author.display_name} joined the game!")
|
||||
|
||||
@commands.command(name="startwerewolves", hidden=True)
|
||||
async def start_werewolves(self, ctx):
|
||||
game = self.active_games.get(ctx.channel.id)
|
||||
if not game or len(game.players) < 2:
|
||||
await ctx.reply("Need at least 6 players to start!")
|
||||
return
|
||||
if game.started:
|
||||
await ctx.reply("Game already started!")
|
||||
return
|
||||
game.started = True
|
||||
game.assign_roles()
|
||||
|
||||
# Create threads for roles
|
||||
await self.create_role_threads(game)
|
||||
|
||||
# Announce roles in threads (description only once per thread)
|
||||
# Werewolf thread: mention all werewolves, send description once
|
||||
werewolves = game.get_players_by_role("Werewolf")
|
||||
if werewolves:
|
||||
thread = game.role_threads["Werewolf"]
|
||||
mentions = " ".join([p.mention for p in werewolves])
|
||||
desc = ROLE_DESCRIPTIONS["Werewolf"]
|
||||
await thread.send(f"{mentions}\nYou are **Werewolves**!\n\n{desc}")
|
||||
|
||||
# Seer thread
|
||||
seer = game.get_players_by_role("Seer")
|
||||
if seer:
|
||||
thread = game.role_threads["Seer"]
|
||||
desc = ROLE_DESCRIPTIONS["Seer"]
|
||||
await thread.send(f"{seer[0].mention}\nYou are the **Seer**!\n\n{desc}")
|
||||
|
||||
# Doctor thread
|
||||
doctor = game.get_players_by_role("Doctor")
|
||||
if doctor:
|
||||
thread = game.role_threads["Doctor"]
|
||||
desc = ROLE_DESCRIPTIONS["Doctor"]
|
||||
await thread.send(f"{doctor[0].mention}\nYou are the **Doctor**!\n\n{desc}")
|
||||
|
||||
await ctx.send(
|
||||
"Game started! Roles have been assigned in private threads.\nThe game cycle will begin in 30 seconds..."
|
||||
)
|
||||
|
||||
# Start the game cycle after 30 seconds
|
||||
await asyncio.sleep(30)
|
||||
await self.start_cycle(ctx)
|
||||
|
||||
async def create_role_threads(self, game: WerewolvesGame):
|
||||
# Werewolves share a thread, others get solo threads
|
||||
channel = game.channel
|
||||
werewolves = game.get_players_by_role("Werewolf")
|
||||
seer = game.get_players_by_role("Seer")
|
||||
doctor = game.get_players_by_role("Doctor")
|
||||
|
||||
# Create werewolf group thread
|
||||
if werewolves:
|
||||
thread = await channel.create_thread(
|
||||
name="Werewolves Actions",
|
||||
type=discord.ChannelType.private_thread,
|
||||
auto_archive_duration=60,
|
||||
reason="Werewolves game: Werewolves thread",
|
||||
)
|
||||
for member in werewolves:
|
||||
await thread.add_user(member)
|
||||
game.role_threads["Werewolf"] = thread
|
||||
|
||||
# Create seer thread
|
||||
if seer:
|
||||
thread = await channel.create_thread(
|
||||
name="Seer Actions",
|
||||
type=discord.ChannelType.private_thread,
|
||||
auto_archive_duration=60,
|
||||
reason="Werewolves game: Seer thread",
|
||||
)
|
||||
await thread.add_user(seer[0])
|
||||
game.role_threads["Seer"] = thread
|
||||
|
||||
# Create doctor thread
|
||||
if doctor:
|
||||
thread = await channel.create_thread(
|
||||
name="Doctor Actions",
|
||||
type=discord.ChannelType.private_thread,
|
||||
auto_archive_duration=60,
|
||||
reason="Werewolves game: Doctor thread",
|
||||
)
|
||||
await thread.add_user(doctor[0])
|
||||
game.role_threads["Doctor"] = thread
|
||||
|
||||
@commands.command(name="playwerewolves")
|
||||
@commands.guild_only()
|
||||
async def play_werewolves(self, ctx):
|
||||
"""Create a private game room for Werewolves and start the game setup."""
|
||||
guild = ctx.guild
|
||||
overwrites = {
|
||||
guild.default_role: discord.PermissionOverwrite(read_messages=False),
|
||||
ctx.author: discord.PermissionOverwrite(
|
||||
read_messages=True, send_messages=True
|
||||
),
|
||||
}
|
||||
category = ctx.channel.category
|
||||
channel_name = f"werewolves-room-{ctx.author.display_name}".replace(
|
||||
" ", "-"
|
||||
).lower()
|
||||
channel = await guild.create_text_channel(
|
||||
name=channel_name,
|
||||
overwrites=overwrites,
|
||||
category=category,
|
||||
reason="Private Werewolves game room",
|
||||
)
|
||||
await channel.send(
|
||||
f"{ctx.author.mention} Your private **Werewolves** game room has been created!\n"
|
||||
f"Use `py addwerewolves <username>` in this channel to add others.\n"
|
||||
f"Use `py joinwerewolves` in this channel to join the game.\n"
|
||||
f"Once enough players have joined, use `py startwerewolves` to start the game."
|
||||
)
|
||||
# Optionally, auto-join the creator to the game
|
||||
game = WerewolvesGame(channel)
|
||||
game.add_player(ctx.author)
|
||||
self.active_games[channel.id] = game
|
||||
|
||||
@commands.command(name="addwerewolves", hidden=True)
|
||||
@commands.guild_only()
|
||||
async def add_werewolves(
|
||||
self,
|
||||
ctx,
|
||||
member: discord.Member | None = None,
|
||||
*,
|
||||
identifier: str | None = None,
|
||||
):
|
||||
"""Add a user to the current Werewolves game room by username (case-insensitive, not mention)."""
|
||||
game = self.active_games.get(ctx.channel.id)
|
||||
if not game:
|
||||
await ctx.reply("This is not a Werewolves game room.")
|
||||
return
|
||||
|
||||
# Only allow the creator (first player) to add others
|
||||
if ctx.author != game.players[0]:
|
||||
await ctx.reply("Only the game creator can add players to the room.")
|
||||
return
|
||||
|
||||
# Find member by username (case-insensitive)
|
||||
if member is None and identifier:
|
||||
try:
|
||||
member = ctx.guild.get_member(int(identifier))
|
||||
except ValueError:
|
||||
member = discord.utils.find(
|
||||
lambda m: m.name.lower() == identifier.lower()
|
||||
or m.display_name.lower() == identifier.lower(),
|
||||
ctx.guild.members,
|
||||
)
|
||||
if not member:
|
||||
await ctx.reply(f"User '{member}' not found in this server.")
|
||||
return
|
||||
|
||||
await ctx.channel.set_permissions(
|
||||
member,
|
||||
read_messages=True,
|
||||
send_messages=True,
|
||||
)
|
||||
await ctx.send(
|
||||
f"{member.mention} has been added to the game room! They can now join the game with `py joinwerewolves`."
|
||||
)
|
||||
|
||||
@commands.command(name="startcycle", hidden=True)
|
||||
async def start_cycle(self, ctx):
|
||||
"""Start the Werewolves game cycle (night/day loop)."""
|
||||
game = self.active_games.get(ctx.channel.id)
|
||||
if not game or not game.started:
|
||||
await ctx.send("No active game to start the cycle.")
|
||||
return
|
||||
|
||||
await ctx.send("The game cycle is starting!")
|
||||
game.phase = "night"
|
||||
game.alive = set(game.players)
|
||||
game.reset_votes()
|
||||
|
||||
while True:
|
||||
# NIGHT PHASE
|
||||
await ctx.send(
|
||||
"🌙 **Night falls!** Werewolves, Seer, and Doctor, check your threads."
|
||||
)
|
||||
await asyncio.sleep(10) # Replace with your night action logic and timing
|
||||
|
||||
# DAY PHASE
|
||||
await ctx.send(
|
||||
"☀️ **Day breaks!** Discuss and vote to eliminate a player. Use `py vote <username>`."
|
||||
)
|
||||
game.reset_votes()
|
||||
await self.day_voting(ctx, game)
|
||||
voted_out_id = game.tally_votes()
|
||||
if voted_out_id:
|
||||
voted_out = discord.utils.get(ctx.guild.members, id=voted_out_id)
|
||||
if voted_out in game.alive:
|
||||
game.alive.remove(voted_out)
|
||||
await ctx.send(f"{voted_out.mention} has been eliminated!")
|
||||
else:
|
||||
await ctx.send("No one was eliminated today.")
|
||||
|
||||
# Check win condition (example: only werewolves or villagers left)
|
||||
werewolves = [p for p in game.alive if game.roles.get(p.id) == "Werewolf"]
|
||||
villagers = [p for p in game.alive if game.roles.get(p.id) != "Werewolf"]
|
||||
if not werewolves:
|
||||
await ctx.send("Villagers win! 🎉")
|
||||
break
|
||||
if len(werewolves) >= len(villagers):
|
||||
await ctx.send("Werewolves win! 🐺")
|
||||
break
|
||||
|
||||
await asyncio.sleep(5) # Short pause before next night
|
||||
|
||||
@commands.command(name="vote", hidden=True)
|
||||
async def vote(
|
||||
self,
|
||||
ctx,
|
||||
member: discord.Member | None = None,
|
||||
*,
|
||||
identifier: str | None = None,
|
||||
):
|
||||
"""Vote to eliminate a player during the day phase."""
|
||||
game = self.active_games.get(ctx.channel.id)
|
||||
if not game or ctx.author not in game.alive:
|
||||
await ctx.reply("You are not in the game or not alive.")
|
||||
return
|
||||
|
||||
# Find member by username (case-insensitive)
|
||||
if member is None and identifier:
|
||||
try:
|
||||
member = ctx.guild.get_member(int(identifier))
|
||||
except ValueError:
|
||||
member = discord.utils.find(
|
||||
lambda m: m.name.lower() == identifier.lower()
|
||||
or m.display_name.lower() == identifier.lower(),
|
||||
ctx.guild.members,
|
||||
)
|
||||
if not member:
|
||||
await ctx.reply(f"User '{member}' not found in this server.")
|
||||
return
|
||||
|
||||
if not member or member not in game.alive:
|
||||
await ctx.reply("That player is not alive or not found.")
|
||||
return
|
||||
game.vote(ctx.author, member)
|
||||
await ctx.send(
|
||||
f"{ctx.author.display_name} voted to eliminate {member.display_name}."
|
||||
)
|
||||
|
||||
async def day_voting(self, ctx, game, timeout=30):
|
||||
"""Wait for votes during the day phase."""
|
||||
await ctx.send(f"You have {timeout} seconds to vote.")
|
||||
await asyncio.sleep(timeout)
|
||||
|
||||
|
||||
async def setup(client):
|
||||
await client.add_cog(Games(client))
|
||||
Executable
Executable
+129
@@ -0,0 +1,129 @@
|
||||
import discord
|
||||
import datetime
|
||||
|
||||
from discord.ext import commands, tasks
|
||||
from utils.sql_commands import DatabaseManager
|
||||
from dotenv import dotenv_values
|
||||
|
||||
|
||||
|
||||
|
||||
def check(author):
|
||||
def inner_check(message):
|
||||
return message.author == author
|
||||
|
||||
return inner_check
|
||||
|
||||
|
||||
class Birthday(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.db = DatabaseManager("")
|
||||
self.birthday_checks = {}
|
||||
|
||||
async def cog_load(self):
|
||||
self.check_birthdays.start()
|
||||
|
||||
async def cog_unload(self):
|
||||
self.check_birthdays.cancel()
|
||||
|
||||
@tasks.loop(seconds=60*60*24) # daily
|
||||
async def check_birthdays(self):
|
||||
today = datetime.date.today()
|
||||
for guild in self.client.guilds:
|
||||
guild_id = guild.id
|
||||
for member in guild.members:
|
||||
user_id = member.id
|
||||
user_data = self.db.fetch_one("SELECT * FROM users WHERE ID = %s", (user_id,))
|
||||
if user_data is None or user_data.get("birthday") is None:
|
||||
continue
|
||||
|
||||
birthday = datetime.datetime.strptime(user_data.get("birthday"), "%Y-%m-%d").date()
|
||||
if birthday.month == today.month and birthday.day == today.day:
|
||||
# send birthday message
|
||||
try:
|
||||
channel = guild.text_channels[0]
|
||||
await channel.send(f"Happy birthday, {member.mention}! 🎉")
|
||||
except discord.Forbidden:
|
||||
continue
|
||||
except discord.HTTPException:
|
||||
continue
|
||||
# give birthday reward
|
||||
reward = self.db.fetch_one("SELECT * FROM rewards WHERE type = 'birthday'")
|
||||
if reward is not None:
|
||||
try:
|
||||
await self.client.get_cog("Economy").add_points(user_id, guild_id, reward.get("amount"), "birthday reward")
|
||||
except Exception as e:
|
||||
print(f"Error giving birthday reward: {e}")
|
||||
# give birthday star role
|
||||
role = discord.utils.get(guild.roles, name="Birthday Star")
|
||||
if role is not None:
|
||||
try:
|
||||
await member.add_roles(role)
|
||||
except discord.Forbidden:
|
||||
continue
|
||||
except discord.HTTPException:
|
||||
continue
|
||||
# log birthday reward
|
||||
try:
|
||||
self.db.execute_query("INSERT INTO logs (guild_id, user_id, type, message) VALUES (%s, %s, 'birthday_reward', 'claimed birthday reward')", (guild_id, user_id))
|
||||
except Exception as e:
|
||||
print(f"Error logging birthday reward: {e}")
|
||||
|
||||
@check_birthdays.before_loop
|
||||
async def before_check_birthdays(self):
|
||||
await self.client.wait_until_ready()
|
||||
|
||||
@commands.command(name="setbirthday", aliases=["sb"])
|
||||
async def setbirthday(self, ctx, month: int, day: int):
|
||||
if month < 1 or month > 12 or day < 1 or day > 31:
|
||||
return await ctx.send("Invalid date")
|
||||
|
||||
user_id = ctx.author.id
|
||||
user_data = self.db.fetch_one("SELECT * FROM users WHERE ID = %s", (user_id,))
|
||||
|
||||
if user_data is not None and user_data.get("birthday") is not None:
|
||||
return await ctx.send("You've already set your birthday")
|
||||
|
||||
self.db.execute_query("UPDATE users SET birthday = %s WHERE ID = %s", (f"{datetime.date.today().year}-{month}-{day}", user_id))
|
||||
await ctx.send("Your birthday has been set")
|
||||
|
||||
@commands.command(name="claimbirthday", aliases=["cb"])
|
||||
async def claimbirthday(self, ctx):
|
||||
user_id = ctx.author.id
|
||||
user_data = self.db.fetch_one("SELECT * FROM users WHERE ID = %s", (user_id,))
|
||||
|
||||
if user_data is None or user_data.get("birthday") is None:
|
||||
return await ctx.send("You haven't set your birthday")
|
||||
|
||||
today = datetime.date.today()
|
||||
birthday = datetime.datetime.strptime(user_data.get("birthday"), "%Y-%m-%d").date()
|
||||
if birthday.month != today.month or birthday.day != today.day:
|
||||
return await ctx.send("It's not your birthday")
|
||||
|
||||
reward = self.db.fetch_one("SELECT * FROM rewards WHERE type = 'birthday'")
|
||||
if reward is None:
|
||||
return await ctx.send("No birthday reward set")
|
||||
|
||||
await self.client.get_cog("Economy").add_points(user_id, ctx.guild.id, reward.get("amount"), "birthday reward")
|
||||
await ctx.send("You've claimed your birthday reward")
|
||||
|
||||
@commands.command(name="listbirthdays", aliases=["lb"])
|
||||
async def listbirthdays(self, ctx):
|
||||
today = datetime.date.today()
|
||||
birthdays = []
|
||||
for guild in self.client.guilds:
|
||||
for member in guild.members:
|
||||
user_id = member.id
|
||||
user_data = self.db.fetch_one("SELECT * FROM users WHERE ID = %s", (user_id,))
|
||||
if user_data is None or user_data.get("birthday") is None:
|
||||
continue
|
||||
|
||||
birthday = datetime.datetime.strptime(user_data.get("birthday"), "%Y-%m-%d").date()
|
||||
if birthday.month == today.month and birthday.day == today.day:
|
||||
birthdays.append(member.mention)
|
||||
|
||||
if not birthdays:
|
||||
await ctx.send("No birthdays today")
|
||||
else:
|
||||
await ctx.send(f"Happy birthday to {', '.join(birthdays)}! 🎉")
|
||||
Executable
+174
@@ -0,0 +1,174 @@
|
||||
# blush cry dance lewd pout shrug sleepy smile smug thumbsup wag thinking triggered teehee deredere thonking scoff happy thumbs grin
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils import sql_commands as mydb
|
||||
from random import choice
|
||||
|
||||
|
||||
class Emotes(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.emotes = {
|
||||
"blush": ["https://media.giphy.com/media/3o6Zt6ML6BklcajjsA/giphy.gif"],
|
||||
"cry": ["https://media.giphy.com/media/ROF8OQvDmxytW/giphy.gif"],
|
||||
"dance": ["https://media.giphy.com/media/l0MYt5jPR6QX5pnqM/giphy.gif"],
|
||||
# ...add more emotes...
|
||||
}
|
||||
self.emote_packs = {
|
||||
"happy": ["smile", "grin", "thumbsup"],
|
||||
"sleepy": ["sleepy", "yawn"],
|
||||
# ...add more packs...
|
||||
}
|
||||
self.cooldowns = {} # {user_id: {emote: timestamp}}
|
||||
self.emote_usage = {} # {user_id: {emote: count}}
|
||||
self.unlocked_emotes = {} # {user_id: set(emote_names)}
|
||||
self.pending_emotes = [] # [(user_id, emote_name, url)]
|
||||
|
||||
# --- Emote Command for Each Emote ---
|
||||
@commands.command(name="emote")
|
||||
async def emote(self, ctx, emote_name: str, member: discord.Member = None):
|
||||
"""Send an emote GIF, optionally mentioning a user."""
|
||||
emote = emote_name.lower()
|
||||
# Economy/level check
|
||||
if emote not in self.emotes and emote not in self.get_custom_emotes():
|
||||
await ctx.send("Unknown emote.")
|
||||
return
|
||||
if not self.has_unlocked(ctx.author.id, emote):
|
||||
await ctx.send("You haven't unlocked this emote yet!")
|
||||
return
|
||||
|
||||
# Cooldown check (10s per emote per user)
|
||||
import time
|
||||
|
||||
now = time.time()
|
||||
user_cooldowns = self.cooldowns.setdefault(ctx.author.id, {})
|
||||
if emote in user_cooldowns and now - user_cooldowns[emote] < 10:
|
||||
await ctx.send("You're using that emote too quickly!")
|
||||
return
|
||||
user_cooldowns[emote] = now
|
||||
|
||||
# Usage tracking
|
||||
user_usage = self.emote_usage.setdefault(ctx.author.id, {})
|
||||
user_usage[emote] = user_usage.get(emote, 0) + 1
|
||||
|
||||
url = choice(self.emotes.get(emote, self.get_custom_emotes().get(emote, [""])))
|
||||
mention = member.mention if member else ""
|
||||
await ctx.send(
|
||||
f"{ctx.author.mention} {mention}", embed=discord.Embed().set_image(url=url)
|
||||
)
|
||||
|
||||
# --- Individual Emote Commands (e.g., !blush, !cry, !dance) ---
|
||||
# Dynamically add a command for each emote
|
||||
def __init_subclass__(cls):
|
||||
def make_emote_cmd(emote):
|
||||
async def _cmd(self, ctx, member: discord.Member = None):
|
||||
await self.emote(ctx, emote, member)
|
||||
|
||||
return _cmd
|
||||
|
||||
for emote in ["blush", "cry", "dance"]: # Add all your emote names here
|
||||
setattr(cls, emote, commands.command(name=emote)(make_emote_cmd(emote)))
|
||||
|
||||
# --- Emote Combos ---
|
||||
@commands.command(name="emotecombo")
|
||||
async def emote_combo(self, ctx, *emotes):
|
||||
"""Send multiple emotes in one message."""
|
||||
urls = []
|
||||
for emote in emotes:
|
||||
if self.has_unlocked(ctx.author.id, emote) and (
|
||||
emote in self.emotes or emote in self.get_custom_emotes()
|
||||
):
|
||||
urls.append(
|
||||
choice(
|
||||
self.emotes.get(
|
||||
emote, self.get_custom_emotes().get(emote, [""])
|
||||
)
|
||||
)
|
||||
)
|
||||
if not urls:
|
||||
await ctx.send("No valid emotes found.")
|
||||
return
|
||||
for url in urls:
|
||||
embed = discord.Embed().set_image(url=url)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
# --- Emote Packs ---
|
||||
@commands.command(name="emotepack")
|
||||
async def emote_pack(self, ctx, pack_name: str):
|
||||
"""Send all emotes from a pack."""
|
||||
pack = self.emote_packs.get(pack_name.lower())
|
||||
if not pack:
|
||||
await ctx.send("Unknown emote pack.")
|
||||
return
|
||||
for emote in pack:
|
||||
if self.has_unlocked(ctx.author.id, emote) and (
|
||||
emote in self.emotes or emote in self.get_custom_emotes()
|
||||
):
|
||||
url = choice(
|
||||
self.emotes.get(emote, self.get_custom_emotes().get(emote, [""]))
|
||||
)
|
||||
await ctx.send(embed=discord.Embed().set_image(url=url))
|
||||
|
||||
# --- Emote Leaderboard ---
|
||||
@commands.command(name="emoteleaderboard")
|
||||
async def emote_leaderboard(self, ctx):
|
||||
"""Show top emote users."""
|
||||
leaderboard = sorted(
|
||||
((uid, sum(uses.values())) for uid, uses in self.emote_usage.items()),
|
||||
key=lambda x: x[1],
|
||||
reverse=True,
|
||||
)[:10]
|
||||
desc = ""
|
||||
for i, (uid, count) in enumerate(leaderboard, 1):
|
||||
user = self.client.get_user(uid)
|
||||
desc += f"{i}. {user.mention if user else uid}: {count} emotes\n"
|
||||
await ctx.send(
|
||||
embed=discord.Embed(
|
||||
title="Emote Leaderboard", description=desc or "No data."
|
||||
)
|
||||
)
|
||||
|
||||
# --- Emote Reactions ---
|
||||
@commands.command(name="react")
|
||||
async def react(self, ctx, emote_name: str, message_id: int):
|
||||
"""React to a message with an emote (as emoji if available)."""
|
||||
emote = emote_name.lower()
|
||||
try:
|
||||
msg = await ctx.channel.fetch_message(message_id)
|
||||
# If you have custom emoji, use them; else, fallback to unicode or skip
|
||||
await msg.add_reaction("😄") # Replace with actual emoji logic
|
||||
await ctx.send("Reacted!")
|
||||
except Exception:
|
||||
await ctx.send("Could not react to that message.")
|
||||
|
||||
# --- Custom Emotes Submission ---
|
||||
@commands.command(name="submit_emote")
|
||||
async def submit_emote(self, ctx, emote_name: str, url: str):
|
||||
"""Submit a custom emote for approval."""
|
||||
self.pending_emotes.append((ctx.author.id, emote_name, url))
|
||||
await ctx.send(
|
||||
f"Emote `{emote_name}` submitted for approval! (Admins: review pending list)"
|
||||
)
|
||||
|
||||
def get_custom_emotes(self):
|
||||
# In production, load from DB or approved list
|
||||
return {}
|
||||
|
||||
# --- Unlock Emotes with Level/Economy ---
|
||||
@commands.command(name="unlock_emote")
|
||||
async def unlock_emote(self, ctx, emote_name: str):
|
||||
"""Unlock an emote using currency or level."""
|
||||
# Example: check user balance/level, deduct cost, unlock emote
|
||||
# Replace with your actual economy/level logic
|
||||
user_id = ctx.author.id
|
||||
unlocked = self.unlocked_emotes.setdefault(user_id, set())
|
||||
unlocked.add(emote_name)
|
||||
await ctx.send(f"Emote `{emote_name}` unlocked!")
|
||||
|
||||
def has_unlocked(self, user_id, emote_name):
|
||||
# Always unlocked for now, add logic for locked emotes
|
||||
return True
|
||||
|
||||
|
||||
async def setup(client):
|
||||
await client.add_cog(Emotes(client))
|
||||
Executable
+281
@@ -0,0 +1,281 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from random import choice
|
||||
import requests
|
||||
from dotenv import dotenv_values
|
||||
|
||||
# Initialize responses for eightball command
|
||||
eightball_responses = [
|
||||
"Yes, definitely.",
|
||||
"No, not at all.",
|
||||
"Ask again later.",
|
||||
"Outlook not so good.",
|
||||
"Most likely.",
|
||||
"Don't count on it.",
|
||||
"Signs point to yes.",
|
||||
"Very doubtful.",
|
||||
"Without a doubt.",
|
||||
"My sources say no.",
|
||||
"Outlook is good.",
|
||||
"It is certain.",
|
||||
"Cannot predict now.",
|
||||
"Better not tell you now.",
|
||||
"Yes, in due time.",
|
||||
"No, never.",
|
||||
"Concentrate and ask again.",
|
||||
"Very likely.",
|
||||
"My reply is no.",
|
||||
"Outlook not good.",
|
||||
"Yes, for sure.",
|
||||
"Don't hold your breath.",
|
||||
"As I see it, yes.",
|
||||
"It is decidedly so.",
|
||||
"My sources say yes.",
|
||||
"Definitely not.",
|
||||
"Yes, definitely.",
|
||||
"Very unlikely.",
|
||||
"Ask again another time.",
|
||||
"Cannot foretell now.",
|
||||
"The outlook is bleak.",
|
||||
"Not in a million years.",
|
||||
"It is certain.",
|
||||
"Outlook not good.",
|
||||
"Signs point to yes.",
|
||||
"Without a doubt.",
|
||||
"My sources say no.",
|
||||
"Yes, in due time.",
|
||||
"Definitely not.",
|
||||
"Ask again later.",
|
||||
"Better not tell you now.",
|
||||
"Very likely.",
|
||||
"Yes, for sure.",
|
||||
"No, not at all.",
|
||||
"Don't count on it.",
|
||||
"As I see it, yes.",
|
||||
"My reply is no.",
|
||||
"Concentrate and ask again.",
|
||||
"Outlook is good.",
|
||||
"It is decidedly so.",
|
||||
"My sources say yes.",
|
||||
"Very unlikely.",
|
||||
"Cannot predict now.",
|
||||
"Yes, definitely.",
|
||||
"Not in a million years.",
|
||||
"Ask again another time.",
|
||||
"Without a doubt.",
|
||||
"Signs point to yes.",
|
||||
"The outlook is bleak.",
|
||||
"Definitely not.",
|
||||
"Outlook not good.",
|
||||
"Yes, for sure.",
|
||||
"Better not tell you now.",
|
||||
"My sources say no.",
|
||||
"It is certain.",
|
||||
"Very likely.",
|
||||
"Don't count on it.",
|
||||
"Concentrate and ask again.",
|
||||
"No, not at all.",
|
||||
"Ask again later.",
|
||||
"Yes, definitely.",
|
||||
"Without a doubt.",
|
||||
"Outlook is good.",
|
||||
"My sources say yes.",
|
||||
"Not in a million years.",
|
||||
"Very unlikely.",
|
||||
"Definitely not.",
|
||||
"Better not tell you now.",
|
||||
"As I see it, yes.",
|
||||
"The outlook is bleak.",
|
||||
"Concentrate and ask again.",
|
||||
"Yes, for sure.",
|
||||
"Signs point to yes.",
|
||||
"My reply is no.",
|
||||
"Cannot predict now.",
|
||||
"Ask again another time.",
|
||||
"No, not at all.",
|
||||
"Don't count on it.",
|
||||
"Outlook not so good.",
|
||||
"It is certain.",
|
||||
"Without a doubt.",
|
||||
"Very likely.",
|
||||
"Yes, definitely.",
|
||||
"My sources say no.",
|
||||
"Better not tell you now.",
|
||||
"Definitely not.",
|
||||
"Ask again later.",
|
||||
"Concentrate and ask again.",
|
||||
"The outlook is bleak.",
|
||||
"Not in a million years.",
|
||||
"Yes, for sure.",
|
||||
"As I see it, yes.",
|
||||
"Don't count on it.",
|
||||
"Very unlikely.",
|
||||
"My reply is no.",
|
||||
"Ask again another time.",
|
||||
"Signs point to yes.",
|
||||
"Outlook is good.",
|
||||
"Without a doubt.",
|
||||
"It is certain.",
|
||||
"Yes, definitely.",
|
||||
"Definitely not.",
|
||||
"Better not tell you now.",
|
||||
"Concentrate and ask again.",
|
||||
"No, not at all.",
|
||||
"My sources say yes.",
|
||||
"The outlook is bleak.",
|
||||
"Very likely.",
|
||||
"Ask again later.",
|
||||
"Don't count on it.",
|
||||
"As I see it, yes.",
|
||||
"Yes, for sure.",
|
||||
"Not in a million years.",
|
||||
"Cannot predict now.",
|
||||
"Outlook not good.",
|
||||
"Without a doubt.",
|
||||
"My reply is no.",
|
||||
"Yes, definitely.",
|
||||
"Better not tell you now.",
|
||||
"Concentrate and ask again.",
|
||||
"Definitely not.",
|
||||
"Ask again another time.",
|
||||
"Signs point to yes.",
|
||||
"It is certain.",
|
||||
"Very unlikely.",
|
||||
"Outlook is good.",
|
||||
"No, not at all.",
|
||||
"The outlook is bleak.",
|
||||
"As I see it, yes.",
|
||||
"My sources say no.",
|
||||
"Without a doubt.",
|
||||
"Better not tell you now.",
|
||||
"Concentrate and ask again.",
|
||||
"Yes, for sure.",
|
||||
"Not in a million years.",
|
||||
"Ask again later.",
|
||||
"Definitely not.",
|
||||
"My reply is no.",
|
||||
"Outlook not so good.",
|
||||
"Very likely.",
|
||||
"It is certain.",
|
||||
"Signs point to yes.",
|
||||
"Don't count on it.",
|
||||
"Yes, definitely.",
|
||||
"Better not tell you now.",
|
||||
"Concentrate and ask again.",
|
||||
"Without a doubt.",
|
||||
"Ask again another time.",
|
||||
"Definitely not.",
|
||||
"Outlook is good.",
|
||||
"No, not at all.",
|
||||
"The outlook is bleak.",
|
||||
"As I see it, yes.",
|
||||
"Very unlikely.",
|
||||
"My sources say yes.",
|
||||
"Yes, for sure.",
|
||||
"Cannot predict now.",
|
||||
"Better not tell you now.",
|
||||
"Concentrate and ask again.",
|
||||
"It is certain.",
|
||||
"Ask again later.",
|
||||
"Signs point to yes.",
|
||||
"Outlook not good.",
|
||||
"Without a doubt.",
|
||||
"Definitely not.",
|
||||
"Don't count on it.",
|
||||
"My reply is no.",
|
||||
"The outlook is bleak.",
|
||||
"Very likely.",
|
||||
"Yes, for sure.",
|
||||
"As I see it, yes.",
|
||||
"No, not at all.",
|
||||
"Concentrate and ask again.",
|
||||
"Better not tell you now.",
|
||||
"Ask again another time.",
|
||||
"My sources say yes.",
|
||||
"It is certain.",
|
||||
"Outlook is good.",
|
||||
"Yes, definitely.",
|
||||
"Without a doubt.",
|
||||
"Definitely not.",
|
||||
"Very unlikely.",
|
||||
"The outlook is bleak.",
|
||||
"Concentrate and ask again.",
|
||||
"Signs point to yes.",
|
||||
"No, not at all.",
|
||||
"Ask again later.",
|
||||
"Better not tell you now.",
|
||||
"As I see it, yes.",
|
||||
"My reply is no.",
|
||||
"Yes, for sure.",
|
||||
]
|
||||
|
||||
|
||||
class Fun(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
# Load environment variables for API keys
|
||||
config = dotenv_values(".env")
|
||||
self.tenor_api_key = config.get("TENORAPI")
|
||||
|
||||
@commands.command(
|
||||
name="eightball",
|
||||
aliases=["eb", "8ball", "8b"],
|
||||
brief="Ask a question and I shall answer.",
|
||||
description="I will predict the answer to any and all of your questions.",
|
||||
)
|
||||
async def eight_ball(self, ctx, *, question: str):
|
||||
"""Respond with a random answer to a yes/no question."""
|
||||
if question:
|
||||
response = choice(eightball_responses)
|
||||
await ctx.send(f"Answer: {response}")
|
||||
else:
|
||||
await ctx.send("Please ask a question for the 8ball to answer.")
|
||||
|
||||
@commands.command()
|
||||
async def randomgif(self, ctx, *query):
|
||||
"""Fetch and display a random GIF based on a search query."""
|
||||
if not self.tenor_api_key:
|
||||
await ctx.send("Tenor API key is not configured.")
|
||||
return
|
||||
|
||||
search_query = "%20".join(query) if query else "funny"
|
||||
api_url = f"https://tenor.googleapis.com/v2/search?q={search_query}&key={self.tenor_api_key}&limit=10"
|
||||
|
||||
try:
|
||||
response = requests.get(api_url)
|
||||
response.raise_for_status()
|
||||
gif_data = response.json()
|
||||
|
||||
if gif_data.get("results"):
|
||||
gif_url = choice(gif_data["results"])["media_formats"]["gif"]["url"]
|
||||
embed = discord.Embed(title="Here's a random GIF!")
|
||||
embed.set_image(url=gif_url)
|
||||
await ctx.send(embed=embed)
|
||||
else:
|
||||
await ctx.send("No GIFs found for your query.")
|
||||
|
||||
except requests.RequestException as error:
|
||||
await ctx.send(f"Error fetching Tenor GIF: {error}")
|
||||
|
||||
@commands.command()
|
||||
async def meme(self, ctx):
|
||||
"""Fetch and display a random meme."""
|
||||
api_url = "https://meme-api.com/memes/random"
|
||||
|
||||
try:
|
||||
response = requests.get(api_url)
|
||||
response.raise_for_status()
|
||||
meme_data = response.json()
|
||||
|
||||
meme_url = meme_data.get("url")
|
||||
if meme_url:
|
||||
await ctx.send(meme_url)
|
||||
else:
|
||||
await ctx.send("Couldn't fetch a meme at the moment.")
|
||||
|
||||
except requests.RequestException:
|
||||
await ctx.send("Error fetching meme.")
|
||||
|
||||
|
||||
async def setup(client):
|
||||
await client.add_cog(Fun(client))
|
||||
Executable
+120
@@ -0,0 +1,120 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils import sql_commands as mydb
|
||||
from random import choice
|
||||
|
||||
slap = ["https://media1.tenor.com/m/tfu-SnLkaP4AAAAC/alarm-clock.gif", "https://media1.tenor.com/m/yCC2nXLRPBgAAAAC/utku-tokat.gif", "https://media.tenor.com/fZQYHVUDfckAAAAi/slap.gif", "https://media.tenor.com/TVPYqh_E1JYAAAAi/peach-goma-peach-and-goma.gif"]
|
||||
sad = ["https://media1.tenor.com/m/a-ooSHLa2lsAAAAC/im-sad.gif","https://media1.tenor.com/m/M_V1fbsCEbAAAAAd/sad-om-nom.gif", "https://media1.tenor.com/m/bwtBRKlfEzgAAAAC/sad-crying.gif", "https://media1.tenor.com/m/5t-iIxnzE8MAAAAC/sad-bear-cry.gif", "https://media1.tenor.com/m/CKez5CfynccAAAAd/looking-out-the-window-om-nom.gif"]
|
||||
hug = ["https://media1.tenor.com/m/2qjVr7KUQKgAAAAC/hug.gif", "https://media1.tenor.com/m/8v4XfJiHf4AAAAAC/hug.gif", "https://media1.tenor.com/m/9Vj8kHt3n4wAAAAC/hug-cute.gif", "https://media1.tenor.com/m/KkO5IjxVQKMAAAAC/hug.gif", "https://media1.tenor.com/m/0qJj8kVb8nQAAAAC/hug.gif", "https://media1.tenor.com/m/4yPv5fJiHf4AAAAC/hug.gif", "https://media1.tenor.com/m/2qjVr7KUQKgAAAAC/hug.gif", ]
|
||||
kiss = ["https://media1.tenor.com/m/0qJj8kVb8nQAAAAC/kiss.gif", "https://media1.tenor.com/m/KkO5IjxVQKMAAAAC/kiss.gif", "https://media1.tenor.com/m/4yPv5fJiHf4AAAAC/kiss.gif", "https://media1.tenor.com/m/2qjVr7KUQKgAAAAC/kiss.gif", ]
|
||||
pat = ["https://media1.tenor.com/m/EiIiIiIiIiIiIiIi.gif", "https://media1.tenor.com/m/7yTtTtTtTtTtTtTt.gif", "https://media1.tenor.com/m/8v4XfJiHf4AAAAAC/head-pat.gif", "https://media1.tenor.com/m/9Vj8kHt3n4wAAAAC/head-pat.gif", "https://media1.tenor.com/m/0qJj8kVb8nQAAAAC/head-pat.gif", ]
|
||||
wave = ["https://media1.tenor.com/m/a-ooSHLa2lsAAAAC/bye.gif", "https://media1.tenor.com/m/a-ooSHLa2lsAAAAC/bye.gif", "https://media1.tenor.com/m/8v4XfJiHf4AAAAAC/wave.gif", "https://media1.tenor.com/m/9Vj8kHt3n4wAAAAC/wave.gif", "https://media1.tenor.com/m/0qJj8kVb8nQAAAAC/wave.gif", ]
|
||||
poke = ["https://media1.tenor.com/m/EiIiIiIiIiIiIiIi.gif", "https://media1.tenor.com/m/7yTtTtTtTtTtTtTt.gif", "https://media1.tenor.com/m/8v4XfJiHf4AAAAAC/poke.gif", "https://media1.tenor.com/m/9Vj8kHt3n4wAAAAC/poke.gif", "https://media1.tenor.com/m/0qJj8kVb8nQAAAAC/poke.gif", ]
|
||||
dance = ["https://media1.tenor.com/m/EiIiIiIiIiIiIiIi.gif", "https://media1.tenor.com/m/7yTtTtTtTtTtTtTt.gif", "https://media1.tenor.com/m/8v4XfJiHf4AAAAAC/dance.gif", "https://media1.tenor.com/m/9Vj8kHt3n4wAAAAC/dance.gif", "https://media1.tenor.com/m/0qJj8kVb8nQAAAAC/dance.gif", ]
|
||||
cry = ["https://media1.tenor.com/m/2qjVr7KUQKgAAAAC/cry.gif", "https://media1.tenor.com/m/8v4XfJiHf4AAAAAC/cry.gif", "https://media1.tenor.com/m/9Vj8kHt3n4wAAAAC/cry.gif", "https://media1.tenor.com/m/0qJj8kVb8nQAAAAC/cry.gif", ]
|
||||
laugh = ["https://media1.tenor.com/m/EiIiIiIiIiIiIiIi.gif", "https://media1.tenor.com/m/7yTtTtTtTtTtTtTt.gif", "https://media1.tenor.com/m/8v4XfJiHf4AAAAAC/laugh.gif", "https://media1.tenor.com/m/9Vj8kHt3n4wAAAAC/laugh.gif", "https://media1.tenor.com/m/0qJj8kVb8nQAAAAC/laugh.gif", ]
|
||||
highfive = ["https://media1.tenor.com/m/EiIiIiIiIiIiIiIi.gif", "https://media1.tenor.com/m/7yTtTtTtTtTtTtTt.gif", "https://media1.tenor.com/m/8v4XfJiHf4AAAAAC/highfive.gif", "https://media1.tenor.com/m/9Vj8kHt3n4wAAAAC/highfive.gif", "https://media1.tenor.com/m/0qJj8kVb8nQAAAAC/highfive.gif", ]
|
||||
punch = ["https://media1.tenor.com/m/EiIiIiIiIiIiIiIi.gif", "https://media1.tenor.com/m/7yTtTtTtTtTtTtTt.gif", "https://media1.tenor.com/m/8v4XfJiHf4AAAAAC/punch.gif", "https://media1.tenor.com/m/9Vj8kHt3n4wAAAAC/punch.gif", "https://media1.tenor.com/m/0qJj8kVb8nQAAAAC/punch.gif", ]
|
||||
blush = ["https://media1.tenor.com/m/EiIiIiIiIiIiIiIi.gif", "https://media1.tenor.com/m/7yTtTtTtTtTtTtTt.gif", "https://media1.tenor.com/m/8v4XfJiHf4AAAAAC/blush.gif", "https://media1.tenor.com/m/9Vj8kHt3n4wAAAAC/blush.gif", "https://media1.tenor.com/m/0qJj8kVb8nQAAAAC/blush.gif", ]
|
||||
class Interactions(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
@commands.command(name="slap")
|
||||
async def _slap_gif(self, ctx, member:discord.Member, *reason):
|
||||
embed = discord.Embed()
|
||||
embed.set_image(url=choice(slap))
|
||||
embed.add_field(name=f"{ctx.author.name} slaps {member.name}", value=" ".join(reason))
|
||||
await ctx.reply(embed=embed)
|
||||
|
||||
@commands.command(name="sad")
|
||||
async def _sad_gif(self, ctx, *reason):
|
||||
embed = discord.Embed()
|
||||
embed.set_image(url=choice(sad))
|
||||
embed.add_field(name=f"{ctx.author.name} is Sad", value=" ".join(reason))
|
||||
await ctx.reply(embed=embed)
|
||||
|
||||
@commands.command(name="hug")
|
||||
async def _hug_gif(self, ctx, member:discord.Member, *reason):
|
||||
|
||||
embed = discord.Embed()
|
||||
embed.set_image(url=choice(hug))
|
||||
embed.add_field(name=f"{ctx.author.name} hugs {member.name}", value=" ".join(reason))
|
||||
await ctx.reply(embed=embed)
|
||||
|
||||
@commands.command(name="kiss")
|
||||
async def _kiss_gif(self, ctx, member:discord.Member, *reason):
|
||||
embed = discord.Embed()
|
||||
embed.set_image(url=choice(kiss))
|
||||
embed.add_field(name=f"{ctx.author.name} kisses {member.name}", value=" ".join(reason))
|
||||
await ctx.reply(embed=embed)
|
||||
|
||||
@commands.command(name="pat")
|
||||
async def _pat_gif(self, ctx, member:discord.Member, *reason):
|
||||
embed = discord.Embed()
|
||||
embed.set_image(url=choice(pat))
|
||||
embed.add_field(name=f"{ctx.author.name} pats {member.name}", value=" ".join(reason))
|
||||
await ctx.reply(embed=embed)
|
||||
|
||||
@commands.command(name="wave")
|
||||
async def _wave_gif(self, ctx, member:discord.Member, *reason):
|
||||
embed = discord.Embed()
|
||||
embed.set_image(url=choice(wave))
|
||||
embed.add_field(name=f"{ctx.author.name} waves at {member.name}", value=" ".join(reason))
|
||||
await ctx.reply(embed=embed)
|
||||
|
||||
@commands.command(name="poke")
|
||||
async def _poke_gif(self, ctx, member:discord.Member, *reason):
|
||||
embed = discord.Embed()
|
||||
embed.set_image(url=choice(poke))
|
||||
embed.add_field(name=f"{ctx.author.name} pokes {member.name}", value=" ".join(reason))
|
||||
await ctx.reply(embed=embed)
|
||||
|
||||
@commands.command(name="dance")
|
||||
async def _dance_gif(self, ctx, *reason):
|
||||
embed = discord.Embed()
|
||||
embed.set_image(url=choice(dance))
|
||||
embed.add_field(name=f"{ctx.author.name} dances", value=" ".join(reason))
|
||||
await ctx.reply(embed=embed)
|
||||
|
||||
@commands.command(name="cry")
|
||||
async def _cry_gif(self, ctx, *reason):
|
||||
embed = discord.Embed()
|
||||
embed.set_image(url=choice(cry))
|
||||
embed.add_field(name=f"{ctx.author.name} cries", value=" ".join(reason))
|
||||
await ctx.reply(embed=embed)
|
||||
|
||||
@commands.command(name="laugh")
|
||||
async def _laugh_gif(self, ctx, *reason):
|
||||
embed = discord.Embed()
|
||||
embed.set_image(url=choice(laugh))
|
||||
embed.add_field(name=f"{ctx.author.name} laughs", value=" ".join(reason))
|
||||
await ctx.reply(embed=embed)
|
||||
|
||||
@commands.command(name="highfive")
|
||||
async def _highfive_gif(self, ctx, member:discord.Member, *reason):
|
||||
embed = discord.Embed()
|
||||
embed.set_image(url=choice(highfive))
|
||||
embed.add_field(name=f"{ctx.author.name} highfives {member.name}", value=" ".join(reason))
|
||||
await ctx.reply(embed=embed)
|
||||
|
||||
@commands.command(name="punch")
|
||||
async def _punch_gif(self, ctx, member:discord.Member, *reason):
|
||||
embed = discord.Embed()
|
||||
embed.set_image(url=choice(punch))
|
||||
embed.add_field(name=f"{ctx.author.name} punches {member.name}", value=" ".join(reason))
|
||||
await ctx.reply(embed=embed)
|
||||
|
||||
@commands.command(name="blush")
|
||||
async def _blush_gif(self, ctx, *reason):
|
||||
embed = discord.Embed()
|
||||
embed.set_image(url=choice(blush))
|
||||
embed.add_field(name=f"{ctx.author.name} blushes", value=" ".join(reason) or "No reason provided")
|
||||
await ctx.reply(embed=embed)
|
||||
|
||||
|
||||
|
||||
|
||||
async def setup(client):
|
||||
await client.add_cog(Interactions(client))
|
||||
|
||||
Executable
+509
@@ -0,0 +1,509 @@
|
||||
import discord
|
||||
from discord.ext import commands, tasks
|
||||
from utils.sql_commands import DatabaseManager
|
||||
from utils.bank_functions import bank_data, update_money
|
||||
from random import choice
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
|
||||
TICKET_TYPES = {
|
||||
"standard": {"price": 500, "weight": 1},
|
||||
"premium": {"price": 2000, "weight": 5},
|
||||
}
|
||||
LOTTERY_INTERVAL_HOURS = 24 # Draw every 24 hours
|
||||
MAX_TICKETS_PER_USER = 10
|
||||
ROLLOVER_BONUS = 500
|
||||
|
||||
|
||||
class Lottery(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.db = DatabaseManager()
|
||||
self.lottery_draw.start()
|
||||
|
||||
def cog_unload(self):
|
||||
self.lottery_draw.cancel()
|
||||
|
||||
def get_jackpot(self) -> int:
|
||||
try:
|
||||
result = self.db.fetch_one("SELECT jackpot FROM lottery_state WHERE id = 1")
|
||||
return int(result["jackpot"]) if result else 0
|
||||
except Exception as e:
|
||||
logging.error(f"Error fetching jackpot: {e}")
|
||||
return 0
|
||||
|
||||
def set_jackpot(self, amount: int) -> None:
|
||||
try:
|
||||
self.db.execute_query(
|
||||
"UPDATE lottery_state SET jackpot = %s WHERE id = 1", (amount,)
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error(f"Error setting jackpot: {e}")
|
||||
|
||||
def add_to_jackpot(self, amount: int) -> None:
|
||||
try:
|
||||
self.db.execute_query(
|
||||
"UPDATE lottery_state SET jackpot = jackpot + %s WHERE id = 1",
|
||||
(amount,),
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error(f"Error adding to jackpot: {e}")
|
||||
|
||||
def get_last_draw_time(self) -> datetime:
|
||||
try:
|
||||
result = self.db.fetch_one(
|
||||
"SELECT last_draw FROM lottery_draw_time WHERE id = 1"
|
||||
)
|
||||
last_draw = result["last_draw"] if result and result["last_draw"] else None
|
||||
if last_draw is None:
|
||||
# Set to now if missing and update DB
|
||||
now = datetime.utcnow()
|
||||
self.set_last_draw_time(now)
|
||||
return now
|
||||
if isinstance(last_draw, str):
|
||||
try:
|
||||
last_draw = datetime.fromisoformat(last_draw)
|
||||
except Exception:
|
||||
last_draw = datetime.strptime(last_draw, "%Y-%m-%d %H:%M:%S")
|
||||
return last_draw
|
||||
except Exception as e:
|
||||
logging.error(f"Error fetching last draw time: {e}")
|
||||
now = datetime.utcnow()
|
||||
self.set_last_draw_time(now)
|
||||
return now
|
||||
|
||||
def set_last_draw_time(self, draw_time: datetime) -> None:
|
||||
try:
|
||||
self.db.execute_query(
|
||||
"UPDATE lottery_draw_time SET last_draw = %s WHERE id = 1", (draw_time,)
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error(f"Error setting last draw time: {e}")
|
||||
|
||||
def notify_lottery_result(self, user_id: int):
|
||||
try:
|
||||
result = self.db.fetch_one(
|
||||
"SELECT * FROM lottery_results WHERE WINNER_ID = %s AND CLAIMED = 0 ORDER BY DRAW_TIME DESC LIMIT 1",
|
||||
(user_id,),
|
||||
)
|
||||
if not result:
|
||||
return None
|
||||
# Remove the result after notifying
|
||||
self.db.execute_query(
|
||||
"DELETE FROM lottery_results WHERE ID = %s", (result["ID"],)
|
||||
)
|
||||
return (True, result["AMOUNT"])
|
||||
except Exception as e:
|
||||
logging.error(f"Error notifying lottery result: {e}")
|
||||
return None
|
||||
|
||||
@commands.command(
|
||||
name="buyticket",
|
||||
help=f"Buy one or more lottery tickets for yourself or a group.",
|
||||
)
|
||||
async def buy_ticket(
|
||||
self,
|
||||
ctx: commands.Context,
|
||||
ticket_type: str = "standard",
|
||||
amount: int = 1,
|
||||
group_id: str | None = None,
|
||||
member: discord.Member | None = None,
|
||||
):
|
||||
ticket_type = ticket_type.lower()
|
||||
if ticket_type not in TICKET_TYPES:
|
||||
await ctx.reply(
|
||||
f"Invalid ticket type. Choose from: {', '.join(TICKET_TYPES)}"
|
||||
)
|
||||
return
|
||||
if amount < 1 or amount > MAX_TICKETS_PER_USER:
|
||||
await ctx.reply(
|
||||
f"You can only buy between 1 and {MAX_TICKETS_PER_USER} tickets at once."
|
||||
)
|
||||
return
|
||||
user = member or ctx.author
|
||||
price = TICKET_TYPES[ticket_type]["price"] * amount
|
||||
try:
|
||||
# Notify about last draw if relevant (only if user is the winner)
|
||||
notify = self.notify_lottery_result(user.id)
|
||||
if notify:
|
||||
await ctx.reply(
|
||||
f"🎉 You won the last lottery! Jackpot: {notify[1]:,} coins.",
|
||||
mention_author=False,
|
||||
)
|
||||
|
||||
user_data = await bank_data(user)
|
||||
wallet = int(user_data.get("WALLET", 0))
|
||||
if wallet < price:
|
||||
await ctx.reply(
|
||||
f"You need at least {price} coins to buy {amount} {ticket_type} ticket(s).",
|
||||
mention_author=False,
|
||||
)
|
||||
return
|
||||
|
||||
if group_id:
|
||||
group_exists = self.db.fetch_one(
|
||||
"SELECT 1 FROM lottery_groups WHERE group_id = %s", (group_id,)
|
||||
)
|
||||
in_group = self.db.fetch_one(
|
||||
"SELECT 1 FROM lottery_group_members WHERE group_id = %s AND user_id = %s",
|
||||
(group_id, user.id),
|
||||
)
|
||||
if not group_exists or not in_group:
|
||||
await ctx.reply(
|
||||
"You must be a member of the group to buy tickets for it."
|
||||
)
|
||||
return
|
||||
|
||||
user_ticket_count = self.db.fetch_one(
|
||||
"SELECT COUNT(*) as count FROM lottery_tickets WHERE USERID = %s",
|
||||
(user.id,),
|
||||
)["count"]
|
||||
if user_ticket_count + amount > MAX_TICKETS_PER_USER:
|
||||
await ctx.reply(
|
||||
f"You can only have {MAX_TICKETS_PER_USER} tickets per draw. You currently have {user_ticket_count}."
|
||||
)
|
||||
return
|
||||
|
||||
# Deduct ticket price
|
||||
await update_money(user, wallet=-price)
|
||||
self.add_to_jackpot(price)
|
||||
|
||||
# Store tickets in DB (one row per ticket for higher odds with multiple tickets)
|
||||
for _ in range(amount):
|
||||
self.db.execute_query(
|
||||
"INSERT INTO lottery_tickets (USERID, TIMESTAMP, TICKET_TYPE, group_id) VALUES (%s, %s, %s, %s)",
|
||||
(user.id, datetime.utcnow(), ticket_type, group_id),
|
||||
)
|
||||
|
||||
jackpot = self.get_jackpot()
|
||||
await ctx.reply(
|
||||
f"🎟️ {amount} {ticket_type.capitalize()} ticket(s) bought! Jackpot is now {jackpot:,} coins.",
|
||||
mention_author=False,
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error(f"Error in buy_ticket: {e}")
|
||||
await ctx.reply(
|
||||
"An error occurred while buying your ticket(s).", mention_author=False
|
||||
)
|
||||
|
||||
@commands.command(name="lotterystats", help="Show current lottery stats.")
|
||||
async def lottery_stats(self, ctx: commands.Context):
|
||||
try:
|
||||
# Show last winner and their prize
|
||||
last_result = self.db.fetch_one(
|
||||
"SELECT * FROM lottery_results ORDER BY DRAW_TIME DESC LIMIT 1"
|
||||
)
|
||||
winner_text = "No draws yet."
|
||||
if last_result:
|
||||
winner = self.client.get_user(
|
||||
last_result["WINNER_ID"]
|
||||
) or await self.client.fetch_user(last_result["WINNER_ID"])
|
||||
winner_text = f"Last winner: {winner.mention if winner else 'Unknown'} ({last_result['AMOUNT']:,} coins)"
|
||||
|
||||
ticket_count = self.db.fetch_one(
|
||||
"SELECT COUNT(*) as count FROM lottery_tickets"
|
||||
)["count"]
|
||||
jackpot = self.get_jackpot()
|
||||
|
||||
# Check if the user has an unclaimed win
|
||||
unclaimed = self.db.fetch_one(
|
||||
"SELECT * FROM lottery_results WHERE WINNER_ID = %s AND CLAIMED = 0 ORDER BY DRAW_TIME DESC LIMIT 1",
|
||||
(ctx.author.id,),
|
||||
)
|
||||
winner_note = ""
|
||||
if unclaimed:
|
||||
# Mark as claimed and pay out
|
||||
self.db.execute_query(
|
||||
"UPDATE lottery_results SET CLAIMED = 1 WHERE ID = %s",
|
||||
(unclaimed["ID"],),
|
||||
)
|
||||
await update_money(ctx.author, wallet=unclaimed["AMOUNT"])
|
||||
winner_note = f"\n🎉 **You have won {unclaimed['AMOUNT']:,} coins! Your prize has been paid out.**"
|
||||
|
||||
await ctx.reply(
|
||||
f"{winner_text}\n"
|
||||
f"🎰 There are currently **{ticket_count}** tickets in the pool.\n"
|
||||
f"💰 Jackpot: **{jackpot:,}** coins.\n"
|
||||
f"Next draw in: {self.time_until_draw()}"
|
||||
f"{winner_note}",
|
||||
mention_author=False,
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error(f"Error in lottery_stats: {e}")
|
||||
await ctx.reply(
|
||||
"An error occurred while fetching lottery stats.", mention_author=False
|
||||
)
|
||||
|
||||
def time_until_draw(self) -> str:
|
||||
now = datetime.utcnow()
|
||||
last_draw = self.get_last_draw_time()
|
||||
next_draw = last_draw + timedelta(hours=LOTTERY_INTERVAL_HOURS)
|
||||
remaining = next_draw - now
|
||||
if remaining.total_seconds() < 0:
|
||||
return "Drawing soon!"
|
||||
hours, remainder = divmod(int(remaining.total_seconds()), 3600)
|
||||
minutes, seconds = divmod(remainder, 60)
|
||||
return f"{hours}h {minutes}m {seconds}s"
|
||||
|
||||
@tasks.loop(minutes=1)
|
||||
async def lottery_draw(self):
|
||||
now = datetime.utcnow()
|
||||
last_draw = self.get_last_draw_time()
|
||||
if (now - last_draw).total_seconds() < LOTTERY_INTERVAL_HOURS * 3600:
|
||||
return # Not time yet
|
||||
|
||||
await self._run_lottery_draw()
|
||||
|
||||
@commands.command(name="testdraw", help="Manually trigger the lottery draw.")
|
||||
@commands.is_owner()
|
||||
async def testdraw(self, ctx: commands.Context):
|
||||
await ctx.send("Starting lottery draw...")
|
||||
await self._run_lottery_draw()
|
||||
|
||||
async def _run_lottery_draw(self):
|
||||
try:
|
||||
await self.client.wait_until_ready()
|
||||
tickets = self.db.fetch_all(
|
||||
"SELECT USERID, TICKET_TYPE FROM lottery_tickets"
|
||||
)
|
||||
jackpot = self.get_jackpot()
|
||||
if not tickets or jackpot <= 0:
|
||||
self.set_jackpot(self.get_jackpot() + ROLLOVER_BONUS)
|
||||
self.set_last_draw_time(datetime.utcnow())
|
||||
return
|
||||
|
||||
# Build weighted ticket list
|
||||
weighted_tickets = []
|
||||
for t in tickets:
|
||||
luck_row = self.db.fetch_one(
|
||||
"SELECT LUCK FROM lottery_luck WHERE USERID = %s", (t["USERID"],)
|
||||
)
|
||||
luck = luck_row["LUCK"] if luck_row else 0
|
||||
ticket_type_weight = TICKET_TYPES[t["TICKET_TYPE"]]["weight"]
|
||||
weight = ticket_type_weight * (1 + luck)
|
||||
weighted_tickets.extend([t["USERID"]] * weight)
|
||||
|
||||
winner_id = choice(weighted_tickets)
|
||||
# Find group for winning ticket BEFORE deleting tickets
|
||||
group_id = self.db.fetch_one(
|
||||
"SELECT group_id FROM lottery_tickets WHERE USERID = %s LIMIT 1",
|
||||
(winner_id,),
|
||||
)["group_id"]
|
||||
|
||||
self.db.execute_query("DELETE FROM lottery_tickets") # Reset for next round
|
||||
|
||||
if group_id:
|
||||
members = self.db.fetch_all(
|
||||
"SELECT user_id FROM lottery_group_members WHERE group_id = %s",
|
||||
(group_id,),
|
||||
)
|
||||
member_ids = [m["user_id"] for m in members]
|
||||
split_prize = (jackpot // 2) // len(member_ids)
|
||||
for uid in member_ids:
|
||||
member = self.client.get_user(uid) or await self.client.fetch_user(
|
||||
uid
|
||||
)
|
||||
await update_money(member, wallet=split_prize)
|
||||
# Store group win in results with WIN_TYPE='group'
|
||||
self.db.execute_query(
|
||||
"INSERT INTO lottery_results (WINNER_ID, AMOUNT, DRAW_TIME, CLAIMED, WIN_TYPE) VALUES (%s, %s, %s, 0, %s)",
|
||||
(group_id, jackpot // 2, datetime.utcnow(), "group"),
|
||||
)
|
||||
else:
|
||||
# Store solo win in results with WIN_TYPE='user'
|
||||
self.db.execute_query(
|
||||
"INSERT INTO lottery_results (WINNER_ID, AMOUNT, DRAW_TIME, CLAIMED, WIN_TYPE) VALUES (%s, %s, %s, 0, %s)",
|
||||
(winner_id, jackpot // 2, datetime.utcnow(), "user"),
|
||||
)
|
||||
# Carry over half the jackpot (rounded down)
|
||||
carryover = jackpot // 2
|
||||
self.set_jackpot(carryover)
|
||||
self.set_last_draw_time(datetime.utcnow())
|
||||
|
||||
# Reset winner's luck, increment others'
|
||||
self.db.execute_query(
|
||||
"UPDATE lottery_luck SET LUCK = 0 WHERE USERID = %s", (winner_id,)
|
||||
)
|
||||
self.db.execute_query(
|
||||
"UPDATE lottery_luck SET LUCK = LUCK + 1 WHERE USERID != %s",
|
||||
(winner_id,),
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error(f"Error in _run_lottery_draw: {e}")
|
||||
|
||||
@lottery_draw.before_loop
|
||||
async def before_lottery_draw(self):
|
||||
await self.client.wait_until_ready()
|
||||
|
||||
@commands.command(name="lotteryhistory")
|
||||
async def lottery_history(self, ctx):
|
||||
results = self.db.fetch_all(
|
||||
"SELECT * FROM lottery_results ORDER BY DRAW_TIME DESC LIMIT 10"
|
||||
)
|
||||
if not results:
|
||||
await ctx.reply("No lottery draws yet.")
|
||||
return
|
||||
lines = []
|
||||
for res in results:
|
||||
if res.get("WIN_TYPE") == "group":
|
||||
lines.append(
|
||||
f"{res['DRAW_TIME'].strftime('%Y-%m-%d')}: Group `{res['WINNER_ID']}` won {res['AMOUNT']:,} coins"
|
||||
)
|
||||
else:
|
||||
winner = self.client.get_user(
|
||||
res["WINNER_ID"]
|
||||
) or await self.client.fetch_user(res["WINNER_ID"])
|
||||
lines.append(
|
||||
f"{res['DRAW_TIME'].strftime('%Y-%m-%d')}: {winner.mention if winner else 'Unknown'} won {res['AMOUNT']:,} coins"
|
||||
)
|
||||
await ctx.reply("\n".join(lines))
|
||||
|
||||
@commands.command(name="lotteryleaderboard")
|
||||
async def lottery_leaderboard(self, ctx):
|
||||
winners = self.db.fetch_all(
|
||||
"SELECT WINNER_ID, COUNT(*) as wins, SUM(AMOUNT) as total FROM lottery_results GROUP BY WINNER_ID ORDER BY wins DESC LIMIT 10"
|
||||
)
|
||||
if not winners:
|
||||
await ctx.reply("No winners yet.")
|
||||
return
|
||||
lines = []
|
||||
for w in winners:
|
||||
user = self.client.get_user(w["WINNER_ID"]) or await self.client.fetch_user(
|
||||
w["WINNER_ID"]
|
||||
)
|
||||
lines.append(
|
||||
f"{user.mention if user else 'Unknown'}: {w['wins']} wins, {w['total']:,} coins"
|
||||
)
|
||||
await ctx.reply("\n".join(lines))
|
||||
|
||||
@commands.command(name="initluck")
|
||||
@commands.is_owner()
|
||||
async def init_luck(self, ctx):
|
||||
"""Initialize or reset the luck of a user."""
|
||||
user = ctx.author
|
||||
self.db.execute_query(
|
||||
"INSERT IGNORE INTO lottery_luck (USERID, LUCK) VALUES (%s, 0)", (user.id,)
|
||||
)
|
||||
await ctx.reply(f"Your luck has been initialized/reset.")
|
||||
|
||||
@commands.command(name="creategroup")
|
||||
async def create_group(self, ctx, group_id: str):
|
||||
exists = self.db.fetch_one(
|
||||
"SELECT 1 FROM lottery_groups WHERE group_id = %s", (group_id,)
|
||||
)
|
||||
if exists:
|
||||
await ctx.reply("A group with that ID already exists.")
|
||||
return
|
||||
self.db.execute_query(
|
||||
"INSERT INTO lottery_groups (group_id, creator_id) VALUES (%s, %s)",
|
||||
(group_id, ctx.author.id),
|
||||
)
|
||||
self.db.execute_query(
|
||||
"INSERT INTO lottery_group_members (group_id, user_id) VALUES (%s, %s)",
|
||||
(group_id, ctx.author.id),
|
||||
)
|
||||
await ctx.reply(f"Group `{group_id}` created and you have joined it.")
|
||||
|
||||
@commands.command(name="joingroup")
|
||||
async def join_group(self, ctx, group_id: str):
|
||||
exists = self.db.fetch_one(
|
||||
"SELECT 1 FROM lottery_groups WHERE group_id = %s", (group_id,)
|
||||
)
|
||||
if not exists:
|
||||
await ctx.reply("That group does not exist.")
|
||||
return
|
||||
already = self.db.fetch_one(
|
||||
"SELECT 1 FROM lottery_group_members WHERE group_id = %s AND user_id = %s",
|
||||
(group_id, ctx.author.id),
|
||||
)
|
||||
if already:
|
||||
await ctx.reply("You are already in this group.")
|
||||
return
|
||||
self.db.execute_query(
|
||||
"INSERT INTO lottery_group_members (group_id, user_id) VALUES (%s, %s)",
|
||||
(group_id, ctx.author.id),
|
||||
)
|
||||
await ctx.reply(f"You have joined group `{group_id}`.")
|
||||
|
||||
@commands.command(name="leavegroup")
|
||||
async def leave_group(self, ctx, group_id: str):
|
||||
self.db.execute_query(
|
||||
"DELETE FROM lottery_group_members WHERE group_id = %s AND user_id = %s",
|
||||
(group_id, ctx.author.id),
|
||||
)
|
||||
await ctx.reply(f"You have left group `{group_id}`.")
|
||||
|
||||
@commands.command(name="deletegroup")
|
||||
async def delete_group(self, ctx, group_id: str):
|
||||
# Check if group exists and if user is creator
|
||||
group = self.db.fetch_one(
|
||||
"SELECT creator_id FROM lottery_groups WHERE group_id = %s", (group_id,)
|
||||
)
|
||||
if not group:
|
||||
await ctx.reply("That group does not exist.")
|
||||
return
|
||||
if group["creator_id"] != ctx.author.id:
|
||||
await ctx.reply("Only the group creator can delete this group.")
|
||||
return
|
||||
# Check if group is empty (no members except possibly the creator)
|
||||
members = self.db.fetch_all(
|
||||
"SELECT user_id FROM lottery_group_members WHERE group_id = %s", (group_id,)
|
||||
)
|
||||
if len(members) > 1 or (
|
||||
len(members) == 1 and members[0]["user_id"] != ctx.author.id
|
||||
):
|
||||
await ctx.reply(
|
||||
"You can only delete a group if it is empty (no members except you)."
|
||||
)
|
||||
return
|
||||
# Delete group and membership
|
||||
self.db.execute_query(
|
||||
"DELETE FROM lottery_group_members WHERE group_id = %s", (group_id,)
|
||||
)
|
||||
self.db.execute_query(
|
||||
"DELETE FROM lottery_groups WHERE group_id = %s", (group_id,)
|
||||
)
|
||||
await ctx.reply(f"Group `{group_id}` has been deleted.")
|
||||
|
||||
@commands.command(name="refundtickets")
|
||||
async def refund_tickets(self, ctx, amount: int = 1):
|
||||
user = ctx.author
|
||||
tickets = self.db.fetch_all(
|
||||
"SELECT * FROM lottery_tickets WHERE USERID = %s", (user.id,)
|
||||
)
|
||||
if not tickets:
|
||||
await ctx.reply("You have no tickets to refund for this draw.")
|
||||
return
|
||||
|
||||
if amount is None:
|
||||
amount = len(tickets)
|
||||
if amount < 1 or amount > len(tickets):
|
||||
await ctx.reply(
|
||||
f"You can only refund between 1 and {len(tickets)} tickets."
|
||||
)
|
||||
return
|
||||
|
||||
# Refund only the specified amount
|
||||
tickets_to_refund = tickets[:amount]
|
||||
total_refund = 0
|
||||
ticket_ids = []
|
||||
for ticket in tickets_to_refund:
|
||||
price = TICKET_TYPES[ticket["TICKET_TYPE"]]["price"]
|
||||
refund = int(price * 0.8)
|
||||
total_refund += refund
|
||||
ticket_ids.append(ticket["ID"])
|
||||
|
||||
# Remove only the refunded tickets
|
||||
format_strings = ",".join(["%s"] * len(ticket_ids))
|
||||
self.db.execute_query(
|
||||
f"DELETE FROM lottery_tickets WHERE ID IN ({format_strings})",
|
||||
tuple(ticket_ids),
|
||||
)
|
||||
await update_money(user, wallet=total_refund)
|
||||
await ctx.reply(
|
||||
f"{amount} ticket(s) refunded for {total_refund:,} coins (80% of purchase price)."
|
||||
)
|
||||
|
||||
|
||||
async def setup(client):
|
||||
await client.add_cog(Lottery(client))
|
||||
Executable
Executable
+54
@@ -0,0 +1,54 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
|
||||
class Roles(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
@commands.command(name="giveRole")
|
||||
@commands.has_permissions(manage_roles=True)
|
||||
async def _add_role(self, ctx, member: discord.Member, role):
|
||||
role = discord.utils.get(ctx.guild.roles, name=role)
|
||||
await member.add_roles(role)
|
||||
await ctx.reply("role given", delete_after=2)
|
||||
|
||||
@commands.command(name="showRoles")
|
||||
@commands.has_permissions(manage_roles=True)
|
||||
async def _show_role(self, ctx):
|
||||
roles = [role for role in ctx.guild.roles][1:]
|
||||
embed = discord.Embed(
|
||||
colour=discord.Colour.purple(),
|
||||
timestamp=ctx.message.created_at,
|
||||
title=f"Roles",
|
||||
)
|
||||
embed.add_field(
|
||||
name="Roles:", value="\n".join([role.mention for role in roles])
|
||||
)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command(name="takeRole")
|
||||
@commands.has_permissions(manage_roles=True)
|
||||
async def _take_role(self, ctx, member: discord.Member, role):
|
||||
role = discord.utils.get(ctx.guild.roles, name=role)
|
||||
await member.remove_roles(role)
|
||||
await ctx.reply("role taken", delete_after=2)
|
||||
|
||||
@commands.command(name="deleteRole")
|
||||
@commands.has_permissions(manage_roles=True)
|
||||
async def _delete_role(self, ctx, role_name):
|
||||
role_object = discord.utils.get(ctx.guild.roles, name=role_name)
|
||||
await role_object.delete()
|
||||
|
||||
@commands.command("addRole")
|
||||
@commands.has_permissions(
|
||||
manage_roles=True
|
||||
) # Check if the user executing the command can manage roles
|
||||
async def _create_role(self, ctx, name):
|
||||
guild = ctx.guild
|
||||
await guild.create_role(name=name)
|
||||
await ctx.send(f"Role `{name}` has been created")
|
||||
|
||||
|
||||
async def setup(client):
|
||||
await client.add_cog(Roles(client))
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
import discord
|
||||
import requests
|
||||
from discord.ext import commands
|
||||
from discord.ui import Select, View
|
||||
from random import shuffle
|
||||
from utils import sql_commands as mydb
|
||||
from utils.bank_functions import *
|
||||
|
||||
class MyView(View):
|
||||
def __init__(self, right: str, wrong: list):
|
||||
super().__init__()
|
||||
self.right = right
|
||||
options = wrong.copy()
|
||||
options.append(right)
|
||||
shuffle(options)
|
||||
|
||||
select = Select(
|
||||
placeholder="Pick an answer:",
|
||||
min_values=1,
|
||||
max_values=1,
|
||||
options=[discord.SelectOption(label=option) for option in options]
|
||||
)
|
||||
|
||||
self.add_item(select)
|
||||
|
||||
select.callback = self.select_callback # Set the callback method
|
||||
|
||||
async def select_callback(self, interaction: discord.Interaction):
|
||||
if interaction.data['values'][0] == self.right: # type: ignore
|
||||
await interaction.response.edit_message(content=f"You are correct!\n{self.right} was the right answer.", view=None)
|
||||
else:
|
||||
await interaction.response.edit_message(content=f"Oh no, that's not right!\n{self.right} was the right answer.", view=None)
|
||||
|
||||
class Test(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
@commands.is_owner()
|
||||
@commands.command(name="quiz")
|
||||
async def quiz(self, ctx):
|
||||
response = requests.get("https://opentdb.com/api.php?amount=1&category=18&difficulty=medium&type=multiple").json()["results"][0]
|
||||
question = response["question"]
|
||||
|
||||
view = MyView(response["correct_answer"], response["incorrect_answers"])
|
||||
await ctx.send(question, view=view)
|
||||
|
||||
@commands.is_owner()
|
||||
@commands.command(name="lottery", aliases=["bet" , "lotto"])
|
||||
async def lottery(self, ctx, amount:int=None): #type: ignore
|
||||
if amount == None:
|
||||
await ctx.send(mydb.select("JACKPOT", "global", True, "ID", "1"))
|
||||
return
|
||||
# mydb.select()
|
||||
|
||||
|
||||
async def setup(client):
|
||||
await client.add_cog(Test(client))
|
||||
Executable
+302
@@ -0,0 +1,302 @@
|
||||
import discord
|
||||
from random import shuffle, choice
|
||||
from discord.ext import commands
|
||||
import utils.sql_commands as mydb
|
||||
|
||||
|
||||
lottery_list = ["🎁", "🎮", "🎷", "🔫", "📸", "🎃", "🏅"]
|
||||
|
||||
lottery_win = {
|
||||
"🎁": 2,
|
||||
"🎁🎁": 5,
|
||||
"🎁🎁🎁": 500,
|
||||
"🔫🔫🔫": 25,
|
||||
"📸📸📸": 50,
|
||||
"🎃🎃🎃": 75,
|
||||
"🏅🏅🏅": 100,
|
||||
"🎮🎮🎮": 250,
|
||||
"🎷🎷🎷": 1000,
|
||||
}
|
||||
|
||||
bj_values = {
|
||||
"2♦️": 2,
|
||||
"2♥️": 2,
|
||||
"2♣️": 2,
|
||||
"2♠️": 2,
|
||||
"3♦️": 3,
|
||||
"3♥️": 3,
|
||||
"3♣️": 3,
|
||||
"3♠️": 3,
|
||||
"4♦️": 4,
|
||||
"4♥️": 4,
|
||||
"4♣️": 4,
|
||||
"4♠️": 4,
|
||||
"5♦️": 5,
|
||||
"5♥️": 5,
|
||||
"5♣️": 5,
|
||||
"5♠️": 5,
|
||||
"6♦️": 6,
|
||||
"6♥️": 6,
|
||||
"6♣️": 6,
|
||||
"6♠️": 6,
|
||||
"7♦️": 7,
|
||||
"7♥️": 7,
|
||||
"7♣️": 7,
|
||||
"7♠️": 7,
|
||||
"8♦️": 8,
|
||||
"8♥️": 8,
|
||||
"8♣️": 8,
|
||||
"8♠️": 8,
|
||||
"9♦️": 9,
|
||||
"9♥️": 9,
|
||||
"9♣️": 9,
|
||||
"9♠️": 9,
|
||||
"10♦️": 10,
|
||||
"10♥️": 10,
|
||||
"10♣️": 10,
|
||||
"10♠️": 10,
|
||||
"J♦️": 10,
|
||||
"J♥️": 10,
|
||||
"J♣️": 10,
|
||||
"J♠️": 10,
|
||||
"Q♦️": 10,
|
||||
"Q♥️": 10,
|
||||
"Q♣️": 10,
|
||||
"Q♠️": 10,
|
||||
"K♦️": 10,
|
||||
"K♥️": 10,
|
||||
"K♣️": 10,
|
||||
"K♠️": 10,
|
||||
"A♦️": 11,
|
||||
"A♥️": 11,
|
||||
"A♣️": 11,
|
||||
"A♠️": 11,
|
||||
}
|
||||
|
||||
|
||||
class Deck:
|
||||
def __init__(self):
|
||||
self.deck = list(bj_values.keys())
|
||||
shuffle(self.deck)
|
||||
|
||||
async def deal(self):
|
||||
return self.deck.pop()
|
||||
|
||||
|
||||
class Hand:
|
||||
def __init__(self, name, bet):
|
||||
self.cards = []
|
||||
self.value = 0
|
||||
self.aces = 0
|
||||
self.bust = False
|
||||
self.name = name
|
||||
self.bet = bet
|
||||
|
||||
async def win_bet(self):
|
||||
pass
|
||||
|
||||
async def lose_bet(self):
|
||||
pass
|
||||
|
||||
async def add_card(self, card):
|
||||
self.cards.append(card)
|
||||
self.aces = ("".join(self.cards)).count("A")
|
||||
value = 0
|
||||
for card in self.cards:
|
||||
value += bj_values[card]
|
||||
self.value = value
|
||||
while self.value > 21 and self.aces:
|
||||
self.value -= 10
|
||||
self.aces -= 1
|
||||
|
||||
|
||||
async def hit(deck, hand):
|
||||
await hand.add_card(await deck.deal())
|
||||
|
||||
|
||||
async def double(deck, hand):
|
||||
pass
|
||||
|
||||
|
||||
async def split(deck, hands):
|
||||
pass
|
||||
|
||||
|
||||
async def payout(ctx, play, amount, multiplier):
|
||||
await ctx.reply(
|
||||
f"{play}\nBet: {amount:,}\nYou won {amount * multiplier:,} Flooneys"
|
||||
)
|
||||
|
||||
|
||||
async def check_winner(msg: discord.Interaction, player_hand, dealer_hand):
|
||||
p_value, d_value = player_hand.value, dealer_hand.value
|
||||
embed = discord.Embed(description="Blackjack")
|
||||
embed.add_field(name="Bet: ", value="amount", inline=True)
|
||||
embed.add_field(
|
||||
name=player_hand.name,
|
||||
value=f"{', '.join(player_hand.cards)}: {player_hand.value}",
|
||||
inline=False,
|
||||
)
|
||||
embed.add_field(
|
||||
name=dealer_hand.name,
|
||||
value=f"{', '.join(dealer_hand.cards)}: {dealer_hand.value}",
|
||||
inline=False,
|
||||
)
|
||||
|
||||
if (p_value > d_value and not player_hand.bust) or dealer_hand.bust:
|
||||
embed.add_field(name="Winner:", value=f"{player_hand.name}", inline=False)
|
||||
player_balance = int(mydb.select("WALLET", "economy", True, "ID", player_hand.name.id).get("WALLET", None))
|
||||
mydb.add("economy", "ID", "WALLET", player_hand.name.id, player_balance + player_hand.bet*2, True)
|
||||
elif p_value < d_value and dealer_hand.bust:
|
||||
embed.add_field(name="Winner:", value=f"{dealer_hand.name}", inline=False)
|
||||
else:
|
||||
embed.add_field(name="Winner:", value=f"Draw", inline=False)
|
||||
await msg.edit_original_response(embed=embed)
|
||||
|
||||
|
||||
async def dealer(msg: discord.Interaction, player_hand, dealer_hand, cards):
|
||||
while dealer_hand.value < 17 and not player_hand.bust and not dealer_hand.bust:
|
||||
await hit(cards, dealer_hand)
|
||||
if dealer_hand.value > 21:
|
||||
dealer_hand.bust = True
|
||||
embed = discord.Embed(description="Blackjack")
|
||||
embed.add_field(name="Bet: ", value="amount", inline=True)
|
||||
embed.add_field(
|
||||
name=player_hand.name,
|
||||
value=f"{', '.join(player_hand.cards)}: {player_hand.value}",
|
||||
inline=False,
|
||||
)
|
||||
embed.add_field(
|
||||
name=dealer_hand.name,
|
||||
value=f"{', '.join(dealer_hand.cards)}: {dealer_hand.value}",
|
||||
inline=False,
|
||||
)
|
||||
await msg.edit_original_response(embed=embed)
|
||||
await check_winner(msg, player_hand, dealer_hand)
|
||||
|
||||
|
||||
class MyView(discord.ui.View):
|
||||
def __init__(self, player_hand, dealer_hand, cards):
|
||||
super().__init__()
|
||||
self.player_hand = player_hand
|
||||
self.dealer_hand = dealer_hand
|
||||
self.cards = cards
|
||||
|
||||
@discord.ui.button(label="Hit", style=discord.ButtonStyle.primary, emoji="✅")
|
||||
async def hit(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
await hit(self.cards, self.player_hand)
|
||||
embed = discord.Embed(description="Blackjack")
|
||||
embed.add_field(name="Bet: ", value="amount", inline=True)
|
||||
embed.add_field(
|
||||
name=self.player_hand.name,
|
||||
value=f"{', '.join(self.player_hand.cards)}: {self.player_hand.value}",
|
||||
inline=False,
|
||||
)
|
||||
embed.add_field(
|
||||
name=self.dealer_hand.name,
|
||||
value=f"{(self.dealer_hand.cards)[0]}",
|
||||
inline=False,
|
||||
)
|
||||
msg = interaction
|
||||
if self.player_hand.value < 21:
|
||||
await msg.response.edit_message(embed=embed)
|
||||
else:
|
||||
await msg.response.edit_message(embed=embed, view=None)
|
||||
if self.player_hand.value > 21:
|
||||
self.player_hand.bust = True
|
||||
await dealer(msg, self.player_hand, self.dealer_hand, self.cards)
|
||||
|
||||
@discord.ui.button(label="Stand", style=discord.ButtonStyle.secondary, emoji="🛑")
|
||||
async def stand(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
embed = discord.Embed(description="Blackjack")
|
||||
embed.add_field(name="Bet: ", value=self.player_hand.bet, inline=True)
|
||||
embed.add_field(
|
||||
name=self.player_hand.name,
|
||||
value=f"{', '.join(self.player_hand.cards)}: {self.player_hand.value}",
|
||||
inline=False,
|
||||
)
|
||||
embed.add_field(
|
||||
name=self.dealer_hand.name,
|
||||
value=f"{(self.dealer_hand.cards)[0]}",
|
||||
inline=False,
|
||||
)
|
||||
msg = interaction
|
||||
await msg.response.edit_message(embed=embed, view=None)
|
||||
await dealer(msg, self.player_hand, self.dealer_hand, self.cards)
|
||||
|
||||
|
||||
class Gamble(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
@commands.command()
|
||||
async def blackjack(self, ctx, bet):
|
||||
amount = int(bet)
|
||||
|
||||
cards = Deck()
|
||||
player_balance = int(mydb.select("WALLET", "economy", True, "ID", ctx.author.id).get(
|
||||
"WALLET", None
|
||||
)
|
||||
)
|
||||
if player_balance < bet or bet < 0:
|
||||
await ctx.reply("You have insufficient funds.")
|
||||
return
|
||||
mydb.add("economy", "ID", "WALLET", ctx.author.id, player_balance - bet, True)
|
||||
player_hand, dealer_hand = Hand(ctx.author, amount), Hand("Dealer", None)
|
||||
|
||||
# setup
|
||||
for _ in range(2):
|
||||
await player_hand.add_card(await cards.deal())
|
||||
await dealer_hand.add_card(await cards.deal())
|
||||
|
||||
embed = discord.Embed(description="Blackjack")
|
||||
embed.add_field(name="Bet: ", value=amount, inline=True)
|
||||
embed.add_field(
|
||||
name=ctx.author,
|
||||
value=f"{', '.join(player_hand.cards)}: {player_hand.value}",
|
||||
inline=False,
|
||||
)
|
||||
embed.add_field(name="Dealer", value=f"{(dealer_hand.cards)[0]}", inline=False)
|
||||
|
||||
view = MyView(player_hand, dealer_hand, cards)
|
||||
await ctx.send(embed=embed, view=view)
|
||||
|
||||
@commands.command()
|
||||
async def show_buttons(self, ctx):
|
||||
await ctx.reply(
|
||||
"This is a button!", view=MyView(None, None, None)
|
||||
) # Send a message with our View class that contains the button
|
||||
|
||||
|
||||
@commands.cooldown(1, 1, commands.BucketType.user)
|
||||
@commands.command(name="lottery", aliases=["slots"])
|
||||
async def lottery(self, ctx, bet: int):
|
||||
player_balance = int(
|
||||
mydb.select("WALLET", "economy", True, "ID", ctx.author.id).get(
|
||||
"WALLET", None
|
||||
)
|
||||
)
|
||||
if player_balance < bet or bet < 0:
|
||||
await ctx.reply("You have insufficient funds.")
|
||||
return
|
||||
play = "".join([choice(lottery_list) for _ in range(3)])
|
||||
|
||||
if play in lottery_win:
|
||||
mult = lottery_win[play]
|
||||
await payout(ctx, play, bet, mult)
|
||||
|
||||
elif play.count("🎁") == 2:
|
||||
mult = lottery_win["🎁🎁"]
|
||||
await payout(ctx, play, bet, mult)
|
||||
|
||||
elif play.count("🎁") == 1:
|
||||
mult = lottery_win["🎁"]
|
||||
await payout(ctx, play, bet, mult)
|
||||
|
||||
else:
|
||||
mydb.add("economy", "ID", "WALLET", ctx.author.id, player_balance - bet, True)
|
||||
await ctx.reply(f"{play}\nYou lost {int(bet):,} Flooneys")
|
||||
|
||||
|
||||
async def setup(client):
|
||||
await client.add_cog(Gamble(client))
|
||||
Reference in New Issue
Block a user