import discord from discord import app_commands import os from dotenv import load_dotenv import sqlite3 import datetime load_dotenv() intents = discord.Intents.default() client = discord.Client(intents=intents) tree = app_commands.CommandTree(client) token = os.getenv('TOKEN') @tree.command(name="register_channel", description="sets up a checkout ledger in the current channel") async def register_channel(interaction: discord.Interaction) -> None: db_init(interaction.channel_id) await interaction.response.send_message("database initialised, bot is ready for use in this channel", ephemeral=True) @tree.command(name="checkout", description="marks the specified file as being 'checked out'") @app_commands.describe(file="the file to check out") async def checkout(interaction: discord.Interaction, file: str) -> None: if db_table_exists(interaction.channel_id): if db_file_exists(interaction.channel_id, file=file) != True: await interaction.response.send_message(embed=get_embed(file=file, user=interaction.user)) message = await interaction.original_response() db_add_row(channel_id=interaction.channel_id,file=file,message_id=message.id,user=interaction.user) else: message_id = db_get_message_id_by_file(channel_id=interaction.channel_id, file=file) await interaction.response.send_message(f"the requested file has already been checked out by another user [here](https://discordapp.com/channels/{interaction.guild_id}/{interaction.channel_id}/{message_id})...", ephemeral=True) else: await interaction.response.send_message("the current channel does not have a ledger, either run `/register_channel` or move to a different channel and try again...", ephemeral=True) @tree.command(name="checkin", description="allows the user to select a checked out file to check back in") async def checkin(interaction: discord.Interaction) -> None: if db_table_exists(interaction.channel_id): if db_user_exists(interaction.channel_id, interaction.user): files_by_user = db_get_files_by_user(channel_id=interaction.channel_id, user=interaction.user) if len(files_by_user) == 1: await interaction.response.send_modal(CheckinConfirmationModal(channel_id=interaction.channel_id,file=files_by_user[0])) else: await interaction.response.send_modal(CheckinModal(channel_id=interaction.channel_id,user=interaction.user)) else: await interaction.response.send_message("you haven't checked out any files in this channel yet!", ephemeral=True) else: await interaction.response.send_message("the current channel does not have a ledger, either run `/register_channel` or move to a different channel and try again...", ephemeral=True) @client.event async def on_ready(): await tree.sync() print("Ready!") def db_get_message_id_by_file(channel_id: int, file: str) -> int: conn = sqlite3.connect("ledgers.db") cursor = conn.cursor() args = (file,) message_id = cursor.execute(""" SELECT "message_id" FROM "%s" WHERE "file" = ?; """ %(str(channel_id)), args).fetchall() conn.close() if len(message_id) > 0: return int(message_id[0][0]) else: return 0 def db_get_files_by_user(channel_id: int, user: discord.User) -> list: conn = sqlite3.connect("ledgers.db") cursor = conn.cursor() args = (str(user.id),) db_output = cursor.execute(""" SELECT "file" FROM "%s" WHERE "user_id" = ?; """ %(str(channel_id)), args).fetchall() conn.close() filtered_output = list() for x in db_output: filtered_output.append(x[0]) return filtered_output def db_add_row(channel_id: int, file: str, message_id: int, user: discord.User): conn = sqlite3.connect("ledgers.db") cursor = conn.cursor() args = (file, str(message_id), str(user.id)) cursor.execute(""" INSERT INTO "%s"(file,message_id,user_id) VALUES(?,?,?) """ %(str(channel_id)), args) conn.commit() conn.close() def db_delete_row_by_file(channel_id: int, file: str): conn = sqlite3.connect("ledgers.db") cursor = conn.cursor() args = (file,) cursor.execute(""" DELETE FROM "%s" WHERE "file" = ?; """ %(str(channel_id)), args) conn.commit() conn.close() def get_embed(file: str, user: discord.User) -> discord.Embed: my_embed = discord.Embed( title="File checked out!", color=discord.Color.dark_blue(), timestamp=datetime.datetime.now() ) my_embed.add_field(name="File", value=file) my_embed.add_field(name="By", value=f"<@{user.id}>") return my_embed def db_file_exists(channel_id: int, file: str) -> bool: conn = sqlite3.connect("ledgers.db") cursor = conn.cursor() args = (file,) rowItems = cursor.execute(""" SELECT * FROM "%s" WHERE "file" = ?; """ %(str(channel_id)), args).fetchall() conn.close() return rowItems != [] def db_user_exists(channel_id: int, user: discord.User) -> bool: conn = sqlite3.connect("ledgers.db") cursor = conn.cursor() args = (str(user.id),) rows = cursor.execute(""" SELECT * FROM "%s" WHERE "user_id" = ?; """ %(str(channel_id)), args).fetchall() conn.close() return rows != [] def db_table_exists(channel_id: int) -> bool: conn = sqlite3.connect("ledgers.db") cursor = conn.cursor() listOfTables = cursor.execute(""" SELECT name FROM sqlite_master WHERE type='table' AND name='%s'; """ %(str(channel_id))).fetchall() conn.close() return listOfTables != [] def db_init(channel_id: int): conn = sqlite3.connect("ledgers.db") cursor = conn.cursor() cursor.execute(""" CREATE TABLE IF NOT EXISTS "%s" ( file TEXT PRIMARY KEY, message_id TEXT UNIQUE, user_id TEXT ) """ %(str(channel_id))) conn.commit() conn.close() class CheckinModal(discord.ui.Modal, title='Check a file back in!'): options_list = list() channel_id = 0 def __init__(self, channel_id: int, user: discord.User) -> None: self.channel_id = channel_id files_list = db_get_files_by_user(channel_id,user) # print(files_list) self.options_list.clear() for x in files_list: self.options_list.append(discord.SelectOption(label=x)) super().__init__() select = discord.ui.Label( text='Which file are you done with?', component=discord.ui.Select( placeholder="Select a file...", options=options_list ) ) async def on_submit(self, interaction): channel = await client.fetch_channel(self.channel_id) message = await channel.fetch_message(db_get_message_id_by_file(channel_id=self.channel_id,file=self.select.component.values[0])) await message.delete() db_delete_row_by_file(channel_id=self.channel_id,file=self.select.component.values[0]) return await interaction.response.send_message(f"Successfully checked in file '{self.select.component.values[0]}'!", ephemeral=True) class CheckinConfirmationModal(discord.ui.Modal, title='Please confirm this is correct...'): text = discord.ui.TextDisplay("hello world") channel_id = 0 file = "" def __init__(self, channel_id: int, file: str) -> None: self.text.content = f"Do you want to check in the file '{file}'?" self.channel_id = channel_id self.file = file super().__init__() async def on_submit(self, interaction): channel = await client.fetch_channel(self.channel_id) message_id = db_get_message_id_by_file(channel_id=self.channel_id,file=self.file) if message_id != 0: message = await channel.fetch_message(message_id) await message.delete() db_delete_row_by_file(channel_id=self.channel_id,file=self.file) return await interaction.response.send_message(f"Successfully checked in file '{self.file}'!", ephemeral=True) client.run(token)