# 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 asyncio import logging import os import tempfile import aiohttp import discord import qrcode from discord.ext import commands logger = logging.getLogger(__name__) try: from PIL import Image from pyzbar.pyzbar import decode as decode_qr DECODE_AVAILABLE = True except ImportError: DECODE_AVAILABLE = False logger.warning("pyzbar or PIL not available, decode command will be disabled") class QR(commands.Cog): def __init__(self, bot): self.bot = bot async def _download_image(self, url): """Download image from URL to temporary file""" async with aiohttp.ClientSession() as session: async with session.get(url) as response: if response.status == 200: content = await response.read() with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as f: f.write(content) return f.name return None @commands.slash_command( name="qr_generuj", description="Generuje kod QR z podanego tekstu" ) async def qr( self, ctx, text: str = discord.Option(str, "Tekst do zakodowania", required=True), ): await ctx.defer() temp_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "temp") os.makedirs(temp_dir, exist_ok=True) temp_path = os.path.join( temp_dir, f"qr_{os.getpid()}_{asyncio.get_event_loop().time()}.png" ) try: loop = asyncio.get_event_loop() await loop.run_in_executor(None, self._generate_qr, text, temp_path) await ctx.respond(file=discord.File(temp_path, filename="qr.png")) logger.info(f"Generated QR for text: {text}") except Exception as e: logger.error(f"QR generation error: {e}") await ctx.respond("Error generating QR code", ephemeral=True) finally: try: if os.path.exists(temp_path): os.unlink(temp_path) except Exception as e: logger.error(f"Error cleaning up temp file: {e}") def _generate_qr(self, text, output_path): """Generate QR code (runs in executor)""" qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=10, border=4, ) qr.add_data(text) qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") img.save(output_path) @commands.slash_command(name="qr_dekoduj", description="Dekoduje kod QR z obrazka") async def decode( self, ctx, image: discord.Attachment = discord.Option( discord.Attachment, "Obrazek z kodem QR", required=True ), ): if not DECODE_AVAILABLE: await ctx.respond( "Dekodowanie nie jest dostępne (brak biblioteki pyzbar)", ephemeral=True ) return await ctx.defer() try: temp_path = await self._download_image(image.url) if not temp_path: await ctx.respond("Nie udało się pobrać obrazka", ephemeral=True) return try: loop = asyncio.get_event_loop() decoded = await loop.run_in_executor(None, self._decode_qr, temp_path) if decoded: await ctx.respond(f"```\n{decoded}\n```") logger.info(f"Decoded QR: {decoded}") else: await ctx.respond( "Nie znaleziono kodu QR na obrazku", ephemeral=True ) finally: if os.path.exists(temp_path): os.unlink(temp_path) except Exception as e: logger.error(f"QR decode error: {e}") await ctx.respond("Error dekodowania QR", ephemeral=True) def _decode_qr(self, image_path): """Decode QR code from image (runs in executor)""" try: from PIL import Image img = Image.open(image_path) decoded_list = decode_qr(img) if decoded_list: return decoded_list[0].data.decode("utf-8") except Exception as e: logger.error(f"Error decoding QR: {e}") return None def setup(bot): bot.add_cog(QR(bot))