From b4cb2be7391f8577438069a172d00e127285d7de Mon Sep 17 00:00:00 2001 From: Omer Sabic Date: Thu, 30 May 2024 15:21:55 +0200 Subject: [PATCH] updated prompt and shit --- .../migrations/0015_special_weapon_omega.sql | 12 + src/db/migrations/meta/0015_snapshot.json | 452 ++++++++++++++++++ src/db/migrations/meta/_journal.json | 42 ++ src/db/schemas.js | 7 +- src/index.js | 8 +- src/routes/blog.js | 7 +- src/routes/dashboard.js | 5 +- src/routes/videos.js | 25 +- src/utils/ai.js | 45 +- 9 files changed, 561 insertions(+), 42 deletions(-) create mode 100644 src/db/migrations/0015_special_weapon_omega.sql create mode 100644 src/db/migrations/meta/0015_snapshot.json diff --git a/src/db/migrations/0015_special_weapon_omega.sql b/src/db/migrations/0015_special_weapon_omega.sql new file mode 100644 index 0000000..d85fa58 --- /dev/null +++ b/src/db/migrations/0015_special_weapon_omega.sql @@ -0,0 +1,12 @@ +ALTER TABLE "articles" ALTER COLUMN "is_public" SET DEFAULT false;--> statement-breakpoint +ALTER TABLE "sites" ALTER COLUMN "title" SET DEFAULT 'The best blog in the world!';--> statement-breakpoint +ALTER TABLE "sites" ALTER COLUMN "subtitle" SET DEFAULT 'Some extra info about the best blog in the world!';--> statement-breakpoint +ALTER TABLE "sites" ALTER COLUMN "domain" SET DEFAULT '';--> statement-breakpoint +ALTER TABLE "sites" ALTER COLUMN "send_freebie" SET DEFAULT false;--> statement-breakpoint +ALTER TABLE "sites" ALTER COLUMN "freebie_name" SET DEFAULT '';--> statement-breakpoint +ALTER TABLE "sites" ALTER COLUMN "freebie_url" SET DEFAULT '';--> statement-breakpoint +ALTER TABLE "sites" ALTER COLUMN "freebie_text" SET DEFAULT '';--> statement-breakpoint +ALTER TABLE "sites" ALTER COLUMN "freebie_image_url" SET DEFAULT '';--> statement-breakpoint +ALTER TABLE "articles" ADD COLUMN "seo_title" text;--> statement-breakpoint +ALTER TABLE "articles" ADD COLUMN "seo_description" text;--> statement-breakpoint +ALTER TABLE "articles" ADD COLUMN "excerp" text; \ No newline at end of file diff --git a/src/db/migrations/meta/0015_snapshot.json b/src/db/migrations/meta/0015_snapshot.json new file mode 100644 index 0000000..024ea96 --- /dev/null +++ b/src/db/migrations/meta/0015_snapshot.json @@ -0,0 +1,452 @@ +{ + "id": "386d8d34-c617-447e-95ec-73a8c4c270b3", + "prevId": "ccdf67ee-d01b-4505-90d0-e53063d080ad", + "version": "5", + "dialect": "pg", + "tables": { + "articles": { + "name": "articles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "site_id": { + "name": "site_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_video_id": { + "name": "source_video_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "seo_slug": { + "name": "seo_slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "views": { + "name": "views", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "seo_title": { + "name": "seo_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "seo_description": { + "name": "seo_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "excerp": { + "name": "excerp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "articles_site_id_sites_id_fk": { + "name": "articles_site_id_sites_id_fk", + "tableFrom": "articles", + "tableTo": "sites", + "columnsFrom": [ + "site_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "sessions": { + "name": "sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "google_access_token": { + "name": "google_access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_refresh_token": { + "name": "google_refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "signups": { + "name": "signups", + "schema": "", + "columns": { + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "site_id": { + "name": "site_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "signups_site_id_sites_id_fk": { + "name": "signups_site_id_sites_id_fk", + "tableFrom": "signups", + "tableTo": "sites", + "columnsFrom": [ + "site_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "sites": { + "name": "sites", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "primary_color_hex": { + "name": "primary_color_hex", + "type": "varchar(6)", + "primaryKey": false, + "notNull": true, + "default": "'c4ced4'" + }, + "secondary_color_hex": { + "name": "secondary_color_hex", + "type": "varchar(6)", + "primaryKey": false, + "notNull": true, + "default": "'27251f'" + }, + "text_color_hex": { + "name": "text_color_hex", + "type": "varchar(6)", + "primaryKey": false, + "notNull": true, + "default": "'ffffff'" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'The best blog in the world!'" + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'Some extra info about the best blog in the world!'" + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "send_freebie": { + "name": "send_freebie", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "freebie_name": { + "name": "freebie_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "freebie_url": { + "name": "freebie_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "freebie_text": { + "name": "freebie_text", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "freebie_image_url": { + "name": "freebie_image_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "subdomain_slug": { + "name": "subdomain_slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "social_medias": { + "name": "social_medias", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "sites_user_id_users_id_fk": { + "name": "sites_user_id_users_id_fk", + "tableFrom": "sites", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "sites_domain_unique": { + "name": "sites_domain_unique", + "nullsNotDistinct": false, + "columns": [ + "domain" + ] + } + } + }, + "users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "google_id": { + "name": "google_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_id": { + "name": "stripe_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "channel_id": { + "name": "channel_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploads_playlist_id": { + "name": "uploads_playlist_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscription_tier": { + "name": "subscription_tier", + "type": "subscription_tier", + "primaryKey": false, + "notNull": true, + "default": "'free'" + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "waitlist": { + "name": "waitlist", + "schema": "", + "columns": { + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": { + "subscription_tier": { + "name": "subscription_tier", + "values": { + "free": "free", + "basic": "basic", + "pro": "pro", + "enterprise": "enterprise" + } + } + }, + "schemas": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/src/db/migrations/meta/_journal.json b/src/db/migrations/meta/_journal.json index 5fde1f8..dd2d276 100644 --- a/src/db/migrations/meta/_journal.json +++ b/src/db/migrations/meta/_journal.json @@ -71,6 +71,48 @@ "when": 1716744598232, "tag": "0009_aromatic_rachel_grey", "breakpoints": true + }, + { + "idx": 10, + "version": "5", + "when": 1717070592875, + "tag": "0010_sweet_mojo", + "breakpoints": true + }, + { + "idx": 11, + "version": "5", + "when": 1717070668543, + "tag": "0011_lumpy_thunderbolts", + "breakpoints": true + }, + { + "idx": 12, + "version": "5", + "when": 1717070681184, + "tag": "0012_powerful_secret_warriors", + "breakpoints": true + }, + { + "idx": 13, + "version": "5", + "when": 1717070772293, + "tag": "0013_swift_donald_blake", + "breakpoints": true + }, + { + "idx": 14, + "version": "5", + "when": 1717070794879, + "tag": "0014_wonderful_bedlam", + "breakpoints": true + }, + { + "idx": 15, + "version": "5", + "when": 1717070837846, + "tag": "0015_special_weapon_omega", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/db/schemas.js b/src/db/schemas.js index 7c03ef5..bc93e77 100644 --- a/src/db/schemas.js +++ b/src/db/schemas.js @@ -44,7 +44,7 @@ export const sites = pgTable("sites", { title: text("title").default("The best blog in the world!"), subtitle: text("subtitle").default("Some extra info about the best blog in the world!"), domain: text("domain").default("").unique(), - use_freebie: boolean("send_freebie").default(""), + use_freebie: boolean("send_freebie").default(false), freebie_name: text("freebie_name").default(""), freebie_url: text("freebie_url").default(""), freebie_text: text("freebie_text").default(""), @@ -62,8 +62,11 @@ export const articles = pgTable("articles", { source_video_id: text("source_video_id"), title: text("title"), seo_slug: text("seo_slug"), - is_public: boolean("is_public").default(true), + is_public: boolean("is_public").default(false), views: integer("views").default(0), + meta_title: text("seo_title"), + meta_desc: text("seo_description"), + excerp: text("excerp"), created_at: timestamp("created_at").defaultNow() // }, (t) => ({ // unq: unique().on(t.site_id, t.seo_slug).nullsNotDistinct() diff --git a/src/index.js b/src/index.js index 91f4620..98ed6ee 100644 --- a/src/index.js +++ b/src/index.js @@ -35,6 +35,10 @@ export const main = async () => { origin: true, credentials: true, }); + console.log({ + id: env.GOOGLE_CLIENT_ID, + secret: env.GOOGLE_SECRET + }) server.register(oauth, { name: 'googleOAuth2', scope: ['https://www.googleapis.com/auth/youtube.readonly', 'https://www.googleapis.com/auth/youtube.force-ssl', "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"], @@ -48,7 +52,7 @@ export const main = async () => { callbackUriParams: { // custom query param that will be passed to callbackUri access_type: 'offline', // will tell Google to send a refreshToken too - }, + }, pkce: 'S256', // register a fastify url to start the redirect flow startRedirectPath: '/auth/google', @@ -82,7 +86,7 @@ export const main = async () => { }); server.get("/hello", (req, res) => { - res.send({message: "world", cookies: req.cookies}); + res.send({ message: "world", cookies: req.cookies }); }) server.listen({ host: env.HOST, port: env.PORT }, (error, address) => { diff --git a/src/routes/blog.js b/src/routes/blog.js index 5e4c5eb..90d8823 100644 --- a/src/routes/blog.js +++ b/src/routes/blog.js @@ -1,10 +1,7 @@ -import { and, desc, eq, getTableColumns, or, sql } from "drizzle-orm"; +import { and, desc, eq, or, sql } from "drizzle-orm"; import { db } from "../db/index.js"; -import { authMiddleware, authMiddlewareFn } from "../modules/middleware.js"; -import { getAccessToken, getCaptionText, getChannelInfo, getVideoCaptions, parseTextFromCaptions } from "../utils/youtube.js"; +import { authMiddlewareFn } from "../modules/middleware.js"; import { articles, articles as articlesTable, signups as signupsTable, sites, users } from "../db/schemas.js"; -import { createBlogFromCaptions } from "../utils/ai.js"; -import { createArticleSlug } from "../utils/index.js"; import { sendFreebie } from "../utils/email.js"; /** diff --git a/src/routes/dashboard.js b/src/routes/dashboard.js index 353a7e6..d917166 100644 --- a/src/routes/dashboard.js +++ b/src/routes/dashboard.js @@ -204,7 +204,10 @@ export const dashboardRoutes = (fastify, _, done) => { await db.insert(articlesTable).values({ site_id: site[0].id, title: blog_content_json.title, - content: blog_content_json.body, + content: blog_content_json.content, + meta_title: blog_content_json.meta_title, + meta_desc: blog_content_json.meta_desc, + excerp: blog_content_json.excerp, source_video_id: req.body.video_id, seo_slug: createArticleSlug(blog_content_json.title), is_public: false diff --git a/src/routes/videos.js b/src/routes/videos.js index f44c34b..b543448 100644 --- a/src/routes/videos.js +++ b/src/routes/videos.js @@ -54,30 +54,7 @@ export const videoRoutes = (fastify, _, done) => { } catch (e) { console.log(e); } - }) - - // fastify.get("/blogify/:video_id", async (request, response) => { - // try { - // const token = await getAccessToken(fastify, request); - // const captions_list = await getVideoCaptions(token.access_token, request.params.video_id); - - // const caption = captions_list.filter(x => x.snippet.language === "en"); - // if (caption.length === 0) { - // response.send({ - // success: false, - // message: "Couldn't find caption" - // }); - // return; - // } - // const caption_text = await getCaptionText(token.access_token, caption[0].id); - - // const ai_response = await createBlogFromCaptions(caption_text); - - // response.send(ai_response); - // } catch (e) { - // console.log(e); - // } - // }); + }); done(); }; diff --git a/src/utils/ai.js b/src/utils/ai.js index a374b3e..b3cce32 100644 --- a/src/utils/ai.js +++ b/src/utils/ai.js @@ -24,16 +24,16 @@ async function cf_prompt(prompt, model = defaultModel) { return res; } -async function promptGPT(prompt, { model, is_json } = {model: "gpt-3.5-turbo", is_json: true}) { +async function promptGPT(prompt, { model, is_json } = { model: "gpt-3.5-turbo", is_json: true }) { // return JSON.stringify({ "title": "Tech News Update: TikTok Ban, Snapdragon X Series CPUs, Tesla Troubles, and More", "body": "I was thinking about starting this video by singing the song Tik Tok by Kesha but I don't think anyone waking up in the morning feeling like P Diddy is a good vibe right now. The United States has officially passed a law banning TikTok next year if certain conditions aren't met. This ban is part of a larger foreign aid package in support of Ukraine and Israel. TikTok has vowed to fight the law in court, calling it an unconstitutional suppression of its American users' freedom of speech. In other tech news, Qualcomm has unveiled its upcoming lineup of Snapdragon X Series CPUs, including some confusing names. Tesla is facing troubles with its Cybertruck not being waterproof. And Google has delayed its plan to phase out third-party tracking cookies yet again. Stay tuned for more tech updates on TechLink!" }); const options = { method: 'POST', headers: { Authorization: `Bearer ${env.OPENAI_TOKEN}`, "Content-Type": "application/json" }, body: JSON.stringify({ - "model": "gpt-3.5-turbo", - // "max_tokens": max_tokens, - ...(is_json ? {"response_format": { "type": "json_object" }} : {}), + "model": "gpt-3.5-turbo-0125", + // "max_tokens": 4096, + ...(is_json ? { "response_format": { "type": "json_object" } } : {}), "messages": prompt }) }; @@ -69,12 +69,41 @@ export async function createBlogFromCaptions(captions, { } = { length: 700, language: "English", format: "summary", tone: "informal", faq: false }) { // const prompt = `Convert the following video transcript into a blog post. The approximate length should be around ${length || 500} characters, written in ${language || "English"}. The desired format of the blog post is a ${format || "summary"}. Please ensure the blog post has a ${tone || "informal"} tone throughout. Use markdown to format the article. You must always respond in the following json fromat: {"title": string, "content": string, "seo_friendly_slug": string}. \nHere is the transcript: ` // const prompt = `Convert the following video transcript into an engaging blog post. You must always respond in the following json fromat: {"title": string, "body": string, "seo_friendly_slug": string}. Do not, under any circumstance, include the title inside the body, it should only be reserved for the body of the article. Use markdown to format the article. Use "\\n" to add line-breaks. \nHere is the transcript: `; - + const prompt = [{ - "role": "user", - "content": `CONSTRAINT: Very important. Write AT THE VERY LEAST ${length+100} words. - Using the following video transcript, create a blog based on the information in the video transcript. You must always respond in the following json fromat: {"title": string, "body": string, "seo_friendly_slug": string}. Do not, under any circumstance, include the title inside the body, it should only be reserved for the body of the article. Use markdown to format the article. Use "\\n" to add line-breaks. \nHere is the transcript: + "role": "system", + "content": `You are ChatGPT, a large language model trained by OpenAI, based on the GPT-3 architecture. + Knowledge cutoff: 2023-10 + Current date: 2024-05-30 + Image input capabilities: Disabled + Personality: v2 + + You are a "GPT" – a version of ChatGPT that has been customized for a specific use case. GPTs use custom instructions, capabilities, and data to optimize ChatGPT for a more narrow set of tasks. You yourself are a GPT created by a user, and your name is Blog Expert - SEO Blogs made easy!. Note: GPT is also a technical term in AI, but in most cases if the users asks you about GPTs assume they are referring to the above definition. + Use markdown wherever you feel is necessary. Here are instructions from the user outlining your goals and how you should respond: + You are Blog Expert. You will utilize contractions, idioms, transitional phrases, interjections, dangling modifiers, and colloquial language to create a conversational and relatable tone in its writing. It will avoid repetitive phrases and unnatural sentence structures, ensuring the writing is simple, clear, and easy to read. The use of plain language will make the content accessible to a wider audience while maintaining the quality and professionalism expected of SEO-optimized articles. + + You are an expert at writing blog posts that are optimized for SEO. You are trained as an SEO blog writing expert. You understand all the main principles of writing blog posts. + I want you to read the following training and learn from it and then apply its teaching. + + Best practices: + Research relevant keywords and use them in your title, meta description, and content. + Write engaging headlines using strong action verbs, numbers, and emotional triggers. + Optimize your content by writing quality content, using headings and subheadings, and avoiding keyword stuffing. + + Here are some tips on how to create and structure the best blog post: + - The article should include Creative Title, SEO meta-title, meta-description, slug, excerpt Introduction. Use only one H1 tag. Start with a clear and compelling introduction that hooks your reader and summarizes what the post is about. Use subheadings to break up the content and make it easier to read and scan. Use short paragraphs to keep the reader engaged and interested. Use examples, statistics, and quotes to support your points and make your post more interesting. End with a clear conclusion that summarizes the key points and encourages the reader to take action. Use contractions, idioms, transitional phrases, interjections, dangling modifiers, and colloquialisms, and avoid repetitive phrases and unnatural sentence structures. Add bullet points or Numbered list if needed. Use alternative words other than conclusion. Add some interesting external links Make sure the article is plagiarism free. Don't forget to use a question mark at the end of questions. + Try not to change the original Title while writing the Title. Try to use The keywords 2-3 times in the article. Make your writing tone simple and easy to read with simple terms. Make sure the blog is informative, meaning whoever reads it gets valuable information from it. Following these instructions please write a 100% unique, creative, and human-like style article of at least ${length*2} words about the following Topic and Keywords given. + + Every Blog has to follow the instructions exactly. + ${faq ? "Include an FAQ section at the bottom of the article to help users out with some extra information." : ""} + + You should only respond in json with the following format and all fields filled out: { meta_title: string, meta_desc: string, slug: string, excerp: string, title: string, blog_outline_sections: string[], content: string }` + }, { + role: "user", + content: `Generate a blog post based on the following youtube video: + Title: ${title} + Captions: ${captions}` }]; const result = await promptGPT(prompt);