Написать пост

Как создать первый проект с ChatGPT и Next.js без навыков кодинга

В этой статье мы расскажем, как создать первый проект на Next.js и ChatGPT и собрать 10000 пользователей, не имея при этом навыков кодинга.

В этой статье мы расскажем, как создать первый проект на Next.js и ChatGPT и собрать 10 тысяч пользователей, и не иметь при этом никаких навыков кодинга. 

Это — перевод оригинальной статьи «How I built my first Open Source project with ChatGPT and Next.js. 10k users in 24 hours» автора Iuliia Shnai. Далее повествование ведётся от лица автора.

Раньше я никогда не программировал, потому что кодинг казался мне очень сложным. Два месяца назад я решился попробовать: я подумал, почему бы не начать программировать сейчас.

Друзья посоветовали мне начать с написания проектов с открытым исходным кодом и помогли мне с первыми настройками проекта.

Практически на каждом шаге я использовал ChatGPT, чтобы разобраться, как настроить что-то, как установить и подключить API или как понять, что означает код.

Теперь я расскажу немного больше о первом проекте, который я создал.

Этапы разработки проекта

  1. Настройка среды.
  2. Поиск проектов с открытым исходным кодом и разработка на их основе.
  3. Разбор кода.
  4. Создание проекта.
  5. Отправка проекта на GitHub.
  6. Публикация поста о проекте в социальных сетях.

Мне потребовалась неделя, чтобы разобраться во всем и запустить проект в Linkedin.

На настройку ушли 1-2 часа, для управления кодом из открытых проектов потребовалось 3-4 дня, а для размещения на GitHub и Vercel потребовался один день.

Какой проект я создал

Я сделал генератор постов Linkedin, который использовал ИИ. 

Вот ссылка на него на GitHub: 

Вы можете использовать его, чтобы создать свой собственный генератор постов.

Почему генератор постов Linkedin

В первое время я проводил эксперименты на Linkedin.

Я тратил много времени на написание постов на Linkedin. По крайней мере, у меня уходил час на каждый пост, и искал способ упростить написание.

Итак, я провел анализ постов более 100 разных авторов на Linkedin и 300 различных промптов, чтобы найти способы генерации эффективных постов.

Теперь я расскажу об основных шагах разработки, которые я делал поэтапно.

Шаг 1. Настройка среды

Прежде чем начать, мне рекомендовали установить менеджер пакетов, такой как tea, чтобы управлять средой разработки.

В этот момент слова “менеджер пакетов” были мне неизвестны. 

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

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

Как я понимаю, tea помогает установить Node.js, npm, Vercel и любые другие пакеты, которые понадобятся для разработки.

Настройка Next.js с использованием TypeScript и Tailwind CSS

У меня было базовое представление о том, что понадобится мне для фронтенда.

Мне подсказали начать с создания нового проекта Next.js. Также я использовал TypeScript и Tailwind CSS, поэтому следовал этим шагам:

			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
		

Шаг 2. Поиск проектов с открытым исходным кодом и разработка на их основе

Я использовал два проекта с открытым исходным кодом:

Алгоритм Twitter, чтобы оценивать посты пользователей на Linkedin.

Генератор био в аккаунтах Twitter. Он помог мне разобраться, как подключить OpenAI и генерировать посты для Linkedin с помощью GPT. В проекте по ссылке он генерировал информацию в био для аккаунтов.

Я загрузил эти проекты в виде zip-архивов на свой компьютер.

Шаг 3. Разбор кода

Я был немного шокирован тем, что увидел, и сперва не мог ничего понять.

Поэтому я обратился к ChatGPT и спросил о базовой структуре моего приложения.

Я скопировал код каждой страницы и спросил, что он делает. Также я спрашивал, как вносить изменения в код.

Таким образом, я начал понимать, где находится фронтенд приложения и CSS.

Некоторые из вопросов, которые я задавал ChatGPT, были очень простыми, зато сейчас они мне полностью понятны.

В те моменты я задавал все вопросы, даже откровенно глупые.

Шаг 4. Создание проекта

После того, как я разобрался в базовых вещах, я приступил к изменениям в скачанных проектах и начал создавать собственное приложение на их основе.

Приложение состоит из двух частей: Ранжирование + Генератор.

Алгоритм генерации постов для LinkedIn

Ранжирование – это оценка вашего сообщения на основе различных критериев, которая повышает его эффективность.

Я адаптировал алгоритм ранжирования постов из Twitter для LinkedIn, использовав следующие функции:

  1. Функция для обнаружения нескольких хэштегов.
  2. Функция для обнаружения изображений или видео.
  3. Функция для обнаружения URL-адресов в сообщении.
  4. Функция, которая предпочитает сообщения, содержащие эмодзи.
  5. Функция для продвижения негативного контента.
  6. Функция для приоритетного форматирования постов.
  7. Функция для сокращения длины строки.
  8. Функция для задавания вопросов.

В отличие от алгоритма Twitter, алгоритм LinkedIn не является общедоступным.

			// 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.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?/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"
    };
  }
}
		

Интерфейс пользователя алгоритма

Он обнаруживает все функции в вышеуказанном коде и показывает, как улучшить пост. Я не настроил интерфейс для всех функций.

			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>
    </>
  );
};
		

API OpenAI и генератор промптов

Я использую функцию обработчика handlePrompt для создания поста. Также применяется фильтр по типам, поэтому у меня есть 5 различных промптов в зависимости от типа.

Я просто подключил свой API OpenAI для этого.

			const handlePrompt = () => {
    let prompt;
    switch (vibe) {
		
			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`;
		

Интерфейс генератора

Это мой индекс-файл. Он относится к генератору постов.

			<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>
    </>
  );
}
		

Шаг 5. Отправка проекта

После того, как я внёс изменения в исходные проекты, я был готов к отправке собственного приложения на GitHub.

Я создал репозиторий.

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

Затем я создал аккаунт на Vercel, чтобы отправить проект и проверить наличие ошибок.

Каждое обновление я отправлял при помощи команды:

			git add .

git commit -m “fix type”

git push
		

Для поиска ошибок я использовал ChatGPT. Он очень помог в исправлении ошибок, ведь я даже не понимал, как искать их.

			npm run build
		

Шаг 6. Распространение в социальных сетях и сбор обратной связи

Поскольку это был проект для LinkedIn, я разместил пост там, и он стал вирусным, набрав 200 тыс. просмотров. У него даже появились хэйтеры. ?

Статистика за первые 24 часа:

  1. 20000 просмотров на LinkedIn.
  2. 7000 просмотров веб-сайта.
  3. 600 лайков.
  4. Более 11000 сгенерированных постов.
  5. 3+ хэйтера.
  6. 3+ предложений о совместном проекте.

Что я делаю сейчас

Я создаю различные микро-инструменты и вношу вклад в существующие проекты с открытым исходным кодом.

Вот один из новых проектов с открытым исходным кодом, над которым я работаю.

Это платформа для обмена документами и презентациями со встроенной аналитикой. У меня было много проблем с использованием Docsend раньше, и они меня очень раздражали. Поэтому я считаю, что будет круто иметь альтернативу с открытым исходным кодом.

Следите за новыми постами
Следите за новыми постами по любимым темам
1К открытий3К показов