# 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 math import os import re import discord from discord.ext import commands logger = logging.getLogger(__name__) try: import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt PLOT_AVAILABLE = True except ImportError: PLOT_AVAILABLE = False logger.warning("matplotlib not available, plot command will be disabled") def parse_expression(expr: str) -> str: """Convert user expression (x^3) to Python (x**3)""" expr = expr.replace("^", "**") expr = re.sub(r"(\d)([x])", r"\1*\2", expr) expr = re.sub(r"([x])(\d)", r"\1*\2", expr) return expr class Plot(commands.Cog): def __init__(self, bot): self.bot = bot def _generate_plot(self, func_expr: str, output_path: str): """Generate plot (runs in executor)""" parsed_expr = parse_expression(func_expr) x = [i * 0.1 for i in range(-100, 101)] y = [] for xi in x: try: result = eval( parsed_expr, { "x": xi, "math": math, "sin": math.sin, "cos": math.cos, "tan": math.tan, "sqrt": math.sqrt, "log": math.log, "exp": math.exp, "pi": math.pi, "e": math.e, }, ) if math.isfinite(result): y.append(result) else: y.append(float("nan")) except Exception: y.append(float("nan")) fig, ax = plt.subplots(figsize=(10, 6)) ax.plot(x, y, linewidth=2, color="#00ff00") ax.set_title(f"y = {func_expr}", fontsize=14) ax.set_xlabel("x", fontsize=12) ax.set_ylabel("y", fontsize=12) ax.grid(True, alpha=0.3) ax.axhline(y=0, color="white", linewidth=0.5) ax.axvline(x=0, color="white", linewidth=0.5) ax.set_facecolor("#1a1a1a") fig.patch.set_facecolor("#1a1a1a") plt.savefig(output_path, dpi=100, bbox_inches="tight") plt.close() @commands.slash_command( name="funkcja", description="Generuje wykres funkcji matematycznej" ) async def plot( self, ctx, funkcja: str = discord.Option( str, "Funkcja np. x^3, sin(x), x^2 + 2*x - 1", required=True ), ): if not PLOT_AVAILABLE: await ctx.respond( "Generowanie wykresów nie jest dostępne (brak matplotlib)", ephemeral=True, ) return 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"plot_{os.getpid()}_{asyncio.get_event_loop().time()}.png" ) try: loop = asyncio.get_event_loop() await loop.run_in_executor(None, self._generate_plot, funkcja, temp_path) await ctx.respond(file=discord.File(temp_path, filename="plot.png")) logger.info(f"Generated plot for function: {funkcja}") except Exception as e: logger.error(f"Plot generation error: {e}") await ctx.respond(f"Błąd generowania wykresu: {str(e)}", 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 setup(bot): bot.add_cog(Plot(bot))