# 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))