import discord import os # default module from dotenv import load_dotenv import json import mariadb import sys from discord import default_permissions load_dotenv() # load all the variables from the env file with open('config.json') as user_file: config = json.loads(user_file.read()) def connect_db(): try: conn = mariadb.connect( user=config["db_user"], password=config["db_password"], host=config["db_host"], port=3306, database=config["db_name"] ) return conn except mariadb.Error as e: print(f"Error connecting to MariaDB Platform: {e}") sys.exit(1) def recruit_update(member, recruit, cur, conn): cur.execute("INSERT INTO recruits (discord_id, nickname, recruit) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE nickname=?, recruit=?;", (member.id, member.display_name, recruit, member.display_name, recruit)) conn.commit() intents = discord.Intents.default() intents.members = True bot = discord.Bot(intents = intents) @bot.event async def on_ready(): global Yes_select_inst Yes_select_inst = Yes_select() bot.add_view(Yes_select_inst) global no_select_inst no_select_inst = No_select() bot.add_view(no_select_inst) print(f"{bot.user} is ready and online!") conn = connect_db() cur = conn.cursor() # create Slash Command group with bot.create_group settings = bot.create_group("set", "Einstellungen") def get_count_recruits(): cur.execute("SELECT count(*) FROM recruits WHERE recruit=1") count = cur.fetchone()[0] return count def build_option_list(): cur.execute("SELECT discord_id, nickname, recruit FROM recruits WHERE recruit=1") options = [] records = cur.fetchall() for row in records: options.append(discord.SelectOption(value=str(row[0]), label=str(row[1]))) return options def insert_yes_vote(recruit_id, voter_id): cur.execute("SELECT discord_id_recruit, discord_id_voter FROM yes_votes WHERE discord_id_recruit = ? AND discord_id_voter = ?", (recruit_id, voter_id)) result = cur.fetchone() if result is None: cur.execute("INSERT INTO yes_votes(discord_id_recruit, discord_id_voter) VALUES (?, ?)", (recruit_id, voter_id)) conn.commit() cur.execute("SELECT nickname FROM recruits WHERE discord_id = ?", (recruit_id, )) return cur.fetchone()[0] class Yes_select(discord.ui.View): def __init__(self): super().__init__(timeout=None) # timeout of the view must be set to None @discord.ui.select( custom_id="yes1", placeholder="Rekruten wählen...", min_values=1, max_values=get_count_recruits(), options=build_option_list() ) async def select_callback(self, select, interaction): # the function called when the user is done selecting options message = "Du hast für folgende Rekruten mit **Ja** abgestimmt:\n\n" for recruit in select.values: member = insert_yes_vote(recruit, interaction.user.id) message = message + member + "\n" embed = discord.Embed( title="Aktion abgeschlossen", description=message, color=discord.Colour.green(), ) await interaction.response.send_message(embed = embed, ephemeral = True) def create_yes_embed(): embed = discord.Embed( title="Rekrutenbesichtigung: Ja-Stimme", description="Geb hier deine Ja-Stimme für einen Rekruten ab!", color=discord.Colour.green(), ) return embed async def send_yes_message(channel): message = await channel.send(embed=create_yes_embed(), view=Yes_select_inst) cur.execute("UPDATE settings SET VALUE = ? WHERE name = 'message_voting_yes'", (message.id, )) async def edit_yes_message(): cur.execute("SELECT value FROM settings WHERE name='channel_voting'") channel = bot.get_channel(int(cur.fetchone()[0])) cur.execute("SELECT value FROM settings WHERE name='message_voting_yes'") message = await channel.fetch_message(int(cur.fetchone()[0])) conn.commit() global Yes_select_inst Yes_select_inst.children[0].options = build_option_list() Yes_select_inst.children[0].max_values = get_count_recruits() await message.edit(embed=create_yes_embed(), view=Yes_select_inst) print("Edited!") def insert_no_vote(recruit_id, voter_id, reason): cur.execute("INSERT INTO no_votes(discord_id_recruit, discord_id_voter, reason) VALUES (?, ?, ?)", (recruit_id, voter_id, reason)) conn.commit() cur.execute("SELECT nickname FROM recruits WHERE discord_id = ?", (recruit_id, )) return cur.fetchone()[0] class No_reason(discord.ui.Modal): recruit_name = "" recruit_id = "" def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.add_item(discord.ui.InputText(label="Begründung", style=discord.InputTextStyle.long)) async def callback(self, interaction: discord.Interaction): insert_no_vote(self.recruit_id, interaction.user.id, self.children[0].value) message = ("Du hast deine Nein-Stimme erfolgreich abgegeben.") embed = discord.Embed( title="Aktion abgeschlossen", description=message, color=discord.Colour.green(), ) embed.add_field(name="Rekrut:", value=self.recruit_name) embed.add_field(name="Dein Grund", value=self.children[0].value) embed.add_field(name="Hinweis:", value="Die Rekrutierungsleiter werden dich demnächst kontaktieren.") await interaction.response.send_message(embeds=[embed], ephemeral=True) async def reason(discord_id, interaction): cur.execute("SELECT nickname FROM recruits WHERE discord_id = ?", (discord_id,)) nickname = cur.fetchone()[0] no_reason_instance = No_reason(title="Begründung für " + nickname) no_reason_instance.recruit_id = discord_id no_reason_instance.recruit_name = nickname await interaction.response.send_modal(no_reason_instance) class no_question(discord.ui.View): discord_id = "" @discord.ui.button(label="Ja", style=discord.ButtonStyle.green,) async def yes(self, button, interaction): await reason(self.discord_id, interaction) @discord.ui.button(label="Nein", style=discord.ButtonStyle.grey) async def no(self, button, interaction): embed = discord.Embed( title="Aktion abgebrochen", description="Es wurde nichts gespeichert.\nDeine Nein-Stimme mit dem angegebenen Grund bleibt bestehen.\nWenn du diese löschen möchtest, wähle die Option 'Stimme entfernen'", color=discord.Colour.blurple(), # Pycord provides a class with default colors you can choose from ) await interaction.response.send_message(embed=embed, ephemeral=True) class No_select(discord.ui.View): def __init__(self): super().__init__(timeout=None) # timeout of the view must be set to None @discord.ui.select( custom_id="no1", placeholder="Rekruten wählen...", min_values=1, max_values=1, options=build_option_list() ) async def select_callback(self, select, interaction): # the function called when the user is done selecting options cur.execute("SELECT no_votes.reason, recruits.nickname FROM recruits, no_votes WHERE recruits.discord_id = no_votes.discord_id_recruit AND no_votes.discord_id_voter = ? AND no_votes.discord_id_recruit = ?", (interaction.user.id, select.values[0])) vote = cur.fetchall() if not vote: await reason(select.values[0], interaction) else: no_question_inst = no_question() no_question_inst.discord_id = select.values[0] embed = discord.Embed( title="Du hast bereits für den Rekruten abgestimmt.", description="Möchtest du deine jetzige Stimme löschen und einen neuen Grund angeben?", color=discord.Colour.blurple(), # Pycord provides a class with default colors you can choose from ) embed.add_field(name="Rekrut", value=vote[0][1]) embed.add_field(name="Angegebener Grund", value=vote[0][0]) await interaction.response.send_message(embed=embed, view=no_question_inst, ephemeral = True) def create_no_embed(): embed = discord.Embed( title="Rekrutenbesichtigung: Nein-Stimme", description="Geb hier deine Nein-Stimme für einen Rekruten ab!", color=discord.Colour.red(), ) return embed async def send_no_message(channel): message = await channel.send(embed=create_no_embed(), view=no_select_inst) cur.execute("UPDATE settings SET VALUE = ? WHERE name = 'message_voting_no'", (message.id, )) async def edit_no_message(): cur.execute("SELECT value FROM settings WHERE name='channel_voting'") channel = bot.get_channel(int(cur.fetchone()[0])) cur.execute("SELECT value FROM settings WHERE name='message_voting_no'") message = await channel.fetch_message(int(cur.fetchone()[0])) conn.commit() global no_select_inst no_select_inst.children[0].options = build_option_list() await message.edit(embed=create_no_embed(), view=no_select_inst) print("Edited!") @settings.command(description="Sendet die Nachricht zum Abstimmen in den aktuellen Channel.") @default_permissions(manage_roles=True) async def message_voting(ctx): cur.execute("UPDATE settings SET VALUE = ? WHERE name = 'channel_voting'", (ctx.channel_id, )) await send_yes_message(ctx.channel) await send_no_message(ctx.channel) conn.commit() await ctx.respond(f"Done.", ephemeral=True) @settings.command(description="Sendet den Report in den aktuellen Kanal.") @default_permissions(manage_roles=True) async def message_report(ctx): cur.execute("UPDATE settings SET VALUE = ? WHERE name = 'channel_report'", (ctx.channel_id, )) conn.commit() await ctx.respond(f"Done.", ephemeral=True) @settings.command(description="Setzt die ID der Rekruten-Rolle") @default_permissions(manage_roles=True) async def role_recruit( ctx, rolearg: discord.Option(discord.SlashCommandOptionType.role) ): cur.execute("UPDATE settings SET VALUE = ? WHERE name = 'role_recruit'", (rolearg.id, )) conn.commit() await ctx.respond(f"Done.", ephemeral=True) @settings.command(description="Setzt den Discord-Server") @default_permissions(manage_roles=True) async def guild(ctx): cur.execute("UPDATE settings SET VALUE = ? WHERE name = 'guild'", (ctx.guild_id, )) conn.commit() await ctx.respond(f"Done.", ephemeral=True) def check_roles(member, recruit_id): for role in member.roles: if role.id == recruit_id: return "Yes" @settings.command(description="Aktualisiert die internen Daten.") @default_permissions(manage_roles=True) async def refresh(ctx): cur.execute("SELECT value FROM settings WHERE name='guild'") #conn.commit() guild_id = cur.fetchone() guild_id = guild_id[0] cur.execute("SELECT value FROM settings WHERE name='role_recruit'") recruit_id = cur.fetchone()[0] guild = bot.get_guild(guild_id) for role in guild.roles: if role.id == recruit_id: for member in role.members: recruit_update(member, 1, cur, conn) for member in guild.members: result = check_roles(member, recruit_id) if result == "Yes": pass else: cur.execute("UPDATE recruits SET recruit = ?, nickname = ? WHERE discord_id = ?", (0, member.display_name, member.id)) conn.commit() await edit_yes_message() await edit_no_message() await ctx.respond(f"Done.", ephemeral=True) @bot.event async def on_member_update(previous, after): cur.execute("SELECT value FROM settings WHERE name='role_recruit'") recruit_id = cur.fetchone() recruit_id = recruit_id[0] found = False for role in after.roles: if role.id == recruit_id: recruit_update(after, 1, cur, conn) found = True break if found: pass # Kein Rekrut else: for role in previous.roles: if role.id == recruit_id: recruit_update(after, 0, cur, conn) break await edit_yes_message() await edit_no_message() bot.run(os.getenv('TOKEN')) # run the bot with the token