Gradii commited on
Commit
4fa2170
·
2 Parent(s): 1900ea7f19cc50

Merge branch 'main' into backend-setup

Browse files
Files changed (3) hide show
  1. configManager.js +38 -0
  2. guildConfigs.json +7 -0
  3. index.js +383 -50
configManager.js ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ const filePath = path.resolve("./guildConfigs.json");
5
+
6
+ export const DEFAULT_CONFIG = {
7
+ logChannelId: null,
8
+ textModel: "yaya36095/xlm-roberta-text-detector",
9
+ imageModel: "capcheck/ai-image-detection"
10
+ };
11
+
12
+ export function loadConfig(guildId) {
13
+ if (!fs.existsSync(filePath)) {
14
+ fs.writeFileSync(filePath, JSON.stringify({}));
15
+ }
16
+ try {
17
+ const data = JSON.parse(fs.readFileSync(filePath, "utf-8"));
18
+ return data[guildId] || { ...DEFAULT_CONFIG };
19
+ } catch (err) {
20
+ console.error("Błąd podczas odczytu konfiguracji:", err);
21
+ return { ...DEFAULT_CONFIG };
22
+ }
23
+ }
24
+
25
+ export function saveConfig(guildId, newConfig) {
26
+ if (!fs.existsSync(filePath)) {
27
+ fs.writeFileSync(filePath, JSON.stringify({}));
28
+ }
29
+ try {
30
+ const data = JSON.parse(fs.readFileSync(filePath, "utf-8"));
31
+ data[guildId] = { ...DEFAULT_CONFIG, ...data[guildId], ...newConfig };
32
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
33
+ return true;
34
+ } catch (err) {
35
+ console.error("Błąd podczas zapisu konfiguracji:", err);
36
+ return false;
37
+ }
38
+ }
guildConfigs.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "1515307986963267595": {
3
+ "logChannelId": "1515373138937123007",
4
+ "textModel": "yaya36095/xlm-roberta-text-detector",
5
+ "imageModel": "capcheck/ai-image-detection"
6
+ }
7
+ }
index.js CHANGED
@@ -9,9 +9,19 @@ import {
9
  TextInputBuilder,
10
  TextInputStyle,
11
  ActionRowBuilder,
12
- MessageFlags
 
 
 
 
 
 
 
 
13
  } from "discord.js";
14
 
 
 
15
  const client = new Client({
16
  intents: [
17
  GatewayIntentBits.Guilds,
@@ -22,6 +32,15 @@ const client = new Client({
22
 
23
  const API_URL = process.env.API_URL || "http://127.0.0.1:8000";
24
 
 
 
 
 
 
 
 
 
 
25
  client.once(Events.ClientReady, async () => {
26
  console.log(`Bot ready: ${client.user.tag}`);
27
 
@@ -30,14 +49,48 @@ client.once(Events.ClientReady, async () => {
30
  {
31
  name: "detect",
32
  description: "Otwiera okienko do wklejenia linku lub tekstu do analizy",
 
33
  },
 
 
 
 
 
 
 
 
 
 
34
  ]);
35
- console.log("Pomyślnie zarejestrowano komendę /detect");
36
  } catch (error) {
37
- console.error("Błąd podczas rejestracji komendy:", error);
38
  }
39
  });
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  function preparePayload(input) {
42
  const trimmed = input.trim();
43
  const isUrl = trimmed.startsWith("http://") || trimmed.startsWith("https://");
@@ -66,7 +119,7 @@ function preparePayload(input) {
66
  type: "file",
67
  payload: {
68
  file_url: trimmed,
69
- content_type: "file"
70
  }
71
  };
72
  }
@@ -81,7 +134,208 @@ function preparePayload(input) {
81
  };
82
  }
83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  client.on(Events.InteractionCreate, async (interaction) => {
 
85
  if (interaction.isChatInputCommand()) {
86
  if (interaction.commandName === "detect") {
87
  const modal = new ModalBuilder()
@@ -100,66 +354,145 @@ client.on(Events.InteractionCreate, async (interaction) => {
100
 
101
  await interaction.showModal(modal);
102
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  }
104
 
105
  if (interaction.isModalSubmit()) {
106
  if (interaction.customId === "detectModal") {
107
  const userContent = interaction.fields.getTextInputValue("detectInput");
 
 
 
108
 
109
- await interaction.deferReply({ flags: [MessageFlags.Ephemeral] });
 
110
 
111
- try {
112
- const { type, payload } = preparePayload(userContent);
113
-
114
- console.log(`Wysyłanie zapytania typu: ${type} do API...`);
115
-
116
- const response = await fetch(`${API_URL}/analyze`, {
117
- method: "POST",
118
- headers: {
119
- "Content-Type": "application/json",
120
- },
121
- body: JSON.stringify(payload),
122
  });
 
 
123
 
124
- if (!response.ok) {
125
- const errorData = await response.json().catch(() => ({}));
126
- console.error("Szczegóły błędu z FastAPI:", JSON.stringify(errorData, null, 2));
127
-
128
- let errorMsg = `Błąd serwera API (Status ${response.status})`;
129
- if (errorData.detail) {
130
- if (Array.isArray(errorData.detail)) {
131
- errorMsg = errorData.detail
132
- .map(err => `• Pole \`${err.loc.join(".")}\`: ${err.msg}`)
133
- .join("\n");
134
- } else {
135
- errorMsg = errorData.detail;
136
- }
137
- }
138
- throw new Error(errorMsg);
139
- }
140
 
141
- const data = await response.json();
142
 
143
- const statusEmoji = data.is_deepfake ? "⚠️ **Wykryto potencjalny Deepfake!**" : "✅ **Zawartość wydaje się oryginalna**";
144
- const confidencePercent = (data.confidence * 100).toFixed(2);
145
- const timeSec = data.analysis_time.toFixed(3);
 
 
 
 
 
 
 
146
 
147
- const replyMessage =
148
- `### Wyniki Analizy (${data.content_type.toUpperCase()})\n` +
149
- `${statusEmoji}\n\n` +
150
- `* **Pewność modelu:** \`${confidencePercent}%\`\n` +
151
- `* **Użyty model:** \`${data.used_model}\`\n` +
152
- `* **Czas przetwarzania:** \`${timeSec} sekund\`\n`;
153
 
154
- await interaction.editReply({
155
- content: replyMessage,
156
- });
157
 
158
- } catch (error) {
159
- console.error("Błąd podczas analizy:", error);
160
- await interaction.editReply({
161
- content: `❌ Nie udało się przeprowadzić analizy.\n\n**Szczegóły błędu:**\n${error.message}`,
162
- });
 
 
 
163
  }
164
  }
165
  }
 
9
  TextInputBuilder,
10
  TextInputStyle,
11
  ActionRowBuilder,
12
+ MessageFlags,
13
+ ApplicationCommandType,
14
+ EmbedBuilder,
15
+ ButtonBuilder,
16
+ ButtonStyle,
17
+ PermissionFlagsBits,
18
+ ChannelSelectMenuBuilder,
19
+ StringSelectMenuBuilder,
20
+ ChannelType
21
  } from "discord.js";
22
 
23
+ import { loadConfig, saveConfig } from "./configManager.js";
24
+
25
  const client = new Client({
26
  intents: [
27
  GatewayIntentBits.Guilds,
 
32
 
33
  const API_URL = process.env.API_URL || "http://127.0.0.1:8000";
34
 
35
+ // Domyślne modele zapasowe (używane gdyby backend był wyłączony podczas konfiguracji)
36
+ const FALLBACK_MODELS = {
37
+ text: ["yaya36095/xlm-roberta-text-detector", "mock"],
38
+ image: ["capcheck/ai-image-detection", "mock"]
39
+ };
40
+
41
+ // Pamięć podręczna przechowuje konfigurację oraz pobrane dynamicznie modele
42
+ const activeSetupSessions = new Map();
43
+
44
  client.once(Events.ClientReady, async () => {
45
  console.log(`Bot ready: ${client.user.tag}`);
46
 
 
49
  {
50
  name: "detect",
51
  description: "Otwiera okienko do wklejenia linku lub tekstu do analizy",
52
+ type: ApplicationCommandType.ChatInput
53
  },
54
+ {
55
+ name: "setup",
56
+ description: "Ustawienia kanału logów i modeli analizy (Wymaga Administratora)",
57
+ default_member_permissions: PermissionFlagsBits.Administrator.toString(),
58
+ type: ApplicationCommandType.ChatInput
59
+ },
60
+ {
61
+ name: "Przeanalizuj tekst",
62
+ type: ApplicationCommandType.Message
63
+ }
64
  ]);
65
+ console.log("Pomyślnie zarejestrowano komendy (/detect, /setup oraz menu kontekstowe)");
66
  } catch (error) {
67
+ console.error("Błąd podczas rejestracji komend:", error);
68
  }
69
  });
70
 
71
+ // Funkcja pobierająca aktualne modele bezpośrednio z FastAPI w czasie rzeczywistym
72
+ async function fetchAvailableModels() {
73
+ try {
74
+ const response = await fetch(API_URL);
75
+ if (response.ok) {
76
+ const data = await response.json();
77
+ if (data.available_models) {
78
+ const textModels = data.available_models.text || [];
79
+ const imageModels = data.available_models.image || [];
80
+
81
+ // Upewniamy się, że zawsze mamy opcję testową "mock"
82
+ if (!textModels.includes("mock")) textModels.push("mock");
83
+ if (!imageModels.includes("mock")) imageModels.push("mock");
84
+
85
+ return { text: textModels, image: imageModels };
86
+ }
87
+ }
88
+ } catch (err) {
89
+ console.warn("Nie udało się pobrać modeli z API (użyto modeli zapasowych):", err.message);
90
+ }
91
+ return FALLBACK_MODELS;
92
+ }
93
+
94
  function preparePayload(input) {
95
  const trimmed = input.trim();
96
  const isUrl = trimmed.startsWith("http://") || trimmed.startsWith("https://");
 
119
  type: "file",
120
  payload: {
121
  file_url: trimmed,
122
+ content_type: "file"
123
  }
124
  };
125
  }
 
134
  };
135
  }
136
 
137
+ function getProgressBar(confidence, isDeepfake) {
138
+ const totalBlocks = 10;
139
+ const filledBlocks = Math.min(totalBlocks, Math.max(0, Math.round(confidence * totalBlocks)));
140
+ const emptyBlocks = totalBlocks - filledBlocks;
141
+ const blockEmoji = isDeepfake ? "🟥" : "🟩";
142
+ return blockEmoji.repeat(filledBlocks) + "⬛".repeat(emptyBlocks);
143
+ }
144
+
145
+ // ZMIANA: Funkcja przyjmuje teraz pobrane dynamicznie modele jako drugi parametr
146
+ function generateSetupView(tempConfig, availableModels) {
147
+ const embed = new EmbedBuilder()
148
+ .setColor(0x5865F2)
149
+ .setTitle("⚙️ Konfiguracja Systemu Detekcji")
150
+ .setDescription("Wybierz kanał do wysyłania logów oraz aktywne modele analizy z menu poniżej.")
151
+ .addFields(
152
+ {
153
+ name: "📂 Kanał logów (Raporty)",
154
+ value: tempConfig.logChannelId ? `<#${tempConfig.logChannelId}>` : "*Wysyłanie tylko do konsoli*",
155
+ inline: false
156
+ },
157
+ {
158
+ name: "📝 Model tekstowy",
159
+ value: `\`${tempConfig.textModel}\``,
160
+ inline: true
161
+ },
162
+ {
163
+ name: "🖼️ Model obrazów",
164
+ value: `\`${tempConfig.imageModel}\``,
165
+ inline: true
166
+ }
167
+ )
168
+ .setFooter({ text: "Wybierz opcje i kliknij Zapisz ustawienia" })
169
+ .setTimestamp();
170
+
171
+ const channelSelect = new ChannelSelectMenuBuilder()
172
+ .setCustomId("setup_log_channel")
173
+ .setPlaceholder("Wybierz kanał dla raportów")
174
+ .addChannelTypes(ChannelType.GuildText);
175
+
176
+ // DYNAMICZNE mapowanie modeli tekstowych z API
177
+ const textOptions = availableModels.text.map(model => ({
178
+ label: model === "mock" ? "Mock (Model testowy)" : model,
179
+ value: model,
180
+ default: tempConfig.textModel === model
181
+ }));
182
+
183
+ const textModelSelect = new StringSelectMenuBuilder()
184
+ .setCustomId("setup_text_model")
185
+ .setPlaceholder("Wybierz model tekstu")
186
+ .addOptions(textOptions);
187
+
188
+ // DYNAMICZNE mapowanie modeli graficznych z API
189
+ const imageOptions = availableModels.image.map(model => ({
190
+ label: model === "mock" ? "Mock (Model testowy)" : model,
191
+ value: model,
192
+ default: tempConfig.imageModel === model
193
+ }));
194
+
195
+ const imageModelSelect = new StringSelectMenuBuilder()
196
+ .setCustomId("setup_image_model")
197
+ .setPlaceholder("Wybierz model obrazów")
198
+ .addOptions(imageOptions);
199
+
200
+ const buttonsRow = new ActionRowBuilder().addComponents(
201
+ new ButtonBuilder()
202
+ .setCustomId("setup_save")
203
+ .setLabel("Zapisz ustawienia")
204
+ .setStyle(ButtonStyle.Success)
205
+ .setEmoji("💾"),
206
+ new ButtonBuilder()
207
+ .setCustomId("setup_cancel")
208
+ .setLabel("Anuluj")
209
+ .setStyle(ButtonStyle.Danger)
210
+ .setEmoji("❌")
211
+ );
212
+
213
+ return {
214
+ embeds: [embed],
215
+ components: [
216
+ new ActionRowBuilder().addComponents(channelSelect),
217
+ new ActionRowBuilder().addComponents(textModelSelect),
218
+ new ActionRowBuilder().addComponents(imageModelSelect),
219
+ buttonsRow
220
+ ]
221
+ };
222
+ }
223
+
224
+ async function sendLogToDiscord(guild, embedToSend) {
225
+ const config = loadConfig(guild.id);
226
+ if (!config.logChannelId) return;
227
+
228
+ try {
229
+ const channel = await guild.channels.fetch(config.logChannelId);
230
+ if (channel) {
231
+ await channel.send({ embeds: [embedToSend] });
232
+ }
233
+ } catch (err) {
234
+ console.warn(`Nie można wysłać logu na kanał ${config.logChannelId}:`, err.message);
235
+ }
236
+ }
237
+
238
+ async function handleAnalysis(interaction, userContent, targetMessage = null) {
239
+ await interaction.deferReply({ flags: [MessageFlags.Ephemeral] });
240
+
241
+ const serverConfig = loadConfig(interaction.guildId);
242
+
243
+ try {
244
+ const { type, payload } = preparePayload(userContent);
245
+
246
+ if (type === "text") {
247
+ payload.model = serverConfig.textModel;
248
+ } else if (type === "image") {
249
+ payload.model = serverConfig.imageModel;
250
+ }
251
+
252
+ console.log(`Wysyłanie zapytania typu: ${type} do API z modelem: ${payload.model}...`);
253
+
254
+ const response = await fetch(`${API_URL}/analyze`, {
255
+ method: "POST",
256
+ headers: {
257
+ "Content-Type": "application/json",
258
+ },
259
+ body: JSON.stringify(payload),
260
+ });
261
+
262
+ if (!response.ok) {
263
+ const errorData = await response.json().catch(() => ({}));
264
+ console.error("Szczegóły błędu z FastAPI:", JSON.stringify(errorData, null, 2));
265
+
266
+ let errorMsg = `Błąd serwera API (Status ${response.status})`;
267
+ if (errorData.detail) {
268
+ if (Array.isArray(errorData.detail)) {
269
+ errorMsg = errorData.detail
270
+ .map(err => `• Pole \`${err.loc.join(".")}\`: ${err.msg}`)
271
+ .join("\n");
272
+ } else {
273
+ errorMsg = errorData.detail;
274
+ }
275
+ }
276
+ throw new Error(errorMsg);
277
+ }
278
+
279
+ const data = await response.json();
280
+
281
+ if (targetMessage) {
282
+ try {
283
+ if (data.is_deepfake) {
284
+ await targetMessage.react('⚠️');
285
+ } else {
286
+ await targetMessage.react('✅');
287
+ }
288
+ } catch (reactError) {
289
+ console.warn("Nie udało się dodać reakcji do wiadomości:", reactError.message);
290
+ }
291
+ }
292
+
293
+ const embedColor = data.is_deepfake ? 0xFF0000 : 0x00FF00;
294
+ const verdictText = data.is_deepfake ? "⚠️ Wykryto potencjalny Deepfake!" : "✅ Zawartość wydaje się oryginalna";
295
+ const progressBar = getProgressBar(data.confidence, data.is_deepfake);
296
+ const confidencePercent = (data.confidence * 100).toFixed(2);
297
+
298
+ const embed = new EmbedBuilder()
299
+ .setColor(embedColor)
300
+ .setTitle("🛡️ Wynik Analizy Treści")
301
+ .setDescription(`**Werdykt:** ${verdictText}`)
302
+ .addFields(
303
+ { name: "Pewność modelu", value: `\`${confidencePercent}%\` \n${progressBar}` },
304
+ { name: "Czas przetwarzania", value: `\`${data.analysis_time.toFixed(3)}s\``, inline: true },
305
+ { name: "Użyty model", value: `\`${data.model_used}\``, inline: true },
306
+ { name: "Format danych", value: `\`${data.content_type.toUpperCase()}\``, inline: true }
307
+ )
308
+ .setTimestamp()
309
+ .setFooter({ text: "Deepfake Detection Service", iconURL: client.user.displayAvatarURL() });
310
+
311
+ const buttonRow = new ActionRowBuilder().addComponents(
312
+ new ButtonBuilder()
313
+ .setCustomId("modelCorrect")
314
+ .setLabel("Model odpowiedział poprawnie")
315
+ .setStyle(ButtonStyle.Success)
316
+ .setEmoji("✅"),
317
+ new ButtonBuilder()
318
+ .setCustomId("reportError")
319
+ .setLabel("Zgłoś błąd analizy")
320
+ .setStyle(ButtonStyle.Danger)
321
+ .setEmoji("⚠️")
322
+ );
323
+
324
+ await interaction.editReply({
325
+ embeds: [embed],
326
+ components: [buttonRow]
327
+ });
328
+
329
+ } catch (error) {
330
+ console.error("Błąd podczas analizy:", error);
331
+ await interaction.editReply({
332
+ content: `❌ Nie udało się przeprowadzić analizy.\n\n**Szczegóły błędu:**\n${error.message}`,
333
+ });
334
+ }
335
+ }
336
+
337
  client.on(Events.InteractionCreate, async (interaction) => {
338
+
339
  if (interaction.isChatInputCommand()) {
340
  if (interaction.commandName === "detect") {
341
  const modal = new ModalBuilder()
 
354
 
355
  await interaction.showModal(modal);
356
  }
357
+
358
+ // ZMIANA: Pobieranie modeli z API na żywo przed pokazaniem setupu
359
+ if (interaction.commandName === "setup") {
360
+ const guildId = interaction.guildId;
361
+ const currentConfig = loadConfig(guildId);
362
+
363
+ // Informujemy Discord, że pobieramy konfigurację z API
364
+ await interaction.deferReply({ flags: [MessageFlags.Ephemeral] });
365
+
366
+ // Pobieramy aktywne modele bezpośrednio z FastAPI
367
+ const availableModels = await fetchAvailableModels();
368
+
369
+ // Zapisujemy w sesji zarówno konfigurację, jak i pobrane modele
370
+ activeSetupSessions.set(guildId, {
371
+ config: { ...currentConfig },
372
+ availableModels
373
+ });
374
+
375
+ const setupView = generateSetupView(currentConfig, availableModels);
376
+ await interaction.editReply(setupView);
377
+ }
378
+ }
379
+
380
+ // OBSŁUGA ZMIANY KANAŁU LOGÓW
381
+ if (interaction.isChannelSelectMenu()) {
382
+ if (interaction.customId === "setup_log_channel") {
383
+ const guildId = interaction.guildId;
384
+ const tempSession = activeSetupSessions.get(guildId);
385
+ if (tempSession) {
386
+ tempSession.config.logChannelId = interaction.values[0];
387
+ await interaction.update(generateSetupView(tempSession.config, tempSession.availableModels));
388
+ }
389
+ }
390
+ }
391
+
392
+ // OBSŁUGA ZMIANY MODELI
393
+ if (interaction.isStringSelectMenu()) {
394
+ const guildId = interaction.guildId;
395
+ const tempSession = activeSetupSessions.get(guildId);
396
+
397
+ if (tempSession) {
398
+ if (interaction.customId === "setup_text_model") {
399
+ tempSession.config.textModel = interaction.values[0];
400
+ } else if (interaction.customId === "setup_image_model") {
401
+ tempSession.config.imageModel = interaction.values[0];
402
+ }
403
+ await interaction.update(generateSetupView(tempSession.config, tempSession.availableModels));
404
+ }
405
+ }
406
+
407
+ if (interaction.isMessageContextMenuCommand()) {
408
+ if (interaction.commandName === "Przeanalizuj tekst") {
409
+ const targetMessage = interaction.targetMessage;
410
+
411
+ let contentToAnalyze = targetMessage.content;
412
+ const attachment = targetMessage.attachments.first();
413
+
414
+ if (attachment) {
415
+ contentToAnalyze = attachment.url;
416
+ }
417
+
418
+ if (!contentToAnalyze || contentToAnalyze.trim().length === 0) {
419
+ return interaction.reply({
420
+ content: "❌ Ta wiadomość nie zawiera tekstu ani załączników do analizy.",
421
+ flags: [MessageFlags.Ephemeral]
422
+ });
423
+ }
424
+
425
+ await handleAnalysis(interaction, contentToAnalyze, targetMessage);
426
+ }
427
  }
428
 
429
  if (interaction.isModalSubmit()) {
430
  if (interaction.customId === "detectModal") {
431
  const userContent = interaction.fields.getTextInputValue("detectInput");
432
+ await handleAnalysis(interaction, userContent);
433
+ }
434
+ }
435
 
436
+ if (interaction.isButton()) {
437
+ const guildId = interaction.guildId;
438
 
439
+ if (interaction.customId === "setup_save") {
440
+ const tempSession = activeSetupSessions.get(guildId);
441
+ if (tempSession) {
442
+ saveConfig(guildId, tempSession.config);
443
+ activeSetupSessions.delete(guildId);
444
+ await interaction.update({
445
+ content: "✅ **Ustawienia zostały pomyślnie zapisane!**",
446
+ embeds: [],
447
+ components: []
 
 
448
  });
449
+ }
450
+ }
451
 
452
+ if (interaction.customId === "setup_cancel") {
453
+ activeSetupSessions.delete(guildId);
454
+ await interaction.update({
455
+ content: "❌ **Konfiguracja została anulowana.**",
456
+ embeds: [],
457
+ components: []
458
+ });
459
+ }
460
+
461
+ if (interaction.customId === "reportError") {
462
+ await interaction.reply({
463
+ content: "✅ **Dziękujemy!** Twoje zgłoszenie błędu zostało zarejestrowane.",
464
+ flags: [MessageFlags.Ephemeral]
465
+ });
 
 
466
 
467
+ console.log(`[RAPORT BŁĘDU] Użytkownik ${interaction.user.tag} (ID: ${interaction.user.id}) zgłosił błędną klasyfikację.`);
468
 
469
+ const originalEmbed = interaction.message.embeds[0];
470
+ if (originalEmbed) {
471
+ const logEmbed = EmbedBuilder.from(originalEmbed)
472
+ .setColor(0xFFAA00)
473
+ .setTitle("⚠️ Zgłoszenie błędu analizy")
474
+ .setDescription(`Użytkownik **${interaction.user.tag}** (ID: \`${interaction.user.id}\`) zgłosił błąd analizy w poniższym raporcie.`);
475
+
476
+ await sendLogToDiscord(interaction.guild, logEmbed);
477
+ }
478
+ }
479
 
480
+ if (interaction.customId === "modelCorrect") {
481
+ await interaction.reply({
482
+ content: "✅ **Dziękujemy!** Twoje potwierdzenie zostało pomyślnie zapisane.",
483
+ flags: [MessageFlags.Ephemeral]
484
+ });
 
485
 
486
+ console.log(`[POTWIERDZENIE] Użytkownik ${interaction.user.tag} (ID: ${interaction.user.id}) potwierdził poprawną klasyfikację.`);
 
 
487
 
488
+ const originalEmbed = interaction.message.embeds[0];
489
+ if (originalEmbed) {
490
+ const logEmbed = EmbedBuilder.from(originalEmbed)
491
+ .setColor(0x00AAFF)
492
+ .setTitle("✅ Potwierdzona poprawność analizy")
493
+ .setDescription(`Użytkownik **${interaction.user.tag}** (ID: \`${interaction.user.id}\`) potwierdził poprawność raportu.`);
494
+
495
+ await sendLogToDiscord(interaction.guild, logEmbed);
496
  }
497
  }
498
  }