1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
# 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__)
_plt_module = None
_plot_available = False
def _ensure_plot():
global _plt_module, _plot_available
if _plt_module is not None:
return _plot_available
try:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as _plt_module
_plot_available = True
except ImportError:
_plot_available = False
logger.warning("matplotlib not available, plot command will be disabled")
return _plot_available
def parse_expression(expr: str) -> str:
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):
global _plt_module
if not _ensure_plot():
raise RuntimeError("matplotlib not available")
plt = _plt_module
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 _ensure_plot():
await ctx.respond("Generowanie wykresów nie jest dostępne (brak matplotlib)", ephemeral=True)
return
await ctx.defer()
from cogs.utils.temp import get_temp_dir
temp_path = os.path.join(get_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))
|