OpenAI CodeX ChatBot
Tags: openai, webdev, vite, beginners
Published: 2022-12-25 16:20:53

Hey there! How's it going? It's been a while since we last had our interaction in dev. Here we build stuff, little but great and cool.

Today we're gonna build and deploy a live chatbot that can help you answer any question you have regarding to coding and stuff (and definitely not your relationship doubts, eh? Just kidding!).

[Repo ⚡](https://github.com/JagadeeshKJ/codeX)
[Live 🚀](https://codex-psi-lemon.vercel.app/)

The final output looks something like this:

![end result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tsb6r14gcw708kpmyx3x.png)

With that said, let's jump into action.

**Stack used**

- HTML5
- CSS3
- JavaScript
- Vite
- NodeJS
- Express 
- OpenAI API

**Let's Start Building**

We start with two folders named client and server in our project folder which looks in heirarchy like this.

.
├── client/
│   ├── assets
│   ├── index.html
│   ├── style.css
│   └── script.js
└── server/
    └── server.js

You can checkout my GtHub repo provided above to see the files you need and how it actually structured. 

The assets folder consists nothing but some images required for the project. 

I hope you'd node js in your system. You're gonna need node js to run this project.

Create a template for our project in plain vanilla javascript.

`npm create vite@latest client --template vanilla`

**CLIENT**

Now we start with client side first. Assuming you'd know how to link styles and script files.

**index.html** 

```
<body>
    <div id="app">
      <div id="chat_container">
        <!-- Main Portion -->
      </div>

      <form>
        <textarea name="prompt" placeholder="What questions do you have?" cols="1" rows="1"></textarea>
        <button type="submit"><img src="assets/send.svg" alt="send it"></button>
      </form>
    </div>

    <script type="module" src="script.js"></script>
  </body>
```

The <body> tag contains all of the content for the page. Inside of the body, there is a div with an id of "app" which contains two elements: a div with an id of "chat_container" and a form with a textarea and submit button. Finally, there is a script tag which links to an external JavaScript file.


**style.css**

```
@import url('https://fonts.googleapis.com/css?family=Muli&display=swap');

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  /* font-family: "Alegreya Sans", sans-serif; */
  font-family: 'Muli';
}

body {
  background: #343541;
}

#app {
  width: 100vw;
  height: 100vh;
  background: #343541;

  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
}

#chat_container {
  flex: 1;
  width: 100%;
  height: 100%;
  overflow-y: scroll;

  display: flex;
  flex-direction: column;
  gap: 10px;

  -ms-overflow-style: none;
  scrollbar-width: none;

  padding-bottom: 20px;
  scroll-behavior: smooth;
}

/* hides scrollbar */
#chat_container::-webkit-scrollbar {
  display: none;
}

.wrapper {
  width: 100%;
  padding: 15px;
}

.ai {
  background: #40414F;
}

.chat {
  width: 100%;
  max-width: 1280px;
  margin: 0 auto;

  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 10px;
}

.profile {
  width: 36px;
  height: 36px;
  border-radius: 5px;

  background: #5436DA;

  display: flex;
  justify-content: center;
  align-items: center;
}

.ai .profile {
  background: #10a37f;
}

.profile img {
  width: 60%;
  height: 60%;
  object-fit: contain;
}

.message {
  flex: 1;

  color: #dcdcdc;
  font-size: 20px;

  max-width: 100%;
  overflow-x: scroll;

  white-space: pre-wrap; 

  -ms-overflow-style: none;
  scrollbar-width: none;
}

/* hides scrollbar */
.message::-webkit-scrollbar {
  display: none;
}

form {
  width: 100%;
  max-width: 1280px;
  margin: 0 auto;
  padding: 10px;
  background: #40414F;

  display: flex;
  flex-direction: row;
  gap: 10px;
}

textarea {
  width: 100%;

  color: #fff;
  font-size: 18px;

  padding: 10px;
  background: transparent;
  border-radius: 5px;
  border: none;
  outline: none;
}

button {
  outline: 0;
  border: 0;
  cursor: pointer;
  background: transparent;
}

form img {
  width: 30px;
  height: 30px;
}
```

That's all the styling we need for making this project looking good with UI. 

**script.js**

```
import bot from './assets/bot.svg';
import user from './assets/user.svg';

const form = document.querySelector('form');
const chatContainer = document.querySelector('#chat_container');

// AI Loading...
let loadInterval;

function loader(element) {
  element.textContent = '';

  loadInterval = setInterval(() => {
    element.textContent += '.';

    // reset 
    if(element.textContent === '....') {
      element.textContent = '';
    }
  }, 300)
}

// AI typing...
function typeText(element, text) {
  let index = 0;
  
  let interval = setInterval(() => {
    if(index < text.length) {
      element.innerHTML += text.charAt(index);
      index++;
    } else {
      clearInterval(interval);
    }
  }, 20)
}

function generateUniqueId() {
  const timestamp = Date.now();
  const randomNumber = Math.random();
  const hexaStr = randomNumber.toString(16);

  return `id-${timestamp}-${hexaStr}`;
}

// chat dark and light grey stripes
function chatStripe (isAI, value, uniqueId) {
  return (
    `
    <div class = "wrapper ${isAI && 'ai'}">
      <div class = "chat">
        <div class = "profile">
          <img
            src = "${isAI ? bot : user}"
            alt = "${isAI ? 'bot' : 'user'}"
          />
        </div>
        
        <div class = "message" id = ${uniqueId}>
          ${value}
        </div>
      </div>
    </div>
    `
  )
}

const handleSubmit = async(e) => {
  e.preventDefault();

  const data = new FormData(form);

  // user's chat stripe
  chatContainer.innerHTML += chatStripe(false, data.get('prompt'));

  form.reset();

  // bot's chat stripe
  const uniqueId = generateUniqueId();
  chatContainer.innerHTML += chatStripe(true, " ", uniqueId);
  chatContainer.scrollTop = chatContainer.scrollHeight;

  const messageDiv = document.getElementById(uniqueId);

  loader(messageDiv);

  // fetch data from server -> bot's response

  const response = await fetch('https://codex-djxd.onrender.com/', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      prompt: data.get('prompt')
    })
  })

  clearInterval(loadInterval);
  messageDiv.innerHTML = '';

  if(response.ok) {
    const data = await response.json();
    const parsedData = data.bot.trim();

    typeText(messageDiv, parsedData);
  } else {
    const err = await response.text();

    messageDiv.innerHTML = "Something went wrong";

    alert(err);
  }
}

// Submit by pressing enter
form.addEventListener('submit', handleSubmit);
form.addEventListener('keyup', (e) => {
  if(e.keyCode === 13) {
    handleSubmit(e);
  }
})
```

This code is a chatbot program. It imports two images, one for the user and one for the bot. It then creates a form element and a chat container element. It then defines functions to load, type text, generate unique IDs, and create chat stripes. 

The handleSubmit function is called when the user submits the form or presses enter. This function sends the user's message to the server and receives a response from the bot. The response is then displayed in the chat container with a unique ID.

**SERVER**

Let's make our project work by fetching and uploading data as in client-server architecture.

Generate a new package.json file inside your server folder by entering the command below.

`npm init -y`

Now we need to install a couple of dependencies to do our server side work properly.

`npm install cors dotenv express nodemon openai`

**server.js**

```
import express from 'express';
import * as dotenv from 'dotenv';
import cors from 'cors';
import { Configuration, OpenAIApi } from 'openai';

dotenv.config();

const configuration = new Configuration({
    apiKey: process.env.OPENAI_API_KEY,
});

const openai = new OpenAIApi(configuration);

const app = express();
app.use(cors());
app.use(express.json());

app.get('/', async(req, res) => {
    res.status(200).send({
        message: 'Hello from CodeX',
    })
});

// fetches data from frontend body
app.post('/', async(req, res) => {
    try {
        const prompt = req.body.prompt;

        // used from openai examples
        const response = await openai.createCompletion({
            model: "text-davinci-003",
            prompt: `${prompt}`,
            temperature: 0,
            max_tokens: 3000,
            top_p: 1,
            frequency_penalty: 0.5,
            presence_penalty: 0,
        });

        res.status(200).send({
            bot: response.data.choices[0].text
        })
    } catch (error) {
        console.log(error);
        res.status(500).send({ error })
    }
})

app.listen(5000, () => console.log('Server is running on port http://localhost:5000'));
```

This code is setting up an Express server that will be used to communicate with the OpenAI API. It starts by importing the necessary packages and configuring the environment variables.

Then it sets up an Express server and adds a GET route that will return a message when called. 

Finally, it adds a POST route that will take in data from the frontend, use it to call the OpenAI API, and return the response from OpenAI back to the frontend.

You can do deploy the server side in Render and client side using vercel after creating a git repository. 

Checkout [here](https://www.youtube.com/watch?v=2FeymQoKvrk&t=404s&ab_channel=JavaScriptMastery ) for the complete tutorial. Helpful!

That's it for the day folks. See you next time!