Tools: I Built a Telegram Bot That Writes Cover Letters for $0.14

Tools: I Built a Telegram Bot That Writes Cover Letters for $0.14

Source: Dev.to

The User Flow ## Tech Stack ## Step 1: Set Up the Bot ## Step 2: Handle the Cover Letter Flow ## Step 3: Telegram Stars Payment ## Step 4: AI Generation ## Step 5: Deploy ## Economics ## Expanding the Bot ## Why Telegram? ## Try It Job applications are tedious. You find a great role, then spend 30 minutes writing a cover letter that probably won't get read. I built a Telegram bot that does it in 10 seconds for $0.14. Here's exactly how it works and how you can build something similar. No signups. No credit cards. No friction. Create your bot with @BotFather on Telegram. Get the token. The conversation has three steps: get the job description, get the user's background, generate the letter. Telegram Stars let users pay without leaving the app. No Stripe integration needed. The bot runs 24/7 and restarts automatically if it crashes. Each cover letter costs about $0.003 in AI API fees. Users pay 10 Stars ($0.14). That's a 97% margin per request. At 10 letters/day, that's $1.40 revenue and $0.03 in costs. Not life-changing, but it compounds. And the bot runs itself — no customer support, no manual work. Once the cover letter flow works, add more services: Each service follows the same pattern: collect input, charge Stars, generate with AI. Telegram has 900M+ monthly users. Payments are built in. No app store approval. No website needed. Users can find your bot through search or shared links. The friction is near zero: tap a link, tap a button, paste text, pay with Stars. No account creation, no credit card forms, no page loads. The live bot is at @avatrix_ai_bot. Try the /cover command to see the full flow in action. Or build your own using the steps above. Built by Avatrix LLC. Also available as a web app at CoverCraft. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK: mkdir telegram-bot && cd telegram-bot npm init -y npm install node-telegram-bot-api Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: mkdir telegram-bot && cd telegram-bot npm init -y npm install node-telegram-bot-api CODE_BLOCK: mkdir telegram-bot && cd telegram-bot npm init -y npm install node-telegram-bot-api COMMAND_BLOCK: // src/index.ts import TelegramBot from 'node-telegram-bot-api'; const bot = new TelegramBot(process.env.TELEGRAM_BOT_TOKEN!, { polling: true, }); bot.onText(/\/start/, (msg) => { bot.sendMessage(msg.chat.id, 'I write tailored cover letters using AI.\n\n' + 'Commands:\n' + '/cover - Generate a cover letter\n' + '/help - How it works' ); }); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: // src/index.ts import TelegramBot from 'node-telegram-bot-api'; const bot = new TelegramBot(process.env.TELEGRAM_BOT_TOKEN!, { polling: true, }); bot.onText(/\/start/, (msg) => { bot.sendMessage(msg.chat.id, 'I write tailored cover letters using AI.\n\n' + 'Commands:\n' + '/cover - Generate a cover letter\n' + '/help - How it works' ); }); COMMAND_BLOCK: // src/index.ts import TelegramBot from 'node-telegram-bot-api'; const bot = new TelegramBot(process.env.TELEGRAM_BOT_TOKEN!, { polling: true, }); bot.onText(/\/start/, (msg) => { bot.sendMessage(msg.chat.id, 'I write tailored cover letters using AI.\n\n' + 'Commands:\n' + '/cover - Generate a cover letter\n' + '/help - How it works' ); }); COMMAND_BLOCK: const sessions = new Map<number, { step: string; jobDesc?: string }>(); bot.onText(/\/cover/, (msg) => { const chatId = msg.chat.id; sessions.set(chatId, { step: 'awaiting_job' }); bot.sendMessage(chatId, 'Paste the job description:'); }); bot.on('message', async (msg) => { const chatId = msg.chat.id; const session = sessions.get(chatId); if (!session) return; if (session.step === 'awaiting_job') { session.jobDesc = msg.text; session.step = 'awaiting_resume'; bot.sendMessage(chatId, 'Now paste your key skills and experience (a few bullet points is fine):' ); } else if (session.step === 'awaiting_resume') { // Trigger payment before generating await requestPayment(chatId, session.jobDesc!, msg.text!); } }); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: const sessions = new Map<number, { step: string; jobDesc?: string }>(); bot.onText(/\/cover/, (msg) => { const chatId = msg.chat.id; sessions.set(chatId, { step: 'awaiting_job' }); bot.sendMessage(chatId, 'Paste the job description:'); }); bot.on('message', async (msg) => { const chatId = msg.chat.id; const session = sessions.get(chatId); if (!session) return; if (session.step === 'awaiting_job') { session.jobDesc = msg.text; session.step = 'awaiting_resume'; bot.sendMessage(chatId, 'Now paste your key skills and experience (a few bullet points is fine):' ); } else if (session.step === 'awaiting_resume') { // Trigger payment before generating await requestPayment(chatId, session.jobDesc!, msg.text!); } }); COMMAND_BLOCK: const sessions = new Map<number, { step: string; jobDesc?: string }>(); bot.onText(/\/cover/, (msg) => { const chatId = msg.chat.id; sessions.set(chatId, { step: 'awaiting_job' }); bot.sendMessage(chatId, 'Paste the job description:'); }); bot.on('message', async (msg) => { const chatId = msg.chat.id; const session = sessions.get(chatId); if (!session) return; if (session.step === 'awaiting_job') { session.jobDesc = msg.text; session.step = 'awaiting_resume'; bot.sendMessage(chatId, 'Now paste your key skills and experience (a few bullet points is fine):' ); } else if (session.step === 'awaiting_resume') { // Trigger payment before generating await requestPayment(chatId, session.jobDesc!, msg.text!); } }); COMMAND_BLOCK: async function requestPayment( chatId: number, jobDesc: string, resume: string ) { await bot.sendInvoice(chatId, 'Cover Letter', 'AI-generated tailored cover letter', 'cover_letter', '', 'XTR', [ { label: 'Cover Letter', amount: 1 }, // 1 Star ≈ $0.014 ]); // Store data for after payment sessions.set(chatId, { step: 'awaiting_payment', jobDesc, resume, }); } bot.on('pre_checkout_query', (query) => { bot.answerPreCheckoutQuery(query.id, true); }); bot.on('successful_payment', async (msg) => { const chatId = msg.chat.id; const session = sessions.get(chatId); if (!session) return; bot.sendMessage(chatId, 'Payment received! Generating your cover letter...'); const letter = await generateCoverLetter( session.jobDesc!, session.resume! ); bot.sendMessage(chatId, letter); sessions.delete(chatId); }); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: async function requestPayment( chatId: number, jobDesc: string, resume: string ) { await bot.sendInvoice(chatId, 'Cover Letter', 'AI-generated tailored cover letter', 'cover_letter', '', 'XTR', [ { label: 'Cover Letter', amount: 1 }, // 1 Star ≈ $0.014 ]); // Store data for after payment sessions.set(chatId, { step: 'awaiting_payment', jobDesc, resume, }); } bot.on('pre_checkout_query', (query) => { bot.answerPreCheckoutQuery(query.id, true); }); bot.on('successful_payment', async (msg) => { const chatId = msg.chat.id; const session = sessions.get(chatId); if (!session) return; bot.sendMessage(chatId, 'Payment received! Generating your cover letter...'); const letter = await generateCoverLetter( session.jobDesc!, session.resume! ); bot.sendMessage(chatId, letter); sessions.delete(chatId); }); COMMAND_BLOCK: async function requestPayment( chatId: number, jobDesc: string, resume: string ) { await bot.sendInvoice(chatId, 'Cover Letter', 'AI-generated tailored cover letter', 'cover_letter', '', 'XTR', [ { label: 'Cover Letter', amount: 1 }, // 1 Star ≈ $0.014 ]); // Store data for after payment sessions.set(chatId, { step: 'awaiting_payment', jobDesc, resume, }); } bot.on('pre_checkout_query', (query) => { bot.answerPreCheckoutQuery(query.id, true); }); bot.on('successful_payment', async (msg) => { const chatId = msg.chat.id; const session = sessions.get(chatId); if (!session) return; bot.sendMessage(chatId, 'Payment received! Generating your cover letter...'); const letter = await generateCoverLetter( session.jobDesc!, session.resume! ); bot.sendMessage(chatId, letter); sessions.delete(chatId); }); COMMAND_BLOCK: async function generateCoverLetter( jobDesc: string, resume: string ): Promise<string> { const response = await fetch('https://api.anthropic.com/v1/messages', { method: 'POST', headers: { 'content-type': 'application/json', 'x-api-key': process.env.ANTHROPIC_API_KEY!, 'anthropic-version': '2023-06-01', }, body: JSON.stringify({ model: 'claude-sonnet-4-20250514', max_tokens: 1024, messages: [{ role: 'user', content: `Write a professional cover letter for this job: Job Description: ${jobDesc} Candidate Background: ${resume} Rules: - 3-4 paragraphs - Match skills to job requirements - Professional but not stuffy - Include a specific detail from the job description - End with a clear call to action`, }], }), }); const result = await response.json(); return result.content[0].text; } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: async function generateCoverLetter( jobDesc: string, resume: string ): Promise<string> { const response = await fetch('https://api.anthropic.com/v1/messages', { method: 'POST', headers: { 'content-type': 'application/json', 'x-api-key': process.env.ANTHROPIC_API_KEY!, 'anthropic-version': '2023-06-01', }, body: JSON.stringify({ model: 'claude-sonnet-4-20250514', max_tokens: 1024, messages: [{ role: 'user', content: `Write a professional cover letter for this job: Job Description: ${jobDesc} Candidate Background: ${resume} Rules: - 3-4 paragraphs - Match skills to job requirements - Professional but not stuffy - Include a specific detail from the job description - End with a clear call to action`, }], }), }); const result = await response.json(); return result.content[0].text; } COMMAND_BLOCK: async function generateCoverLetter( jobDesc: string, resume: string ): Promise<string> { const response = await fetch('https://api.anthropic.com/v1/messages', { method: 'POST', headers: { 'content-type': 'application/json', 'x-api-key': process.env.ANTHROPIC_API_KEY!, 'anthropic-version': '2023-06-01', }, body: JSON.stringify({ model: 'claude-sonnet-4-20250514', max_tokens: 1024, messages: [{ role: 'user', content: `Write a professional cover letter for this job: Job Description: ${jobDesc} Candidate Background: ${resume} Rules: - 3-4 paragraphs - Match skills to job requirements - Professional but not stuffy - Include a specific detail from the job description - End with a clear call to action`, }], }), }); const result = await response.json(); return result.content[0].text; } COMMAND_BLOCK: npm install -g pm2 pm2 start dist/index.js --name "cover-bot" pm2 save pm2 startup Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: npm install -g pm2 pm2 start dist/index.js --name "cover-bot" pm2 save pm2 startup COMMAND_BLOCK: npm install -g pm2 pm2 start dist/index.js --name "cover-bot" pm2 save pm2 startup COMMAND_BLOCK: bot.onText(/\/services/, (msg) => { bot.sendMessage(msg.chat.id, 'Available services:\n\n' + '/cover - Cover letter ($0.14)\n' + '/resume - Resume review ($0.28)\n' + '/linkedin - LinkedIn summary ($0.14)\n' + '/email - Professional email ($0.07)' ); }); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: bot.onText(/\/services/, (msg) => { bot.sendMessage(msg.chat.id, 'Available services:\n\n' + '/cover - Cover letter ($0.14)\n' + '/resume - Resume review ($0.28)\n' + '/linkedin - LinkedIn summary ($0.14)\n' + '/email - Professional email ($0.07)' ); }); COMMAND_BLOCK: bot.onText(/\/services/, (msg) => { bot.sendMessage(msg.chat.id, 'Available services:\n\n' + '/cover - Cover letter ($0.14)\n' + '/resume - Resume review ($0.28)\n' + '/linkedin - LinkedIn summary ($0.14)\n' + '/email - Professional email ($0.07)' ); }); - User opens @avatrix_ai_bot on Telegram - Types /cover and pastes a job description - Bot asks for their resume highlights - User pays with Telegram Stars (roughly $0.14) - Bot generates a tailored cover letter in 10 seconds - User copies it and applies - Runtime: Node.js with node-telegram-bot-api - AI: Claude API for letter generation - Payments: Telegram Stars (built-in, no Stripe needed) - Hosting: PM2 on a server (could use Railway or Fly.io)