Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import os
- import json
- import random
- import string
- import discord
- from discord import app_commands
- from discord.ext import tasks
- from dotenv import load_dotenv
- from ldap3 import Server, Connection, ALL, MODIFY_REPLACE
- from ldap3 import MODIFY_ADD
- # Load environment variables
- load_dotenv()
- DISCORD_BOT_TOKEN = os.getenv("DISCORD_BOT_TOKEN")
- LDAP_SERVER_URL = os.getenv("LDAP_SERVER_URL")
- LDAP_BIND_DN = os.getenv("LDAP_BIND_DN")
- LDAP_BIND_PASSWORD = os.getenv("LDAP_BIND_PASSWORD")
- LDAP_BASE_DN = os.getenv("LDAP_BASE_DN")
- LLDAP_LOGIN_URL = os.getenv("LLDAP_LOGIN_URL")
- SUBSCRIBER_ROLE_NAME = os.getenv("SUBSCRIBER_ROLE_NAME")
- LDAP_USERS_DN = f"ou=people,{LDAP_BASE_DN}" # Remove duplicate ou=people
- LDAP_GROUPS_DN = f"ou=groups,{LDAP_BASE_DN}"
- SUBSCRIBERS_GROUP_DN = f"cn=subscribers,{LDAP_GROUPS_DN}"
- ASSOCIATION_FILE = "user_associations.json"
- # Bot setup
- intents = discord.Intents.default()
- intents.guilds = True
- intents.members = True
- bot = discord.Client(intents=intents)
- tree = app_commands.CommandTree(bot)
- # -------------------------------
- # Utility Functions
- # -------------------------------
- def load_associations():
- if not os.path.exists(ASSOCIATION_FILE):
- return {}
- with open(ASSOCIATION_FILE, "r") as f:
- return json.load(f)
- def save_associations(data):
- with open(ASSOCIATION_FILE, "w") as f:
- json.dump(data, f)
- def generate_random_password(length=12):
- chars = string.ascii_letters + string.digits + "!@#$%^&*"
- return ''.join(random.choice(chars) for _ in range(length))
- def ldap_connect():
- server = Server(LDAP_SERVER_URL, get_info=ALL)
- conn = Connection(server, user=LDAP_BIND_DN, password=LDAP_BIND_PASSWORD, auto_bind=True)
- return conn
- def create_ldap_user(conn, username, email, password):
- user_dn = f"uid={username},{LDAP_USERS_DN}"
- conn.add(
- dn=user_dn,
- object_class=["inetOrgPerson", "organizationalPerson", "person", "top"],
- attributes={
- "cn": username,
- "sn": username,
- "mail": email,
- "uid": username,
- "userPassword": password
- }
- )
- return conn.result["description"] == "success"
- def add_user_to_subscribers(conn, user_dn):
- print(f"\n🔍 DEBUG: Checking if user exists: {user_dn}")
- # Ensure user exists before modifying group
- search_filter = f"(uid={user_dn.split(',')[0].split('=')[1]})"
- conn.search(LDAP_USERS_DN, search_filter, attributes=["dn"])
- if not conn.entries:
- print(f"❌ ERROR: User {user_dn} not found in LDAP. Cannot add to Subscribers group.")
- return False
- print(f"✅ User found in LDAP: {user_dn}")
- # DEBUG: Check existing members of the group
- print(f"\n🔍 DEBUG: Checking existing members of {SUBSCRIBERS_GROUP_DN}")
- conn.search(SUBSCRIBERS_GROUP_DN, "(objectClass=groupOfNames)", attributes=["member"])
- current_members = []
- if conn.entries:
- current_members = conn.entries[0]["member"] if "member" in conn.entries[0] else []
- print(f"📜 Current members before modification: {current_members}")
- else:
- print("❌ ERROR: Subscribers group not found or empty.")
- # Use the correct modify request format
- modify_request = {"memberUid": [(MODIFY_ADD, [user_dn.split(',')[0].split('=')[1]])]} # Extracts only the username
- # DEBUG: Print the request being sent
- print(f"\n✏️ DEBUG: Attempting to add {user_dn} to {SUBSCRIBERS_GROUP_DN}")
- print(f"📜 LDAP Modify Request: {modify_request}")
- conn.modify(SUBSCRIBERS_GROUP_DN, modify_request)
- if conn.result["description"] != "success":
- print(f"❌ LDAP Modify Error: {conn.result}")
- else:
- print(f"✅ Successfully added {user_dn} to {SUBSCRIBERS_GROUP_DN}")
- return conn.result["description"] == "success"
- def remove_user_from_subscribers(conn, user_dn):
- conn.modify(
- SUBSCRIBERS_GROUP_DN,
- {"member": [(MODIFY_REPLACE, __update_member_list(conn, SUBSCRIBERS_GROUP_DN, user_dn, add=False))]}
- )
- def __update_member_list(conn, group_dn, user_dn, add=True):
- conn.search(group_dn, "(objectClass=groupOfNames)", attributes=["member"])
- current_members = conn.entries[0].member if conn.entries and conn.entries[0].member else []
- members = set(str(m) for m in current_members)
- if add:
- members.add(user_dn)
- else:
- members.discard(user_dn)
- return list(members)
- def is_user_in_subscribers(conn, user_dn):
- conn.search(SUBSCRIBERS_GROUP_DN, "(objectClass=groupOfNames)", attributes=["member"])
- if not conn.entries:
- return False
- members = conn.entries[0].member if conn.entries[0].member else []
- return user_dn in [str(m) for m in members]
- def ldap_user_exists(conn, email):
- search_filter = f"(mail={email})"
- conn.search(LDAP_USERS_DN, search_filter, attributes=["mail"])
- return len(conn.entries) > 0
- def get_all_subscribers(conn):
- conn.search(SUBSCRIBERS_GROUP_DN, "(objectClass=groupOfNames)", attributes=["member"])
- if not conn.entries:
- return []
- return [str(m) for m in conn.entries[0].member]
- # -------------------------------
- # Periodic Sync Task
- # -------------------------------
- @tasks.loop(minutes=5)
- async def sync_subscribers():
- conn = ldap_connect()
- associations = load_associations()
- for guild in bot.guilds:
- subscriber_role = discord.utils.get(guild.roles, name=SUBSCRIBER_ROLE_NAME)
- if not subscriber_role:
- continue
- for discord_id, user_dn in associations.items():
- member = guild.get_member(int(discord_id))
- if not member:
- continue
- if subscriber_role in member.roles:
- if not is_user_in_subscribers(conn, user_dn):
- add_user_to_subscribers(conn, user_dn)
- else:
- if is_user_in_subscribers(conn, user_dn):
- remove_user_from_subscribers(conn, user_dn)
- conn.unbind()
- # -------------------------------
- # Slash Commands
- # -------------------------------
- @tree.command(name="register", description="Register an LDAP account.")
- async def register(interaction: discord.Interaction, email: str):
- associations = load_associations()
- user_id_str = str(interaction.user.id)
- if user_id_str in associations:
- await interaction.response.send_message("You are already linked to an LDAP account.", ephemeral=True)
- return
- conn = ldap_connect()
- if ldap_user_exists(conn, email):
- await interaction.response.send_message("That email is already linked to an LDAP account.", ephemeral=True)
- conn.unbind()
- return
- username = interaction.user.name.lower().replace(" ", "")
- password = generate_random_password()
- success = create_ldap_user(conn, username, email, password)
- if not success:
- await interaction.response.send_message("Failed to create LDAP account.", ephemeral=True)
- print(f"LDAP Creation Error: {conn.result}")
- conn.unbind()
- return
- user_dn = f"uid={username},{LDAP_USERS_DN}"
- add_user_to_subscribers(conn, user_dn)
- associations[user_id_str] = user_dn
- save_associations(associations)
- conn.unbind()
- await interaction.user.send(f"Your LDAP account has been created.\nUsername: {username}\nPassword: {password}\nChange it here: {LLDAP_LOGIN_URL}")
- await interaction.response.send_message("Your LDAP account has been created.", ephemeral=True)
- @tree.command(name="list_subscribed", description="List all subscribed LDAP users (Admin Only)")
- async def list_subscribed(interaction: discord.Interaction):
- if not interaction.user.guild_permissions.administrator:
- await interaction.response.send_message("You don't have permission for this.", ephemeral=True)
- return
- conn = ldap_connect()
- subscribers = get_all_subscribers(conn)
- conn.unbind()
- response = "\n".join(subscribers) if subscribers else "No subscribers found."
- await interaction.response.send_message(f"**Subscribers:**\n```{response}```", ephemeral=True)
- @bot.event
- async def on_ready():
- await bot.wait_until_ready() # Ensure bot is fully loaded
- try:
- synced = await tree.sync() # Sync commands to all guilds
- print(f"Synced {len(synced)} commands globally.")
- except Exception as e:
- print(f"Failed to sync commands: {e}")
- print(f"Bot logged in as {bot.user}")
- sync_subscribers.start()
- bot.run(DISCORD_BOT_TOKEN)
Advertisement
Add Comment
Please, Sign In to add comment