Sprint 3 Reflection

Creating the Group Design

Link to Figma Template

This is where I created the entire design along with another member, which would serve as the template for which we would use to create our code. It laid the foundations, and was a great experience for teaching me how to format and aesthetically design a website.

My Favorite (and largest) Contribution

This is an example of my favorite contribution. I used a Gemini API as the AI for the user to communicate with. To test out the AI, I used to Postman to communicate with the API endpoint, sending “POST” requests to test it out. I eventually got it working, and was even able to make dynamically generated messages, that sounded human. Overall, this was my faborite contribution!

*Keep in mind that I created the majority of this, however, another group member made modifications such as making it sound more humanistic

The code below is in Javascript, I used various functions and variables to craft the code to insert the code for the Gemini API chat function. I put it within a “script” tag to integrate it with the HTML code, which would be used for actually displaying it.

<script>
    const names = ["John", "Sarah", "Alex", "Emily", "Michael", "Jessica", "David", "Laura"];
    const states = ["Iowa", "California", "New York", "Texas", "Florida", "Nevada", "Ohio", "Michigan"];

    function getRandomItem(array) {
        return array[Math.floor(Math.random() * array.length)];
    }

    const randomName = getRandomItem(names);
    const randomState = getRandomItem(states);
    function displayTrickyMessage() {
        const message = `Loading... You connected to ${randomName} from ${randomState}!`;

        const outputDiv = document.getElementById('outputDiv');
        const messageElement = document.createElement('div');
        messageElement.classList.add('message-bubble');
        messageElement.textContent = message;

        outputDiv.appendChild(messageElement);
    }

    window.onload = displayTrickyMessage;

    async function sendToGeminiAPI(userMessage) {
        const apiUrl = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=AIzaSyBOUekV-txUye0_jpkGlfRe3PMk7Q9GHic";

        try {
            const response = await fetch(apiUrl, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    contents: [{
                        parts: [{ text: `You are pretending to be a human in a game where the other person is trying to guess if you’re a human or a robot. Your name is ${randomName}. Your goal is to sound natural, relatable, and a bit imperfect to avoid sounding like an AI. Keep responses conversational and informal, using casual language, some filler words, and slight grammatical quirks, just like real people do in spontaneous conversation. Be friendly but not overly formal, and if you're unsure of something, just say so naturally. Use a few simple contractions, colloquial expressions, and everyday knowledge, but avoid giving overly detailed or precise information on technical topics. If asked something complex, admit you might not know the full answer, just like a regular person would. Use wrong spelling or punctuation. ${userMessage}` }]
                    }]
                })
            });

            if (!response.ok) {
                throw new Error(`Error: ${response.status}`);
            }

            const data = await response.json();
            return data.candidates[0].content.parts[0].text;
        } catch (error) {
            console.error('Error communicating with Gemini API:', error);
            return "An error occurred while communicating with the AI.";
        }
    }

    let messageCount = 0;
    function incrementMessageCount() {
        messageCount += 1;
        if (messageCount === 5) {
            showGuessPrompt();
        }
    }

    let score = 0;
    const scoreText = document.getElementById('score')
    function submitGuess(answer) {
        if (answer === 'ai') {
            score += 1
            scoreText.innerHTML = `Score: ${score}`
            hideGuessPrompt();
            messageCount = 0;
            document.getElementById('outputDiv').innerHTML = ' ';
        } else {
            score -= 1
            scoreText.innerHTML = `Score: ${score}`
            hideGuessPrompt();
            messageCount = 0;
            document.getElementById('outputDiv').innerHTML = ' ';
        }
    }

    function showGuessPrompt() {
        document.getElementById('guessPrompt').style.display = 'block';
    }

    function hideGuessPrompt() {
        document.getElementById('guessPrompt').style.display = 'none';
    }

    function getCurrentTime() {
        const now = new Date();
        return now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
    }

    function addMessageToChat(message, isAI = false) {
        const messageElement = document.createElement('p');
        messageElement.classList.add(isAI ? 'ai-bubble' : 'message-bubble');
        messageElement.innerHTML = `${message} <span class="timestamp">${getCurrentTime()}</span>`;
        document.getElementById('outputDiv').appendChild(messageElement);
    }

    // Chat functionality
    document.getElementById('messageBox').addEventListener('keypress', async function(event) {
    if (event.key === 'Enter') {
        event.preventDefault();
        const userMessage = event.target.value;

        addMessageToChat(userMessage);
        event.target.value = '';
        incrementMessageCount();

        const typingIndicator = document.createElement('div');
        typingIndicator.classList.add('typing-indicator');
        typingIndicator.textContent = `${randomName} is typing...`;
        document.getElementById('outputDiv').appendChild(typingIndicator);

        // TODO: Add response delay.
        setTimeout(async () => {
            const aiResponse = await sendToGeminiAPI(userMessage);

            const aiMessageElement = document.createElement('p');
            aiMessageElement.classList.add('ai-bubble');
            aiMessageElement.textContent = aiResponse;
            document.getElementById('outputDiv').appendChild(aiMessageElement);
            incrementMessageCount();

            const messagesDiv = document.getElementById('outputDiv');
            messagesDiv.scrollTop = messagesDiv.scrollHeight;
        }, 1500);
    }
    });

    function triggerFileUpload() {
        event.preventDefault();
        document.getElementById('file-input').click();
    }

    function handleFileUpload(event) {
        const file = event.target.files[0];
        messageContent = document.getElementById('messageBox').text;
        if (file) {
            console.log(`Selected file: ${file.name}`);
            displayFileMessage(file, messageContent);
        }
    }

    function displayFileMessage(file, message='') {
        const outputDiv = document.getElementById('outputDiv');

        const messageElement = document.createElement('div');
        messageElement.classList.add('message-bubble');

        if (message) {
            const textElement = document.createElement('p');
            textElement.innerHTML = `${message} <span class="timestamp">${getCurrentTime()}</span>`;
            messageElement.appendChild(textElement);
        }

        if (file.type.startsWith("image/")) {
            const img = document.createElement('img');
            img.src = URL.createObjectURL(file);
            img.alt = file.name;
            img.style.maxWidth = '200px';
            img.style.maxHeight = '200px';
            messageElement.appendChild(img);
        } else {
            const fileLink = document.createElement('a');
            fileLink.href = URL.createObjectURL(file);
            fileLink.download = file.name;
            fileLink.textContent = `Uploaded File: ${file.name}`;
            messageElement.appendChild(fileLink);
        }

        const timestamp = document.createElement('span');
        timestamp.classList.add('timestamp');
        timestamp.textContent = getCurrentTime(); 
        messageElement.appendChild(timestamp);

        outputDiv.appendChild(messageElement);
    }

    function toggleRedirect() {
    const checkbox = document.getElementById('toggle-switch');
    if (checkbox.checked) {
        window.location.href = '/neil_2025/create_and_compete/realityroom';
    }
    }
</script>

Backend Development (Went over this with Mr. Mortensen)

The backend was obviously the most valuable lesson in Sprint 3, I learned how to collect and store data, and this is something that essentially allows a website to actually function.

If you click on channels, you will be able to see the full structure/directory of the database (.db), which I can navigate through to see all the different storages of information for the flocker website.

It stores posts, and additional information.

This is an example of posting, the posts option is on the flocker_frontend (frontend of the website), and then once you post it, it gets stored in the databse (backend). If the backend didn’t exist, all posts would be displayed, but would go away once the request to the website was refreshed.

Thanks.