From 1cac1d2e667b3e736fd4c315f37c05f0070505f7 Mon Sep 17 00:00:00 2001 From: Enzo Nunes Date: Tue, 2 Dec 2025 23:15:40 +0000 Subject: [PATCH] fix main file. adapt dockerfile. --- Dockerfile | 12 ++-- main.py | 186 +++++++++++++++++++++++++++-------------------------- 2 files changed, 104 insertions(+), 94 deletions(-) diff --git a/Dockerfile b/Dockerfile index a5924bf..74543e6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,16 +2,20 @@ FROM python:3.14-slim RUN set -eux; \ apt-get update; \ - apt-get install -y --no-install-recommends cloc; \ - apt-get dist-clean + apt-get install -y --no-install-recommends cloc git; \ + apt-get clean; \ + rm -rf /var/lib/apt/lists/* WORKDIR /app -RUN pip install --no-cache-dir discord asyncio requests +RUN pip install --no-cache-dir discord.py requests COPY main.py . COPY PICable.py . -RUN useradd -m appuser +RUN useradd -m appuser && \ + mkdir -p /temp && \ + chown appuser:appuser /temp + USER appuser CMD ["python", "main.py"] diff --git a/main.py b/main.py index fd6110f..57e305c 100644 --- a/main.py +++ b/main.py @@ -11,100 +11,106 @@ MAX_STORAGE_KB = 50 * 1024 * 1024 # 50GB DISCORD_TOKEN = os.getenv("PICABLE_DISCORD_TOKEN") GITHUB_TOKEN = os.getenv("PICABLE_GITHUB_TOKEN") -if __name__ == "__main__": - current_storage_kb = 0 - storage_condition = Condition() - current_repositories = set() - current_repositories_lock = Lock() +current_storage_kb = 0 +storage_condition = Condition() +current_repositories = set() +current_repositories_lock = Lock() - if not DISCORD_TOKEN or not GITHUB_TOKEN: - print("Discord or Github tokens not set in env. Use variables PICABLE_DISCORD_TOKEN and PICABLE_GITHUB_TOKEN") - exit(1) +if not DISCORD_TOKEN or not GITHUB_TOKEN: + print("Discord or Github tokens not set in env. Use variables PICABLE_DISCORD_TOKEN and PICABLE_GITHUB_TOKEN") + exit(1) - intents = Intents.default() - intents.message_content = True - client = commands.Bot(command_prefix="/", intents=intents) - client.run(DISCORD_TOKEN) +intents = Intents.default() +intents.message_content = True +client = commands.Bot(command_prefix="/", intents=intents) - @client.event - async def on_ready(): - print(f"We have logged in as {client.user}") - await client.change_presence(activity=Game("with PIC ideas")) + +@client.event +async def on_ready(): + print(f"We have logged in as {client.user}") + await client.change_presence(activity=Game("with PIC ideas")) + try: + synced = await client.tree.sync() + print(f"Synced {len(synced)} command(s)") + except Exception as e: + print(f"Failed to sync commands: {e}") + + +@client.tree.command(name="ping", description="Check the bot's latency.") +async def ping(interaction: Interaction): + latency = client.latency * 1000 + await interaction.response.send_message(f"Pong! Latency: {latency:.2f}ms") + + +async def handle_picable(owner: str, repository: str, repository_size_kb: int, github_token: str): + global current_storage_kb + async with storage_condition: + await storage_condition.wait_for(lambda: current_storage_kb + repository_size_kb <= MAX_STORAGE_KB) + current_storage_kb += repository_size_kb + + print(f"Storage is {current_storage_kb / MAX_STORAGE_KB * 100:.2f}% full.") + return await to_thread(PICable, owner, repository, github_token) + + +@client.tree.command(name="picable", description="Check if a Github repository is eligible for PIC.") +@app_commands.describe(owner="The owner of the repository", repository="The name of the repository") +async def picable(interaction: Interaction, owner: str, repository: str): + await interaction.response.defer(thinking=True) + + repository_full_name = f"{owner}/{repository}" + async with current_repositories_lock: + if repository_full_name in current_repositories: + await interaction.followup.send( + f"The repository {repository_full_name} is already being analyzed. Please wait for the current analysis to complete. :warning:" + ) + return + + current_repositories.add(repository_full_name) + + url = f"https://api.github.com/repos/{owner}/{repository}" + headers = {"Authorization": f"token {GITHUB_TOKEN}"} + response = requests.get(url, headers=headers) + + if not response.status_code == 200: + await interaction.followup.send( + "Invalid Repository. Please check the repository name and owner and try again. :x:" + ) + return + + response.raise_for_status() + repo_info = response.json() + repository_size_kb = repo_info["size"] + print(f"Repository size: {repository_size_kb / 1024} MB") + + if repository_size_kb > MAX_STORAGE_KB: + await interaction.followup.send( + f"The repository {repository_full_name} is too large to analyze. Please try a smaller repository. :warning:" + ) + current_repositories.remove(repository_full_name) + return + + global current_storage_kb + try: + result_future = create_task(handle_picable(owner, repository, repository_size_kb, GITHUB_TOKEN)) try: - synced = await client.tree.sync() - print(f"Synced {len(synced)} command(s)") - except Exception as e: - print(f"Failed to sync commands: {e}") - - @client.tree.command(name="ping", description="Check the bot's latency.") - async def ping(interaction: Interaction): - latency = client.latency * 1000 - await interaction.response.send_message(f"Pong! Latency: {latency:.2f}ms") - - async def handle_picable(owner: str, repository: str, repository_size_kb: int, github_token: str): - global current_storage_kb - async with storage_condition: - await storage_condition.wait_for(lambda: current_storage_kb + repository_size_kb <= MAX_STORAGE_KB) - current_storage_kb += repository_size_kb - - print(f"Storage is {current_storage_kb / MAX_STORAGE_KB * 100:.2f}% full.") - return await to_thread(PICable, owner, repository, github_token) - - @client.tree.command(name="picable", description="Check if a Github repository is eligible for PIC.") - @app_commands.describe(owner="The owner of the repository", repository="The name of the repository") - async def picable(interaction: Interaction, owner: str, repository: str): - await interaction.response.defer(thinking=True) - - repository_full_name = f"{owner}/{repository}" + result = await wait_for(shield(result_future), timeout=600) + await interaction.followup.send(result) + except TimeoutError: + await interaction.followup.send( + f"The analysis for {repository_full_name} is still taking place. The results will be posted here once the analysis is complete. :clock4:" + ) + result = await result_future + await interaction.channel.send(f"{interaction.user.mention}\n{result}") + except Exception as e: + print(f"Error processing PICable check: {e}") + await interaction.channel.send("An error occurred while processing your request. Please try again later.") + finally: async with current_repositories_lock: - if repository_full_name in current_repositories: - await interaction.followup.send( - f"The repository {repository_full_name} is already being analyzed. Please wait for the current analysis to complete. :warning:" - ) - return - - current_repositories.add(repository_full_name) - - url = f"https://api.github.com/repos/{owner}/{repository}" - headers = {"Authorization": f"token {GITHUB_TOKEN}"} - response = requests.get(url, headers=headers) - - if not response.status_code == 200: - await interaction.followup.send( - "Invalid Repository. Please check the repository name and owner and try again. :x:" - ) - return - - response.raise_for_status() - repo_info = response.json() - repository_size_kb = repo_info["size"] - print(f"Repository size: {repository_size_kb / 1024} MB") - - if repository_size_kb > MAX_STORAGE_KB: - await interaction.followup.send( - f"The repository {repository_full_name} is too large to analyze. Please try a smaller repository. :warning:" - ) current_repositories.remove(repository_full_name) - return + async with storage_condition: + current_storage_kb -= repository_size_kb + storage_condition.notify_all() - global current_storage_kb - try: - result_future = create_task(handle_picable(owner, repository, repository_size_kb, GITHUB_TOKEN)) - try: - result = await wait_for(shield(result_future), timeout=600) - await interaction.followup.send(result) - except TimeoutError: - await interaction.followup.send( - f"The analysis for {repository_full_name} is still taking place. The results will be posted here once the analysis is complete. :clock4:" - ) - result = await result_future - await interaction.channel.send(f"{interaction.user.mention}\n{result}") - except Exception as e: - print(f"Error processing PICable check: {e}") - await interaction.channel.send("An error occurred while processing your request. Please try again later.") - finally: - async with current_repositories_lock: - current_repositories.remove(repository_full_name) - async with storage_condition: - current_storage_kb -= repository_size_kb - storage_condition.notify_all() + +if __name__ == "__main__": + client.run(DISCORD_TOKEN)