Files

255 lines
8.2 KiB
Python

import discord
from discord.ext import commands
from PIL import Image, ImageDraw, ImageFont, ImageOps
import io
import os
from dotenv import load_dotenv
import requests
from random import randint
# Bot setup
load_dotenv()
TOKEN = os.getenv("TOKEN")
intents = discord.Intents.all()
bot = commands.Bot(command_prefix="!?", intents=intents)
# Simple user data structure
user_data = {}
def get_user_data(user_id):
"""Get or initialize user data."""
if user_id not in user_data:
user_data[user_id] = {"xp": 0, "level": 53}
return user_data[user_id]
import io
from PIL import Image, ImageDraw, ImageFont
async def create_profile_card(user, background_image_path):
"""
Create a profile card image for a user with a custom background and text overlay.
Args:
user (discord.Member): The user whose profile card is being generated.
background_image_path (str): Path to the background image.
Returns:
io.BytesIO: A BytesIO object containing the profile card image.
"""
# Profile card dimensions
card_width, card_height = 400, 200
text_color = (255, 255, 255)
progress_bar_color = (0, 255, 0)
outline_color = (255, 255, 255)
tint_color = (50, 50, 50)
transparency = 25
opacity = int(255 * transparency / 100)
# Load and resize the background image
background_image = Image.open(background_image_path).resize((card_width, card_height))
# Create a new image with the background
card_image = Image.new("RGBA", (card_width, card_height))
card_image.paste(background_image, (0, 0))
# Create a semi-transparent overlay
overlay = Image.new("RGBA", card_image.size, tint_color + (opacity,))
card_image = Image.alpha_composite(card_image, overlay)
draw = ImageDraw.Draw(card_image)
# Load fonts
font_path = "arial.ttf"
font = ImageFont.truetype(font_path, 20)
# Draw user info
draw.text((10, 10), f"User: {user.display_name}", fill=text_color, font=font)
user_info = get_user_data(user.id)
draw.text((10, 40), f"Level: {user_info['level']}", fill=text_color, font=font)
draw.text((10, 70), f"XP: {user_info['xp']}/{user_info['level'] * 100}", fill=text_color, font=font)
# Draw the progress bar
max_xp = user_info['level'] * 100
progress_ratio = user_info['xp'] / max_xp if max_xp > 0 else 0
progress_width = int(progress_ratio * 200)
draw.rectangle([(10, 100), (210, 120)], outline=outline_color, width=2)
draw.rectangle([(10, 100), (10 + progress_width, 120)], fill=progress_bar_color)
# Draw the user's profile picture
try:
profile_size = 96
margin = 30
profile_picture_data = await user.avatar.read()
profile_image = Image.open(io.BytesIO(profile_picture_data)).resize((profile_size, profile_size))
card_image.paste(profile_image, (card_width - profile_size - margin, margin), profile_image.convert("RGBA"))
except Exception as error:
print(f"Error fetching profile picture: {error}")
# Save image to a BytesIO object
image_bytes = io.BytesIO()
card_image.save(image_bytes, format="PNG")
image_bytes.seek(0)
return image_bytes
async def create_balance_card(user, credits, tokens, bucks):
# Card dimensions
width, height = 450, 250
card = Image.new("RGB", (width, height), (255, 255, 255))
draw = ImageDraw.Draw(card)
# Gradient Background
gradient_color_1 = (38, 0, 77) # Dark purple
gradient_color_2 = (128, 0, 128) # Purple
for y in range(height):
blend = y / height
r = int(gradient_color_1[0] * (1 - blend) + gradient_color_2[0] * blend)
g = int(gradient_color_1[1] * (1 - blend) + gradient_color_2[1] * blend)
b = int(gradient_color_1[2] * (1 - blend) + gradient_color_2[2] * blend)
draw.line([(0, y), (width, y)], fill=(r, g, b))
# Avatar with circular border
try:
avatar_bytes = await user.avatar.read()
avatar = Image.open(io.BytesIO(avatar_bytes)).resize((60, 60))
except Exception:
# Fallback: blank avatar
avatar = Image.new("RGBA", (60, 60), (128, 128, 128, 255))
mask = Image.new("L", avatar.size, 0)
ImageDraw.Draw(mask).ellipse((0, 0, 60, 60), fill=255)
avatar = ImageOps.fit(avatar, mask.size, centering=(0.5, 0.5))
avatar.putalpha(mask)
# Draw avatar circle background
avatar_bg = Image.new("RGBA", (70, 70), (255, 255, 255, 0))
draw_bg = ImageDraw.Draw(avatar_bg)
draw_bg.ellipse((0, 0, 70, 70), fill=(255, 255, 255, 100)) # Border color
card.paste(avatar_bg, (20, height - 110), avatar_bg)
card.paste(avatar, (25, height - 105), avatar)
# Fonts
title_font = ImageFont.load_default()
balance_font = ImageFont.load_default()
# Header Text
draw.text((110, 20), "tatsu.", font=title_font, fill="white")
draw.text((width - 90, 25), "Member", font=title_font, fill="white")
# Balance Background Box
balance_box = (100, 70, width - 20, 200)
draw.rounded_rectangle(balance_box, radius=15, fill=(255, 255, 255, 50))
# Balance Items
icon_x = 120
text_x = icon_x + 35
# Credits
draw.ellipse((icon_x, 90, icon_x + 25, 115), fill=(255, 215, 0)) # Coin icon color
draw.text((text_x, 90), f"{credits:,} Credits", font=balance_font, fill="black")
# Tokens
draw.ellipse(
(icon_x, 125, icon_x + 25, 150), fill=(0, 255, 127)
) # Token icon color
draw.text((text_x, 125), f"{tokens:,} Tokens", font=balance_font, fill="black")
# Bucks
draw.ellipse(
(icon_x, 160, icon_x + 25, 185), fill=(50, 205, 50)
) # Bucks icon color
draw.text((text_x, 160), f"{bucks:,} Guild Bucks", font=balance_font, fill="black")
# User tag
user_tag = f"{user.name}#{user.discriminator}"
draw.text((110, height - 35), user_tag, font=title_font, fill="white")
# Add Chip (Credit Card Style)
chip_width, chip_height = 50, 35
chip_x = width - 80
chip_y = 100
draw.rectangle(
[chip_x, chip_y, chip_x + chip_width, chip_y + chip_height],
fill=(192, 192, 192), # Light gray color for the chip
outline="white",
width=2,
)
# Chip lines for texture
line_spacing = 7
for i in range(1, chip_height // line_spacing):
y = chip_y + i * line_spacing
draw.line(
[(chip_x + 5, y), (chip_x + chip_width - 5, y)], fill="white", width=1
)
# Save to a BytesIO object to send on Discord
with io.BytesIO() as image_binary:
card.save(image_binary, "PNG")
image_binary.seek(0)
return discord.File(fp=image_binary, filename="balance_card.png")
@bot.event
async def on_ready():
if bot.user is not None:
print(f"Logged in as {bot.user.name}")
else:
print(f"Logged in")
@bot.command(name="profile")
async def profile(ctx):
"""Command to display user profile."""
user = ctx.author
user_info = get_user_data(user.id)
# Create profile card image
profile_image = create_profile_card(user, f"images/image_{randint(1,100)}.jpg")
# Send the image in the channel
await ctx.send(file=discord.File(await profile_image, "profile.png"))
@bot.command()
async def balance(ctx):
user = ctx.author
# Example balance data; replace with actual data retrieval
credits = 9493006
tokens = 58
bucks = 234328
file = await create_balance_card(user, credits, tokens, bucks)
await ctx.send(file=file)
# Example command to add XP for testing
@bot.command(name="addxp")
async def add_xp(ctx, amount: int):
"""Command to add XP for testing."""
user_info = get_user_data(ctx.author.id)
user_info["xp"] += amount
# Level up logic (example: every 100 XP earns a level)
if user_info["xp"] >= user_info["level"] * 100:
user_info["level"] += 1
user_info["xp"] = 0 # Reset XP after leveling up
await ctx.send(
f"Added {amount} XP to {ctx.author.mention}. Total XP: {user_info['xp']} | Level: {user_info['level']}"
)
if TOKEN is not None:
bot.run(TOKEN)
else:
print(f"{TOKEN=}")