From c687d9fa1d89fef8ddc7150d1487dfef07d11abb Mon Sep 17 00:00:00 2001 From: salmonik Date: Mon, 20 Apr 2026 15:05:51 +0200 Subject: uploader.py and voice fixes Signed-off-by: salmonik --- cogs/uploader.py | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 cogs/uploader.py (limited to 'cogs/uploader.py') diff --git a/cogs/uploader.py b/cogs/uploader.py new file mode 100644 index 0000000..68a3938 --- /dev/null +++ b/cogs/uploader.py @@ -0,0 +1,154 @@ +# This file is part of Senator Sperma +# Licensed under the GPL-3.0 License +# Copyright (C) 2026 Salmonik @ salmonik@tuta.io +# https://codeberg.org/salmonik/senator-sperma + +import os +import aiofiles +import requests +import urllib.parse +from typing import List, Optional +import discord +from discord.ext import commands +from discord import AutocompleteContext +from cogs.utils import file_search as search_utils +import logging + +logger = logging.getLogger(__name__) + + +class Uploader(commands.Cog): + def __init__(self, bot): + from cogs.utils.temp import get_temp_dir + self.bot = bot + self.temp_dir = get_temp_dir() + self._cached_urls = {} + self._uploadurl = os.getenv("COPYPARTY_URL_UPLOAD") + + async def _file_autocomplete(self, ctx: AutocompleteContext): + query = ctx.value.lower() if ctx.value else "" + results = await search_utils.UniversalSearcher.search_copyparty_autocomplete(query, max_results=25) + + user_id = ctx.interaction.user.id + self._cached_urls[user_id] = {urllib.parse.unquote(url.split('/')[-1]): url for url in results} + + choices = [] + for url in results[:25]: + filename = urllib.parse.unquote(url.split('/')[-1]) + display_name = filename[:99] + "…" if len(filename) > 100 else filename + choices.append(discord.OptionChoice(name=display_name, value=filename)) + return choices + + async def _download_from_url(self, url: str) -> Optional[str]: + """Download file from copyparty URL and return local path""" + try: + response = requests.get(url) + if response.status_code != 200: + return None + + filename = url.split('/')[-1].split('?')[0] + file_path = os.path.join(self.temp_dir, filename) + + async with aiofiles.open(file_path, 'wb') as f: + await f.write(response.content) + return file_path + except Exception as e: + logger.error(f"Error downloading from {url}: {e}") + return None + + async def _upload_to_copyparty(self, file_path: str) -> Optional[str]: + """Upload file to copyparty and return download URL""" + try: + async with aiofiles.open(file_path, 'rb') as f: + file_content = await f.read() + + files = {'f': (os.path.basename(file_path), file_content, 'application/octet-stream')} + headers = {'Cookie': f'cppwd={os.getenv("COPYPARTY_PASSWORD")}'} + params = {'j': '1', 'ck': 'b2'} + + response = requests.post(self._uploadurl, files=files, headers=headers, params=params) + + if response.status_code in [200, 201]: + result = response.json() + return result.get('fileurl') + except Exception as e: + logger.error(f"Upload error: {e}") + return None + + async def _upload_file(self, file_path: str) -> Optional[str]: + """Upload a file and return the URL""" + if not self._uploadurl: + logger.error("COPYPARTY_URL_UPLOAD not set!") + return None + + return await self._upload_to_copyparty(file_path) + + @commands.message_command( + name="upload", + description="wyślij załączniki na copyparty", + ) + async def message_upload(self, ctx, message: discord.Message): + if not message.attachments: + await ctx.respond("Brak załączników w wiadomości", ephemeral=True) + return + + await ctx.defer() + + uploaded_urls = [] + failed_count = 0 + + for attachment in message.attachments: + file_path = os.path.join(self.temp_dir, attachment.filename) + await attachment.save(file_path) + + url = await self._upload_file(file_path) + if url: + uploaded_urls.append(url) + else: + failed_count += 1 + + if os.path.exists(file_path): + os.remove(file_path) + + if uploaded_urls: + response = f"Wysłano {len(uploaded_urls)} plików:\n" + "\n".join(uploaded_urls) + if failed_count > 0: + response += f"\n\nNie wysłano {failed_count} plików" + await ctx.respond(response) + else: + await ctx.respond("Nie udało się wysłać żadnych plików", ephemeral=True) + + @commands.slash_command( + name="upload", + description="wyślij plik na copyparty", + ) + @discord.option( + name="zalacznik", + description="plik z discorda", + required=True, + type=discord.Attachment + ) + async def upload(self, ctx, zalacznik: discord.Attachment = None): + await ctx.defer() + + file_path = None + file_path = os.path.join(self.temp_dir, zalacznik.filename) + await zalacznik.save(file_path) + + if not file_path or not os.path.exists(file_path): + await ctx.respond("Nie udało się pobrać pliku", ephemeral=True) + return + + try: + dl_url = await self._upload_file(file_path) + if dl_url: + await ctx.respond(dl_url) + else: + await ctx.respond("Nie udało się wysłać pliku", ephemeral=True) + finally: + if file_path and os.path.exists(file_path): + os.remove(file_path) + + +def setup(bot): + bot.add_cog(Uploader(bot)) -- cgit v1.2.3