How I built my first Open Source project with ChatGPT and Next.js. 10k users in 24 hours 🤯
Tags: opensource, chatgpt, nextjs, beginners
Published: 2023-06-12 17:57:18


Small intro, I never coded before and it looked super hard to me and 2 months ago I decided why not now.🤷‍♀️

My friends suggested me to start with Open Source projects and also helped me with first simple steps for the set up. 

I used ChatGpt practically for every move I made from figuring out how to set smth up, install, connect API to what the code means, how to rewrite the functions or change the sizes. 

Now I  am gonna tell a bit more about the first project I build. 

## What you will find in this article? 

![What am I doing GIF](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oczeoeb9dm01o0xlck0d.gif)

Me evolving from 🥚 to 🐥 

🥚 Step 1. Setup environment
🥚 Step 2. Find open source projects and build on top 
🐣 Step.3 Figuring out the code
🐣 Step 4 Building the project
🐥 Step 5 Pushing the project 
🐥 Step 6.Sharing on social (stats here)

It took me a week to figure out everything and launch the project on Linkedin. From moment I set up things (which was quick, 1-2  hours with some help). Then managing existing code from OS projects (3-4 days). And push to Github and Vercel (took one day). 

## What is the project I built? 

Linkedin Post Generator - post generator to create AI generated posts on LinkedIn. https://www.postgenerator.app

On GitHub https://github.com/shnai0/linkedin-post-generator
Use it to build your own post generator. 
:eyes: for new forks  and stars :)

![Linkedin Post Generator](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9lkfl99wv01niclnjv6i.gif)

Below I share all the stats for first days of launch and becoming viral on LinkedIn. 

## Why Linkedin Post Generator? 

First I was experimenting with Linkedin platform for quite some time.

I spend a lot of time for writing posts on linkedin, like 1 hour per post at least and was thinking on shortcut.🤯

So, I analysed 100+ different creators and 300+ different prompts to find the  ways to generate more effective posts faster. 

So, here I am gonna share the basics of what I did step by step.



## 🥚 Step 1. Setup environment

### Set up tea
Before I get started, I get recommendation to set up a package manager, like tea to handle development environment. 

🥹 These words are still unknown at this point "a package manager" 

```
sh <(curl https://tea.xyz)

# --- OR ---
# using brew
brew install tea

```
As I get it tea help installing node, npm, vercel and any other packages I need for development.

So all in one. 


### Setting up Next.js with TypeScript and Tailwindcss

I had a basic understanding that I need smth for front end. 


I was taught to start by generating a new Next.js project. Also using TypeScript and Tailwind CSS, so followed these steps:

```
npx create-next-app

# ---
# you'll be asked the following prompts
What is your project named?  my-app
Would you like to add TypeScript with this project?  Y/N
# select `Y` for typescript
Would you like to use ESLint with this project?  Y/N
# select `Y` for ESLint
Would you like to use Tailwind CSS with this project? Y/N
# select `Y` for Tailwind CSS
Would you like to use the `src/ directory` with this project? Y/N
# select `N` for `src/` directory
What import alias would you like configured? `@/*`
# enter `@/*` for import alias
```


## 🥚 Step 2. Find open source projects and build on top 

I used these two Open Source projects:

1. Twitter Alghoritm 
https://github.com/coryetzkorn/twitter-algorithm
So I could rate the post input from users based of what is the algorithm of Linkedin. 

![Twitte alghoritm](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s91p1m6ljoshh2ik3mrt.png)


2.Twitter Bio generator 

https://github.com/Nutlope/twitterbio 
This one helped me to figure out how to connect Open.AI and generate with it posts for Linkedin. In current code it generated BIO

![Twitter Bio generator ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2vbljgcmno6iwucgk8ht.png)

To set and open each of the projects separately I downloaded them as zip on my computer


![Start with open source](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k1m134uh5owzangz5y5a.png)



## 🐣 Step.3 Figuring out the code

So I kind of was shocked with what I see and could not figure out anything in the beggining. 

So what I did I asked ChatGPT about basic structure of my application. 

I copied every page code in it and ask what it does, and basically ask how to make changes. So I start getting a better idea, where is front of application, where is CSS. 

I still have no full idea of everything and missing the stuff but I think it is definitely quick learning. 

Some of the request I made to ChatGPT were super basic, and now look really clear to me, at those point I asked everything, all stupid questions.  

![Change color request in ChatGPT](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r3s4buquqoxi0rnm9zku.png)


## 🐣 Step 4 Building the project

So after figuring out some basics I move on making my own changes. start building application on top of these projects.

It has two parts =Ranking + Generator

### Linkedin Post Generator Alghoritm 

Ranking is ranking your post based on different criteria which increase performance. 

I adapted algorithm for what is know for Linkedin, using the functions: 
 
1. Function to detect multiple hashtags
2. Function to detect image or video
3. Function to detect urls in post
4. Function to favor posts that use emojis 
5. Function to promote negative content
6. Function to prioritize break like post formatting.
7. Function to reduce line length
8. Function to ask questions

In comparison with Twitter algorithm, Linkedin is not public. 

```ts

// function to detect multiple hashtags
function multipleHashtags({ post }: PostData): Rank {
  const regex = /#[\w-]+/g;
  const hashtags = post.match(regex);
  const lowerCasePost = post.toLowerCase();

  if (hashtags && hashtags.length > 3) {
    return {
      score: 0.5,
      message: `Too many hashtags.`,
    };
  }
  if (hashtags && hashtags.length <= 3) {
    if (
      lowerCasePost.includes("#follow") ||
      lowerCasePost.includes("#comment") ||
      lowerCasePost.includes("#like")
    ) {
      return {
        score: 0.5,
        message: `Avoid using hashtags like "follow," "comment," or "like".`,
      };
    }
    return {
      score: 1,
      message: `Combine general and specific hashtags.`,
    };
  }

  return {
    score: 1.0,
  };
}

// function to detect image or video
function imageVideoBoost({ postMedia }: PostData): Rank {
  const has_media = postMedia;
  if (has_media) {
    return {
      score: 2.0,
      // message: `Contains image or video.`,
    };
  }
  return {
    score: 1.0,
  };
}


// function to detect urls in post
function postHasUrl({ post }: PostData): Rank {
  const regex =
    /https?:\/\/[\w-]+(\.[\w-]+)+([\w.,@?^=%&amp;:/~+#-]*[\w@?^=%&amp;/~+#-])?/g;
  const urls = post.match(regex);
  if (urls && urls.length > 0) {
    return {
      score: 0.5,
      message: `Remove the link from post and add in comments.`,
    };
  }
  return {
    score: 1.0,
  };
}


/**
 * Function to favor posts that use emojis 
 */
function emojis({ post, sentiment }: PostData): Rank {
  const regex = new RegExp("[\uD800-\uDBFF][\uDC00-\uDFFF]", "g");
  const emojis = post.match(regex) || [];
  const totalMatches = emojis.length;
  if (totalMatches > 0) {
    return {
      score: 1.5,
      // message: `Included ${totalMatches} emojis in the post.`,
    };
  }
  return {
    score: 1,
    message: "No emojis found in the post.",
    type: "negative"
  };
}


/**
 * Promote negative content because it's more likely to go viral.
 * Hide anything positive or uplifting.
 */
function sentiment({ post, sentiment }: PostData): Rank {
  if (sentiment.comparative >= 0.5) {
    if (sentiment.comparative > 1.5) {
      return {
        score: 1.5,
        // message: `Exceptionally positive.`,
      };
    } else {
      return {
        score: 1.1,
        // message: `Positive sentiment.`,
      };
    }
  } else if (sentiment.comparative <= -0.5) {
    if (sentiment.comparative < -1.5) {
      return {
        score: 0.5,
        // message: `Exceptionally negative.`,
      };
    } else {
      return {
        score: 0.9,
        // message: `Negative sentiment.`,
      };
    }
  } else {
    return {
      score: 1,
    };
  }
}

/**
 * Prioritize break like post formatting.
 */
function lineBreaks({ post, sentiment }: PostData): Rank {
  const breaks = post.split(/\n\s*\n/);
  const totalBreaks = breaks.length - 1;
  if (totalBreaks >= 1) {

    return {
      score: 1.5,
      // message: `Used ${totalBreaks} line breaks.`,
    };
  } else {
    return {
      score: 1,
      message: `Add line breaks between the lines.`,
      type: "negative"
    };
  }
}

/**
 * Reduce line length
 */
function lineLength({ post }: PostData): Rank {
  const lines = post.split('\n');
  let score = 1.0;
  for (let i = 0; i < lines.length; i++) {
    if (lines[i].length > 200) {
      return {
        score: 0.9,
        message: `Reduce line length to improve readability (200 characters).`,
      };
    }
  }
  return {
    score: 1,
    // message: `Good, keep line length 200 characters or less.`,
    type: "positive"
  };
}
/**
* Function to ask questions
*/
function questions({ post, sentiment }: PostData): Rank {
  if (post.includes("?")) {
    return {
      score: 1.5,
      // message: "Great! Questions can help to activate discussion"
    };
  } else {
    return {
      score: 1,
      message: "Add questions to activate discussion",
      type: "negative"
    };
  }
}

```


### User interface of algorithm 

It detect all function in the above code and for some of them shows how to improve post. I did not adjust it for all functions.

```ts
  return (
    <>
      <div>
        <div className="slider bg-gray-300 h-4 rounded-full relative overflow-hidden">
          <div
            className={classNames(
              "absolute top-0 transition-width duration-250 ease-linear h-20",
              sliderColor
            )}
            style={{ width: percentage }}
          />
        </div>
        {/* <p className="explanation text-gray-600 italic text-sm mt-2">
          Positive rankings result in greater reach 
        </p> */}

        <ul className="mt-5 p-0">
          {positive.map((item, index) => (
            <li
              className="positive text-green-600 flex items-center space-x-2 list-style-none my-5 text-sm"
              key={`positive-${index}`}
            >
              <span>👍</span>
              <span>{item.message.replace(/\(\s*[+-]?\d+\s*\)/, '')}</span>
            </li>
          ))}
          {negative.map((item, index) => (
            <li
              className="negative text-red-600 flex items-center space-x-2 list-style-none my-1 text-sm"
              key={`negative-${index}`}
            >
              <span>👎</span>
              <span>{item.message.replace(/\(\s*[+-]?\d+\s*\)/, '')}</span>
            </li>
          ))}
        </ul>
      </div>
      <style jsx>{`
        .slider:after {
          content: " ";
          display: block;
          width: 2px;
          height: 20px;
          position: absolute;
          top: 0;
          left: calc(25% - 1px);
          background: #000;
        }
      `}</style>
    </>
  );
};

```

![Ranking results](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yrz8ivhz5k4frpg9ulh9.png)


### Open AI Api and Prompts Generator  

I use handle Prompt to generate the post. Also there were filter for types, so I had 5 different prompts based on type. 
I just connected my OpenAI API for it. 

```
const handlePrompt = () => {
    let prompt;
    switch (vibe) {

```

Prompt below

```
 prompt = `Generate post using this prompt, based on ${post}.  You are a LinkedinGPT, a large language model that generates viral posts for Linkedin. You are given a prompt of a post and must generate a post that is more likely to be liked and reposted than the original post.
The Linkedin algorithm contains boosts and demotions based on what you are writing. Positive boosts are:

- in each post add emoji
- 200 characters in sentence maximum
- Start each sentecnce from new line and ad numbers in first 2 lines
- add 3 hashtags which 2 are generic and one very specific (at the end) Tags relate to post theme
- add a question at the end of the post to start a discussion. Before the hashtags
- first two lines should be catchy
- Dont add links - links are not good.
- If post copied in the field contain some numbers keep them the same.

Add idea about which image or visual can be added at the end of the post (this text is not counted as part of post)
${post}
---
Generated post length must be more than 800-1200 characters
---
Between each line must be a space
---
Keep all mentions of people in there
---
Start the firs line from smth like: I did smth, In year, I do, Tired of, Sometimes it is just, A path toward, Because this is not,I've been struggling,  (change the begginign depends on the context )
---
Add emoji if it fits
---
It should be a story`;
```

### Generator interface 
This is my index file. From of the post generator. 

```tsx

      <main>
        <nav className="bg-blue-900 text-white ">
          <div className="px-5">
            <div className="max-w-5xl mx-auto">
              <div className="flex justify-between items-center h-16 ">
                <div className="flex items-center text-base ">
                  <a target="_blank"
                    href="https://www.linkedin.com/in/iuliia-shnai/"
                    rel="noreferrer"
                    className="text-white flex max-w-fit items-center justify-center space-x-2 text-xl"
                  >
                    <p>👩‍💼</p>

                  </a>
                </div>
                
              </div>
            </div>
          </div>
        </nav>
        <section className="py-10 lg:py-20 ">
          {/* bg-[url('/image1.svg')] */}
          <div className="px-4">
            <div className="max-w-5xl mx-auto">
              <div className="w-full mx-auto">
                <h1 className="text-6xl text-center font-bold pb-1 text-slate-900">

                  Linkedin Post Generator  🚀
                </h1>
                <p className="mt-3 mb-10 text-center">
                  See how your post performs and generate a better one with AI. Time to go viral. <br />

                </p>
                <div className="flex flex-col md:flex-row w-full md:space-x-20">
                  <div className="flex md:w-1/2 flex-col">
                    <h2 className="text-xl font-bold">
                      Your Ranking
                    </h2>
                    <div className="pt-1">
                      <Ranking ranking={ranking} />
                    </div>

                    <div className="w-full my-1 mx-auto">
                      <Post
                        post={post}
                        setPost={setPost}
                        media={media}
                        setMedia={setMedia}
                      />
                    </div>

                    <div className="flex mb-5 items-center space-x-3">


                    </div>
                    <div className="block">
                      <DropDown vibe={vibe} setVibe={setVibe} />
                    </div>
                    <div className="my-4">
                      <button
                        disabled={loading}
                        onClick={(e) => optimizePost(e)}
                        className="bg-blue-800 font-medium rounded-md w-full text-white px-4 py-2 hover:bg-blue-600 disabled:bg-blue-800"
                      >
                        {loading && <LoadingDots color="white" style="large" />}
                        {!loading && `Generate new post `}
                      </button>
                    </div>

                  </div>
                  <div className="flex md:w-1/2 md:flex-col">
                    <Toaster
                      position="top-right"
                      reverseOrder={false}
                      toastOptions={{ duration: 2000 }}
                    />
                    {optimizedPost && (
                      <div className="my-1">
                        <div className="flex justify-between items-center pb-2 border-b border-gray-300">
                          <h2 className="text-xl font-bold">
                            Your Generated Post
                          </h2>
                        </div>
                        <div className="max-w-2xl my-4 mx-auto">
                          <div
                            className="bg-white rounded-xl shadow-md p-4 hover:bg-gray-100 transition cursor-copy border"
                            onClick={() => {
                              navigator.clipboard.write([
                                new ClipboardItem({
                                  "text/html": new Blob([optimizedPost], { type: "text/html" }),
                                }),
                              ]);
                              toast("Post copied to clipboard", {
                                icon: "📋",
                              });
                            }}
                            key={optimizedPost}
                          >
                            <p className="text-black-700" dangerouslySetInnerHTML={{ __html: optimizedPost }} />
                          </div>
                        </div>
                      </div>
                    )}

                  </div>
                </div>
              </div>
            </div>
          </div>
        </section>
        <div className="max-w-5xl mx-auto">
          <Footer />
        </div>
      </main>
    </>
  );
}

```

![Main generator page ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7q7v3jlzewcntginlecn.png)


## 🐥 Step 5 Pushing the project 

Final step, I was ready to push. 

I create repository on GitHub 

```
$ git remote add origin .. 
git branch -M main
git push -u origin main
```

And further on created account on Vercel, to push it with Vercel and check errors. 

Every updates than I pushed via 

```
git add .

git commit -m “fix type”

git push
```

For checking errors I used, so that I dont push with all errors. ChatGPT helped with fixing errors a lot, while I did not even understand how to find them. 

```
npm run build
```


## 🐥 Step 6.Sharing on social and gathering feedback

As it was linkedin project I dropped the post there. and it got Viral  with 200k views and haters even:)

Link to post
https://www.linkedin.com/feed/update/urn:li:activity:7053373191133499392/

Stats in first 24 h:
⭐️ 20000 Linkedin impressions
⭐️ 7000 website views
⭐️ 600 likes
⭐️ 11000+ posts generated
⭐️ 3+ haters
⭐️ 3+ joint project offers 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6hnhrkscnnawfz1cq7h6.png)



## What am I doing now? 

I am building different micro-tools and contributing to existing open source projects. 

If you like this article and would like to support me on my coding journey, here one of the new open source project I am on. 

Papermark.io - open source alternative to Docsend

[Give us a star ⭐️](https://github.com/mfts/papermark)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dl5w9vgr0h8na9afavdz.gif)


It is a document/Pitchdeck sharing with analytics build in. I had so many troubles with Docsend before, when I was trying to raise capital that it annoyed me a lot. 

So I think, it would be cool to have Open Source alternative, if project will grow. 

If you are building something in Open Source, share, I am curious to fork it 👀

Follow my journey, more micro-projects here https://linktr.ee/shnai  
My Twitter  https://twitter.com/shnai0