Code viewer for World: Spam/Ham Analyser (clone b...
$('body').css("margin", "10px");
$('body').css("padding", "10px");

document.write(`
<h2>Spam/Ham Analyser</h2>

<div style="display:flex; gap:20px; background:#e6ffe6; padding:20px; border-radius:10px; border:2px solid #b2d8b2; width:1000px;">
    <div>
        <label>Gemini API Key:</label><br>
        <input id="apiKey1" type="text" style="width:250px; margin-top:10px">
    </div>

    <div>
        <label>Groq API Key:</label><br>
        <input id="apiKey2" type="text" style="width:250px; margin-top:10px">
    </div>

    <div>
        <button onclick="setKeys()" class="ab-normbutton"
            style="height:40px;padding:0 15px;cursor:pointer;background:#4CAF50;color:white;border:none;border-radius:5px; margin-top:15px">
            <b>Set API Keys</b>
        </button>
    </div>

</div>

<div style="display:flex; gap:30px;background:#e6ffe6;padding:20px;border-radius:10px;border:2px solid #b2d8b2;width:1000px;margin-top:10px;">
    <div>
        <label>Enter your message:</label><br>
        <textarea id="promptInput" rows="4" cols="50" style="margin-top:5px" placeholder="Write your message here..."></textarea>
    </div>

    <div>
        <button onclick="analyse()" id="btnAnalyse"
            style="height:40px;padding:0 15px;margin-top:30px;cursor:pointer;background:#4CAF50;color:white;border:none;border-radius:5px; font-size: 15px;">
            <b>Analyse</b>
        </button>
    </div>
</div>

<hr>

<div style="background:#e6ffe6;padding:20px;border-radius:10px;border:2px solid #b2d8b2;margin-top:20px; width:1000px;">
    <h3>API Responses</h3>

    <div style="display:flex;gap:20px;flex-wrap:wrap;">
        <!-- Gemini -->
        <div style="flex:1; padding:10px; background:#d9f2d9; border:2px solid #4CAF50; border-radius:10px;">
            <h4 style="margin-bottom:10px;">Gemini Response</h4>
            <div style="padding:10px; background:white; border:1px solid #ccc; border-radius:5px; margin-bottom:10px;">
                <strong>Status:</strong> <span id="status1">N/A</span>
            </div>
            <div style="padding:10px; background:white; border:1px solid #ccc; border-radius:5px;">
                <strong>Accuracy:</strong> <span id="accuracy1">0%</span>
            </div>
        </div>

        <!-- Groq -->
        <div style="flex:1; padding:10px; background:#d9f2d9; border:2px solid #4CAF50; border-radius:10px;">
            <h4 style="margin-bottom:10px;">Groq Response</h4>
            <div style="padding:10px; background:white; border:1px solid #ccc; border-radius:5px; margin-bottom:10px;">
                <strong>Status:</strong> <span id="status2">N/A</span>
            </div>
            <div style="padding:10px; background:white; border:1px solid #ccc; border-radius:5px;">
                <strong>Accuracy:</strong> <span id="accuracy2">0%</span>
            </div>
        </div>
    </div>

    <!-- Dataset Result (hidden by default) -->
    <div id="datasetResultBox" style="display:none; margin-top:20px; padding:10px; background:white; border:1px solid #ccc; border-radius:5px;">
        <strong>Correct response: </strong> <span id="datasetResult"></span>
    </div>

    <!-- Custom Alert (hidden by default) -->
    <div id="customAlertBox" style="display:none; margin-top:10px; padding:10px; background:#ffe6e6; border:1px solid #ff4d4d; border-radius:5px; color:red;">
        <span id="custom-alert"></span>
    </div>
</div>
`);



// Global variables
let GEMINI_KEY = "";
let GROQ_KEY = "";

let DATASET = [];

const GEMINI_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent";
const GROQ_URL   = "https://api.groq.com/openai/v1/chat/completions";

let startTime = null;

const DATASET_URL = "https://raw.githubusercontent.com/justmarkham/DAT8/master/data/sms.tsv";

// function to set API keys
function setKeys() {

    const k1 = document.getElementById("apiKey1").value.trim();
    const k2 = document.getElementById("apiKey2").value.trim();

    if (!k1 || !k2) {
        showCustomAlert("Both API keys are required");
        return;
    }
    if (k1 === k2) {
        showCustomAlert("Both keys cannot be the same.");
        return;
    }

    GEMINI_KEY = k1;
    GROQ_KEY = k2;

    showCustomAlert("API Keys set successfully");
}

// function to analyse message whether its spam or ham
function analyse() {
    //begin by setting status and accuracy as N/A and 0% 
    document.getElementById("status1").innerText = "N/A";
    document.getElementById("status2").innerText = "N/A";
    document.getElementById("accuracy1").innerText = "0%";
    document.getElementById("accuracy2").innerText = "0%";
    
    // Check if both API keys are set
    if(!GEMINI_KEY || !GROQ_KEY){
        showCustomAlert("Set API keys.");
        return;
    }
    // Check if dataset is choosen
    if(DATASET.length === 0){
        showCustomAlert("Choose Dataset.");
        return;
    }
    //Check if the message is provided to analyse
    const message = document.getElementById("promptInput").value.trim();
    if (!message) {
        showCustomAlert("Please enter a message.");
        return;
    }
    
    console.info("Analysing started");
    const prompt = `Classify this text strictly as "spam" or "ham": ${message}`;
    console.log("prompt: ", prompt);
    startTimer();
    analyseWithGroq(prompt);
    analyseWithGemini(prompt);
}

// function to analyse the message using Gemini Call, passing promt as varible
function analyseWithGemini(prompt) {
    document.getElementById("status1").innerText = "Analyzing...";
    console.info("Analysing with Gemini started");

    fetch(GEMINI_URL + "?key=" + GEMINI_KEY, {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({
            contents: [{ parts: [{ text: prompt }] }]
        }),
    })
    .then(res => res.json())
    .then(data => {

        const text = data?.candidates?.[0]?.content?.parts?.[0]?.text || "error";
        const cleaned = text.toLowerCase().includes("spam") ? "spam" : "ham";

        document.getElementById("status1").innerText = cleaned;
        console.log("Gemini response: ", cleaned);
        
        computeAccuracy(cleaned, "accuracy1");
    });
}

// function to analyse the message using Groq Call, passing promt as varible
function analyseWithGroq(prompt) {

    document.getElementById("status2").innerText = "Analyzing...";
    console.info("Analysing with Groq started");

    fetch(GROQ_URL, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
            "Authorization": "Bearer " + GROQ_KEY
        },
        body: JSON.stringify({
            model: "llama-3.1-8b-instant",
            messages: [{ role: "user", content: prompt }]
        }),
    })
    .then(res => res.json())
    .then(data => {

        const text = data?.choices?.[0]?.message?.content || "error";
        const cleaned = text.toLowerCase().includes("spam") ? "spam" : "ham";

        document.getElementById("status2").innerText = cleaned;
        console.log("Groq response: ", cleaned);

        computeAccuracy(cleaned, "accuracy2");
    });
}

// For Accuracy & Dataset Comparison

// Function to load CSV dataset
function loadDataset() {
    const file = document.getElementById("datasetFile").files[0];
    if (!file) return;

    const reader = new FileReader();

    reader.onload = function(e) {
        const text = e.target.result;
        DATASET = parseCSV(text);
        console.log("Dataset loaded:", DATASET);
        showCustomAlert("Dataset loaded successfully!");
    };

    reader.readAsText(file);
}

// Robust TSV parser
function parseTSV(tsvText) {
    const lines = tsvText.trim().split("\n");
    const result = [];

    for (let i = 1; i < lines.length; i++) {
        const [label, message] = lines[i].split("\t");

        result.push({
            label: label.trim().toLowerCase(),
            message: message.trim()
        });
    }

    return result;
}

// Start timer when user types or clicks Analyse (to identify the total time taken to analyse)
function startTimer() {
    startTime = Date.now();
}

// Function to get actual label for a given message from dataset
function getActualLabelFromDataset(userMessage) {
    const lowerMessage = userMessage.toLowerCase();
    const userWords = lowerMessage.split(/\s+/);

    const row = DATASET.find(r => {
        const datasetWords = r.message.toLowerCase().split(/\s+/);
        // Match if at least 50% of user words appear in dataset
        const matchCount = userWords.filter(word => datasetWords.includes(word)).length;
        return matchCount / userWords.length >= 0.5;
    });

    return row ? row.label.toLowerCase() : "unknown";// return spam/ham if message is found, else return unknown
}

// Compute accuracy as a percentage of two factors (response and time taken)
function computeAccuracy(prediction, accuracyElementId) {
    const userMessage = document.getElementById("promptInput").value.trim();
    const actual = getActualLabelFromDataset(userMessage);

    const datasetBox = document.getElementById("datasetResultBox");

    if (actual === "unknown") {
        document.getElementById(accuracyElementId).innerText = "--";
        datasetBox.style.display = "none"; // to hide if message not found
        showCustomAlert("Message not found in dataset.");
        return;
    }

    datasetBox.style.display = "block"; // to show dataset result
    document.getElementById("datasetResult").innerText = actual;

    const factors = [];
    // Factor 1: Correctness (weight 70%)
    const correctnessScore = (prediction.toLowerCase() === actual) ? 100 : 0;
    factors.push({ score: correctnessScore, weight: 0.7 });

    // Factor 2: Time taken (weight 30%)
    let timeScore = 100; // start at 100%
    if (startTime) {
        const elapsedMilliseconds = Date.now() - startTime;
        console.log("Elapsed milliseconds:", elapsedMilliseconds);

        // Reduce accuracy by 0.0001% per 1ms
        timeScore = Math.max(0, 100 - (elapsedMilliseconds * 0.0001));
    }
    factors.push({ score: timeScore, weight: 0.3 });

    // Weighted average
    const finalAccuracy = factors.reduce((sum, f) => sum + f.score * f.weight, 0);
    document.getElementById(accuracyElementId).innerText = finalAccuracy.toFixed(2) + "%";
}

// function to show alert messages
function showCustomAlert(message) {
    const alertBox = document.getElementById("customAlertBox");
    const alertSpan = document.getElementById("custom-alert");

    alertSpan.innerText = message;
    alertBox.style.display = "block";

    // Automatically hide after 10 seconds
    setTimeout(() => {
        alertBox.style.display = "none";
    }, 10000);
}

// Load dataset automatically from internet
function loadDataset() {

    showCustomAlert("Loading dataset from internet...");

    fetch(DATASET_URL)
        .then(res => {
            if (!res.ok) throw new Error("Network error");
            return res.text();
        })
        .then(text => {
            DATASET = parseTSV(text);
            console.log("Dataset loaded:", DATASET);
            showCustomAlert("Dataset loaded successfully!");
        })
        .catch(err => {
            console.error(err);
            showCustomAlert("Failed to load dataset from internet.");
        });
}

// Load dataset automatically on page load
window.onload = function() {
    loadDataset();
};