210 lines
9.9 KiB
Python
210 lines
9.9 KiB
Python
import discord
|
|
from discord import app_commands
|
|
from discord.ext import commands
|
|
import pymongo
|
|
from bson.objectid import ObjectId
|
|
import logging
|
|
from config import DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET, DISCORD_REDIRECT_URI, DISCORD_BOT_TOKEN, DISCORD_GUILD_ID, ACCOUNT_LINKED_ROLE_ID, DISCORD_WEBHOOK_URL
|
|
|
|
# Set up logging
|
|
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s:%(levelname)s:%(name)s: %(message)s')
|
|
logger = logging.getLogger('resonance_rumble_bot')
|
|
|
|
|
|
# MongoDB setup
|
|
client = pymongo.MongoClient('mongodb://localhost:27017/')
|
|
db = client['resonance_rumble']
|
|
classes_collection = db['classes']
|
|
weapons_collection = db['weapons']
|
|
skins_collection = db['skins']
|
|
users_collection = db['users']
|
|
achievements_collection = db['achievements']
|
|
|
|
|
|
intents = discord.Intents.all()
|
|
intents.message_content = True #v2
|
|
intents.members = True
|
|
bot = commands.Bot(command_prefix='!', intents=intents)
|
|
|
|
LINKED_ROLE_NAME = "Account-linked"
|
|
|
|
|
|
@bot.event
|
|
async def on_ready():
|
|
print(f'{bot.user} has connected to Discord!')
|
|
try:
|
|
synced = await bot.tree.sync()
|
|
print(f"Synced {len(synced)} command(s)")
|
|
except Exception as e:
|
|
print(f"Error syncing commands: {e}")
|
|
|
|
@bot.event
|
|
async def on_member_join(member):
|
|
logger.info(f"New member joined: {member.name} (ID: {member.id})")
|
|
role = discord.utils.get(member.guild.roles, name="Member")
|
|
if role:
|
|
try:
|
|
await member.add_roles(role)
|
|
logger.info(f"Successfully added Member role to {member.name}")
|
|
except discord.Forbidden:
|
|
logger.error(f"Failed to add Member role to {member.name}: Bot doesn't have permission")
|
|
except discord.HTTPException as e:
|
|
logger.error(f"Failed to add Member role to {member.name}: {str(e)}")
|
|
else:
|
|
logger.error(f"'Member' role not found in the server")
|
|
|
|
# Send a welcome DM to the new member
|
|
try:
|
|
welcome_message = (
|
|
f"Welcome to the Resonance Rumble Discord server, {member.name}!\n\n"
|
|
"We're excited to have you join our community. Here are a few things to get you started:\n"
|
|
"• Come play the game here https://resonancerumble.com\n"
|
|
"• Introduce yourself in the #introductions channel.\n"
|
|
"• If you need any help, feel free to ask in the #help channel.\n\n"
|
|
"Don't forget to link your Discord account in the game to unlock an exclusive class!\n"
|
|
"Have fun and happy rumbling!"
|
|
)
|
|
await member.send(welcome_message)
|
|
logger.info(f"Sent welcome DM to {member.name}")
|
|
except discord.Forbidden:
|
|
logger.error(f"Failed to send welcome DM to {member.name}: User has DMs disabled")
|
|
except discord.HTTPException as e:
|
|
logger.error(f"Failed to send welcome DM to {member.name}: {str(e)}")
|
|
|
|
async def class_autocomplete(interaction: discord.Interaction, current: str) -> list[app_commands.Choice[str]]:
|
|
classes = classes_collection.find({}, {"name": 1})
|
|
return [
|
|
app_commands.Choice(name=class_doc["name"], value=class_doc["name"])
|
|
for class_doc in classes if current.lower() in class_doc["name"].lower()
|
|
][:25] # Discord limits to 25 choices
|
|
|
|
@bot.tree.command(name="class_info", description="Get information about a game class")
|
|
@app_commands.autocomplete(class_name=class_autocomplete)
|
|
async def class_info(interaction: discord.Interaction, class_name: str):
|
|
class_data = classes_collection.find_one({"name": class_name})
|
|
if class_data:
|
|
embed = discord.Embed(title=f"{class_name} Class Info", color=0x00ff00)
|
|
embed.add_field(name="Health", value=class_data['base_attributes']['health'], inline=True)
|
|
embed.add_field(name="Speed", value=class_data['base_attributes']['speed'], inline=True)
|
|
embed.add_field(name="Damage Multiplier", value=class_data['base_attributes']['damage_multiplier'], inline=True)
|
|
embed.add_field(name="Weapon Limit", value=class_data['weapon_limit'], inline=True)
|
|
await interaction.response.send_message(embed=embed)
|
|
else:
|
|
await interaction.response.send_message(f"Class '{class_name}' not found.", ephemeral=True)
|
|
|
|
async def weapon_autocomplete(interaction: discord.Interaction, current: str) -> list[app_commands.Choice[str]]:
|
|
weapons = weapons_collection.find({}, {"name": 1})
|
|
return [
|
|
app_commands.Choice(name=weapon["name"], value=weapon["name"])
|
|
for weapon in weapons if current.lower() in weapon["name"].lower()
|
|
][:25]
|
|
|
|
@bot.tree.command(name="weapon_info", description="Get information about a weapon")
|
|
@app_commands.autocomplete(weapon_name=weapon_autocomplete)
|
|
async def weapon_info(interaction: discord.Interaction, weapon_name: str):
|
|
weapon_data = weapons_collection.find_one({"name": weapon_name})
|
|
if weapon_data:
|
|
embed = discord.Embed(title=f"{weapon_name} Weapon Info", color=0x0000ff)
|
|
embed.add_field(name="Type", value=weapon_data['weapon_type'], inline=True)
|
|
embed.add_field(name="Damage", value=weapon_data['base_attributes']['damage'], inline=True)
|
|
embed.add_field(name="Fire Rate", value=weapon_data['base_attributes']['fire_rate'], inline=True)
|
|
embed.add_field(name="Bullet Speed", value=weapon_data['base_attributes']['bullet_speed'], inline=True)
|
|
await interaction.response.send_message(embed=embed)
|
|
else:
|
|
await interaction.response.send_message(f"Weapon '{weapon_name}' not found.", ephemeral=True)
|
|
|
|
async def skin_autocomplete(interaction: discord.Interaction, current: str) -> list[app_commands.Choice[str]]:
|
|
skins = skins_collection.find({}, {"name": 1})
|
|
return [
|
|
app_commands.Choice(name=skin["name"], value=skin["name"])
|
|
for skin in skins if current.lower() in skin["name"].lower()
|
|
][:25]
|
|
|
|
@bot.tree.command(name="skin_info", description="Get information about a skin")
|
|
@app_commands.autocomplete(skin_name=skin_autocomplete)
|
|
async def skin_info(interaction: discord.Interaction, skin_name: str):
|
|
skin_data = skins_collection.find_one({"name": skin_name})
|
|
if skin_data:
|
|
embed = discord.Embed(title=f"{skin_name} Skin Info", color=0xff00ff)
|
|
embed.add_field(name="Type", value=skin_data['type'], inline=True)
|
|
embed.add_field(name="Rarity", value=skin_data['rarity'], inline=True)
|
|
embed.add_field(name="Effect", value=skin_data['effect'], inline=True)
|
|
await interaction.response.send_message(embed=embed)
|
|
else:
|
|
await interaction.response.send_message(f"Skin '{skin_name}' not found.", ephemeral=True)
|
|
|
|
@bot.tree.command(name="leaderboard", description="Show the top players")
|
|
@app_commands.describe(category="The category for the leaderboard")
|
|
@app_commands.choices(category=[
|
|
app_commands.Choice(name="Level", value="level"),
|
|
app_commands.Choice(name="Synth Coins", value="synth_coins"),
|
|
app_commands.Choice(name="Experience", value="experience")
|
|
])
|
|
async def leaderboard(interaction: discord.Interaction, category: app_commands.Choice[str]):
|
|
top_players = list(db.users.find().sort(category.value, -1).limit(10))
|
|
|
|
embed = discord.Embed(title=f"Top 10 Players - {category.name}", color=0xffa500)
|
|
for i, player in enumerate(top_players, 1):
|
|
embed.add_field(name=f"{i}. {player['username']}", value=f"{category.name}: {player.get(category.value, 0)}", inline=False)
|
|
|
|
await interaction.response.send_message(embed=embed)
|
|
|
|
|
|
@bot.tree.command(name="check_permissions", description="Check bot's permissions")
|
|
@commands.has_permissions(administrator=True)
|
|
async def check_permissions(interaction: discord.Interaction):
|
|
bot_member = interaction.guild.get_member(bot.user.id)
|
|
permissions = bot_member.guild_permissions
|
|
|
|
embed = discord.Embed(title="Bot Permissions", color=0x00ff00)
|
|
crucial_perms = ["manage_roles", "view_channel", "send_messages", "embed_links"]
|
|
|
|
for perm, value in permissions:
|
|
if perm in crucial_perms:
|
|
embed.add_field(name=perm.replace('_', ' ').title(), value=str(value), inline=False)
|
|
|
|
await interaction.response.send_message(embed=embed)
|
|
|
|
@bot.tree.command(name="link_status", description="Check your Resonance Rumble account link status")
|
|
async def link_status(interaction: discord.Interaction):
|
|
user = users_collection.find_one({'discord_id': str(interaction.user.id)})
|
|
if user:
|
|
await add_linked_role(interaction.user)
|
|
await interaction.response.send_message(f"Your Discord account is linked to the Resonance Rumble account: {user['username']}")
|
|
else:
|
|
await interaction.response.send_message("Your Discord account is not linked to any Resonance Rumble account. Use the in-game menu to link your account.")
|
|
|
|
async def add_linked_role(member):
|
|
guild = member.guild
|
|
role = discord.utils.get(guild.roles, name=LINKED_ROLE_NAME)
|
|
if role is None:
|
|
# Create the role if it doesn't exist
|
|
role = await guild.create_role(name=LINKED_ROLE_NAME, color=discord.Color.blue())
|
|
|
|
if role not in member.roles:
|
|
try:
|
|
await member.add_roles(role)
|
|
print(f"Added {LINKED_ROLE_NAME} role to {member.name}")
|
|
except discord.Forbidden:
|
|
print(f"Bot doesn't have permission to add roles to {member.name}")
|
|
except discord.HTTPException as e:
|
|
print(f"Failed to add role to {member.name}: {str(e)}")
|
|
|
|
# You might want to add a command to manually sync roles
|
|
@bot.tree.command(name="sync_linked_role", description="Sync the Account-linked role for all linked users")
|
|
@commands.has_permissions(administrator=True)
|
|
async def sync_linked_role(interaction: discord.Interaction):
|
|
await interaction.response.defer()
|
|
guild = interaction.guild
|
|
linked_users = users_collection.find({'discord_id': {'$exists': True}})
|
|
count = 0
|
|
for user in linked_users:
|
|
member = guild.get_member(int(user['discord_id']))
|
|
if member:
|
|
await add_linked_role(member)
|
|
count += 1
|
|
await interaction.followup.send(f"Synced roles for {count} linked users.")
|
|
|
|
|
|
|
|
bot.run(DISCORD_BOT_TOKEN) |