Meet Your Automated Content Creator: A Telegram Bot Powered by Supabase and OpenAI

Meet Your Automated Content Creator: A Telegram Bot Powered by Supabase and OpenAI

Online products must create vivid and appealing content in the digital era to succeed. Nevertheless, hiring a specific content developer may become expensive as well and sometimes the creativity process may take longer than anticipated. Imagine a scenario where you could get customized, premium-quality content made just for you, without hiring human content developers. Then, the new age of content creation has arrived, only this time – automated!

Meet TelegramGPT - the ultimate tool for turning content creation upside down. Now with just one click on Telegram, tailor-made content is at hand as regards the online products. No more waiting for an idea to come or drafts from a content writer. Meet your content generator - your bot is always ready to feed you with hot content!

Make sure to read my article Building a Telegram Bot with Supabase Edge Functions to get a comprehensive understanding of setting up a Telegram Bot with Supabase. This previous guide will walk you through setting up your Supabase project, deploying your bot, and getting it up and running.

With a functional Telegram Bot in place, now let's enhance its capabilities using OpenAI for intelligent interactions and Supabase for user data management. Here’s a step-by-step guide to take your Telegram Bot to the next level:

Step 1: Setting Up Your OpenAI Account

Sign up for an OpenAI account if you haven’t already. Once signed up, navigate to the View API keys page to generate your API keys.

Step 2: Setting Up Your Project

Make sure you have followed the setup from the previous article. Now, let's add import all the necessary libraries to our project. create utils.ts file, import the necessary libraries and create an instance of each:

import { Bot } from "https://deno.land/x/grammy/mod.ts";
import { OpenAI } from "https://deno.land/x/openai@1.4.2/mod.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js";

export const bot = new Bot(Deno.env.get("BOT_TOKEN")!);
export const openai = new OpenAI(Deno.env.get("OPENAI_KEY")!);
export const supabase = createClient(
    Deno.env.get("URL_SUPABASE")!,
    Deno.env.get("KEY_SUPABASE")!,
  );

Step 3: Creating Intelligent Responses

Now, let's create a function that will generate smart responses for our bot. Create a new file called createPost.ts:

import { openai } from "./utils.ts";

async function createPost(ctx, text) {
  try {
    await ctx.reply("Generating Post...");
    const chat_completion = await openai.createChatCompletion({
      model: "gpt-3.5-turbo",
      messages: [
        {
          role: "system",
          content: `You are a content repurposing professional, 
            you take a text and you rewrite it into compelling 
            content to Instagram ad script. 
            Only include the script. 
            Remember the best instagram ads 
            include the following elements:
          1. Keep it simple and short. Stick to plain text.
          2. Add emojis to your posts where appropriate.
          3. Write a killer headline.
          4. Open with a story where appropriate.
          5. Break up walls of text.
          6. Give specific instructions and unique insights.
          7. Always end by asking a question.
          8. Bring a new, unique angle where possible. 
                Don't be afraid of a little controversy.
          9. Brevity is key.
          Think about whether your post makes sense as a whole, 
            before you start writing.`
        },
        {
          role: "user",
          content: text,
        },
      ],
    });
    await ctx.reply(chat_completion?.choices[0]?.message?.content) 
            || ctx.reply("Please try again.");
  } catch (err) {
    console.error(err);
    await ctx.reply("Error happened while generating post.");
  }
}
export default createPost;

I have picked an exciting niche, which is to use the Bot to create Instagram ad script posts for users! This can benefit the Instagram Shop owners.

Step 4: Managing User Data with Supabase

Supabase is used to manage user data such as credits, company descriptions, and audio output language. Include the following in index.ts:

import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { webhookCallback } from "https://deno.land/x/grammy/mod.ts";
import { bot, openai, supabase } from "./utils.ts";
import createPost from "./createPost.ts";

async function getUserCredits(userId: number): Promise<number> {
  const { data, error } = await supabase
    .from('users')
    .select('credits')
    .eq('user_id', userId)
    .single();

  if (error) throw error;

  return data?.credits || 0;
}

async function decreaseUserCredits(userId: number): Promise<void> {
  const { data: userData, error: fetchError } = await supabase
    .from('users')
    .select('credits')
    .eq('user_id', userId)
    .single();

  if (fetchError) throw fetchError;

  const currentCredits = userData?.credits || 0;

  const { error: updateError } = await supabase
    .from('users')
    .update({ credits: currentCredits - 1 })
    .eq('user_id', userId);

  if (updateError) throw updateError;
}

async function userExists(userId: number): Promise<boolean> {
  const { data, error } = await supabase
    .from('users')
    .select('user_id')
    .eq('user_id', userId);

  if (error) throw error;

  return data && data.length > 0;
}

async function createUser(userId: number): Promise<void> {
  const { error } = await supabase
    .from('users')
    .insert({ user_id: userId, credits: 3 });

  if (error) throw error;
}

bot.command("start", async (ctx) => {
  const userId = ctx.from?.id;

  if (!await userExists(userId!)) {
    await createUser(userId!);
    await ctx.reply("Welcome! Your account has been created with 3 free credits.");
  } else {
    await ctx.reply("Welcome back! Your account is already set up.");
  }
});

bot.on("message:text", async (ctx) => {
  const userId = ctx.from?.id;
  const userCredits = await getUserCredits(userId!);

  if (ctx.message.text.startsWith('/')) {
    return;
  }

  if (userCredits <= 0) {
    return await ctx.reply("Sorry, you've run out of credits.");
  }

  createPost(ctx, ctx.message.text);
  decreaseUserCredits(userId!);
});

const handleUpdate = webhookCallback(bot, "std/http");

serve(async (req) => {
  try {
    const url = new URL(req.url);
    if (url.searchParams.get("secret") !== Deno.env.get("FUNCTION_SECRET"))
      return new Response("not allowed", { status: 405 });

    return await handleUpdate(req);
  } catch (err) {
    console.error(err);
  }
});

Video Overview

Step 5: Adding new features

🚀 Let's add some exciting new features!

  1. Company Description:

Adding a company description to your Telegram Bot, so that it can create a post taking into consideration that specific description can be really helpful when creating an Instagram ad post. Add the following to index.ts:

async function setCompanyDescription(userId: number, description: string): Promise<void> {
  const { error } = await supabase
    .from('users')
    .update({ company_description: description })
    .eq('user_id', userId);

  if (error) throw error;
}

export async function getCompanyDescription(userId: number): Promise<string | null> {
  const { data, error } = await supabase
    .from('users')
    .select('company_description')
    .eq('user_id', userId)
    .single();

  if (error) throw error;

  return data?.company_description || null;
}

bot.command("description", async (ctx) => {
  const userId = ctx.from?.id;
  const description = ctx.match;

  if (description) {
    await setCompanyDescription(userId!, description);
    await ctx.reply("Your company description has been set.");
  }
  else {
    await ctx.reply("You haven't set a company description yet. Provide one after /description to set it.");
  }
});

then createPost.ts should be like this:

import { openai } from "./utils.ts";
import { getCompanyDescription } from "./index.ts";

async function createPost(ctx, text, companyDescription) {
  try {
     await ctx.reply("Generating Post...");
    const chat_completion = await openai.createChatCompletion({
      model: "gpt-3.5-turbo",
      messages: [
        {
          role: "system",
          content: `You are a content repurposing professional, 
            you take a text and you rewrite it into compelling 
            content to Instagram ad script. 
            Only include the script. 
            Remember the best instagram ads 
            include the following elements:
          ${companyDescription ? "\n The company description: " 
                + companyDescription : ""}
          1. Keep it simple and short. Stick to plain text.
          2. Add emojis to your posts where appropriate.
          3. Write a killer headline.
          4. Open with a story where appropriate.
          5. Break up walls of text.
          6. Give specific instructions and unique insights.
          7. Always end by asking a question.
          8. Bring a new, unique angle where possible. 
                Don't be afraid of a little controversy.
          9. Brevity is key.
          Think about whether your post makes sense as a whole, 
            before you start writing.`
        },
        {
          role: "user",
          content: text,
        },
      ],
    });
    await ctx.reply(chat_completion?.choices[0]?.message?.content) || ctx.reply("Please try again.");
  } catch (err) {
    console.error(err);
    await ctx.reply("Error happened while generating post.");
  }
}
export default createPost;

The following snippet will get the company description from the database and upon that, it will create a specific ad post using that specific description.

`${companyDescription ? "\n The company description: " + companyDescription : ""}`
  1. Post Language:

Adding a post language can be helpful when your audience aren't English speakers. Add the following code to index.ts:

import languageCodes from "./languageCodes.json" assert { type: "json" };

async function setUserLanguage(userId: number, responseLang: string): Promise<void> {
  const { error } = await supabase
    .from('users')
    .update({ response_language: responseLang })
    .eq('user_id', userId);

  if (error) throw error;
}

async function getUserLanguage(userId: number): Promise<{ responseLang: string } | null> {
  const { data, error } = await supabase
    .from('users')
    .select('response_language')
    .eq('user_id', userId)
    .single();

  if (error) throw error;

  return data ? { responseLang: data.response_language } : null;
}

export async function getLanguageName(userId: number): Promise<string> {
  const userLanguage = await getUserLanguage(userId!);
  const languageName = languageCodes[userLanguage?.responseLang || "English"];
  return languageName;
}

bot.command("language", async (ctx) => {
  const userId = ctx.from?.id;
  const responseLang = ctx.match;
  await setUserLanguage(userId!, responseLang);
  await ctx.reply(`Response language has been set to ${responseLang}`);
});

languageCodes.json is a JSON file that contains mapped language codes to their language name. Now, let's modify createPost.ts:

import { openai } from "./utils.ts";
import { getCompanyDescription, getLanguageName } from "./index.ts";

async function createPost(ctx, text, language, companyDescription) {
  try {
    await ctx.reply("Generating Post...");
    const chat_completion = await openai.createChatCompletion({
      model: "gpt-3.5-turbo",
      messages: [
        {
          role: "system",
          content: `You are a content repurposing professional, 
            you take a text and you rewrite it into compelling 
            content to Instagram ad script. 
            Write it in ${language} language. 
            Only include the script. 
            Remember the best instagram ads include the
            following elements: 
            ${companyDescription ? "\nThe company description: "
                 + companyDescription : ""}
          1. Keep it simple and short. Stick to plain text.
          2. Add emojis to your posts where appropriate.
          3. Write a killer headline.
          4. Open with a story where appropriate.
          5. Break up walls of text.
          6. Give specific instructions and unique insights.
          7. Always end by asking a question.
          8. Bring a new, unique angle where possible. 
                Don't be afraid of a little controversy.
          9. Brevity is key.
          Think about whether your post makes sense as a whole,
             before you start writing.`
        },
        {
          role: "user",
          content: text,
        },
      ],
    });
    await ctx.reply(chat_completion?.choices[0]?.message?.content) || ctx.reply("Please try again.");
  } catch (err) {
    console.error(err);
    await ctx.reply("Error happened while generating post.");
  }
}
export default createPost;

The following snippet will write the post in whatever language you told him to write with:

`Write it in ${language} language.`

Video Overview

  1. Fetch YouTube transcriptions and make an Instagram post from it.

This shouldn't necessarily be used only for Instagram ads, but also to be used to create Twitter or LinkedIn posts. Remember that you can change the picked niche to anything you can imagine, such as LinkedIn or Twitter posts.

import { YoutubeTranscript } from "https://esm.sh/youtube-transcript@1.0.6";

async function fetchYouTubeTranscript(ctx, url, language, companyDescription) {
  try {
    await ctx.reply("Transcribing...");

    const transcript = await YoutubeTranscript.fetchTranscript(url);

    const formattedTranscript = transcript
      .map((item) => item.text)
      .join(" ")
      .replaceAll("\n", " ");

    const chunkSize = 4096;
    for (let i = 0; i < formattedTranscript.length; i += chunkSize) {
      const chunk = formattedTranscript.slice(i, i + chunkSize);
      // Assuming you also want to send the chunks to Telegram
      await ctx.reply(chunk);
    }

    createPost(ctx, formattedTranscript, language, companyDescription);

  } catch (error) {
    console.error("Error fetching transcript:", error);
    await ctx.reply("An error occurred while fetching the transcript.");
  }
}

bot.command("youtube", async (ctx) => {
  const userId = ctx.from?.id;
  const companyDescription = await getCompanyDescription(userId!);
  const language = await getLanguageName(userId!);

  const url = ctx.match;
  if (!url) {
    return ctx.reply("Please provide a YouTube URL.");
  }

  fetchYouTubeTranscript(ctx, url, language, companyDescription);
});

Step 5: Voice to Instagram Post

If you're lazy enough, like me 🤝🏻, and just want to send your Bot a voice message so that it creates a post, I have built that as well.

  • Create a new function in index.ts named getTranscribe which will be responsible for handling voice messages, transcribing them, and generating a post out of the transcription:
async function getTranscribe(ctx, voiceId, voiceInfo) {
  const userId = ctx.from?.id;
  const companyDescription = await getCompanyDescription(userId!);
  const language = await getLanguageName(userId!);
  const userCredits = await getUserCredits(userId!);
  if (userCredits <= 0) {
    return await ctx.reply("Sorry, you've run out of credits.");
  }
  try {
    const fileLink = `https://api.telegram.org/file/bot${Deno.env.get(
      "BOT_TOKEN"
    )}/${voiceInfo.file_path}`;
    const fileResponse = await fetch(fileLink);
    if (!fileResponse.ok) {
      return ctx.reply("Error fetching file");
    }
    const fileBuffer = await fileResponse.arrayBuffer();
    const file = new File([fileBuffer], voiceId, {
      type: "audio/ogg",
    });

    // transcribe voice message.
    await ctx.reply("Transcribing...");
    const transcribe = await openai.createTranscription({
      file: file,
      model: "whisper-1",
    });

    // generate post.
    createPost(ctx, transcribe.text, language, companyDescription);

    // decrease user credits.
    decreaseUserCredits(userId!);
  }
  catch (err) {
    console.error(err);
    await ctx.reply("Error fetching transcribe");
  }
}

bot.on("message:voice", async (ctx) => {
  try {
    const voiceId = ctx.message.voice!.file_id;
    const voiceInfo = await bot.api.getFile(voiceId);
    getTranscribe(ctx, voiceId, voiceInfo);

  } catch (err) {
    console.error(err);
    await ctx.reply("Error");
  }
});

The following snippet constructs the file URL and fetches the voice message file from Telegram:

  const fileLink = `https://api.telegram.org/file/bot${Deno.env.get("BOT_TOKEN")}/${voiceInfo.file_path}`;
  const fileResponse = await fetch(fileLink);
  if (!fileResponse.ok) {
    return ctx.reply("Error fetching file");
  }
  const fileBuffer = await fileResponse.arrayBuffer();
  const file = new File([fileBuffer], voiceId, {
    type: "audio/ogg",
  });

The following snippet uses OpenAI's Whisper API to transcribe the voice message:

  const transcribe = await openai.createTranscription({
    file: file,
    model: "whisper-1",
  });

Finally, with the transcribed text, call the createPost function to generate a post:

createPost(ctx, transcribe.text, language, companyDescription);

Video Overview

Help command

The /help command replies with a message containing a list of all available commands along with a brief description of each. This way, users can easily see what commands are available and learn how to interact with the bot.

bot.command("help", (ctx) => {
  const commandsList = [
    "🚀 /start - Start using the bot",
    "📝 /description [description] - Set company description",
    "🗣️ /language [language] - Set response language",
    "🎥 /youtube [YouTube URL] - Get transcript from a YouTube video",
    "ℹ️ /help - Show available commands",
  ];

  const commandsText = commandsList.join("\n\n");

  const helpMessage = `Available commands: \n\n${commandsText}`;

  ctx.reply(helpMessage);
});

Step 6: Deploy to Supabase

Do not forget to deploy your Bot to Supabase!

npx supabase functions deploy telegram-bot --no-verify-jwt --project-ref *****

And do not forget to add your Supabase secrets! We've added new environment variables.

npx supabase secrets set --env-file ./supabase/functions/telegram-bot/.env --project-ref *****

The entire code is posted on my GitHub.

Thank you for reading ❤️! Feel free to contribute on GitHub, it's open source.