Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import discord
- from discord.ext import commands, tasks
- from datetime import datetime, timedelta
- import asyncio
- import random
- import json
- import os
- from typing import Optional, Union, List, Dict
- # Initialize bot with all required intents
- intents = discord.Intents.default()
- intents.members = True
- intents.message_content = True
- bot = commands.Bot(command_prefix=".", intents=intents, case_insensitive=True)
- # File to store data
- DATA_FILE = "relationships.json"
- # Relationship types
- RELATIONSHIP_TYPES = {
- "dating": "Dating",
- "married": "Married",
- "engaged": "Engaged",
- "friends": "Friends with Benefits",
- "complicated": "It's Complicated"
- }
- # Load data from file
- def load_data():
- if os.path.exists(DATA_FILE):
- with open(DATA_FILE, 'r') as f:
- return json.load(f)
- return {"relationships": [], "proposals": [], "cooldowns": {}}
- # Save data to file
- def save_data(data):
- with open(DATA_FILE, 'w') as f:
- json.dump(data, f, indent=4)
- # Relationship class
- class Relationship:
- def __init__(self, user1: int, user2: int, rel_type: str, since: str):
- self.user1 = user1
- self.user2 = user2
- self.type = rel_type
- self.since = since
- def to_dict(self):
- return {
- "user1": self.user1,
- "user2": self.user2,
- "type": self.type,
- "since": self.since
- }
- @classmethod
- def from_dict(cls, data: dict):
- return cls(data["user1"], data["user2"], data["type"], data["since"])
- def involves(self, user_id: int) -> bool:
- return self.user1 == user_id or self.user2 == user_id
- def get_partner(self, user_id: int) -> int:
- if self.user1 == user_id:
- return self.user2
- return self.user1
- def __contains__(self, user_id: int) -> bool:
- return self.involves(user_id)
- # Proposal class
- class Proposal:
- def __init__(self, from_user: int, to_user: int, rel_type: str, expires: str):
- self.from_user = from_user
- self.to_user = to_user
- self.type = rel_type
- self.expires = expires
- def to_dict(self):
- return {
- "from_user": self.from_user,
- "to_user": self.to_user,
- "type": self.type,
- "expires": self.expires
- }
- @classmethod
- def from_dict(cls, data: dict):
- return cls(data["from_user"], data["to_user"], data["type"], data["expires"])
- def is_expired(self) -> bool:
- return datetime.fromisoformat(self.expires) < datetime.utcnow()
- # Cooldown tracker
- class CooldownTracker:
- def __init__(self, user_id: int, command: str, expires: str):
- self.user_id = user_id
- self.command = command
- self.expires = expires
- def to_dict(self):
- return {
- "user_id": self.user_id,
- "command": self.command,
- "expires": self.expires
- }
- @classmethod
- def from_dict(cls, data: dict):
- return cls(data["user_id"], data["command"], data["expires"])
- def is_expired(self) -> bool:
- return datetime.fromisoformat(self.expires) < datetime.utcnow()
- # Initialize data
- bot.relationships = []
- bot.proposals = []
- bot.cooldowns = {}
- # Load data on startup
- @bot.event
- async def on_ready():
- print(f'Logged in as {bot.user.name} ({bot.user.id})')
- data = load_data()
- bot.relationships = [Relationship.from_dict(r) for r in data.get("relationships", [])]
- bot.proposals = [Proposal.from_dict(p) for p in data.get("proposals", [])]
- bot.cooldowns = {k: CooldownTracker.from_dict(v) for k, v in data.get("cooldowns", {}).items()}
- # Start background tasks
- cleanup_expired_data.start()
- print('Bot is ready!')
- # Background task to clean up expired data
- @tasks.loop(minutes=5)
- async def cleanup_expired_data():
- # Clean expired proposals
- bot.proposals = [p for p in bot.proposals if not p.is_expired()]
- # Clean expired cooldowns
- bot.cooldowns = {k: v for k, v in bot.cooldowns.items() if not v.is_expired()}
- save_data({
- "relationships": [r.to_dict() for r in bot.relationships],
- "proposals": [p.to_dict() for p in bot.proposals],
- "cooldowns": {k: v.to_dict() for k, v in bot.cooldowns.items()}
- })
- # Check if user is on cooldown for a command
- def check_cooldown(user_id: int, command: str, cooldown_seconds: int) -> Optional[str]:
- key = f"{user_id}_{command}"
- if key in bot.cooldowns and not bot.cooldowns[key].is_expired():
- remaining = (datetime.fromisoformat(bot.cooldowns[key].expires) - datetime.utcnow()).total_seconds()
- return f"You're on cooldown for this command. Try again in {int(remaining)} seconds."
- # Set new cooldown
- expires = (datetime.utcnow() + timedelta(seconds=cooldown_seconds)).isoformat()
- bot.cooldowns[key] = CooldownTracker(user_id, command, expires)
- return None
- # Get user's current relationship
- def get_relationship(user_id: int) -> Optional[Relationship]:
- for rel in bot.relationships:
- if rel.involves(user_id):
- return rel
- return None
- # Check if proposal exists between users
- def get_proposal(from_user: int, to_user: int) -> Optional[Proposal]:
- for prop in bot.proposals:
- if prop.from_user == from_user and prop.to_user == to_user:
- return prop
- return None
- # Relationship command group
- @bot.group(name="relationship", aliases=["rel", "r"], invoke_without_command=True)
- async def relationship(ctx):
- """Main relationship command group"""
- await ctx.send_help(ctx.command)
- @relationship.command(name="status", aliases=["info", "check"])
- async def relationship_status(ctx, user: Optional[discord.Member] = None):
- """Check your or someone else's relationship status"""
- target = user or ctx.author
- rel = get_relationship(target.id)
- if rel:
- partner = await bot.fetch_user(rel.get_partner(target.id))
- rel_type = RELATIONSHIP_TYPES.get(rel.type, rel.type.capitalize())
- embed = discord.Embed(
- title=f"{target.display_name}'s Relationship Status",
- description=f"❤️ {target.display_name} is {rel_type.lower()} with {partner.mention} since {rel.since}",
- color=discord.Color.pink()
- )
- await ctx.send(embed=embed)
- else:
- embed = discord.Embed(
- title=f"{target.display_name}'s Relationship Status",
- description="💔 Currently single",
- color=discord.Color.light_grey()
- )
- await ctx.send(embed=embed)
- @relationship.command(name="propose", aliases=["ask", "request"])
- async def relationship_propose(ctx, user: discord.Member, rel_type: str = "dating"):
- """Propose a relationship to someone"""
- # Validation checks
- if user == ctx.author:
- return await ctx.send("You can't propose to yourself!")
- if user.bot:
- return await ctx.send("You can't propose to a bot!")
- if rel_type.lower() not in RELATIONSHIP_TYPES:
- valid_types = ", ".join(RELATIONSHIP_TYPES.keys())
- return await ctx.send(f"Invalid relationship type. Valid types are: {valid_types}")
- # Check existing relationships
- if get_relationship(ctx.author.id):
- return await ctx.send("You're already in a relationship!")
- if get_relationship(user.id):
- return await ctx.send(f"{user.display_name} is already in a relationship!")
- # Check cooldown
- cooldown_msg = check_cooldown(ctx.author.id, "propose", 3600) # 1 hour cooldown
- if cooldown_msg:
- return await ctx.send(cooldown_msg)
- # Check for existing proposal
- if get_proposal(ctx.author.id, user.id):
- return await ctx.send("You already have a pending proposal with this user!")
- # Create proposal
- expires = (datetime.utcnow() + timedelta(days=7)).isoformat() # 7 days to respond
- proposal = Proposal(ctx.author.id, user.id, rel_type.lower(), expires)
- bot.proposals.append(proposal)
- save_data({
- "relationships": [r.to_dict() for r in bot.relationships],
- "proposals": [p.to_dict() for p in bot.proposals],
- "cooldowns": {k: v.to_dict() for k, v in bot.cooldowns.items()}
- })
- # Send proposal message
- rel_type_display = RELATIONSHIP_TYPES.get(rel_type.lower(), rel_type.capitalize())
- embed = discord.Embed(
- title="💌 Relationship Proposal",
- description=f"{ctx.author.mention} has proposed to {user.mention} to be {rel_type_display.lower()}!",
- color=discord.Color.pink()
- )
- embed.add_field(name="To accept", value=f"Type `-relationship/rel/r accept {ctx.author.name}`")
- embed.add_field(name="To reject", value=f"Type `-relationship/rel/r reject {ctx.author.name}`")
- embed.set_footer(text="This proposal expires in 7 days")
- await ctx.send(embed=embed)
- @relationship.command(name="accept", aliases=["yes"])
- async def relationship_accept(ctx, user: discord.Member):
- """Accept a relationship proposal"""
- proposal = get_proposal(user.id, ctx.author.id)
- if not proposal:
- return await ctx.send(f"No pending proposal from {user.display_name}!")
- # Remove the proposal
- bot.proposals.remove(proposal)
- # Create the relationship
- since = datetime.utcnow().isoformat()
- relationship = Relationship(proposal.from_user, proposal.to_user, proposal.type, since)
- bot.relationships.append(relationship)
- save_data({
- "relationships": [r.to_dict() for r in bot.relationships],
- "proposals": [p.to_dict() for p in bot.proposals],
- "cooldowns": {k: v.to_dict() for k, v in bot.cooldowns.items()}
- })
- # Send acceptance message
- rel_type_display = RELATIONSHIP_TYPES.get(proposal.type, proposal.type.capitalize())
- embed = discord.Embed(
- title="💖 Relationship Formed",
- description=f"{ctx.author.mention} has accepted {user.mention}'s proposal!",
- color=discord.Color.green()
- )
- embed.add_field(name="Relationship Type", value=rel_type_display)
- embed.add_field(name="Since", value=since)
- await ctx.send(embed=embed)
- @relationship.command(name="reject", aliases=["no", "decline"])
- async def relationship_reject(ctx, user: discord.Member):
- """Reject a relationship proposal"""
- proposal = get_proposal(user.id, ctx.author.id)
- if not proposal:
- return await ctx.send(f"No pending proposal from {user.display_name}!")
- # Remove the proposal
- bot.proposals.remove(proposal)
- save_data({
- "relationships": [r.to_dict() for r in bot.relationships],
- "proposals": [p.to_dict() for p in bot.proposals],
- "cooldowns": {k: v.to_dict() for k, v in bot.cooldowns.items()}
- })
- # Send rejection message
- embed = discord.Embed(
- title="💔 Proposal Rejected",
- description=f"{ctx.author.mention} has rejected {user.mention}'s proposal.",
- color=discord.Color.red()
- )
- await ctx.send(embed=embed)
- @relationship.command(name="breakup", aliases=["divorce", "end"])
- async def relationship_breakup(ctx):
- """End your current relationship"""
- rel = get_relationship(ctx.author.id)
- if not rel:
- return await ctx.send("You're not in a relationship!")
- # Get partner
- partner_id = rel.get_partner(ctx.author.id)
- partner = await bot.fetch_user(partner_id)
- # Remove relationship
- bot.relationships.remove(rel)
- save_data({
- "relationships": [r.to_dict() for r in bot.relationships],
- "proposals": [p.to_dict() for p in bot.proposals],
- "cooldowns": {k: v.to_dict() for k, v in bot.cooldowns.items()}
- })
- # Send breakup message
- embed = discord.Embed(
- title="💔 Relationship Ended",
- description=f"{ctx.author.mention} has ended their relationship with {partner.mention}.",
- color=discord.Color.dark_red()
- )
- await ctx.send(embed=embed)
- @relationship.command(name="upgrade", aliases=["promote"])
- async def relationship_upgrade(ctx, new_type: str):
- """Upgrade your relationship type (e.g., from dating to married)"""
- rel = get_relationship(ctx.author.id)
- if not rel:
- return await ctx.send("You're not in a relationship!")
- if new_type.lower() not in RELATIONSHIP_TYPES:
- valid_types = ", ".join(RELATIONSHIP_TYPES.keys())
- return await ctx.send(f"Invalid relationship type. Valid types are: {valid_types}")
- # Check cooldown
- cooldown_msg = check_cooldown(ctx.author.id, "upgrade", 86400) # 24 hour cooldown
- if cooldown_msg:
- return await ctx.send(cooldown_msg)
- # Get partner
- partner_id = rel.get_partner(ctx.author.id)
- # Create upgrade proposal
- expires = (datetime.utcnow() + timedelta(days=7)).isoformat() # 7 days to respond
- proposal = Proposal(ctx.author.id, partner_id, new_type.lower(), expires)
- bot.proposals.append(proposal)
- save_data({
- "relationships": [r.to_dict() for r in bot.relationships],
- "proposals": [p.to_dict() for p in bot.proposals],
- "cooldowns": {k: v.to_dict() for k, v in bot.cooldowns.items()}
- })
- # Send upgrade proposal
- partner = await bot.fetch_user(partner_id)
- new_type_display = RELATIONSHIP_TYPES.get(new_type.lower(), new_type.capitalize())
- current_type_display = RELATIONSHIP_TYPES.get(rel.type, rel.type.capitalize())
- embed = discord.Embed(
- title="💍 Relationship Upgrade Proposal",
- description=f"{ctx.author.mention} wants to upgrade their relationship with {partner.mention} from {current_type_display} to {new_type_display}!",
- color=discord.Color.gold()
- )
- embed.add_field(name="To accept", value=f"Type `-relationship/rel/r accept_upgrade {ctx.author.name}`")
- embed.add_field(name="To reject", value=f"Type `-relationship/rel/r reject_upgrade {ctx.author.name}`")
- embed.set_footer(text="This proposal expires in 7 days")
- await ctx.send(embed=embed)
- @relationship.command(name="accept_upgrade")
- async def relationship_accept_upgrade(ctx, user: discord.Member):
- """Accept a relationship upgrade proposal"""
- proposal = get_proposal(user.id, ctx.author.id)
- if not proposal:
- return await ctx.send(f"No pending upgrade proposal from {user.display_name}!")
- # Find and update the existing relationship
- rel = get_relationship(ctx.author.id)
- if not rel:
- return await ctx.send("You're not in a relationship!")
- rel.type = proposal.type
- bot.proposals.remove(proposal)
- save_data({
- "relationships": [r.to_dict() for r in bot.relationships],
- "proposals": [p.to_dict() for p in bot.proposals],
- "cooldowns": {k: v.to_dict() for k, v in bot.cooldowns.items()}
- })
- # Send acceptance message
- new_type_display = RELATIONSHIP_TYPES.get(proposal.type, proposal.type.capitalize())
- embed = discord.Embed(
- title="💖 Relationship Upgraded",
- description=f"{ctx.author.mention} has accepted {user.mention}'s relationship upgrade proposal!",
- color=discord.Color.green()
- )
- embed.add_field(name="New Relationship Type", value=new_type_display)
- await ctx.send(embed=embed)
- @relationship.command(name="reject_upgrade")
- async def relationship_reject_upgrade(ctx, user: discord.Member):
- """Reject a relationship upgrade proposal"""
- proposal = get_proposal(user.id, ctx.author.id)
- if not proposal:
- return await ctx.send(f"No pending upgrade proposal from {user.display_name}!")
- # Remove the proposal
- bot.proposals.remove(proposal)
- save_data({
- "relationships": [r.to_dict() for r in bot.relationships],
- "proposals": [p.to_dict() for p in bot.proposals],
- "cooldowns": {k: v.to_dict() for k, v in bot.cooldowns.items()}
- })
- # Send rejection message
- embed = discord.Embed(
- title="💔 Upgrade Rejected",
- description=f"{ctx.author.mention} has rejected {user.mention}'s relationship upgrade proposal.",
- color=discord.Color.red()
- )
- await ctx.send(embed=embed)
- @relationship.command(name="list", aliases=["all"])
- async def relationship_list(ctx):
- """List all current relationships in the server"""
- # Get all relationships involving server members
- server_member_ids = {m.id for m in ctx.guild.members}
- server_relationships = []
- for rel in bot.relationships:
- if rel.user1 in server_member_ids and rel.user2 in server_member_ids:
- server_relationships.append(rel)
- if not server_relationships:
- return await ctx.send("No relationships in this server yet!")
- # Create paginated embed
- rel_type_counts = {}
- for rel in server_relationships:
- rel_type_counts[rel.type] = rel_type_counts.get(rel.type, 0) + 1
- embed = discord.Embed(
- title=f"💑 Relationships in {ctx.guild.name}",
- color=discord.Color.pink()
- )
- # Add relationship type counts
- type_lines = []
- for rel_type, count in rel_type_counts.items():
- type_display = RELATIONSHIP_TYPES.get(rel_type, rel_type.capitalize())
- type_lines.append(f"{type_display}: {count}")
- embed.add_field(name="Relationship Types", value="\n".join(type_lines), inline=False)
- # Add some sample relationships (first 5)
- sample_lines = []
- for rel in server_relationships[:5]:
- user1 = await bot.fetch_user(rel.user1)
- user2 = await bot.fetch_user(rel.user2)
- type_display = RELATIONSHIP_TYPES.get(rel.type, rel.type.capitalize())
- sample_lines.append(f"{user1.name} ❤️ {user2.name} ({type_display})")
- if len(server_relationships) > 5:
- sample_lines.append(f"...and {len(server_relationships) - 5} more")
- embed.add_field(name="Sample Relationships", value="\n".join(sample_lines), inline=False)
- embed.set_footer(text=f"Total relationships: {len(server_relationships)}")
- await ctx.send(embed=embed)
- @relationship.command(name="stats", aliases=["leaderboard"])
- async def relationship_stats(ctx):
- """Show relationship statistics and leaderboard"""
- # Get all relationships involving server members
- server_member_ids = {m.id for m in ctx.guild.members}
- server_relationships = []
- for rel in bot.relationships:
- if rel.user1 in server_member_ids and rel.user2 in server_member_ids:
- server_relationships.append(rel)
- if not server_relationships:
- return await ctx.send("No relationships in this server yet!")
- # Count relationships per user
- relationship_counts = {}
- for rel in server_relationships:
- relationship_counts[rel.user1] = relationship_counts.get(rel.user1, 0) + 1
- relationship_counts[rel.user2] = relationship_counts.get(rel.user2, 0) + 1
- # Sort by count (descending)
- sorted_users = sorted(relationship_counts.items(), key=lambda x: x[1], reverse=True)
- # Create embed
- embed = discord.Embed(
- title=f"💑 Relationship Statistics for {ctx.guild.name}",
- color=discord.Color.pink()
- )
- # Add total count
- embed.add_field(
- name="Total Relationships",
- value=str(len(server_relationships)),
- inline=False
- )
- # Add leaderboard (top 5)
- leaderboard_lines = []
- for i, (user_id, count) in enumerate(sorted_users[:5], 1):
- user = await bot.fetch_user(user_id)
- leaderboard_lines.append(f"{i}. {user.name}: {count} relationship{'s' if count > 1 else ''}")
- embed.add_field(
- name="Relationship Leaderboard",
- value="\n".join(leaderboard_lines),
- inline=False
- )
- # Add relationship type distribution
- type_counts = {}
- for rel in server_relationships:
- type_counts[rel.type] = type_counts.get(rel.type, 0) + 1
- type_lines = []
- for rel_type, count in type_counts.items():
- type_display = RELATIONSHIP_TYPES.get(rel_type, rel_type.capitalize())
- type_lines.append(f"{type_display}: {count}")
- embed.add_field(
- name="Relationship Types",
- value="\n".join(type_lines),
- inline=False
- )
- await ctx.send(embed=embed)
- @relationship.command(name="random", aliases=["matchmake"])
- async def relationship_random(ctx):
- """Get a random match suggestion"""
- # Check cooldown
- cooldown_msg = check_cooldown(ctx.author.id, "random", 1200) # 24 hour cooldown
- if cooldown_msg:
- return await ctx.send(cooldown_msg)
- # Get all single members in the server
- single_members = []
- for member in ctx.guild.members:
- if member.bot or member == ctx.author:
- continue
- if not get_relationship(member.id):
- single_members.append(member)
- if not single_members:
- return await ctx.send("No available singles in this server!")
- # Select a random match
- match = random.choice(single_members)
- # Calculate compatibility score (just for fun)
- compatibility = random.randint(50, 100)
- # Send match suggestion
- embed = discord.Embed(
- title="💘 Random Match Suggestion",
- description=f"Your random match is {match.mention}!",
- color=discord.Color.pink()
- )
- embed.add_field(name="Compatibility", value=f"{compatibility}%")
- embed.add_field(name="Next Steps", value=f"Type `-relationship/rel/r propose {match.name}` to start a relationship!")
- embed.set_footer(text="You can get another suggestion in 20 minutes")
- await ctx.send(embed=embed)
- # Error handling
- @relationship.error
- async def relationship_error(ctx, error):
- if isinstance(error, commands.MissingRequiredArgument):
- await ctx.send("Missing required argument! Please check the command usage.")
- elif isinstance(error, commands.BadArgument):
- await ctx.send("Invalid argument! Please check the command usage.")
- elif isinstance(error, commands.CommandInvokeError):
- await ctx.send(f"An error occurred: {str(error.original)}")
- else:
- await ctx.send("An unknown error occurred!")
- # Run the bot
- if __name__ == "__main__":
- bot.run("bot token")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement