aboutsummaryrefslogtreecommitdiffstats
path: root/cogs/uploader.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--cogs/uploader.py155
1 files changed, 155 insertions, 0 deletions
diff --git a/cogs/uploader.py b/cogs/uploader.py
new file mode 100644
index 0000000..df6908e
--- /dev/null
+++ b/cogs/uploader.py
@@ -0,0 +1,155 @@
1# This file is part of Senator Sperma
2# Licensed under the GPL-3.0 License
3# Copyright (C) 2026 Salmonik @ salmonik@tuta.io
4# https://codeberg.org/salmonik/senator-sperma
5
6import os
7import aiofiles
8import requests
9import urllib.parse
10from typing import Optional
11import discord
12from discord.ext import commands
13from discord import AutocompleteContext
14from cogs.utils import file_search as search_utils
15from cogs.utils.settings import get_uploadurl
16import logging
17
18logger = logging.getLogger(__name__)
19
20
21class Uploader(commands.Cog):
22 def __init__(self, bot):
23 from cogs.utils.temp import get_temp_dir
24 self.bot = bot
25 self.temp_dir = get_temp_dir()
26 self._cached_urls = {}
27 self._uploadurl = get_uploadurl()
28
29 async def _file_autocomplete(self, ctx: AutocompleteContext):
30 query = ctx.value.lower() if ctx.value else ""
31 results = await search_utils.UniversalSearcher.search_copyparty_autocomplete(query, max_results=25)
32
33 user_id = ctx.interaction.user.id
34 self._cached_urls[user_id] = {urllib.parse.unquote(url.split('/')[-1]): url for url in results}
35
36 choices = []
37 for url in results[:25]:
38 filename = urllib.parse.unquote(url.split('/')[-1])
39 display_name = filename[:99] + "…" if len(filename) > 100 else filename
40 choices.append(discord.OptionChoice(name=display_name, value=filename))
41 return choices
42
43 async def _download_from_url(self, url: str) -> Optional[str]:
44 """Download file from copyparty URL and return local path"""
45 try:
46 response = requests.get(url)
47 if response.status_code != 200:
48 return None
49
50 filename = url.split('/')[-1].split('?')[0]
51 file_path = os.path.join(self.temp_dir, filename)
52
53 async with aiofiles.open(file_path, 'wb') as f:
54 await f.write(response.content)
55 return file_path
56 except Exception as e:
57 logger.error(f"Error downloading from {url}: {e}")
58 return None
59
60 async def _upload_to_copyparty(self, file_path: str) -> Optional[str]:
61 """Upload file to copyparty and return download URL"""
62 try:
63 async with aiofiles.open(file_path, 'rb') as f:
64 file_content = await f.read()
65
66 files = {'f': (os.path.basename(file_path), file_content, 'application/octet-stream')}
67 headers = {'Cookie': f'cppwd={os.getenv("COPYPARTY_PASSWORD")}'}
68 params = {'j': '1', 'ck': 'b2'}
69
70 response = requests.post(self._uploadurl, files=files, headers=headers, params=params)
71
72 if response.status_code in [200, 201]:
73 result = response.json()
74 return result.get('fileurl')
75 except Exception as e:
76 logger.error(f"Upload error: {e}")
77 return None
78
79 async def _upload_file(self, file_path: str) -> Optional[str]:
80 """Upload a file and return the URL"""
81 if self._uploadurl == "http://localhost:3939":
82 logger.warning("COPYPARTY_URL_UPLOAD not set! Using default")
83 return None
84
85 return await self._upload_to_copyparty(file_path)
86
87 @commands.message_command(
88 name="upload",
89 description="wyślij załączniki na copyparty",
90 )
91 async def message_upload(self, ctx, message: discord.Message):
92 if not message.attachments:
93 await ctx.respond("Brak załączników w wiadomości", ephemeral=True)
94 return
95
96 await ctx.defer()
97
98 uploaded_urls = []
99 failed_count = 0
100
101 for attachment in message.attachments:
102 file_path = os.path.join(self.temp_dir, attachment.filename)
103 await attachment.save(file_path)
104
105 url = await self._upload_file(file_path)
106 if url:
107 uploaded_urls.append(url)
108 else:
109 failed_count += 1
110
111 if os.path.exists(file_path):
112 os.remove(file_path)
113
114 if uploaded_urls:
115 response = f"Wysłano {len(uploaded_urls)} plików:\n" + "\n".join(uploaded_urls)
116 if failed_count > 0:
117 response += f"\n\nNie wysłano {failed_count} plików"
118 await ctx.respond(response)
119 else:
120 await ctx.respond("Nie udało się wysłać żadnych plików", ephemeral=True)
121
122 @commands.slash_command(
123 name="upload",
124 description="wyślij plik na copyparty",
125 )
126 @discord.option(
127 name="zalacznik",
128 description="plik z discorda",
129 required=True,
130 type=discord.Attachment
131 )
132 async def upload(self, ctx, zalacznik: discord.Attachment = None):
133 await ctx.defer()
134
135 file_path = None
136 file_path = os.path.join(self.temp_dir, zalacznik.filename)
137 await zalacznik.save(file_path)
138
139 if not file_path or not os.path.exists(file_path):
140 await ctx.respond("Nie udało się pobrać pliku", ephemeral=True)
141 return
142
143 try:
144 dl_url = await self._upload_file(file_path)
145 if dl_url:
146 await ctx.respond(dl_url)
147 else:
148 await ctx.respond("Nie udało się wysłać pliku", ephemeral=True)
149 finally:
150 if file_path and os.path.exists(file_path):
151 os.remove(file_path)
152
153
154def setup(bot):
155 bot.add_cog(Uploader(bot))