Refactor bot server setup to use Waitress for production; fallback to Flask dev server for local development. Added timeout to HTTP requests in Fun and Test cogs. Improved error handling for missing environment variables. Enhanced secret key management in Flask app. Added request timeout configuration. Introduced new experimental features including user profile and balance cards, and a Tic-Tac-Toe game with Minimax AI. Addressed various database and security issues, and improved code quality across multiple files.

This commit is contained in:
2026-05-31 12:01:12 +00:00
parent be89cc3acd
commit 1b91cbcb2f
17 changed files with 284 additions and 61 deletions
+90
View File
@@ -0,0 +1,90 @@
ticket
jail (Mute)
### 1. **Games and Minigames**
- **Text-based RPG**: Allow users to battle monsters, level up, and collect loot.
- **Trivia**: Users can answer questions in various categories like movies, history, or general knowledge.
- **Hangman, Tic-Tac-Toe, and Word Games**: Classic games with visual feedback using emojis.
- **Coin Flip, Dice Roll, Rock-Paper-Scissors**: Simple yet fun games to settle bets.
- **Roulette / Slots**: Casino-style games where users can win (or lose!) virtual currency.
- **Adventure Games**: Story-driven choices where users select paths that influence outcomes.
### 2. **Economy System**
- **Currency**: Earn coins or points by chatting, completing tasks, or winning games.
- **Jobs**: Users can “work” jobs (e.g., doctor, hacker, miner) to earn currency.
- **Shops**: Buy items like badges, roles, or even upgrades for minigames.
- **Trading**: Allow users to trade items or currency with each other.
- **Bank System**: Deposit or withdraw money to earn interest or protect against theft.
### 3. **Social Features**
- **Profile Cards**: Show off user stats, badges, levels, and custom titles.
- **Reputation and Karma**: Users can give each other points for positive interactions.
- **Pet System**: Adopt virtual pets that can be trained, fed, and leveled up.
- **Marriage or Friendship**: Allow users to "marry" or form friendships with others.
- **User Stats Tracking**: Track things like messages sent, hours active, or games played.
### 4. **Leveling and Experience System**
- **XP for Messages**: Users earn XP based on their activity in the server.
- **Rank Roles**: Unlock new roles or titles as users level up.
- **Leaderboards**: Display top users for XP, messages, or currency.
- **Achievements**: Users earn badges or points for reaching milestones.
### 5. **Custom Commands**
- **Random Meme or Joke**: Pull memes or jokes from popular APIs.
- **Anime and Movie Information**: Pull information from APIs like MyAnimeList or IMDb.
- **Image Manipulation**: Create fun images, like a users avatar in various frames or situations.
- **Quote Generator**: Save memorable messages and recall them with a command.
- **Insults / Compliments**: Light-hearted fun with random funny (and friendly) insults or compliments.
### 6. **Music and Sound Effects**
- **Music Queueing and Playback**: Allow users to play music in voice channels.
- **Soundboard**: Play sound effects when commands are triggered (like applause or laughter).
- **DJ Commands**: Give advanced controls like bass boost, speed change, or filters.
### 7. **Notifications and Alerts**
- **Birthday Reminders**: Keep track of user birthdays and announce them in the server.
- **Reminders and Timers**: Users can set personal reminders that DM them later.
- **Daily Rewards**: Encourage users to come back daily for bonuses in your economy system.
- **Event Announcements**: Set up periodic reminders for server events.
### 8. **Mini Competitions and Challenges**
- **Daily / Weekly Challenges**: Daily puzzles, word games, or math problems.
- **Polls and Voting**: Let users vote on topics, and maybe even earn rewards for participating.
- **Custom Leaderboards**: Create leaderboards for almost anything—XP, currency, reputation points, etc.
- **Scavenger Hunts**: Have clues hidden in messages or require users to find hidden “items” around the server.
### 9. **Image and GIF Fun**
- **Avatar Fusion**: Merge two users avatars to create a funny, blended image.
- **Random Reaction GIFs**: Send GIFs for certain triggers or reactions, like “!slap” or “!hug.”
- **Custom Frames**: Place avatars inside funny or themed frames (like a “wanted” poster).
- **Color-Changing Roles**: Allow users to purchase role colors and dynamically update them.
### 10. **Learning and Educational Features**
- **Word of the Day**: Share new vocabulary daily.
- **Facts and Trivia**: Random facts or trivia about different topics like science or history.
- **Language Translation**: Translate text to other languages on request.
- **Quiz Mode**: Let users compete in quick quiz rounds.
### 11. **Community Interaction and Moderation**
- **Anonymous Confessions**: Allow users to send anonymous messages to a specific channel.
- **Polls and Feedback**: Use polls to gather community feedback.
- **Reaction Roles**: Set up roles that users can self-assign by reacting to messages.
- **Moderation Levels**: Give moderators points or levels for active contributions to the server.
### 12. **Miscellaneous Fun**
- **Fortune Telling / Magic 8-Ball**: Answer user questions with fun responses.
- **Horoscope**: Provide daily horoscopes based on the users zodiac sign.
- **Fake Virus Prank**: A harmless "fake virus" message that freaks out the user momentarily.
- **Custom Emote Creator**: Generate small emotes using user avatars or custom text.
- **Virtual Marketplace**: Allow users to set up small shops and sell items to each other.
### 13. **Interactive Roleplay Features**
- **Adventures and Storylines**: Create episodic adventures with options that affect the story.
- **Character Sheets**: Track character stats for users, which they can upgrade over time.
- **Combat System**: PvP or PvE combat system where users can fight with each other or NPCs.
- **Collectibles**: Users can collect items, weapons, pets, etc., and trade them.
### 14. **Seasonal Events and Rewards**
- **Seasonal Currency or Items**: Introduce limited-time items for holidays.
- **Server-Wide Events**: Organize events like scavenger hunts, trivia contests, or tournaments.
- **Exclusive Roles**: Give out special roles for participation in events.
+254
View File
@@ -0,0 +1,254 @@
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=}")
+62
View File
@@ -0,0 +1,62 @@
import random
class User:
def __init__(self, xp_formula, level_formula):
self.level = 1
self.xp = 0
self.xp_to_next_level = xp_formula(
self.level
) # XP needed to reach the next level
self.xp_formula = xp_formula
self.level_formula = level_formula
def earn_xp(self, amount):
self.xp += amount
print(f"You earned {amount} XP! Total XP: {self.xp}")
# Check for level up
while self.xp >= self.xp_to_next_level:
self.level_up()
def level_up(self):
self.xp -= self.xp_to_next_level
self.level += 1
self.xp_to_next_level = self.xp_formula(
self.level
) # Calculate next level requirement
print(
f"Congratulations! You've reached Level {self.level}! XP to next level: {self.xp_to_next_level}"
)
def simulate_xp_gain(user, num_messages, xp_per_message):
for _ in range(num_messages):
user.earn_xp(xp_per_message())
def main():
# Define your formulas here
def xp_formula(level):
return int(100 * (1.5 ** (level - 1))) # Example: 100, 150, 225, 338, ...
def random_xp_per_message():
return random.randint(1, 5) # Random XP between 1 and 10
user = User(xp_formula, random_xp_per_message)
while True:
num_messages = input("Enter the number of messages (or 'quit' to exit): ")
if num_messages.lower() == "quit":
break
if num_messages.isdigit():
num_messages = int(num_messages)
simulate_xp_gain(user, num_messages, random_xp_per_message)
else:
print("Please enter a valid number.")
if __name__ == "__main__":
main()
+42
View File
@@ -0,0 +1,42 @@
import requests
import os
from PIL import Image
from io import BytesIO
from random import shuffle
# Step 1: Fetch JSON data from the URL
url = "https://picsum.photos/v2/list?limit=1000"
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
data = response.json()
except requests.RequestException as e:
print(f"Failed to fetch image list: {e}")
data = []
# Step 2: Extract image download links
image_urls = [item["download_url"] for item in data]
shuffle(image_urls)
# Step 3: Create a directory to save images
os.makedirs("images", exist_ok=True)
# Desired size for resizing
desired_size = (450, 250)
# Step 4: Download and resize each image
for i, image_url in enumerate(image_urls):
try:
img_response = requests.get(image_url, timeout=10)
img_response.raise_for_status()
# Open image from response content
img = Image.open(BytesIO(img_response.content))
# Resize the image
img = img.resize(desired_size, Image.Resampling.LANCZOS)
# Save the resized image
img.save(f"images/image_{17}.jpg")
print(f"Downloaded and resized: image_{17}.jpg")
else:
print(f"Failed to download image from {image_url}")
if input() !="":
break
+30
View File
@@ -0,0 +1,30 @@
from minimax import aiO, aiX, Terminal, Value
# from player import player
def display_board(board):
print('-------------')
for row in [board[i:i + 3] for i in range(0, 9, 3)]:
print(f'| {row[0]} | {row[1]} | {row[2]} |')
print('-------------')
def main():
results = []
while True:
board = [" " for _ in range(9)]
# board = ['O', 'X', 'O','O', 'X', 'X',' ', ' ', 'X']
display_board(board)
while True:
board[aiX(board)] = "X"
display_board(board)
if Terminal(board):
break
board[aiO(board)] = "O"
display_board(board)
if Terminal(board):
break
results.append(Value(board))
if __name__ == '__main__':
main()
+93
View File
@@ -0,0 +1,93 @@
import random
win_states = [
(0, 1, 2), (3, 4, 5), (6, 7, 8), # horizontal
(0, 3, 6), (1, 4, 7), (2, 5, 8), # vertical
(0, 4, 8), (2, 4, 6) # diagonal
]
def Terminal(state: list):
for win_state in win_states:
if all(state[i] == "O" for i in win_state) or all(state[i] == "X" for i in win_state):
return True
if state.count(" ") == 0:
return True
else:
return False
def Value(state):
for win_state in win_states:
if all(state[i] == "O" for i in win_state):
return -1
elif all(state[i] == "X" for i in win_state):
return 1
if state.count(" ") == 0:
return 0
def Player(state: list):
if state.count("X") == state.count("O"):
return "X"
else:
return "O"
def Actions(state):
indexes = []
for i in range(9):
if state[i] == " ":
indexes.append(i)
return indexes
def Result(state, index, player):
state[index] = player
return state
def Minimax(state):
if Terminal(state):
return Value(state)
if Player(state) == "X":
value = -9999
for a in Actions(state):
value = max(value, Minimax(Result(state.copy(), a, "X")))
return value
elif Player(state) == "O":
value = 9999
for a in Actions(state):
value = min(value, Minimax(Result(state.copy(), a, "O")))
return value
def aiO(state):
best_score = float('inf')
best_move = None
for move in Actions(state):
state[move] = 'O'
score = Minimax(state)
state[move] = ' '
if score < best_score:
best_score = score
best_move = move
return best_move
def aiX(state):
best_score = float('-inf')
best_move = None
for move in Actions(state):
state[move] = 'X'
score = Minimax(state)
state[move] = ' '
if score > best_score:
best_score = score
best_move = move
return best_move
def aiRandom(state):
return random.choice(Actions(state))
+10
View File
@@ -0,0 +1,10 @@
from minimax import Actions
def player(state):
actions = Actions(state)
choice = -1
while choice not in actions:
choice = int(input("Pick a spot between 1-9.> ")) - 1
return choice
+68
View File
@@ -0,0 +1,68 @@
from random import randint
"""def player(start_board):
player_choice = input("Pick a place: ")
if start_board.count(player_choice) == 1:
start_board[start_board.index(player_choice)] = "X"
else:
player(start_board)"""
def player(start_board):
com_choice = str(randint(1, 9))
if start_board.count(com_choice) == 1:
start_board[start_board.index(com_choice)] = "X"
else:
player(start_board)
def com(start_board):
com_choice = str(randint(1, 9))
if start_board.count(com_choice) == 1:
start_board[start_board.index(com_choice)] = "O"
else:
com(start_board)
def print_board(s_board):
board = "".join(s_board)
print(board)
def check(s_b):
if s_b[0] == s_b[2] == s_b[4] == 'X' or s_b[6] == s_b[8] == s_b[10] == 'X' or s_b[12] == s_b[14] == s_b[16] == 'X' \
or s_b[0] == s_b[6] == s_b[12] == 'X' or s_b[2] == s_b[8] == s_b[14] == 'X' or s_b[4] == s_b[10] == s_b[16] \
== 'X' or s_b[0] == s_b[8] == s_b[16] == 'X' or s_b[4] == s_b[8] == s_b[12] == 'X':
print("you won")
main()
elif s_b[0] == s_b[2] == s_b[4] == 'O' or s_b[6] == s_b[8] == s_b[10] == 'O' or s_b[12] == s_b[14] == s_b[16] == 'O' \
or s_b[0] == s_b[6] == s_b[12] == 'O' or s_b[2] == s_b[8] == s_b[14] == 'O' or s_b[4] == s_b[10] == s_b[16] \
== 'O' or s_b[0] == s_b[8] == s_b[16] == 'O' or s_b[4] == s_b[8] == s_b[12] == 'O':
print('computer won')
print_board(s_b)
main()
def main():
# main program
start_board = ["1", "|", "2", "|", "3", "\n",
"4", "|", "5", "|", "6", "\n",
"7", "|", "8", "|", "9", "\n", ]
print_board(start_board)
while 1:
try:
player(start_board)
check(start_board)
com(start_board)
check(start_board)
print_board(start_board)
except RecursionError:
print_board(start_board)
print("draw")
main()
if __name__ == "__main__":
main()