import dotenv from "dotenv"; dotenv.config(); import { Client, GatewayIntentBits, Events, ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder, MessageFlags, ApplicationCommandType, EmbedBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent, ], }); const API_URL = process.env.API_URL || "http://127.0.0.1:8000"; client.once(Events.ClientReady, async () => { console.log(`Bot ready: ${client.user.tag}`); try { await client.application.commands.set([ { name: "detect", description: "Otwiera okienko do wklejenia linku lub tekstu do analizy", type: ApplicationCommandType.ChatInput }, { name: "Przeanalizuj tekst", type: ApplicationCommandType.Message } ]); console.log("Pomyślnie zarejestrowano komendy (/detect oraz menu kontekstowe)"); } catch (error) { console.error("Błąd podczas rejestracji komend:", error); } }); function preparePayload(input) { const trimmed = input.trim(); const isUrl = trimmed.startsWith("http://") || trimmed.startsWith("https://"); if (isUrl) { const lowerUrl = trimmed.toLowerCase(); if (lowerUrl.endsWith(".png") || lowerUrl.endsWith(".jpg") || lowerUrl.endsWith(".jpeg") || lowerUrl.endsWith(".webp") || lowerUrl.endsWith(".gif")) { return { type: "image", payload: { image_url: trimmed, content_type: "image" } }; } else if (lowerUrl.endsWith(".mp4") || lowerUrl.endsWith(".webm") || lowerUrl.endsWith(".mov") || lowerUrl.endsWith(".avi")) { return { type: "video", payload: { video_url: trimmed, content_type: "video" } }; } else { return { type: "file", payload: { file_url: trimmed, content_type: "file" } }; } } return { type: "text", payload: { text: trimmed, content_type: "text" } }; } function getProgressBar(confidence, isDeepfake) { const totalBlocks = 10; const filledBlocks = Math.min(totalBlocks, Math.max(0, Math.round(confidence * totalBlocks))); const emptyBlocks = totalBlocks - filledBlocks; const blockEmoji = isDeepfake ? "🟥" : "🟩"; return blockEmoji.repeat(filledBlocks) + "⬛".repeat(emptyBlocks); } async function handleAnalysis(interaction, userContent, targetMessage = null) { await interaction.deferReply({ flags: [MessageFlags.Ephemeral] }); try { const { type, payload } = preparePayload(userContent); console.log(`Wysyłanie zapytania typu: ${type} do API...`); const response = await fetch(`${API_URL}/analyze`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(payload), }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); console.error("Szczegóły błędu z FastAPI:", JSON.stringify(errorData, null, 2)); let errorMsg = `Błąd serwera API (Status ${response.status})`; if (errorData.detail) { if (Array.isArray(errorData.detail)) { errorMsg = errorData.detail .map(err => `• Pole \`${err.loc.join(".")}\`: ${err.msg}`) .join("\n"); } else { errorMsg = errorData.detail; } } throw new Error(errorMsg); } const data = await response.json(); if (targetMessage) { try { if (data.is_deepfake) { await targetMessage.react('⚠️'); } else { await targetMessage.react('✅'); } } catch (reactError) { console.warn("Nie udało się dodać reakcji do wiadomości:", reactError.message); } } const embedColor = data.is_deepfake ? 0xFF0000 : 0x00FF00; const verdictText = data.is_deepfake ? "⚠️ Wykryto potencjalny Deepfake!" : "✅ Zawartość wydaje się oryginalna"; const progressBar = getProgressBar(data.confidence, data.is_deepfake); const confidencePercent = (data.confidence * 100).toFixed(2); const embed = new EmbedBuilder() .setColor(embedColor) .setTitle("🛡️ Wynik Analizy Treści") .setDescription(`**Werdykt:** ${verdictText}`) .addFields( { name: "Pewność modelu", value: `\`${confidencePercent}%\` \n${progressBar}` }, { name: "Czas przetwarzania", value: `\`${data.analysis_time.toFixed(3)}s\``, inline: true }, { name: "Użyty model", value: `\`${data.model_used}\``, inline: true }, { name: "Format danych", value: `\`${data.content_type.toUpperCase()}\``, inline: true } ) .setTimestamp() .setFooter({ text: "Deepfake Detection Service", iconURL: client.user.displayAvatarURL() }); // TWORZENIE PRZYCISKÓW const buttonRow = new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId("modelCorrect") .setLabel("Model odpowiedział poprawnie") .setStyle(ButtonStyle.Success) .setEmoji("✅"), new ButtonBuilder() .setCustomId("reportError") .setLabel("Zgłoś błąd analizy") .setStyle(ButtonStyle.Danger) .setEmoji("⚠️") ); await interaction.editReply({ embeds: [embed], components: [buttonRow] }); } catch (error) { console.error("Błąd podczas analizy:", error); await interaction.editReply({ content: `❌ Nie udało się przeprowadzić analizy.\n\n**Szczegóły błędu:**\n${error.message}`, }); } } client.on(Events.InteractionCreate, async (interaction) => { if (interaction.isChatInputCommand()) { if (interaction.commandName === "detect") { const modal = new ModalBuilder() .setCustomId("detectModal") .setTitle("Detektor Deepfake"); const textInput = new TextInputBuilder() .setCustomId("detectInput") .setLabel("Wklej tutaj tekst lub link (obraz/wideo):") .setStyle(TextInputStyle.Paragraph) .setPlaceholder("Wklej zawartość...") .setRequired(true); const actionRow = new ActionRowBuilder().addComponents(textInput); modal.addComponents(actionRow); await interaction.showModal(modal); } } if (interaction.isMessageContextMenuCommand()) { if (interaction.commandName === "Przeanalizuj tekst") { const targetMessage = interaction.targetMessage; let contentToAnalyze = targetMessage.content; const attachment = targetMessage.attachments.first(); if (attachment) { contentToAnalyze = attachment.url; } if (!contentToAnalyze || contentToAnalyze.trim().length === 0) { return interaction.reply({ content: "❌ Ta wiadomość nie zawiera tekstu ani załączników do analizy.", flags: [MessageFlags.Ephemeral] }); } await handleAnalysis(interaction, contentToAnalyze, targetMessage); } } if (interaction.isModalSubmit()) { if (interaction.customId === "detectModal") { const userContent = interaction.fields.getTextInputValue("detectInput"); await handleAnalysis(interaction, userContent); } } // GUZIKI if (interaction.isButton()) { // ZGŁOSZENIE BŁĘDU if (interaction.customId === "reportError") { await interaction.reply({ content: "✅ **Dziękujemy!** Twoje zgłoszenie błędu zostało zarejestrowane. Pomoże nam ono udoskonalić algorytmy detekcji.", flags: [MessageFlags.Ephemeral] }); console.log(`[RAPORT BŁĘDU] Użytkownik ${interaction.user.tag} (ID: ${interaction.user.id}) zgłosił błędną klasyfikację bota.`); } // POTWIERDZENIE POPRAWNOŚCI if (interaction.customId === "modelCorrect") { await interaction.reply({ content: "✅ **Dziękujemy!** Twoje potwierdzenie zostało pomyślnie zapisane. Cieszymy się, że model zadziałał prawidłowo.", flags: [MessageFlags.Ephemeral] }); console.log(`[POTWIERDZENIE] Użytkownik ${interaction.user.tag} (ID: ${interaction.user.id}) potwierdził poprawną klasyfikację bota.`); } } }); client.on(Events.MessageCreate, (message) => { if (message.author.bot) return; console.log(`Message from ${message.author.tag}: ${message.content}`); }); client.login(process.env.DISCORD_TOKEN);