Gradii commited on
Commit
db0fed3
·
1 Parent(s): 0ca9fa7
backend/app/api/routes.py CHANGED
@@ -16,7 +16,7 @@ from app.services.image_analyzer import analyze_image
16
  from app.core.config import get_settings
17
  from app.utils.exceptions import DeepfakeDetectionError, SetupRequiredError
18
  from app.core.limiter import limiter
19
- from app.config_manager import save_guild_config
20
 
21
  logger = logging.getLogger(__name__)
22
 
@@ -87,6 +87,18 @@ async def save_discord_guild_setup(guild_id: str, payload: GuildConfigSchema):
87
  "config": config_dict
88
  }
89
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  @router.post(
91
  "/analyze",
92
  response_model=AnalysisResponse,
@@ -102,6 +114,8 @@ async def save_discord_guild_setup(guild_id: str, payload: GuildConfigSchema):
102
  )
103
  @limiter.limit("1/5seconds")
104
  async def analyze(request: Request, payload: AnalysisRequest) -> AnalysisResponse:
 
 
105
  if isinstance(payload, TextAnalysisRequest):
106
  content_type = "text"
107
  elif isinstance(payload, ImageAnalysisRequest):
@@ -113,12 +127,6 @@ async def analyze(request: Request, payload: AnalysisRequest) -> AnalysisRespons
113
  )
114
 
115
  settings = get_settings()
116
- models = settings.AVAILABLE_MODELS.get(content_type)
117
- if not models:
118
- raise HTTPException(status_code=400, detail=f"No model available for {content_type} analysis")
119
-
120
- model = models[0]
121
- logger.info(f"Received {content_type} analysis request, model: {model}")
122
 
123
  try:
124
  if content_type == "text":
@@ -127,7 +135,7 @@ async def analyze(request: Request, payload: AnalysisRequest) -> AnalysisRespons
127
  if len(payload.text) < 50:
128
  raise ValueError("Text content must be at least 50 characters")
129
 
130
- analysis_result = await analyze_text(payload.text)
131
 
132
  elif content_type == "image":
133
  image_bytes = await download_file(str(payload.image_url))
@@ -136,10 +144,12 @@ async def analyze(request: Request, payload: AnalysisRequest) -> AnalysisRespons
136
  if len(image_bytes) > settings.MAX_CONTENT_SIZES["image"]:
137
  raise ValueError(f"Image size exceeds maximum of {settings.MAX_CONTENT_SIZES['image']} bytes")
138
 
139
- analysis_result = await analyze_image(image_bytes)
140
 
141
  except ValueError as e:
142
  raise HTTPException(status_code=400, detail=str(e))
 
 
143
  except DeepfakeDetectionError as e:
144
  raise HTTPException(status_code=e.status_code, detail=e.message)
145
  except Exception as e:
@@ -147,11 +157,12 @@ async def analyze(request: Request, payload: AnalysisRequest) -> AnalysisRespons
147
  raise HTTPException(status_code=500, detail=f"Failed to analyze {content_type}")
148
 
149
  logger.info(f"{content_type.capitalize()} analysis completed. Result: {analysis_result}")
 
150
 
151
  return AnalysisResponse(
152
  is_deepfake=analysis_result["is_deepfake"],
153
  confidence=analysis_result["confidence"],
154
  analysis_time=analysis_result["analysis_time"],
155
- used_model=model,
156
  content_type=content_type,
157
  )
 
16
  from app.core.config import get_settings
17
  from app.utils.exceptions import DeepfakeDetectionError, SetupRequiredError
18
  from app.core.limiter import limiter
19
+ from app.config_manager import _load_all_configs, save_guild_config
20
 
21
  logger = logging.getLogger(__name__)
22
 
 
87
  "config": config_dict
88
  }
89
 
90
+ @router.get("/guilds/{guild_id}/config", tags=["Setup"])
91
+ async def get_discord_guild_config(guild_id: str):
92
+ """Zwraca zapisaną konfigurację dla konkretnego serwera Discord."""
93
+ configs = _load_all_configs()
94
+ guild_config = configs.get(guild_id, {})
95
+
96
+ return {
97
+ "active_text_model": guild_config.get("active_text_model", "none"),
98
+ "active_image_model": guild_config.get("active_image_model", "none"),
99
+ "log_channel_id": guild_config.get("log_channel_id", None)
100
+ }
101
+
102
  @router.post(
103
  "/analyze",
104
  response_model=AnalysisResponse,
 
114
  )
115
  @limiter.limit("1/5seconds")
116
  async def analyze(request: Request, payload: AnalysisRequest) -> AnalysisResponse:
117
+ guild_id = payload.guild_id
118
+
119
  if isinstance(payload, TextAnalysisRequest):
120
  content_type = "text"
121
  elif isinstance(payload, ImageAnalysisRequest):
 
127
  )
128
 
129
  settings = get_settings()
 
 
 
 
 
 
130
 
131
  try:
132
  if content_type == "text":
 
135
  if len(payload.text) < 50:
136
  raise ValueError("Text content must be at least 50 characters")
137
 
138
+ analysis_result = await analyze_text(payload.text, guild_id)
139
 
140
  elif content_type == "image":
141
  image_bytes = await download_file(str(payload.image_url))
 
144
  if len(image_bytes) > settings.MAX_CONTENT_SIZES["image"]:
145
  raise ValueError(f"Image size exceeds maximum of {settings.MAX_CONTENT_SIZES['image']} bytes")
146
 
147
+ analysis_result = await analyze_image(image_bytes, guild_id)
148
 
149
  except ValueError as e:
150
  raise HTTPException(status_code=400, detail=str(e))
151
+ except SetupRequiredError as e:
152
+ raise HTTPException(status_code=400, detail=str(e))
153
  except DeepfakeDetectionError as e:
154
  raise HTTPException(status_code=e.status_code, detail=e.message)
155
  except Exception as e:
 
157
  raise HTTPException(status_code=500, detail=f"Failed to analyze {content_type}")
158
 
159
  logger.info(f"{content_type.capitalize()} analysis completed. Result: {analysis_result}")
160
+ used_model = analysis_result.get("used_model", settings.AVAILABLE_MODELS.get(content_type)[0])
161
 
162
  return AnalysisResponse(
163
  is_deepfake=analysis_result["is_deepfake"],
164
  confidence=analysis_result["confidence"],
165
  analysis_time=analysis_result["analysis_time"],
166
+ used_model=used_model,
167
  content_type=content_type,
168
  )
backend/app/models/schemas.py CHANGED
@@ -5,6 +5,7 @@ from typing import Union, Literal, Optional, Dict, List
5
  class TextAnalysisRequest(BaseModel):
6
  content_type: Literal["text"]
7
  text: str = Field(..., description="Text content to analyze for deepfake detection")
 
8
 
9
  class Config:
10
  json_schema_extra = {
@@ -18,6 +19,7 @@ class TextAnalysisRequest(BaseModel):
18
  class ImageAnalysisRequest(BaseModel):
19
  content_type: Literal["image"]
20
  image_url: HttpUrl = Field(..., description="URL of the image to analyze")
 
21
 
22
  class Config:
23
  json_schema_extra = {
@@ -28,37 +30,10 @@ class ImageAnalysisRequest(BaseModel):
28
  }
29
 
30
 
31
- class VideoAnalysisRequest(BaseModel):
32
- content_type: Literal["video"]
33
- video_url: HttpUrl = Field(..., description="URL of the video to analyze")
34
-
35
- class Config:
36
- json_schema_extra = {
37
- "example": {
38
- "content_type": "video",
39
- "video_url": "https://example.com/video.mp4"
40
- }
41
- }
42
-
43
-
44
- class FileAnalysisRequest(BaseModel):
45
- content_type: Literal["file"]
46
- file_url: HttpUrl = Field(..., description="URL of the file to analyze")
47
-
48
- class Config:
49
- json_schema_extra = {
50
- "example": {
51
- "content_type": "file",
52
- "file_url": "https://example.com/video.mp4"
53
- }
54
- }
55
-
56
 
57
  AnalysisRequest = Union[
58
  TextAnalysisRequest,
59
- ImageAnalysisRequest,
60
- VideoAnalysisRequest,
61
- FileAnalysisRequest,
62
  ]
63
 
64
 
 
5
  class TextAnalysisRequest(BaseModel):
6
  content_type: Literal["text"]
7
  text: str = Field(..., description="Text content to analyze for deepfake detection")
8
+ guild_id: str = Field(..., description="ID serwera Discord, z którego pochodzi żądanie")
9
 
10
  class Config:
11
  json_schema_extra = {
 
19
  class ImageAnalysisRequest(BaseModel):
20
  content_type: Literal["image"]
21
  image_url: HttpUrl = Field(..., description="URL of the image to analyze")
22
+ guild_id: str = Field(..., description="ID serwera Discord, z którego pochodzi żądanie")
23
 
24
  class Config:
25
  json_schema_extra = {
 
30
  }
31
 
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
  AnalysisRequest = Union[
35
  TextAnalysisRequest,
36
+ ImageAnalysisRequest
 
 
37
  ]
38
 
39
 
configManager.js DELETED
@@ -1,44 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
-
4
- const filePath = path.resolve("./guildConfigs.json");
5
-
6
- // Domyślne ustawienia (teraz modele są zapisywane dynamicznie w obiekcie)
7
- export const DEFAULT_CONFIG = {
8
- logChannelId: null,
9
- models: {}
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
- const config = data[guildId] || { ...DEFAULT_CONFIG };
19
-
20
- // Upewniamy się, że obiekt "models" zawsze istnieje
21
- if (!config.models) {
22
- config.models = {};
23
- }
24
- return config;
25
- } catch (err) {
26
- console.error("Błąd podczas odczytu konfiguracji:", err);
27
- return { ...DEFAULT_CONFIG };
28
- }
29
- }
30
-
31
- export function saveConfig(guildId, newConfig) {
32
- if (!fs.existsSync(filePath)) {
33
- fs.writeFileSync(filePath, JSON.stringify({}));
34
- }
35
- try {
36
- const data = JSON.parse(fs.readFileSync(filePath, "utf-8"));
37
- data[guildId] = { ...DEFAULT_CONFIG, ...data[guildId], ...newConfig };
38
- fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
39
- return true;
40
- } catch (err) {
41
- console.error("Błąd podczas zapisu konfiguracji:", err);
42
- return false;
43
- }
44
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
guildConfigs.json DELETED
@@ -1 +0,0 @@
1
- {}
 
 
index.js CHANGED
@@ -20,8 +20,6 @@ import {
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,
@@ -77,6 +75,28 @@ async function fetchAvailableModels() {
77
  return null;
78
  }
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  function preparePayload(input, explicitContentType = null) {
81
  const trimmed = input.trim();
82
 
@@ -194,7 +214,7 @@ function generateSetupView(tempConfig, availableModels) {
194
  }
195
 
196
  async function sendLogToDiscord(guild, embedToSend) {
197
- const config = loadConfig(guild.id);
198
  if (!config.logChannelId) return;
199
 
200
  try {
@@ -214,12 +234,7 @@ async function handleAnalysis(interaction, userContent, targetMessage = null, ex
214
 
215
  try {
216
  const { type, payload } = preparePayload(userContent, explicitContentType);
217
-
218
- // DYNAMICZNE POBIERANIE MODELU Z PLIKU KONFIGURACYJNEGO DLA DANEGO FORMATU (np. text, image, video)
219
- const chosenModel = serverConfig.models[type];
220
- if (chosenModel) {
221
- payload.model = chosenModel;
222
- }
223
 
224
  console.log(`Wysyłanie zapytania typu: ${type} do API z modelem: ${payload.model || "domyślny"}...`);
225
 
@@ -329,28 +344,25 @@ client.on(Events.InteractionCreate, async (interaction) => {
329
 
330
  if (interaction.commandName === "setup") {
331
  const guildId = interaction.guildId;
332
- const currentConfig = loadConfig(guildId);
333
-
334
  await interaction.deferReply({ flags: [MessageFlags.Ephemeral] });
335
 
336
- // Pobieramy aktywne modele bezpośrednio z FastAPI
 
337
  const availableModels = await fetchAvailableModels();
338
 
339
- // Jeśli backend nie działa, natychmiast przerywamy i wyświetlamy błąd
340
  if (!availableModels || Object.keys(availableModels).length === 0) {
341
  return interaction.editReply({
342
  content: "❌ **Błąd konfiguracji:** Nie udało się nawiązać połączenia z backendem (FastAPI). Uruchom swój backend w Pythonie i spróbuj ponownie!"
343
  });
344
  }
345
 
346
- // Inicjalizujemy domyślne modele w konfiguracji, jeśli nie były wcześniej ustawione
347
  for (const [contentType, models] of Object.entries(availableModels)) {
348
  if (!currentConfig.models[contentType] && models.length > 0) {
349
  currentConfig.models[contentType] = models[0];
350
  }
351
  }
352
 
353
- // Zapisujemy sesję z konfiguracją oraz pobranymi modelami
354
  activeSetupSessions.set(guildId, {
355
  config: { ...currentConfig },
356
  availableModels
@@ -432,8 +444,9 @@ client.on(Events.InteractionCreate, async (interaction) => {
432
  "Content-Type": "application/json"
433
  },
434
  body: JSON.stringify({
435
- active_text_model: tempSession.config.active_text_model || "none",
436
- log_channel_id: tempSession.config.log_channel_id || null
 
437
  })
438
  });
439
 
 
20
  ChannelType
21
  } from "discord.js";
22
 
 
 
23
  const client = new Client({
24
  intents: [
25
  GatewayIntentBits.Guilds,
 
75
  return null;
76
  }
77
 
78
+ async function fetchGuildConfig(guildId) {
79
+ try {
80
+ const response = await fetch(`${API_URL}/guilds/${guildId}/config`);
81
+ if (response.ok) {
82
+ const data = await response.json();
83
+ return {
84
+ logChannelId: data.log_channel_id,
85
+ models: {
86
+ text: data.active_text_model || "none",
87
+ image: data.active_image_model || "none"
88
+ }
89
+ };
90
+ }
91
+ } catch (err) {
92
+ console.error(`[CONFIG ERROR] Błąd pobierania konfiguracji dla gildii ${guildId}:`, err.message);
93
+ }
94
+ return {
95
+ logChannelId: null,
96
+ models: {}
97
+ };
98
+ }
99
+
100
  function preparePayload(input, explicitContentType = null) {
101
  const trimmed = input.trim();
102
 
 
214
  }
215
 
216
  async function sendLogToDiscord(guild, embedToSend) {
217
+ const config = await fetchGuildConfig(guild.id);
218
  if (!config.logChannelId) return;
219
 
220
  try {
 
234
 
235
  try {
236
  const { type, payload } = preparePayload(userContent, explicitContentType);
237
+ payload.guild_id = interaction.guildId;
 
 
 
 
 
238
 
239
  console.log(`Wysyłanie zapytania typu: ${type} do API z modelem: ${payload.model || "domyślny"}...`);
240
 
 
344
 
345
  if (interaction.commandName === "setup") {
346
  const guildId = interaction.guildId;
347
+
 
348
  await interaction.deferReply({ flags: [MessageFlags.Ephemeral] });
349
 
350
+ // Pobieramy konfigurację bezpośrednio z FastAPI
351
+ const currentConfig = await fetchGuildConfig(guildId);
352
  const availableModels = await fetchAvailableModels();
353
 
 
354
  if (!availableModels || Object.keys(availableModels).length === 0) {
355
  return interaction.editReply({
356
  content: "❌ **Błąd konfiguracji:** Nie udało się nawiązać połączenia z backendem (FastAPI). Uruchom swój backend w Pythonie i spróbuj ponownie!"
357
  });
358
  }
359
 
 
360
  for (const [contentType, models] of Object.entries(availableModels)) {
361
  if (!currentConfig.models[contentType] && models.length > 0) {
362
  currentConfig.models[contentType] = models[0];
363
  }
364
  }
365
 
 
366
  activeSetupSessions.set(guildId, {
367
  config: { ...currentConfig },
368
  availableModels
 
444
  "Content-Type": "application/json"
445
  },
446
  body: JSON.stringify({
447
+ active_text_model: tempSession.config.models?.text || "none",
448
+ active_image_model: tempSession.config.models?.image || "none",
449
+ log_channel_id: tempSession.config.logChannelId || null
450
  })
451
  });
452