<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged telegram-bot</title>
<link>https://coderlegion.com/tag/telegram-bot</link>
<description>Powered by Question2Answer</description>
<item>
<title>How I Built a Telegram Habit-Tracker Bot with No Programming Experience</title>
<link>https://coderlegion.com/27939/how-i-built-a-telegram-habit-tracker-bot-with-no-programming-experience</link>
<description>&lt;p&gt;Hi everyone!&lt;/p&gt;
&lt;p&gt;This is my first post here. I am writing not for experienced developers, but for those who want to gain their first programming experience using AI models.&lt;/p&gt;
&lt;p&gt;Here is how it went for me.&lt;/p&gt;
&lt;p&gt;Recently, I had no idea about programming and had never built Telegram bots. Three months ago, as an experiment, I started &quot;vibe coding,&quot; and now I have a working Telegram bot with several thousand lines of code, modules for selecting and saving time, passwords, a database, payment processing, 10 languages, and chart generation. You can launch the bot at &lt;a rel=&quot;nofollow&quot; href=&quot;https://telegram.me/I_promise_myself_bot&quot;&gt;https://telegram.me/I_promise_myself_bot&lt;/a&gt; (click the &quot;Try free &amp;amp; Unlock&quot; button).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=2162161034962503601&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=13140846015101676046&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=11338235374769885276&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=9176407238152911181&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=11104493393799405114&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=11143851574970981674&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=18003231601014050572&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=9366016401713054251&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I want to share my experience—how I used several different LLMs in a regular free chat, how I bypassed limits, and how I got out of dead ends when the AIs completely stalled, forgot context, and the bot started breaking.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Where It All Started&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Like everyone else, I started with ChatGPT, then tried Grok, Mistral, Gemini, Qwen, and DeepSeek bit by bit. I wanted to figure out through trial and error what each could do and for how long. I quickly ran into token limits in free chats. Also, over long distances, they lose context at different times, start mixing up file structures, and produce non-working pieces of code.&lt;/p&gt;
&lt;p&gt;As long as there were 200-300 lines of code, working with AIs was manageable, but when the code exceeded 1,000 lines, the problems began. To overcome bottlenecks, I couldn't paste or upload such an amount of code into the window for the AI to see the whole code, find the exact error, or add a new feature.&lt;/p&gt;
&lt;p&gt;Another problem was that any AI, as a rule, forgets the conversation after a few hours or starts hallucinating heavily. After that, you have to kill the chat and create a new one, re-supplying the new chat with the general task, snippets of chat history and code, and almost the entire conversation so it understands what I want from it. I regularly read bloggers, and regarding this, I asked Kostya Doronin for advice on how to work with AIs when code accumulates to more than 1,000 lines across multiple files. Surprisingly, he readily suggested several free and paid workarounds, but I managed with free ones only. I fundamentally wanted to figure out if it's possible to make something working with AIs for free.&lt;/p&gt;
&lt;p&gt;I had to change my approach a bit. First, I roughly figured out what can be squeezed out of this or that AI, when they start glitching, and how to bypass it. I stopped loading one AI with all tasks at once and started dividing tasks among models.&lt;/p&gt;
&lt;p&gt;I settled on these:&lt;/p&gt;
&lt;p&gt;1.Claude is the architect. It has the best logic and understanding of the general code and project structure. I used it for brainstorming and design, modularizing, and creating step-by-step instructions for other AIs. Also as a &quot;rescuer&quot; in dead-end situations when another AI started leading me in circles. The only downside is that free tokens run out very quickly, and you have to wait a few hours.&lt;br&gt;
2.Qwen turned out to be the main &quot;workhorse&quot; over long distances. If you give it a detailed plan from Claude, Qwen handles it well and sticks to the plan. Claude has strict limits on free messages, but with Qwen, you could code all day long in the same chat, solving tasks one step at a time. It holds up for about 7 days before hallucinations begin, after which you need to kill the chat and start a new one.&lt;br&gt;
3.Gemini is an additional tool for quickly double-checking individual pieces of code, like a &quot;fresh perspective&quot; on a problem or a new task. But in the free version, it is not particularly good at programming and architecture, and if you work with it for a whole day or two, it also starts hallucinating. Although there were periods when these same models worked for weeks without any hallucinations at all. And sometimes a day was enough for both Qwen and Gemini to start glitching, which I never encountered with Claude. Probably because it ran out quickly! From which I concluded that model owners for some reason release different quality versions of the same model into the wild.&lt;/p&gt;
&lt;p&gt;In other words, all these LLMs are a very unreliable thing, and for serious projects, you should only work with people. But as assistants in simple coding tasks, they are very good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Project Architecture&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;At the beginning of creating the bot, Claude guided me step-by-step to download and set up all these tools on my computer:&lt;/p&gt;
&lt;p&gt;•Language and framework: Python 3.11+, aiogram 3.x.&lt;br&gt;
•Database: Started with simple Google Sheets, but eventually switched to PostgreSQL, hosted on Supabase. Mistake: should have used PostgreSQL from the very beginning.&lt;br&gt;
•Background tasks: APScheduler for push notifications.&lt;br&gt;
•Graphics: matplotlib for reports.&lt;br&gt;
•Payments: Paddle SDK integration.&lt;br&gt;
•Localization: Multilingual system based on JSON (languages). Mistake: did it at the very end, but should have done it at the very beginning because I had to redo a lot of things.&lt;br&gt;
•Security: Isolating tokens in a separate file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Development Process&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For each new bot feature, I tasked Claude with developing as detailed a step-by-step instruction plan for Qwen as possible. I coded in VS Code, but didn't use LLMs inside it directly, only in chats:&lt;/p&gt;
&lt;p&gt;1.I initially split the entire project into logical modules: there is one main file up to 2,000 lines, and the rest are 200-700 lines. Everything is separate – handlers, database operations, notifications, buttons, and localization.&lt;br&gt;
2.Working with Qwen: Before any changes, I saved all recent files with working code in a separate folder so I could easily roll back. I gave Qwen a step-by-step plan and changed or added functionality step by step. I inserted it into the project, ran the bot, and checked the operation and logs. If there was a problem, I gave the logs to all three AIs at once to find the issue. If the problem hit a dead end, I started over from the last working code, but mentioned in the task to Claude (if tokens remained) that there were such-and-such problems during the previous change and they needed to be avoided. If Claude was exhausted, I asked Gemini. And so I moved step by step.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How I Got Out of Dead Ends&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Somewhere in the middle of the project, the code exceeded a thousand lines, and problems began: the AI forgot the context and broke what was already working when trying to fix a bug.&lt;/p&gt;
&lt;p&gt;At that moment, I brought in Claude:&lt;/p&gt;
&lt;p&gt;1.I described the essence of the technical problem and the bot's startup logs in VS Code.&lt;br&gt;
2.I provided snippets of chat history where Qwen started going in circles.&lt;br&gt;
3.I attached the problematic code.&lt;/p&gt;
&lt;p&gt;Mostly, Claude found the error on the first try and provided a solution. If it didn't know, it asked leading questions and we ran additional checks. Unlike Qwen or Gemini, which immediately &quot;poured out&quot; a bunch of possible causes after which going in infinite circles began. In especially neglected cases, I &quot;pitted models against each other&quot;: I drove the problematic piece in circles between Claude, Gemini, and Qwen until one of them found the bug. Their collective intelligence works much more reliably.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conclusions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After a few months of casual work, I got a working Telegram bot with several thousand lines of code, written from scratch with no experience.&lt;/p&gt;
&lt;p&gt;What I realized for myself:&lt;/p&gt;
&lt;p&gt;1.On simple tasks, AI replaces a team, but you need to understand the project structure well yourself and at least superficially understand the code. If you have no knowledge, you can learn to think systemically and architecturally on such projects.&lt;br&gt;
2.Use different models. Using a single AI for everything is undesirable. The &quot;architect + coder + fresh perspective&quot; bundle works better.&lt;br&gt;
3.Small steps. Wrote a function – tested it. Broke – rolled back to the last working version.&lt;br&gt;
4.Save chat history. Chat history with AI helps you not to lose the logic of the project yourself after a week or month of work, and to provide context to another AI for problem analysis.&lt;/p&gt;
&lt;p&gt;I will be glad to answer questions in the comments!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27939/how-i-built-a-telegram-habit-tracker-bot-with-no-programming-experience</guid>
<pubDate>Mon, 21 Sep 2026 12:26:13 +0000</pubDate>
</item>
<item>
<title>SmartAlert-T (SAT)</title>
<link>https://coderlegion.com/20989/smartalert-t-sat</link>
<description>&lt;h2 id=&quot;smartalert-t-sat-node-js&quot;&gt;SmartAlert-T (SAT) — Node.js&lt;/h2&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;The first line of defense for your servers and applications.&lt;/strong&gt;&lt;br&gt;
Send beautiful, structured &lt;strong&gt;Telegram&lt;/strong&gt; alerts — &lt;code&gt;critical&lt;/code&gt;, &lt;code&gt;warning&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt; — straight from your Node.js code, cron jobs and background workers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;LICENSE&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/License-MIT-green.svg&quot; alt=&quot;License: MIT&quot;&gt;&lt;/a&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://nodejs.org/&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/node-%3E%3D14-339933.svg&quot; alt=&quot;Node&quot;&gt;&lt;/a&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.typescriptlang.org/&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/TypeScript-ready-3178c6.svg&quot; alt=&quot;TypeScript&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Built by &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://giftbalogun.name.ng/&quot;&gt;Gift Balogun&lt;/a&gt;&lt;/strong&gt;. This is the Node.js twin of the PHP/Laravel SmartAlert-T package — &lt;strong&gt;identical message styling and footer&lt;/strong&gt; across both runtimes.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-features&quot;&gt;✨ Features&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;🚨 &lt;strong&gt;Three alert levels&lt;/strong&gt; — Critical, Warning, Info — each with distinct emoji + styling.&lt;/li&gt;
&lt;li&gt;💬 Send to &lt;strong&gt;individual users, groups, supergroups and channels&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;🟦 &lt;strong&gt;TypeScript-first&lt;/strong&gt; — ships with full type definitions.&lt;/li&gt;
&lt;li&gt;⏱️ &lt;strong&gt;Cron &amp;amp; background-process friendly&lt;/strong&gt; — optional non-throwing mode so alerting never crashes your jobs.&lt;/li&gt;
&lt;li&gt;🏷️ Rich &lt;strong&gt;context metadata&lt;/strong&gt; block + automatic timestamp.&lt;/li&gt;
&lt;li&gt;🔒 HTML-safe message escaping.&lt;/li&gt;
&lt;li&gt;🪶 &lt;strong&gt;Zero runtime dependencies&lt;/strong&gt; — built on the Node.js core &lt;code&gt;https&lt;/code&gt; module.&lt;/li&gt;
&lt;li&gt;🖋️ Branded footer with copyright to Gift Balogun on every message.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-installation&quot;&gt;📦 Installation&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install smart-alert-t
# or
yarn add smart-alert-t
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Requires &lt;strong&gt;Node.js &amp;gt;= 14&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-getting-your-telegram-credentials&quot;&gt;🤖 Getting your Telegram credentials&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Create a bot&lt;/strong&gt; — open &lt;a rel=&quot;nofollow&quot; href=&quot;https://t.me/BotFather&quot;&gt;@BotFather&lt;/a&gt;, send &lt;code&gt;/newbot&lt;/code&gt;, and copy the &lt;strong&gt;bot token&lt;/strong&gt; (e.g. &lt;code&gt;123456789:AAH...&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Find your chat id:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Personal chat&lt;/strong&gt;: message &lt;a rel=&quot;nofollow&quot; href=&quot;https://t.me/userinfobot&quot;&gt;@userinfobot&lt;/a&gt; for your numeric id.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Group&lt;/strong&gt;: add the bot to the group, then open &lt;code&gt;https://api.telegram.org/bot&amp;lt;TOKEN&amp;gt;/getUpdates&lt;/code&gt; and read &lt;code&gt;chat.id&lt;/code&gt; (group ids are usually negative).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Channel&lt;/strong&gt;: use &lt;code&gt;@your_channel_username&lt;/code&gt; and make the bot an admin.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-quick-start&quot;&gt;🚀 Quick start&lt;/h3&gt;
&lt;h4 id=&quot;commonjs&quot;&gt;CommonJS&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const { SmartAlert } = require('smart-alert-t');

const alert = SmartAlert.make({
  token: 'YOUR_BOT_TOKEN',
  chatId: 'YOUR_CHAT_ID',   // user, group, or @channel
  appName: 'Payments API',  // optional label on every alert
});

await alert.critical('Database down', 'Primary DB is unreachable.', { host: 'db-01' });
await alert.warning('High memory',   'Worker memory at 86%.',       { service: 'queue' });
await alert.info('Deploy complete',  'v2.4.1 shipped to prod.',     { version: '2.4.1' });
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;es-modules-typescript&quot;&gt;ES Modules / TypeScript&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { SmartAlert } from 'smart-alert-t';

const alert = new SmartAlert({
  token: process.env.SMART_ALERT_TOKEN!,
  chatId: process.env.SMART_ALERT_CHAT_ID!,
  appName: 'Billing Service',
});

await alert.info('Job done', 'Nightly export completed.', { rows: 48211 });
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-the-three-alert-types&quot;&gt;🎨 The three alert types&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Method        &lt;/th&gt;&lt;th&gt;Emoji &lt;/th&gt;&lt;th&gt;Use it for&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;critical()&lt;/code&gt;  &lt;/td&gt;&lt;td&gt;🚨    &lt;/td&gt;&lt;td&gt;Outages, data loss, anything paging-worthy&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;warning()&lt;/code&gt;   &lt;/td&gt;&lt;td&gt;⚠️    &lt;/td&gt;&lt;td&gt;Degraded state that needs attention soon&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;info()&lt;/code&gt;      &lt;/td&gt;&lt;td&gt;ℹ️    &lt;/td&gt;&lt;td&gt;Deploys, job completions, routine heads-up&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Every method shares the same signature and returns a &lt;code&gt;Promise&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;alert.critical(
  title: string,                       // short headline
  message: string,                     // body text
  context?: Record&amp;lt;string, unknown&amp;gt;,   // optional metadata block
  chatId?: string | number | null,     // optional per-call destination override
): Promise&amp;lt;SmartAlertResult&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A rendered alert looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;🚨 [CRITICAL] Database down
App: Payments API

Primary DB is unreachable.

• host: db-01
• region: eu-west-1

🕒 2026-06-03 10:42:11 UTC
Powered by SmartAlert-T © 2026 — Gift Balogun
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-sending-to-users-vs-groups-vs-channels&quot;&gt;👥 Sending to users vs. groups vs. channels&lt;/h3&gt;
&lt;p&gt;The destination is just the &lt;code&gt;chatId&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// Individual user
await alert.info('Hi', 'Personal message.', {}, 123456789);

// Group / supergroup (ids are typically negative)
await alert.warning('Heads up', 'Group message.', {}, -1001234567890);

// Public channel
await alert.critical('Outage', 'Channel broadcast.', {}, '@my_status_channel');
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Set a default &lt;code&gt;chatId&lt;/code&gt; in the constructor and override it per call only when needed.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-using-it-in-cron-jobs-amp-background-processes&quot;&gt;⏰ Using it in cron jobs &amp;amp; background processes&lt;/h3&gt;
&lt;p&gt;Set &lt;code&gt;throwOnError: false&lt;/code&gt; so a transient Telegram hiccup can never crash your scheduled task — the promise resolves to &lt;code&gt;{ ok: false, error }&lt;/code&gt; instead of rejecting.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const os = require('os');
const { SmartAlert } = require('smart-alert-t');

const alert = SmartAlert.make({
  token: process.env.SMART_ALERT_TOKEN,
  chatId: process.env.SMART_ALERT_CHAT_ID,
  appName: `Server: ${os.hostname()}`,
  throwOnError: false,
});

const result = await alert.warning('Disk filling up', 'Root volume at 82%.', { used: '82%' });
if (!result.ok) {
  console.error('[SAT] delivery failed:', result.error);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Crontab example&lt;/strong&gt; (every 5 minutes):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cron&quot;&gt;SMART_ALERT_TOKEN=123456789:AAH...
SMART_ALERT_CHAT_ID=-1001234567890
*/5 * * * * /usr/bin/node /var/www/app/examples/cron-health-check.js &amp;gt;&amp;gt; /var/log/sat.log 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;node-cron example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const cron = require('node-cron');
const { SmartAlert } = require('smart-alert-t');

const alert = SmartAlert.make({
  token: process.env.SMART_ALERT_TOKEN,
  chatId: process.env.SMART_ALERT_CHAT_ID,
  throwOnError: false,
});

cron.schedule('*/5 * * * *', async () =&amp;gt; {
  // your health check...
  await alert.info('Heartbeat', 'Service is alive.');
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See &lt;a rel=&quot;nofollow&quot; href=&quot;examples/basic-usage.js&quot;&gt;&lt;code&gt;examples/basic-usage.js&lt;/code&gt;&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;examples/cron-health-check.js&quot;&gt;&lt;code&gt;examples/cron-health-check.js&lt;/code&gt;&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;examples/typescript-usage.ts&quot;&gt;&lt;code&gt;examples/typescript-usage.ts&lt;/code&gt;&lt;/a&gt; for complete scripts.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-configuration-reference&quot;&gt;⚙️ Configuration reference&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Option         &lt;/th&gt;&lt;th&gt;Type                       &lt;/th&gt;&lt;th&gt;Default   &lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;token&lt;/code&gt;        &lt;/td&gt;&lt;td&gt;&lt;code&gt;string&lt;/code&gt;                   &lt;/td&gt;&lt;td&gt;—         &lt;/td&gt;&lt;td&gt;Telegram bot token from @BotFather. &lt;strong&gt;Required.&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;chatId&lt;/code&gt;       &lt;/td&gt;&lt;td&gt;&lt;code&gt;string \| number \| null&lt;/code&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;null&lt;/code&gt;    &lt;/td&gt;&lt;td&gt;Default destination (user/group/&lt;code&gt;@channel&lt;/code&gt;).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;appName&lt;/code&gt;      &lt;/td&gt;&lt;td&gt;&lt;code&gt;string \| null&lt;/code&gt;           &lt;/td&gt;&lt;td&gt;&lt;code&gt;null&lt;/code&gt;    &lt;/td&gt;&lt;td&gt;Optional label shown on every alert.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;timeout&lt;/code&gt;      &lt;/td&gt;&lt;td&gt;&lt;code&gt;number&lt;/code&gt;                   &lt;/td&gt;&lt;td&gt;&lt;code&gt;10000&lt;/code&gt;   &lt;/td&gt;&lt;td&gt;Request timeout in &lt;strong&gt;milliseconds&lt;/strong&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;throwOnError&lt;/code&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;boolean&lt;/code&gt;                  &lt;/td&gt;&lt;td&gt;&lt;code&gt;true&lt;/code&gt;    &lt;/td&gt;&lt;td&gt;Reject on failure (&lt;code&gt;true&lt;/code&gt;) or resolve an error payload.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-error-handling&quot;&gt;🛡️ Error handling&lt;/h3&gt;
&lt;p&gt;Default mode rejects with a &lt;code&gt;SmartAlertError&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const { SmartAlert, SmartAlertError } = require('smart-alert-t');

try {
  await alert.critical('X', 'Y');
} catch (err) {
  if (err instanceof SmartAlertError) {
    // log, retry, fall back to email, ...
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With &lt;code&gt;throwOnError: false&lt;/code&gt;, every call resolves to &lt;code&gt;{ ok: false, error }&lt;/code&gt; on failure and the decoded Telegram response on success.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-testing-amp-building&quot;&gt;🧪 Testing &amp;amp; building&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install
npm run build   # compile TypeScript -&amp;gt; dist/
npm test        # run the offline unit tests
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The test suite covers alert-type validation, message formatting, HTML escaping and error handling — all &lt;strong&gt;without network calls&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-license&quot;&gt;📄 License&lt;/h3&gt;
&lt;p&gt;MIT © &lt;a rel=&quot;nofollow&quot; href=&quot;https://giftbalogun.name.ng/&quot;&gt;Gift Balogun&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
</description>
<category>Launches</category>
<guid isPermaLink="true">https://coderlegion.com/20989/smartalert-t-sat</guid>
<pubDate>Fri, 19 Jun 2026 18:35:30 +0000</pubDate>
</item>
<item>
<title>How to Build a Telegram Bot for Infrastructure Alerts</title>
<link>https://coderlegion.com/19528/how-to-build-a-telegram-bot-for-infrastructure-alerts</link>
<description>&lt;p&gt;Your infrastructure will not always fail loudly.&lt;/p&gt;
&lt;p&gt;Sometimes your database gets slow.&lt;br&gt;
Sometimes your queue worker stops.&lt;br&gt;
Sometimes your API starts returning errors.&lt;br&gt;
Sometimes your server disk quietly moves from 70% to 95% usage while you are busy fixing “one small bug.”&lt;/p&gt;
&lt;p&gt;The real problem is not that infrastructure breaks.&lt;/p&gt;
&lt;p&gt;The real problem is finding out too late.&lt;/p&gt;
&lt;p&gt;If users are the first people telling you your app is slow or unavailable, your monitoring setup is already behind. As developers, especially when building SaaS products, APIs, mobile apps, internal tools, or homelab powered systems, we need a faster way to know when something goes wrong.&lt;/p&gt;
&lt;p&gt;That is where a Telegram bot for infrastructure alerts becomes useful.&lt;/p&gt;
&lt;p&gt;The solution is simple: create a Telegram bot that sends instant alerts when your server, database, queue, API, or background jobs start misbehaving.&lt;/p&gt;
&lt;p&gt;It does not need to be complicated. You do not need a huge observability stack on day one. You just need a reliable way to get notified before a small issue becomes a production incident.&lt;/p&gt;
&lt;h4 id=&quot;why-use-telegram-for-infrastructure-alerts-&quot;&gt;Why Use Telegram for Infrastructure Alerts?&lt;/h4&gt;
&lt;p&gt;There are many ways to receive infrastructure alerts: email, Slack, Discord, SMS, dashboards, and full monitoring platforms.&lt;/p&gt;
&lt;p&gt;But Telegram has a few advantages that make it very developer friendly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is fast.&lt;/li&gt;
&lt;li&gt;It works well on mobile.&lt;/li&gt;
&lt;li&gt;Bots are easy to create.&lt;/li&gt;
&lt;li&gt;Messages can be sent through a simple API.&lt;/li&gt;
&lt;li&gt;You can send alerts to yourself or a team group.&lt;/li&gt;
&lt;li&gt;It is lightweight enough for small projects, SaaS apps, and homelabs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For solo developers and small teams, Telegram is often enough to start with.&lt;/p&gt;
&lt;p&gt;You do not always need a complex monitoring setup immediately. Sometimes, what you need first is this:&lt;/p&gt;
&lt;p&gt;“Tell me when the database is down.”&lt;/p&gt;
&lt;p&gt;“Tell me when the API is slow.”&lt;/p&gt;
&lt;p&gt;“Tell me when disk space is almost full.”&lt;/p&gt;
&lt;p&gt;“Tell me when a payment webhook fails.”&lt;/p&gt;
&lt;p&gt;That alone can save you hours of debugging and embarrassment.&lt;/p&gt;
&lt;h4 id=&quot;what-we-are-building&quot;&gt;What We Are Building&lt;/h4&gt;
&lt;p&gt;In this guide, we are building a simple Telegram bot for infrastructure alerts.&lt;/p&gt;
&lt;p&gt;The bot will send notifications for events like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Server downtime&lt;/li&gt;
&lt;li&gt;Database connection failure&lt;/li&gt;
&lt;li&gt;Slow API response time&lt;/li&gt;
&lt;li&gt;Queue worker issues&lt;/li&gt;
&lt;li&gt;Failed background jobs&lt;/li&gt;
&lt;li&gt;High disk usage&lt;/li&gt;
&lt;li&gt;Failed backups&lt;/li&gt;
&lt;li&gt;Unexpected application errors&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The basic flow looks like this:&lt;/p&gt;
&lt;p&gt;Your application or server detects a problem.&lt;/p&gt;
&lt;p&gt;Then it sends a message to your Telegram bot.&lt;/p&gt;
&lt;p&gt;The bot delivers the alert to your Telegram chat or group.&lt;/p&gt;
&lt;p&gt;Simple.&lt;/p&gt;
&lt;p&gt;But extremely useful.&lt;/p&gt;
&lt;h4 id=&quot;when-this-becomes-valuable-as-a-developer&quot;&gt;When This Becomes Valuable as a Developer&lt;/h4&gt;
&lt;p&gt;If you are building a Laravel API, a Flutter mobile app, a SaaS dashboard, a Django backend, a NestJS API, or even running a homelab, infrastructure alerts quickly become important.&lt;/p&gt;
&lt;p&gt;In development, you can refresh the page and inspect logs manually.&lt;/p&gt;
&lt;p&gt;In production, that is not enough.&lt;/p&gt;
&lt;p&gt;Users do not care that your queue worker stopped because Supervisor failed.&lt;/p&gt;
&lt;p&gt;They do not care that your database is hosted somewhere with occasional network delay.&lt;/p&gt;
&lt;p&gt;They do not care that your VPS disk got full because logs were growing too fast.&lt;/p&gt;
&lt;p&gt;They only know the app feels broken.&lt;/p&gt;
&lt;p&gt;A Telegram infrastructure alert system gives you a chance to respond before users start complaining.&lt;/p&gt;
&lt;p&gt;That is the real value.&lt;/p&gt;
&lt;h4 id=&quot;step-1-create-a-telegram-bot&quot;&gt;Step 1: Create a Telegram Bot&lt;/h4&gt;
&lt;p&gt;The first step is creating a bot inside Telegram.&lt;/p&gt;
&lt;p&gt;Open Telegram and search for BotFather. BotFather is the official bot used to create and manage Telegram bots.&lt;/p&gt;
&lt;p&gt;Create a new bot and give it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A display name&lt;/li&gt;
&lt;li&gt;A username ending in &lt;code&gt;bot&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After creating the bot, Telegram will give you a bot token.&lt;/p&gt;
&lt;p&gt;That token is important.&lt;/p&gt;
&lt;p&gt;Treat it like a password.&lt;/p&gt;
&lt;p&gt;Do not commit it to GitHub.&lt;br&gt;
Do not paste it inside frontend code.&lt;br&gt;
Do not expose it in public logs.&lt;/p&gt;
&lt;p&gt;Your bot token is what allows your application to send messages through your Telegram bot.&lt;/p&gt;
&lt;h4 id=&quot;step-2-get-your-telegram-chat-id&quot;&gt;Step 2: Get Your Telegram Chat ID&lt;/h4&gt;
&lt;p&gt;After creating your bot, you need to know where the bot should send alerts.&lt;/p&gt;
&lt;p&gt;That destination is called a &lt;code&gt;chat_id&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The chat can be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your private Telegram account&lt;/li&gt;
&lt;li&gt;A Telegram group&lt;/li&gt;
&lt;li&gt;A Telegram channel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For most developers, starting with a private chat is fine.&lt;/p&gt;
&lt;p&gt;Send a message to your bot first. Something simple like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Hello bot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then use the Telegram Bot API &lt;code&gt;getUpdates&lt;/code&gt; method to inspect recent messages and find your &lt;code&gt;chat_id&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once you have the &lt;code&gt;chat_id&lt;/code&gt;, save it. Your application will need it when sending alerts.&lt;/p&gt;
&lt;h4 id=&quot;step-3-store-your-bot-credentials-safely&quot;&gt;Step 3: Store Your Bot Credentials Safely&lt;/h4&gt;
&lt;p&gt;Never hardcode your Telegram bot token directly in your application.&lt;/p&gt;
&lt;p&gt;Use environment variables instead.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-env&quot;&gt;TELEGRAM_BOT_TOKEN=your_bot_token_here
TELEGRAM_CHAT_ID=your_chat_id_here
TELEGRAM_API_BASE=your_telegram_bot_api_base_url
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are using Laravel, you can add the values to your &lt;code&gt;.env&lt;/code&gt; file and reference them through your config.&lt;/p&gt;
&lt;p&gt;Inside &lt;code&gt;config/services.php&lt;/code&gt;, you can add:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;'telegram' =&amp;gt; [
    'bot_token' =&amp;gt; env('TELEGRAM_BOT_TOKEN'),
    'chat_id' =&amp;gt; env('TELEGRAM_CHAT_ID'),
    'base_url' =&amp;gt; env('TELEGRAM_API_BASE'),
],
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This keeps your credentials clean, reusable, and easier to manage across development, staging, and production environments.&lt;/p&gt;
&lt;h4 id=&quot;step-4-create-a-telegram-alert-service-in-laravel&quot;&gt;Step 4: Create a Telegram Alert Service in Laravel&lt;/h4&gt;
&lt;p&gt;If your backend is Laravel, one clean way to structure this is by creating a dedicated service class.&lt;/p&gt;
&lt;p&gt;Something like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;&amp;lt;?php

namespace App\Services;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class TelegramAlertService
{
    public function send(string $title, string $message, string $level = 'warning'): void
    {
        $token = config('services.telegram.bot_token');
        $chatId = config('services.telegram.chat_id');
        $baseUrl = config('services.telegram.base_url');

        if (!$token || !$chatId || !$baseUrl) {
            Log::warning('Telegram alert skipped because credentials are missing.');
            return;
        }

        $text = strtoupper($level) . &quot;\n\n&quot; .
            $title . &quot;\n\n&quot; .
            $message . &quot;\n\n&quot; .
            'Time: ' . now()-&amp;gt;toDateTimeString();

        $response = Http::timeout(10)-&amp;gt;post(
            &quot;{$baseUrl}/bot{$token}/sendMessage&quot;,
            [
                'chat_id' =&amp;gt; $chatId,
                'text' =&amp;gt; $text,
            ]
        );

        if ($response-&amp;gt;failed()) {
            Log::warning('Failed to send Telegram infrastructure alert.', [
                'status' =&amp;gt; $response-&amp;gt;status(),
                'body' =&amp;gt; $response-&amp;gt;body(),
            ]);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This service gives you one reusable place to send infrastructure alerts.&lt;/p&gt;
&lt;p&gt;Now, instead of scattering Telegram API calls across your codebase, you can simply inject or call this service wherever you need it.&lt;/p&gt;
&lt;h4 id=&quot;step-5-send-your-first-test-alert&quot;&gt;Step 5: Send Your First Test Alert&lt;/h4&gt;
&lt;p&gt;Once your service is ready, send a test message.&lt;/p&gt;
&lt;p&gt;For example, inside a route, command, job, or controller, you can do:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;use App\Services\TelegramAlertService;

app(TelegramAlertService::class)-&amp;gt;send(
    'Infrastructure Alert Test',
    'If you are seeing this message, your Telegram alert bot is working.',
    'info'
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If everything is configured correctly, you should receive a Telegram message almost immediately.&lt;/p&gt;
&lt;p&gt;That first message feels small, but it is an important milestone.&lt;/p&gt;
&lt;p&gt;You now have a direct communication line between your infrastructure and your phone.&lt;/p&gt;
&lt;h4 id=&quot;step-6-monitor-your-database-connection&quot;&gt;Step 6: Monitor Your Database Connection&lt;/h4&gt;
&lt;p&gt;A database failure is one of the most important things to monitor.&lt;/p&gt;
&lt;p&gt;If your application depends on MySQL, PostgreSQL, or another database, you should know quickly when the connection fails or becomes too slow.&lt;/p&gt;
&lt;p&gt;In Laravel, you can create a custom Artisan command for health checks:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;php artisan make:command CheckInfrastructureHealth
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside the command, you can test the database connection:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;&amp;lt;?php

namespace App\Console\Commands;

use App\Services\TelegramAlertService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Throwable;

class CheckInfrastructureHealth extends Command
{
    protected $signature = 'infra:check-health';

    protected $description = 'Check infrastructure health and send alerts when something is wrong.';

    public function handle(TelegramAlertService $telegram): int
    {
        try {
            $start = microtime(true);

            DB::connection()-&amp;gt;getPdo();

            $duration = round((microtime(true) - $start) * 1000, 2);

            if ($duration &amp;gt; 1000) {
                $telegram-&amp;gt;send(
                    'Slow Database Response',
                    &quot;Database connection succeeded, but response time is {$duration}ms.&quot;,
                    'warning'
                );
            }

            $this-&amp;gt;info(&quot;Database check passed in {$duration}ms.&quot;);

            return self::SUCCESS;
        } catch (Throwable $e) {
            $telegram-&amp;gt;send(
                'Database Connection Failed',
                $e-&amp;gt;getMessage(),
                'critical'
            );

            return self::FAILURE;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This checks whether your database is reachable.&lt;/p&gt;
&lt;p&gt;It also alerts you when the database responds too slowly.&lt;/p&gt;
&lt;p&gt;That matters because sometimes the database is not fully down. Sometimes it is just slow enough to ruin the user experience.&lt;/p&gt;
&lt;p&gt;And honestly, slow systems can be more frustrating than broken ones.&lt;/p&gt;
&lt;p&gt;At least broken systems are obvious.&lt;/p&gt;
&lt;p&gt;Slow systems make you question everything.&lt;/p&gt;
&lt;h4 id=&quot;step-7-schedule-the-infrastructure-check&quot;&gt;Step 7: Schedule the Infrastructure Check&lt;/h4&gt;
&lt;p&gt;After creating your health check command, schedule it to run automatically.&lt;/p&gt;
&lt;p&gt;If you are using Laravel’s scheduler, you can run the command every minute or every few minutes depending on your needs.&lt;/p&gt;
&lt;p&gt;For newer Laravel versions, your scheduler may look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;use Illuminate\Support\Facades\Schedule;

Schedule::command('infra:check-health')-&amp;gt;everyMinute();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You do not have to check everything every minute.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Database health: every minute&lt;/li&gt;
&lt;li&gt;Disk usage: every 5 minutes&lt;/li&gt;
&lt;li&gt;Backup status: hourly&lt;/li&gt;
&lt;li&gt;SSL certificate expiry: daily&lt;/li&gt;
&lt;li&gt;Queue health: every minute&lt;/li&gt;
&lt;li&gt;Failed jobs count: every few minutes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal is not to spam yourself.&lt;/p&gt;
&lt;p&gt;The goal is to catch important issues early.&lt;/p&gt;
&lt;h4 id=&quot;step-8-monitor-queue-workers&quot;&gt;Step 8: Monitor Queue Workers&lt;/h4&gt;
&lt;p&gt;If your application uses queues, you should monitor them.&lt;/p&gt;
&lt;p&gt;A stopped queue worker can quietly break important features.&lt;/p&gt;
&lt;p&gt;Emails stop sending.&lt;br&gt;
Receipts stop generating.&lt;br&gt;
Webhook processing gets delayed.&lt;br&gt;
Background jobs pile up.&lt;/p&gt;
&lt;p&gt;And the frontend may still look fine.&lt;/p&gt;
&lt;p&gt;That is dangerous.&lt;/p&gt;
&lt;p&gt;For Laravel applications, queue monitoring is especially useful if you rely on jobs for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sending notifications&lt;/li&gt;
&lt;li&gt;Processing transactions&lt;/li&gt;
&lt;li&gt;Handling webhooks&lt;/li&gt;
&lt;li&gt;Syncing external APIs&lt;/li&gt;
&lt;li&gt;Generating reports&lt;/li&gt;
&lt;li&gt;Updating wallet or billing records&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can monitor failed jobs and send an alert when the count crosses a threshold.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;$failedJobs = DB::table('failed_jobs')-&amp;gt;count();

if ($failedJobs &amp;gt; 0) {
    app(TelegramAlertService::class)-&amp;gt;send(
        'Failed Jobs Detected',
        &quot;There are currently {$failedJobs} failed jobs in the queue.&quot;,
        'warning'
    );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is simple, but it gives you visibility.&lt;/p&gt;
&lt;p&gt;And visibility is where good infrastructure starts.&lt;/p&gt;
&lt;h4 id=&quot;step-9-monitor-disk-usage&quot;&gt;Step 9: Monitor Disk Usage&lt;/h4&gt;
&lt;p&gt;Disk usage is one of those boring problems that becomes very serious when ignored.&lt;/p&gt;
&lt;p&gt;Logs grow.&lt;br&gt;
Backups accumulate.&lt;br&gt;
Uploads increase.&lt;br&gt;
Temporary files pile up.&lt;/p&gt;
&lt;p&gt;Then one day, your server runs out of space and your application starts failing in weird ways.&lt;/p&gt;
&lt;p&gt;You can monitor disk usage with a small script or command.&lt;/p&gt;
&lt;p&gt;For example, your server can check disk usage and send a Telegram alert when usage crosses 85%.&lt;/p&gt;
&lt;p&gt;A good alert message should include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Server name&lt;/li&gt;
&lt;li&gt;Current disk usage&lt;/li&gt;
&lt;li&gt;Threshold&lt;/li&gt;
&lt;li&gt;Time of alert&lt;/li&gt;
&lt;li&gt;Suggested action&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example alert:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;CRITICAL

High Disk Usage

Server: production-api-01
Disk usage: 91%
Threshold: 85%
Action: Check logs, backups, uploads, and temporary files.

Time: 2026-06-02 10:00:00
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The best alerts do not just say “something is wrong.”&lt;/p&gt;
&lt;p&gt;They give you enough context to act quickly.&lt;/p&gt;
&lt;h4 id=&quot;step-10-monitor-api-response-time&quot;&gt;Step 10: Monitor API Response Time&lt;/h4&gt;
&lt;p&gt;For user facing applications, uptime is not enough.&lt;/p&gt;
&lt;p&gt;Your API can be technically “up” but still too slow.&lt;/p&gt;
&lt;p&gt;That is why response time monitoring is important.&lt;/p&gt;
&lt;p&gt;If your Laravel API powers a Flutter app, users will feel slow endpoints immediately.&lt;/p&gt;
&lt;p&gt;A mobile app does not hide backend latency very well.&lt;/p&gt;
&lt;p&gt;You can create a simple health endpoint like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;Route::get('/health', function () {
    return response()-&amp;gt;json([
        'status' =&amp;gt; 'ok',
        'timestamp' =&amp;gt; now()-&amp;gt;toDateTimeString(),
    ]);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then monitor that endpoint from a cron job, another server, or a monitoring script.&lt;/p&gt;
&lt;p&gt;If the response time crosses a limit, send a Telegram alert.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;WARNING

Slow API Response

Endpoint: /health
Response time: 1850ms
Threshold: 1000ms
Environment: production

Users may experience delays.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is especially useful when your database, API server, or network connection is under pressure.&lt;/p&gt;
&lt;h4 id=&quot;step-11-avoid-alert-fatigue&quot;&gt;Step 11: Avoid Alert Fatigue&lt;/h4&gt;
&lt;p&gt;One mistake developers make with alerts is sending too many.&lt;/p&gt;
&lt;p&gt;At first, it feels good to be notified about everything.&lt;/p&gt;
&lt;p&gt;Then your phone starts buzzing every two minutes.&lt;/p&gt;
&lt;p&gt;Eventually, you stop paying attention.&lt;/p&gt;
&lt;p&gt;That is alert fatigue.&lt;/p&gt;
&lt;p&gt;And it defeats the whole purpose of monitoring.&lt;/p&gt;
&lt;p&gt;To avoid noisy alerts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Alert only on things that require attention.&lt;/li&gt;
&lt;li&gt;Use thresholds instead of alerting on every small spike.&lt;/li&gt;
&lt;li&gt;Group repeated alerts.&lt;/li&gt;
&lt;li&gt;Add cooldown periods.&lt;/li&gt;
&lt;li&gt;Separate warning alerts from critical alerts.&lt;/li&gt;
&lt;li&gt;Include useful context in every message.&lt;/li&gt;
&lt;li&gt;Resolve alerts when systems recover.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, if your database is slow for one check, maybe that is a warning.&lt;/p&gt;
&lt;p&gt;If it is slow for five consecutive checks, that may deserve a critical alert.&lt;/p&gt;
&lt;p&gt;Not every small problem needs panic.&lt;/p&gt;
&lt;p&gt;Good monitoring helps you think clearly under pressure.&lt;/p&gt;
&lt;p&gt;Bad monitoring just creates anxiety.&lt;/p&gt;
&lt;h4 id=&quot;step-12-make-your-alerts-actually-useful&quot;&gt;Step 12: Make Your Alerts Actually Useful&lt;/h4&gt;
&lt;p&gt;A good infrastructure alert should answer four questions quickly:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What happened?&lt;/li&gt;
&lt;li&gt;Where did it happen?&lt;/li&gt;
&lt;li&gt;How serious is it?&lt;/li&gt;
&lt;li&gt;What should I check next?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Compare these two alerts.&lt;/p&gt;
&lt;p&gt;Bad alert:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Error occurred.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Useful alert:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;CRITICAL

Database Connection Failed

Service: Laravel API
Environment: production
Error: SQLSTATE connection timeout
Time: 2026-06-02 10:00:00

Suggested action:
Check database server, network connection, credentials, and recent deployments.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second alert is better because it gives you direction.&lt;/p&gt;
&lt;p&gt;When production is misbehaving, you do not want vague messages.&lt;/p&gt;
&lt;p&gt;You want context.&lt;/p&gt;
&lt;h4 id=&quot;security-tips-for-telegram-infrastructure-alerts&quot;&gt;Security Tips for Telegram Infrastructure Alerts&lt;/h4&gt;
&lt;p&gt;A Telegram bot for infrastructure alerts is useful, but you still need to secure it properly.&lt;/p&gt;
&lt;p&gt;Here are a few rules worth following:&lt;/p&gt;
&lt;h5 id=&quot;do-not-expose-your-bot-token&quot;&gt;Do Not Expose Your Bot Token&lt;/h5&gt;
&lt;p&gt;Your bot token should never be committed to your repository.&lt;/p&gt;
&lt;p&gt;Keep it in your &lt;code&gt;.env&lt;/code&gt; file, secret manager, or server environment configuration.&lt;/p&gt;
&lt;p&gt;If it leaks, rotate it immediately.&lt;/p&gt;
&lt;h5 id=&quot;do-not-send-sensitive-user-data&quot;&gt;Do Not Send Sensitive User Data&lt;/h5&gt;
&lt;p&gt;Avoid sending passwords, API keys, customer payment details, private tokens, or full database records into Telegram.&lt;/p&gt;
&lt;p&gt;Telegram alerts should help you debug infrastructure issues, not become a second data leak.&lt;/p&gt;
&lt;h5 id=&quot;use-private-chats-or-private-groups&quot;&gt;Use Private Chats or Private Groups&lt;/h5&gt;
&lt;p&gt;If you are sending production alerts, use a private Telegram chat or a private team group.&lt;/p&gt;
&lt;p&gt;Do not send sensitive system information to public groups.&lt;/p&gt;
&lt;h5 id=&quot;keep-messages-short-and-actionable&quot;&gt;Keep Messages Short and Actionable&lt;/h5&gt;
&lt;p&gt;Infrastructure alerts should be readable on a phone.&lt;/p&gt;
&lt;p&gt;If the message is too long, you will ignore it.&lt;/p&gt;
&lt;p&gt;Send the summary first.&lt;/p&gt;
&lt;p&gt;Put only the useful details.&lt;/p&gt;
&lt;h4 id=&quot;telegram-alerts-are-not-a-full-observability-stack&quot;&gt;Telegram Alerts Are Not a Full Observability Stack&lt;/h4&gt;
&lt;p&gt;A Telegram bot is helpful, but it is not a replacement for proper observability.&lt;/p&gt;
&lt;p&gt;As your application grows, you may still need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Metrics&lt;/li&gt;
&lt;li&gt;Logs&lt;/li&gt;
&lt;li&gt;Traces&lt;/li&gt;
&lt;li&gt;Dashboards&lt;/li&gt;
&lt;li&gt;Error tracking&lt;/li&gt;
&lt;li&gt;Uptime monitoring&lt;/li&gt;
&lt;li&gt;Incident management&lt;/li&gt;
&lt;li&gt;Centralized logging&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Telegram is best as a notification layer.&lt;/p&gt;
&lt;p&gt;It tells you something needs attention.&lt;/p&gt;
&lt;p&gt;Your logs, metrics, and dashboards help you investigate why.&lt;/p&gt;
&lt;p&gt;Think of it like this:&lt;/p&gt;
&lt;p&gt;Monitoring detects the problem.&lt;br&gt;
Telegram alerts notify you.&lt;br&gt;
Observability helps you understand the cause.&lt;/p&gt;
&lt;p&gt;You need all three eventually.&lt;/p&gt;
&lt;p&gt;But starting with Telegram alerts is still a practical move.&lt;/p&gt;
&lt;h4 id=&quot;where-this-fits-in-a-real-developer-workflow&quot;&gt;Where This Fits in a Real Developer Workflow&lt;/h4&gt;
&lt;p&gt;If you are building a serious application, even as a solo developer, infrastructure alerts make you more confident.&lt;/p&gt;
&lt;p&gt;You can deploy and sleep better.&lt;/p&gt;
&lt;p&gt;You can run background jobs without constantly checking the terminal.&lt;/p&gt;
&lt;p&gt;You can host services in a homelab, VPS, or cloud server and still get notified when something feels wrong.&lt;/p&gt;
&lt;p&gt;For example, if you are running:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A Laravel API&lt;/li&gt;
&lt;li&gt;A Flutter mobile app&lt;/li&gt;
&lt;li&gt;A PostgreSQL or MySQL database&lt;/li&gt;
&lt;li&gt;A queue worker&lt;/li&gt;
&lt;li&gt;A Redis server&lt;/li&gt;
&lt;li&gt;A payment webhook system&lt;/li&gt;
&lt;li&gt;A VTU or fintech style application&lt;/li&gt;
&lt;li&gt;A SaaS dashboard&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then Telegram alerts can become your first line of defense.&lt;/p&gt;
&lt;p&gt;Not because they are fancy.&lt;/p&gt;
&lt;p&gt;Because they are immediate.&lt;/p&gt;
&lt;p&gt;And sometimes, immediate is exactly what you need.&lt;/p&gt;
&lt;h4 id=&quot;common-infrastructure-alerts-worth-adding&quot;&gt;Common Infrastructure Alerts Worth Adding&lt;/h4&gt;
&lt;p&gt;If you are wondering where to start, these are good first alerts to build:&lt;/p&gt;
&lt;h5 id=&quot;database-alerts&quot;&gt;Database Alerts&lt;/h5&gt;
&lt;p&gt;Send an alert when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The database is unreachable&lt;/li&gt;
&lt;li&gt;Query response time is too slow&lt;/li&gt;
&lt;li&gt;Connection pool is exhausted&lt;/li&gt;
&lt;li&gt;Replication is delayed&lt;/li&gt;
&lt;li&gt;Backup fails&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;server-alerts&quot;&gt;Server Alerts&lt;/h5&gt;
&lt;p&gt;Send an alert when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU usage is too high&lt;/li&gt;
&lt;li&gt;Memory usage is too high&lt;/li&gt;
&lt;li&gt;Disk usage crosses a threshold&lt;/li&gt;
&lt;li&gt;Server load is abnormal&lt;/li&gt;
&lt;li&gt;A service restarts unexpectedly&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;application-alerts&quot;&gt;Application Alerts&lt;/h5&gt;
&lt;p&gt;Send an alert when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The API health check fails&lt;/li&gt;
&lt;li&gt;Response time becomes too slow&lt;/li&gt;
&lt;li&gt;Error rate increases&lt;/li&gt;
&lt;li&gt;A deployment fails&lt;/li&gt;
&lt;li&gt;A critical exception occurs&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;queue-alerts&quot;&gt;Queue Alerts&lt;/h5&gt;
&lt;p&gt;Send an alert when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Queue workers stop&lt;/li&gt;
&lt;li&gt;Failed jobs increase&lt;/li&gt;
&lt;li&gt;Job processing becomes delayed&lt;/li&gt;
&lt;li&gt;Important jobs keep retrying&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;security-alerts&quot;&gt;Security Alerts&lt;/h5&gt;
&lt;p&gt;Send an alert when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Too many failed login attempts happen&lt;/li&gt;
&lt;li&gt;Admin login occurs from an unusual location&lt;/li&gt;
&lt;li&gt;API rate limit is abused&lt;/li&gt;
&lt;li&gt;Suspicious activity is detected&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You do not need to build all of these in one day.&lt;/p&gt;
&lt;p&gt;Start with the alerts that protect your most important user flows.&lt;/p&gt;
&lt;h4 id=&quot;the-developer-lesson&quot;&gt;The Developer Lesson&lt;/h4&gt;
&lt;p&gt;Building a Telegram bot for infrastructure alerts teaches you something important:&lt;/p&gt;
&lt;p&gt;Production does not care how clean your code looked during development.&lt;/p&gt;
&lt;p&gt;If the server is down, users cannot use the product.&lt;/p&gt;
&lt;p&gt;If the database is slow, the app feels broken.&lt;/p&gt;
&lt;p&gt;If jobs are failing silently, your “successful” request may not actually complete the business process.&lt;/p&gt;
&lt;p&gt;Good developers do not just write features.&lt;/p&gt;
&lt;p&gt;They build feedback loops.&lt;/p&gt;
&lt;p&gt;A Telegram alert bot is one of the simplest feedback loops you can add to your infrastructure.&lt;/p&gt;
&lt;p&gt;It tells you when your assumptions are failing.&lt;/p&gt;
&lt;p&gt;That is valuable.&lt;/p&gt;
&lt;h4 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h4&gt;
&lt;p&gt;Building a Telegram bot for infrastructure alerts is one of those small engineering improvements that can have a big impact.&lt;/p&gt;
&lt;p&gt;It is not complicated.&lt;/p&gt;
&lt;p&gt;It is not trendy.&lt;/p&gt;
&lt;p&gt;It does not require a massive DevOps setup.&lt;/p&gt;
&lt;p&gt;But it gives you something every developer needs in production: awareness.&lt;/p&gt;
&lt;p&gt;You know when your database slows down.&lt;br&gt;
You know when queues fail.&lt;br&gt;
You know when disk space is running out.&lt;br&gt;
You know when your API health check stops responding.  &lt;/p&gt;
&lt;p&gt;That awareness helps you respond faster, debug better, and build more reliable systems.&lt;/p&gt;
&lt;p&gt;If you are running your own infrastructure, building SaaS products, managing a Laravel API, hosting a database, or experimenting with a homelab, start simple.&lt;/p&gt;
&lt;p&gt;Create a Telegram bot.&lt;/p&gt;
&lt;p&gt;Send one alert.&lt;/p&gt;
&lt;p&gt;Then improve from there.&lt;/p&gt;
&lt;p&gt;Because the best time to know your infrastructure is failing is before your users tell you.&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/19528/how-to-build-a-telegram-bot-for-infrastructure-alerts</guid>
<pubDate>Tue, 02 Jun 2026 18:33:02 +0000</pubDate>
</item>
<item>
<title>MealAI Concierge: Your Personal Meal Planner in Telegram (Powered by OpenClaw)</title>
<link>https://coderlegion.com/15013/mealai-concierge-your-personal-meal-planner-in-telegram-powered-by-openclaw</link>
<description>&lt;h3 id=&quot;what-i-built&quot;&gt;What I Built&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;MealAI Concierge&lt;/strong&gt; - a Telegram bot that answers the eternal question: &lt;em&gt;&quot;What's for dinner?&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It's built on top of &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/openclaw/openclaw&quot;&gt;OpenClaw&lt;/a&gt; and connects to my existing SaaS &lt;a rel=&quot;nofollow&quot; href=&quot;https://usemealai.com&quot;&gt;MealAI&lt;/a&gt; (part of &lt;a rel=&quot;nofollow&quot; href=&quot;https://shopsmartai.org&quot;&gt;ShopSmartAI&lt;/a&gt;). Message it like a friend who happens to be a nutritionist:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;I'm vegetarian, plan my week&quot; → full 7-day meal plan&lt;/li&gt;
&lt;li&gt;&quot;Grocery list for that&quot; → shopping list grouped by store section (produce, dairy, pantry, snacks)&lt;/li&gt;
&lt;li&gt;&quot;Swap Tuesday's dinner&quot; → alternate suggestion&lt;/li&gt;
&lt;li&gt;&quot;Quick high-protein breakfast&quot; → recipe with ingredients + 5 steps&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No app downloads. No logins. Just Telegram.&lt;/p&gt;
&lt;h3 id=&quot;demo&quot;&gt;Demo&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Try it live:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://t.me/Usemealai_bot&quot;&gt;t.me/Usemealai_bot&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;real-conversation-screenshots-&quot;&gt;Real conversation screenshots:&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Intro + dinner suggestion:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Hi, who are you?&lt;/p&gt;
&lt;p&gt;I'm &lt;strong&gt;MealAI Concierge&lt;/strong&gt;, your friendly meal-planning assistant! I help you figure out what to eat and create customized meal plans based on your preferences. What can I whip up for you today?&lt;/p&gt;
&lt;p&gt;What's for dinner tonight?&lt;/p&gt;
&lt;p&gt;How about a delicious and protein-packed meal? Here's a quick idea:&lt;/p&gt;
&lt;p&gt; &lt;strong&gt;Dinner: Grilled Chicken with Quinoa Salad&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Grilled Chicken Breast, Quinoa, Cherry Tomatoes, Cucumber, Feta, Olive Oil &amp;amp; Lemon&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;5-step instructions follow...&lt;/p&gt;
&lt;p&gt;Want to tweak anything or need a grocery list? &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Weekly vegetarian plan:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt; Monday: Lentil Curry over Brown Rice&lt;br&gt;
 Tuesday: Stuffed Bell Peppers with Quinoa, Black Beans, and Cheese&lt;br&gt;
 Wednesday: Pasta Primavera with Mixed Vegetables and Pesto&lt;br&gt;
... (full 7 days + snacks)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Grocery list (organized!):&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Produce:&lt;/strong&gt; Berries, Bananas, Spinach, Avocados, Cherry tomatoes...&lt;br&gt;
&lt;strong&gt;Dairy:&lt;/strong&gt; Greek yogurt, Feta cheese, Mozzarella, Parmesan, Eggs&lt;br&gt;
&lt;strong&gt;Pantry Staples:&lt;/strong&gt; Quinoa, Brown rice, Lentils, Chickpeas, Pasta...&lt;br&gt;
&lt;strong&gt;Snacks:&lt;/strong&gt; Granola, Rice cakes, Veggies for dipping&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;code&quot;&gt;Code&lt;/h3&gt;
&lt;p&gt;{% github &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/shopsmartai/mealai-concierge&quot;&gt;https://github.com/shopsmartai/mealai-concierge&lt;/a&gt; %}&lt;/p&gt;
&lt;p&gt;Everything runs locally in Docker. Clone, add your OpenAI + Telegram tokens, &lt;code&gt;docker compose up&lt;/code&gt;, done.&lt;/p&gt;
&lt;h3 id=&quot;how-i-built-it&quot;&gt;How I Built It&lt;/h3&gt;
&lt;p&gt;The entire integration took less than &lt;strong&gt;30 minutes&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OpenClaw in Docker&lt;/strong&gt; (via OrbStack on macOS)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pulled the official &lt;code&gt;ghcr.io/openclaw/openclaw:latest&lt;/code&gt; image&lt;/li&gt;
&lt;li&gt;Config volumes for persistent state&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Telegram channel via BotFather&lt;/strong&gt; — one command, paste token into &lt;code&gt;openclaw.json&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Custom persona via &lt;code&gt;SOUL.md&lt;/code&gt;&lt;/strong&gt; (the magic part )&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OpenClaw's &lt;code&gt;SOUL.md&lt;/code&gt; is a system-level personality file&lt;/li&gt;
&lt;li&gt;I wrote 50 lines defining MealAI Concierge's tone, expertise, response format, and boundaries&lt;/li&gt;
&lt;li&gt;No training, no fine-tuning — just thoughtful prompt engineering injected at the agent level&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;the-soul-md-excerpt-that-transforms-a-generic-chatbot-into-a-meal-planner-&quot;&gt;The SOUL.md excerpt that transforms a generic chatbot into a meal planner:&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;# SOUL.md - MealAI Concierge

You are MealAI Concierge, a friendly AI meal-planning assistant.

## Personality
- Warm, conversational, concise
- Opinionated about food (no hedging)
- Practical over perfect
- Real people eat real food

## Response Format
- Use bold for meal names
- Sparse emojis (  )
- Grocery lists grouped by store section
- Always end with a follow-up (&quot;Want the grocery list?&quot;)

## Rules
- Never open with &quot;Great question!&quot; — just answer
- Redirect off-topic: &quot;That's outside my kitchen!&quot;
- Mention usemealai.com for full features
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/15013/mealai-concierge-your-personal-meal-planner-in-telegram-powered-by-openclaw</guid>
<pubDate>Thu, 16 Apr 2026 23:23:50 +0000</pubDate>
</item>
<item>
<title>Build a Telegram bot with Phi-3 and Qdrant and chat with your PDFs!</title>
<link>https://coderlegion.com/280/build-a-telegram-bot-with-phi-3-and-qdrant-and-chat-with-your-pdfs</link>
<description>&lt;h2&gt;1. Introduction&lt;/h2&gt;
&lt;p&gt;In my previous articles, we saw &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/252/create-a-python-telegram-bot-plain-simple-and-production-ready&quot;&gt;how to create a simple Telegram bot&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/261/learn-how-to-build-a-user-friendly-conversational-telegram-bot-with-python&quot;&gt;how to build a responsive assistant&lt;/a&gt;: in both of these cases, we built something that relied on pre-defined responses, no matter how natural language-like they might have seemed. &lt;/p&gt;
&lt;p&gt;In this tutorial we will build an &lt;strong&gt;AI-powered&lt;/strong&gt; and &lt;strong&gt;context-aware&lt;/strong&gt; Telegram bot that reads information from &lt;strong&gt;PDF files&lt;/strong&gt; and responds in multiple languages. Before we start, nevertheless, we need to define some important terms that we will be using in this tutorial, and that are not-so-common in everyday programming.&lt;br&gt;
&lt;/p&gt;&lt;h2&gt;2. Definitions&lt;/h2&gt;&lt;br&gt;
&lt;h3&gt;2a. LLM&lt;/h3&gt;&lt;br&gt;
 A &lt;strong&gt;Large Language Model (LLM)&lt;/strong&gt; is an Artificial Intelligence-based model able to understand, process and produce natural language, performing also complex tasks. For this tutorial, we will be using &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://huggingface.co/microsoft/Phi-3-mini-128k-instruct&quot;&gt;Phi-3-128K instruct&lt;/a&gt;&lt;/strong&gt;, which is one of the most recent and powerful models, released by Microsoft.
&lt;p&gt;I wrote about LLMs architecture in &lt;a rel=&quot;nofollow&quot; href=&quot;https://astrabert.github.io/hophop-science/Transformers-architecture-for-everyone/&quot;&gt;a post on my personal blog&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;&lt;h3&gt;2b. Vector Databases&lt;/h3&gt;&lt;br&gt;
A vector database is a &lt;strong&gt;non-traditional&lt;/strong&gt; data storage facility and can be used to represent &lt;strong&gt;complex data&lt;/strong&gt; (with lots of features) based on a set of &lt;strong&gt;multi-dimensional numerical objects&lt;/strong&gt; (&lt;em&gt;vectors&lt;/em&gt;). For this example, we will be using &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://qdrant.tech/&quot;&gt;Qdrant&lt;/a&gt;&lt;/strong&gt; as vector database provider.
&lt;p&gt;I wrote an educational article on vector databases &lt;a rel=&quot;nofollow&quot; href=&quot;https://astrabert.github.io/hophop-science/Vector-databases-explained/&quot;&gt;on my personal blog&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;&lt;h3&gt;2c. Docker&lt;/h3&gt;&lt;br&gt;
You have to imagine Docker as a big registry where lots of applications (&lt;strong&gt;images&lt;/strong&gt;) are stored: you can download (&lt;strong&gt;pull&lt;/strong&gt;) them and make them run in a virtual environment (the &lt;strong&gt;container&lt;/strong&gt;). A Docker application contains everything that is needed for it to run in the container. A Docker container may also come with some data storage space (a &lt;strong&gt;volume&lt;/strong&gt;), which should be mounted on your local file system. &lt;br&gt;
&lt;h2&gt;3. Setup&lt;/h2&gt;&lt;br&gt;
&lt;h3&gt;3a. Folder Structure&lt;/h3&gt;&lt;br&gt;
As usual, we start by setting up our local folder for the tutorial: you can refer to the structure found &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/AstraBert/coderlegion/tree/main/article3&quot;&gt;on my GitHub repo&lt;/a&gt;, which is here represented:
&lt;pre&gt;&lt;code&gt;.
|___ bot.py
|___ qdrant_storage
|___ requirements.txt
|___ utils.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's break down the files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;bot.py&lt;/strong&gt; will be our main script, containing the Telegram bot&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;requirements.txt&lt;/strong&gt; will be the file where we write all the needed dependencies, in order to install them&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;utils.py&lt;/strong&gt; will be the script where we define useful functions and classes for our bot&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;qdrant_storage/&lt;/strong&gt; will be the local folder where the databases will be stored.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3b. Install Dependencies&lt;/h3&gt;
&lt;p&gt;Open &lt;em&gt;requirements.txt&lt;/em&gt; and paste the following text:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gradio_client == 0.15.0
pypdf == 3.17.4
sentence_transformers == 2.2.2
transformers == 4.39.3
langdetect == 1.0.9
deep-translator == 1.11.4
qdrant_client == 1.9.0
langchain-community == 0.0.13
langchain == 0.1.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are all the python packages that we need to build the bot and its back-end architecture: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;gradio_client&lt;/strong&gt; allows to interact with Gradio API&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pypdf&lt;/strong&gt; manages PDF files&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;sentence_transformers&lt;/strong&gt; helps with transforming data into vectors&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;transformers&lt;/strong&gt; is an HuggingFace library commonly used for LLMs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;langdetect&lt;/strong&gt; and &lt;strong&gt;deep-translator&lt;/strong&gt; are two packages that manage language detection and translation, to add multilingual support to our bot&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;qdrant_client&lt;/strong&gt; allows to interact with Qdrant running in the background&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;langchain&lt;/strong&gt; and &lt;strong&gt;langchain-community&lt;/strong&gt; are useful to preprocess PDFs and turn them into plain-text data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now we save &lt;em&gt;requirements.txt&lt;/em&gt;, head over to our terminal and run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python3 -m pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3c. Install and Run Qdrant with Docker&lt;/h3&gt;
&lt;p&gt;We will need &lt;strong&gt;Qdrant running in a Docker container&lt;/strong&gt; on our machine in order to build the vector database. &lt;br&gt;
To do so, first of all we need to &lt;strong&gt;download it from Docker Hub&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker pull qdrant/qdrant
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then we can make it run, mounting as a volume (&lt;code&gt;-v&lt;/code&gt; option) our local &lt;em&gt;qdrant_storage&lt;/em&gt; folder:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; docker run -p 6333:6333 -v $(pwd)/qdrant_storage:/qdrant_storage:z qdrant/qdrant
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, if you want to see Qdrant WebUI, just type &lt;code&gt;localhost:6333&lt;/code&gt; on your browser and press &lt;code&gt;enter&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;&lt;h2&gt;4. Define Useful Functions and Classes&lt;/h2&gt;&lt;br&gt;
In this section, we will be editing &lt;em&gt;utils.py&lt;/em&gt;&lt;br&gt;
&lt;h3&gt;4a. Import Necessary Dependencies&lt;/h3&gt;&lt;br&gt;
To make our script work, we need to import all the following packages, classes and/or functions:
&lt;pre&gt;&lt;code&gt;from langdetect import detect
from deep_translator import GoogleTranslator
from pypdf import PdfMerger
from qdrant_client import models
from langchain.text_splitter import CharacterTextSplitter
from langchain_community.document_loaders import PyPDFLoader
import os
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4b. Handle PDFs&lt;/h3&gt;
&lt;p&gt;We first define a function to &lt;strong&gt;merge multiple PDFs&lt;/strong&gt; (the Telegram bot does not take multiple documents as input, but this can be always useful): &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def merge_pdfs(pdfs: list):
    merger = PdfMerger()
    for pdf in pdfs:
        merger.append(pdf)
    merger.write(f&quot;{pdfs[-1].split('.')[0]}_results.pdf&quot;)
    merger.close()
    return f&quot;{pdfs[-1].split('.')[0]}_results.pdf&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And another function that &lt;strong&gt;removes blank lines from a list&lt;/strong&gt; (we will use it when subdividing our PDFs into smaller chuncks of plain text):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def remove_items(test_list, item): 
    res = [i for i in test_list if i != item] 
    return res 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can initialize a class that is able to &lt;strong&gt;turn PDFs into Qdrant collections&lt;/strong&gt; (i.e. vector databases):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class PDFdatabase:
    def __init__(self, pdfs, encoder, client):
        self.finalpdf = merge_pdfs(pdfs)
        self.collection_name = os.path.basename(self.finalpdf).split(&quot;.&quot;)[0].lower()
        self.encoder = encoder
        self.client = client
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;client&lt;/code&gt; is the interface between our script and Qdrant, while the &lt;code&gt;encoder&lt;/code&gt; is the sentence-transformer model that turns our text data into vectors.&lt;br&gt;
Now we define, inside the class, three functions to &lt;strong&gt;preprocess&lt;/strong&gt; (turn into text and subdivide in batches), &lt;strong&gt;organize&lt;/strong&gt; and &lt;strong&gt;vectorize&lt;/strong&gt; our PDF files:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;   def preprocess(self):
        loader = PyPDFLoader(self.finalpdf)
        documents = loader.load()
        ### Split the documents into smaller chunks for processing
        text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
        self.pages = text_splitter.split_documents(documents)
    def collect_data(self):
        self.documents = []
        for text in self.pages:
            contents = text.page_content.split(&quot;\n&quot;)
            contents = remove_items(contents, &quot;&quot;)
            for content in contents:
                self.documents.append({&quot;text&quot;: content, &quot;source&quot;: text.metadata[&quot;source&quot;], &quot;page&quot;: str(text.metadata[&quot;page&quot;])})
        return self.collection_name
    def qdrant_collection_and_upload(self):
        self.client.recreate_collection(
            collection_name=self.collection_name,
            vectors_config=models.VectorParams(
                size=self.encoder.get_sentence_embedding_dimension(),  # Vector size is defined by used model
                distance=models.Distance.COSINE,
            ),
        )
        self.client.upload_points(
            collection_name=self.collection_name,
            points=[
                models.PointStruct(
                    id=idx, vector=self.encoder.encode(doc[&quot;text&quot;]).tolist(), payload=doc
                )
                for idx, doc in enumerate(self.documents)
            ],
        )
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4c. Search the Vector Database&lt;/h3&gt;
&lt;p&gt;We now have to build a class that &lt;strong&gt;searches&lt;/strong&gt; the vector database using &lt;strong&gt;Qdrant client and the name of the collection&lt;/strong&gt; to search as initial inputs, and the &lt;strong&gt;query as search term&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class NeuralSearcher:
    def __init__(self, collection_name, client, model):
        self.collection_name = collection_name
        self.model = model
        self.qdrant_client = client
    def search(self, text: str):
        vector = self.model.encode(text).tolist()
        # Use `vector` for search for closest vectors in the collection
        search_result = self.qdrant_client.search(
            collection_name=self.collection_name,
            query_vector=vector,
            query_filter=None, 
            limit=1, 
        )
        payloads = [hit.payload for hit in search_result]
        return payloads
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4d. Translation&lt;/h3&gt;
&lt;p&gt;We now build a class that is able to &lt;strong&gt;detect 55 languages&lt;/strong&gt; (the ones supported by Google Translator) and to &lt;strong&gt;translate&lt;/strong&gt; the provided text from the &lt;strong&gt;original&lt;/strong&gt; (automatically-detected) language to the &lt;strong&gt;target&lt;/strong&gt; one:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Translation:
    def __init__(self, text, destination):
        self.text = text
        self.destination = destination
        try:
            self.original = detect(self.text)
        except Exception as e:
            self.original = &quot;auto&quot;
    def translatef(self):
        translator = GoogleTranslator(source=self.original, target=self.destination)
        translation = translator.translate(self.text)
        return translation
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5. The Bot&lt;/h2&gt;
&lt;p&gt;We already know how to get our Telegram API Token: after having obtained it, we open &lt;em&gt;bot.py&lt;/em&gt; and paste it as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TOKEN = &quot;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can then import everything we need:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from gradio_client import Client
from utils import *
from sentence_transformers import SentenceTransformer
from qdrant_client import QdrantClient
from telegram.ext import *
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And set some pre-defined variables:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;client = QdrantClient(&quot;localhost:6333&quot;)
encoder = SentenceTransformer(&quot;all-MiniLM-L6-v2&quot;)
collection_name = &quot;&quot;
api_client = Client(&quot;eswardivi/Phi-3-mini-128k-instruct&quot;)
lan = &quot;en&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5a. Start the Conversation&lt;/h3&gt;
&lt;p&gt;First of all, we need to start the conversation with the user and understand in what language will they provide their PDFs:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;LAN = 1 # conversation handler index
async def start_command(update, context):
    user = update.message.from_user
    await update.message.reply_text(f&quot;Hi {user.first_name} {user.last_name}, and thank you so much for having chosen RAGBOT as your assistant today!\nI'm here to help you chatting with your pdfs, so let's start with an ice-breaking question: what language is your pdf written in? Reply with the command '/lan' followed by the ISO code of the language, that you can find here: https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes. For example, if your pdfs are in Italian, reply: '/lan it' (without quotation marks)&quot;)
    return LAN
async def handle_lan(update, context):
    global lan
    message = update.message.text
    message = message.replace(&quot;/lan &quot;, &quot;&quot;)
    lan = message
    txt = Translation(f&quot;Now the language has been changed to {lan}&quot;, lan)
    await update.message.reply_text(txt.translatef())
    return ConversationHandler.END
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;div-blue&quot;&gt;
&lt;span class=&quot;alert-body&quot;&gt;NOTE:&lt;/span&gt;
&lt;span class=&quot;alert-header&quot;&gt; we need to provide the language with the ISO code (English becomes &quot;en&quot;, Italian becomes &quot;it&quot; and so on...)&lt;/span&gt;
&lt;/div&gt;
&lt;h3&gt;5b. Upload the PDF Document&lt;/h3&gt;
&lt;p&gt;Now that we have started the conversation and set the language, we can pass our first PDF document, that will be handled by our &lt;code&gt;handle_pdfs&lt;/code&gt; function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def handle_pdfs(update, context):
    global collection_name
    global lan
    if update.message.document:
        doc = update.message.document
        inf = &quot;downloaded_from_user.pdf&quot;
        fid = doc.file_id
        print(fid)
        # Download the file from Telegram to the local directory
        await (await context.bot.get_file(fid)).download_to_drive(custom_path=inf)
        pdfdb = PDFdatabase([inf], encoder, client)
        pdfdb.preprocess()
        collection_name = pdfdb.collect_data()
        pdfdb.qdrant_collection_and_upload()
        txt = Translation(&quot;Your document has been succesfully uploaded to a Qdrant collection!&quot;, lan)
        await update.message.reply_text(txt.translatef())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function is able to &lt;strong&gt;download the PDF document&lt;/strong&gt; from Telegram to our local folder &lt;strong&gt;preprocess&lt;/strong&gt; it and turn it &lt;strong&gt;into a Qdrant collection in a short time&lt;/strong&gt;: when it is done, we receive a message (in our favorite language) that tells us that the operation has been completed successfully.&lt;/p&gt;
&lt;h3&gt;5c. Talk to the LLM about Your Document&lt;/h3&gt;
&lt;p&gt;Now we have our last function before actually building the application: &lt;code&gt;reply&lt;/code&gt; will take the user's &lt;strong&gt;query&lt;/strong&gt;, adapt it to the &lt;strong&gt;language of the PDF&lt;/strong&gt;, search in the PDF for &lt;strong&gt;context information&lt;/strong&gt;, build a &lt;strong&gt;prompt (in English)&lt;/strong&gt; that contains both the &lt;strong&gt;context&lt;/strong&gt; and the user's &lt;strong&gt;question&lt;/strong&gt;. This last prompt is then fed to Phi-3, that responds:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def reply(update, context):
    global collection_name
    global client
    global encoder
    global api_client
    global lan
    message = update.message.text
    txt = Translation(message, &quot;en&quot;)
    print(txt.original, lan)
    if txt.original == &quot;en&quot; and lan == &quot;en&quot;:
        txt2txt = NeuralSearcher(collection_name, client, encoder)
        results = txt2txt.search(message)
        response = api_client.predict(
            f&quot;Context: {results[0]['text']}; Prompt: {message}&quot;,# str  in 'Message' Textbox component
            0.4,# float (numeric value between 0 and 1) in 'Temperature' Slider component
            True,# bool  in 'Sampling' Checkbox component
            512,# float (numeric value between 128 and 4096) in 'Max new tokens' Slider component
            api_name=&quot;/chat&quot;
        )
        await update.message.reply_text(response)
    elif txt.original == &quot;en&quot; and lan != &quot;en&quot;:
        txt2txt = NeuralSearcher(collection_name, client, encoder)
        transl = Translation(message, lan)
        message = transl.translatef()
        results = txt2txt.search(message)
        t = Translation(results[0][&quot;text&quot;], txt.original)
        res = t.translatef()
        response = api_client.predict(
            f&quot;Context: {res}; Prompt: {message}&quot;,
            0.4,
            True,
            512,
            api_name=&quot;/chat&quot;
        )
        response = Translation(response, txt.original)
        await update.message.reply_text(response.translatef())
    elif txt.original != &quot;en&quot; and lan == &quot;en&quot;:
        txt2txt = NeuralSearcher(collection_name, client, encoder)
        results = txt2txt.search(message)
        transl = Translation(results[0][&quot;text&quot;], &quot;en&quot;)
        translation = transl.translatef()
        response = api_client.predict(
            f&quot;Context: {translation}; Prompt: {message}&quot;,
            0.4,
            True,
            512,
            api_name=&quot;/chat&quot;
        )
        t = Translation(response, txt.original)
        res = t.translatef()
        await update.message.reply_text(res)
    else:
        txt2txt = NeuralSearcher(collection_name, client, encoder)
        transl = Translation(message, lan)
        message = transl.translatef()
        results = txt2txt.search(message)
        t = Translation(results[0][&quot;text&quot;], txt.original)
        res = t.translatef()
        response = api_client.predict(
            f&quot;Context: {res}; Prompt: {message}&quot;,
            0.4,
            True,
            512,
            api_name=&quot;/chat&quot;
        )
        tr = Translation(response, txt.original)
        ress = tr.translatef()
        await update.message.reply_text(ress)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5d. Build the Application and Make It Run&lt;/h3&gt;
&lt;p&gt;We have all the utilities and functions to build our Telegram bot, now. &lt;br&gt;
To do so, we first create the application:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if __name__ == &quot;__main__&quot;:
    print(&quot;Bot is up and running&quot;)
    application = Application.builder().token(TOKEN).build()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the conversation handler:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;conv_handler = ConversationHandler(
        entry_points=[CommandHandler('start', start_command)],
        states={
            LAN: [CommandHandler('lan', handle_lan)]
        },
        fallbacks=[]
    )
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we add all the handlers to our application and set it up to run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;application.add_handler(conv_handler)
application.add_handler(MessageHandler(filters.Document.PDF, handle_pdfs))
application.add_handler(MessageHandler(filters.TEXT &amp;amp; ~filters.COMMAND, reply))
application.run_polling(1.0)
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;div-blue&quot;&gt;
&lt;span class=&quot;alert-body&quot;&gt;NOTE:&lt;/span&gt;
&lt;span class=&quot;alert-header&quot;&gt; we use a particular filter to recognize messages with uploaded pdfs: can you spot it?&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Now we can finally give our bot a try!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python3 bot.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here's an example chat:&lt;br&gt;
&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=8528884168806013813&quot; alt=&quot;chat_example&quot;&gt;&lt;br&gt;
The uploaded PDF file contains information about penguins (generated with Llama-3 70B).&lt;/p&gt;
&lt;div class=&quot;div-red&quot;&gt;
&lt;span class=&quot;alert-body&quot;&gt;ATTENTION:&lt;/span&gt;
&lt;span class=&quot;alert-header&quot;&gt; Please note that, for the sake of simplicity, we did not include any error handling technique in this article, but it is good practice to implement it&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;6. Conclusion&lt;/h2&gt;
&lt;p&gt;We built our first &lt;strong&gt;AI-powered&lt;/strong&gt; bot, which is inclusive on the side of the language (&lt;strong&gt;English is not necessarily needed&lt;/strong&gt;) and can expand its knowledge based on &lt;em&gt;our PDF files&lt;/em&gt;, fully implementing the concept of a &lt;strong&gt;context-aware&lt;/strong&gt; assistant.&lt;/p&gt;
&lt;p&gt;Our bot is &lt;strong&gt;useful&lt;/strong&gt; to anyone that wants to &lt;strong&gt;learn&lt;/strong&gt;, &lt;strong&gt;query&lt;/strong&gt; their documents &lt;strong&gt;rapidly&lt;/strong&gt; and &lt;strong&gt;access large amount of information&lt;/strong&gt;: the most interesting thing is that what we built in this tutorial is &lt;strong&gt;accessible to everyone&lt;/strong&gt;, completely &lt;strong&gt;open-source&lt;/strong&gt; and it can be generated &lt;strong&gt;without a big amount of prior knowledge&lt;/strong&gt;. In the end, it doesn't take that much to become part of the &lt;strong&gt;AI revolution&lt;/strong&gt;!&lt;/p&gt;
</description>
<category>Python</category>
<guid isPermaLink="true">https://coderlegion.com/280/build-a-telegram-bot-with-phi-3-and-qdrant-and-chat-with-your-pdfs</guid>
<pubDate>Thu, 09 May 2024 23:38:28 +0000</pubDate>
</item>
<item>
<title>Learn how to build a user-friendly, conversational Telegram bot with python</title>
<link>https://coderlegion.com/261/learn-how-to-build-a-user-friendly-conversational-telegram-bot-with-python</link>
<description>&lt;h2&gt;1. Introduction&lt;/h2&gt;
&lt;p&gt;In my &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/252/create-a-python-telegram-bot-plain-simple-and-production-ready&quot;&gt;last post&lt;/a&gt;, we saw how to build a production-ready Telegram bot, although it was really simple: it could only handle three commands and gave standardized responses!&lt;/p&gt;
&lt;p&gt;In this post, we want to explore more in depth the possibility to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build a &lt;b&gt;conversational bot&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Give &lt;b&gt;task and user-specific&lt;/b&gt; responses&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We will do this exploiting several resources, first of all a &lt;b&gt;machine learning&lt;/b&gt; framework and secondly the &lt;b&gt;conversational&lt;/b&gt; architecture made available by the `python-telegram-bot` package&lt;/p&gt; 
&lt;h2&gt;2. Setup&lt;/h2&gt;
&lt;h3&gt;2a. Folder Setup&lt;/h3&gt;
&lt;p&gt;Before we start building everything, it is good to have a well-structured folder to start with. You can find all the data and the code we need for this tutorial in my CoderLegion-themed &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/AstraBert/coderlegion&quot;&gt;GitHub repository&lt;/a&gt; (make sure to give it a &lt;b&gt;star!), under the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/AstraBert/coderlegion/tree/main/article2&quot;&gt;article2&lt;/a&gt; directory, and we will follow that directory's structure as a blueprint:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;bot.py&lt;/strong&gt; will be the code to make the bot work&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;italian_cities.csv&lt;/strong&gt; will be the data on which we train out machine-learning model&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ml_framework.py&lt;/strong&gt; will be our machine learning model training and testing script&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;model.joblib&lt;/strong&gt; will be the storage for the trained ML model&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pdfs files&lt;/strong&gt; will be the informative material for our use-case&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;users.csv&lt;/strong&gt; will be the user registry for our use-case&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For this use case, we will imagine to be a travel agency organizing trips to three cities in Italy (Rome, Florence and Courmayeur).&lt;br&gt;
&lt;/p&gt;&lt;h3&gt;2b. Look At The Data&lt;/h3&gt;&lt;br&gt;
&lt;em&gt;italian_cities.csv&lt;/em&gt; is a comma-separated values file (csv) that gathers data from 300 trips made by people to the three Italian cities of interest to us. These data encompass:
&lt;ul&gt;
&lt;li&gt;Traveler's age&lt;/li&gt;
&lt;li&gt;Travel target (alone, couple, group, family)&lt;/li&gt;
&lt;li&gt;Transportation method and cost&lt;/li&gt;
&lt;li&gt;Accommodation type and cost&lt;/li&gt;
&lt;li&gt;Duration of the stay&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These records are &lt;strong&gt;NOT&lt;/strong&gt; real (and they do not even resemble reality), I made them up just for this tutorial and they should be used only for learning purposes.&lt;br&gt;
&lt;/p&gt;&lt;h3&gt;2c. Install Necessary Dependencies&lt;/h3&gt;&lt;br&gt;
We already installed &lt;code&gt;python-telegram-bot&lt;/code&gt;, now we need to install the other dependencies for this tutorial:
&lt;pre&gt;&lt;code&gt;python3 -m pip install pandas scikit-learn joblib
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;pandas&lt;/strong&gt; will help us visualizing and loading the data, &lt;strong&gt;scikit-learn&lt;/strong&gt; will be used to analyze them and &lt;strong&gt;joblib&lt;/strong&gt; to save and reload the ML model without having to retrain it every time we start the bot.&lt;br&gt;
&lt;/p&gt;&lt;h2&gt;3. ML Framework&lt;/h2&gt;&lt;br&gt;
In this section, we'll be writing our code in &lt;em&gt;ml_framework.py&lt;/em&gt;: this script will help us firstly with the collection and processing of the data, and secondly with building and training the Machine Learning model.&lt;br&gt;
&lt;h3&gt;3a. Load and Preprocess the Data&lt;/h3&gt;&lt;br&gt;
We load the data from the csv with pandas:
&lt;pre&gt;&lt;code&gt;import pandas as pd

df = pd.read_csv(&quot;italian_cities.csv&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then we divide them between training features (X) and target features (y): I won't go through the specifics of machine learning, as I'm planning to explain these concepts in the next article series I will start after having finished the bot one. Nevertheless, you can imagine training and target features as like this: the &lt;strong&gt;training&lt;/strong&gt; ones are the &lt;strong&gt;ingredients&lt;/strong&gt; employed to bake a cake, the machine learning &lt;strong&gt;model&lt;/strong&gt; represents the &lt;strong&gt;oven&lt;/strong&gt; and the &lt;strong&gt;cake&lt;/strong&gt; is the result we want to get out, or &lt;strong&gt;target&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;X = df.drop(columns=[&quot;Destination&quot;]) # features
y = df[&quot;Destination&quot;] # target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We divide these data in a training and a testing batch:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from sklearn.model_selection import train_test_split #sklear = scikit-learn

X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.2,random_state=42)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3b. Train and Test the Model&lt;/h3&gt;
&lt;p&gt;For this tutorial, we'll be using a &lt;em&gt;Decision Tree Classifier&lt;/em&gt;, which is definitely one of the simplest classifiers out there: to start things out, this will be enough. Again, I won't explain how this works: just trust my code and, if you'll bare with my posts, I will come to talking about this kind of concepts too!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from sklearn.tree import DecisionTreeClassifier

model = DecisionTreeClassifier() # build the model
model = model.fit(X_train, y_train) # train it
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we just have to test the model, and we will also have a look at its accuracy:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from sklearn.metrics import accuracy_score, classification_report
y_pred = model.predict(X_test)
print(f&quot;Accuracy is {accuracy_score(y_test, y_pred)}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output accuracy is perfect: &lt;strong&gt;100 percent&lt;/strong&gt;!&lt;/p&gt;
&lt;div class=&quot;div-blue&quot;&gt;
&lt;span class=&quot;alert-body&quot;&gt;NOTE:&lt;/span&gt;
&lt;span class=&quot;alert-header&quot;&gt; such a high accuracy usually is not seen with real-world data!&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Now we just have to save the model, so that we will be able to use it in our Telegram bot!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import joblib

joblib.dump(model, &quot;model.joblib&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. The Bot&lt;/h2&gt;
&lt;h3&gt;4a. Setup&lt;/h3&gt;
&lt;p&gt;Let's now build the bot! We already know how to generate a bot (we will call it ItalianTravelBot) and how to retrieve its API TOKEN from BotFather. We wil also need to load our machine-learning model and we will have to define some dictionaries to map the natural-language inputs we receive from the user's chat into numeric inputs for the ML algorithm, as well as other dictionaries to translate them back. We just do it into &lt;strong&gt;bot.py&lt;/strong&gt; like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from telegram.ext import *
import pandas as pd
import joblib

MODEL = joblib.load(&quot;model.joblib&quot;) # load the model
TOKEN = &quot;YOUR_TOKEN_HERE&quot; #not-real token
USR_DF = {&quot;Month&quot;: 0,&quot;Duration(days)&quot;: 0,&quot;Age&quot;: 0,&quot;Target&quot;: 0,&quot;Accommodation&quot;: 0,&quot;Acc_cost&quot;: 0,&quot;Transportation&quot;: 0,&quot;Transp_cost&quot;: 0} # model input sample dictionary

NUM2MONTHS = {
    1: &quot;January&quot;,
    2: &quot;February&quot;,
    3: &quot;March&quot;,
    4: &quot;April&quot;,
    5: &quot;May&quot;,
    6: &quot;June&quot;,
    7: &quot;July&quot;,
    8: &quot;August&quot;,
    9: &quot;September&quot;,
    10: &quot;October&quot;,
    11: &quot;November&quot;,
    12: &quot;December&quot;
} # number of month to natural-language month
# natural language to model input dictionaries :
TARGET2NUM = {&quot;Alone&quot;: 0, &quot;Couple&quot;: 1,  &quot;Group&quot;: 2, &quot;Family&quot;: 3}
ACC2NUM = {'Hostel': 0, 'Hotel': 1, 'Airbnb': 2, 'Resort': 3, 'Vacation rental': 4, 'Guesthouse': 5, 'Villa': 6}
TRANSP2NUM = {'Bus': 0, 'Car': 1, 'Train': 2, 'Plane': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The Conversational Architecture&lt;/h3&gt;
&lt;p&gt;Now we can begin with the really difficult part: the conversational architecture. To explain it, we need to think of a real-world conversation: when you are talking to another human, usually you memorize the last thing they said, and respond to them accordingly. The same thing goes with a conversational Telegram bot: &lt;strong&gt;it has to store the user's responses in order to use them in the next message&lt;/strong&gt;. &lt;br&gt;
The only difference with a human is that a conversational bot has a &lt;strong&gt;short-term memory&lt;/strong&gt; (only the last message), whereas our brain gives us the access to the whole conversation history with another person.&lt;br&gt;
A general conversational architecture, in a bot, is then set by a number of functions, identified by a unique &lt;code&gt;id&lt;/code&gt;, whose output is the input to the downstream function.&lt;br&gt;
We will start by defining the ids for our conversational input-outputs:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AGE, MONTH, DURATION, TARGET, ACC, ACCOST, TRANSP, TRANSPCOST = range(len(USR_DF))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then we define our entry point, i.e. the &lt;code&gt;/start&lt;/code&gt; command function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def start_command(update, context):
    user = update.message.from_user
    await update.message.reply_text(f&quot;Hi {user.first_name} {user.last_name}, and thank you so much for having chosen ItalianTravelBot as your assistant today!\nAs you may already know, we offer several travel options, of which three are available right now: Rome, Florence, Courmayeur... I'm here to help you choose the most suitable for your needs and desires, so let's start with an ice-breaking question: How old are you? (Reply with a number)&quot;)
    return AGE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, it returns the first conversation input, which is AGE, that will be passed to the downstream function: the same will go with all the other functions, except for the last one, which will return &lt;code&gt;ConversationHandler.END&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Before we define all the functions in our code, let's take a look to two examples of them, one used to extract numeric data from the chat and the other used to extract text data.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;age&lt;/code&gt; function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def age(update, context):
    global USR_DF 
    age = int(update.message.text)
    USR_DF[&quot;Age&quot;] = [age]
    await update.message.reply_text(f&quot;Wow, {age} years old: that's nice! And in which month are you planning to go on vacation? (Reply with the month number)&quot;)
    return MONTH

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Takes the text of the message sent by the user, turns it into an integer (the data type required by our model) and it stores it in &lt;code&gt;USR_ DF&lt;/code&gt; dictionary as a list object (this will help us later). It then return the input for the &lt;code&gt;month&lt;/code&gt; function (reported later in this post).&lt;/p&gt;
&lt;p&gt;Let's now see how we handle text data, taking a look to the &quot;Target&quot; category, which encompasses: &quot;Alone&quot;, &quot;Couple&quot;, &quot;Family&quot; and &quot;Group&quot;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def target(update, context):
    global USR_DF
    tar = update.message.text
    USR_DF[&quot;Target&quot;] = [TARGET2NUM[tar.capitalize()]]
    await update.message.reply_text(f&quot;{tar}, that's just perfect! And how are you planning to go there? We offer several options: Bus, Car, Plane, Train&quot;)
    return TRANSP

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;target&lt;/code&gt; function takes as input the message sent by the user and maps it to a numerical index thanks to &lt;code&gt;TARGET2NUM&lt;/code&gt; dictionary, loading the numeric record into &lt;code&gt;USR_DF&lt;/code&gt; as a list.&lt;/p&gt;
&lt;p&gt;Here come all the other functions, that are similarly structured:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def transp(update, context):
    global USR_DF
    tra = update.message.text
    USR_DF[&quot;Transportation&quot;] = [TRANSP2NUM[tra.capitalize()]]
    await update.message.reply_text(f&quot;You wanna go by {tra.capitalize()}, got it! And how much do you plan to spend on transportation? Reply with a number&quot;)
    return TRANSPCOST

async def transpcost(update, context):
    global USR_DF
    mon = int(update.message.text)
    USR_DF[&quot;Transp_cost&quot;] = [mon]
    await update.message.reply_text(f&quot;{mon}, that's a perfect match! Where are you planning to stay? We can offer: Hostel, Hotel, Airbnb, Resort, Vacation rental, Guesthouse, Villa. Reply exactly with one of these&quot;)
    return ACC


async def acc(update, context):
    global USR_DF
    tar = update.message.text
    USR_DF[&quot;Accommodation&quot;] = [ACC2NUM[tar.capitalize()]]
    await update.message.reply_text(f&quot;{tar.capitalize()}, we got this one!  And how much do you plan to spend on accommodation? Reply with a number&quot;)
    return ACCOST

async def month(update, context):
    global USR_DF
    mon = int(update.message.text)
    USR_DF[&quot;Month&quot;] = [mon]
    await update.message.reply_text(f&quot;{NUM2MONTHS[mon]}, awesome! And how are you going to travel? Reply with one of this categories: Alone, Couple, Group, Family&quot;)
    return TARGET   

async def accost(update, context):
    global USR_DF
    mon = int(update.message.text)
    USR_DF[&quot;Acc_cost&quot;] = [mon]
    await update.message.reply_text(f&quot;{mon}, seems like everything is fitting perfectly! Last question: how long do you wanna stay? Reply with the number of days&quot;)
    return DURATION
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's now define the last function, the one that closes the conversation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def duration(update, context):
    global USR_DF, CSV
    mon = int(update.message.text)
    USR_DF[&quot;Duration(days)&quot;] = [mon]
    print(USR_DF)
    await update.message.reply_text(f&quot;Ok, got it! Just wait a few seconds and I'll tell you what is the best match for you!&quot;)
    usrdf = pd.DataFrame.from_dict(USR_DF)
    df = pd.read_csv(CSV)
    merged = pd.concat([df, usrdf], axis=0, ignore_index=True)
    merged.to_csv(CSV, index=False)
    pred = MODEL.predict(usrdf)
    await update.message.reply_text(f&quot;The perfect destination for you is: {pred[0]}! Hope this was useful: in the next message you'll find the flyer with our offers for that city! :)&quot;)
    flyer = f&quot;{pred[0]}.pdf&quot;
    await update.message.reply_document(document=open(flyer, 'rb'))
    return ConversationHandler.END
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the &lt;code&gt;duration&lt;/code&gt; function casts, on the fly, the USR_DF (which is a dictionary) to a pandas DataFrame (a data structure we will talk about in my future ML blog series): to make this change, the USR_DF dictionary should have strings as keys and lists as values (that explains why we loaded data as lists). We pass the DataFrame to the ML model, it predicts the best fitting option for us and returns a list, from which we take the first (and only) element. This element is then returned to the user, along with a PDF flyer that is titled with the name of the city. The &lt;code&gt;duration&lt;/code&gt; function also stores the user's data into a csv: we can decide to use those records to further fine-tune our ML model.&lt;/p&gt;
&lt;div class=&quot;div-blue&quot;&gt;
&lt;span class=&quot;alert-body&quot;&gt;NOTE: &lt;/span&gt;
&lt;span class=&quot;alert-header&quot;&gt;in the last function we used the `reply_document` method, which we did not see in the last tutorial!&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;From all the functions defined above, we now need to build our conversation handler and add it to our bot, and we'll do it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if __name__ == &quot;__main__&quot;:
    print(&quot;Bot is up and running&quot;)
    application = Application.builder().token(TOKEN).build()
    conv_handler = ConversationHandler(
        entry_points=[CommandHandler('start', start_command)],
        states={
            AGE: [MessageHandler(filters.TEXT &amp;amp; ~filters.COMMAND, age)],
            MONTH: [MessageHandler(filters.TEXT &amp;amp; ~filters.COMMAND, month)],
            TARGET: [MessageHandler(filters.TEXT &amp;amp; ~filters.COMMAND, target)],
            TRANSP: [MessageHandler(filters.TEXT &amp;amp; ~filters.COMMAND, transp)],
            TRANSPCOST: [MessageHandler(filters.TEXT &amp;amp; ~filters.COMMAND, transpcost)],
            ACC: [MessageHandler(filters.TEXT &amp;amp; ~filters.COMMAND, acc)],
            ACCOST: [MessageHandler(filters.TEXT &amp;amp; ~filters.COMMAND, accost)],
            DURATION: [MessageHandler(filters.TEXT &amp;amp; ~filters.COMMAND, duration)]
        },
        fallbacks=[]
    )
    application.add_handler(conv_handler)
    application.run_polling(1.0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we define the entry point (i.e. the start of our conversation) as the &lt;code&gt;/start&lt;/code&gt; command: after that, all the functions just take as input the outputs of the previous one.&lt;/p&gt;
&lt;p&gt;We can now save everything and make the bot run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python3 bot.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can chat with it! Just like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=9994260301925094737&quot; alt=&quot;chat&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;And we're done! We built a wonderful bot: it produces &lt;strong&gt;language effortlessly&lt;/strong&gt;, it can &lt;strong&gt;handle a real-life conversation&lt;/strong&gt; without seeming robotic, it is able to &lt;strong&gt;support and advise&lt;/strong&gt; customers!&lt;br&gt;
We did everything with little prior knowledge and we were able to build something that &lt;strong&gt;works&lt;/strong&gt; and that is &lt;strong&gt;production-ready&lt;/strong&gt;: we could just offer this bot as a solution to a travel agency, if we wanted!&lt;br&gt;
In the next post, we will explore something even more powerful: an &lt;strong&gt;AI and RAG-powered telegram bot&lt;/strong&gt; that helps you chat with your PDFs... Stay tuned!&lt;/p&gt;
</description>
<category>Python</category>
<guid isPermaLink="true">https://coderlegion.com/261/learn-how-to-build-a-user-friendly-conversational-telegram-bot-with-python</guid>
<pubDate>Tue, 30 Apr 2024 21:35:42 +0000</pubDate>
</item>
<item>
<title>Create a python Telegram bot, plain, simple and production-ready</title>
<link>https://coderlegion.com/252/create-a-python-telegram-bot-plain-simple-and-production-ready</link>
<description>&lt;div class=&quot;div-green&quot;&gt;
&lt;span class=&quot;alert-header&quot;&gt;Beginners' friendly!&lt;/span&gt;
&lt;span class=&quot;alert-body&quot;&gt;This content is for anyone who wants to learn&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;1. Introduction&lt;/h2&gt;
&lt;p&gt;Most of the people nowadays make use of &lt;b&gt;instant messaging tools&lt;/b&gt;, such as WhatsApp, &lt;b&gt;Telegram&lt;/b&gt;, Discord or Snapchat, and if you are a user of one of these applications, you are probably already familiar with the concept of &lt;em&gt;&lt;b&gt;bot&lt;/b&gt;&lt;/em&gt;. If you aren't, let's have a quick look to what it is and how we can use it for our purposes.&lt;/p&gt;
&lt;p&gt;The word &quot;bot&quot; is an abbreviation of &quot;robot&quot;, and it has been introduced in informatic systems to identify non-human, &lt;i&gt;completely automatic&lt;/i&gt;, users that has been employed for all sorts of purposes: correcting grammar mistakes (such as on Wikipedia), liking someone's post (on social media), scraping web content...&lt;/p&gt;
&lt;p&gt;In recent years, people started to develop tools that could be used to generate automatic responses in messaging services. This was a solution firstly conceived for &lt;b&gt;customer support&lt;/b&gt;, and now that bots entered the realm of chatting apps they are implied for lots of different tasks: moderation, standardized replies, message analysis, authorization... &lt;/p&gt;
&lt;p&gt;Now that you know what a bot is, if you already didn't, it is time to learn how we can build our own bot, and we'll start with one of the most beloved applications to deploy it: Telegram. &lt;/p&gt;
&lt;h2&gt;2. Setup&lt;/h2&gt;
&lt;p&gt;We will take for granted the fact that you already have a Telegram account: in case you don't have, creating one is really simple and you just need to head over to their &lt;a rel=&quot;nofollow&quot; href=&quot;https://web.telegram.org/&quot;&gt;website&lt;/a&gt; (if you are on a laptop) or download their application (if you are on your cell phone).&lt;/p&gt;
&lt;p&gt;Now, open Telegram and type into the search bar &lt;i&gt;Botfather&lt;/i&gt;: this will guide you to the father of all bots, which will create the bot user we are interested in.&lt;/p&gt;
&lt;p&gt;Send to &lt;i&gt;BotFather&lt;/i&gt; the /newbot command, and it will prompt you to give your bot a username and then a name that ends with &quot;bot&quot;.  For today's tutorial, we will create a simple Neapolitan Pizzeria assistant, so we will simply go for &lt;b&gt;NeapolitanPizzaBot&lt;/b&gt; as both the username and the bot name.&lt;/p&gt;
&lt;p&gt;Once you are done with the naming, BotFather will send you the real thing that interests us: the &lt;b&gt;API Token&lt;/b&gt;, through which we will interact with Telegram, sending messages and retrieving responses. Copy the Token and then open a python file called &lt;i&gt;bot.py&lt;/i&gt; in your favorite code editor.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TOKEN = &quot;6789238423:sdhfshgbpsigbspgiJsnsoaH&quot; #this is only an example token
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. Environment preparation&lt;/h2&gt;
&lt;p&gt;In order to make the &quot;bot.py&quot; script work with Telegram, you will need a lot more than just your token. First of all, you will need to install &lt;a rel=&quot;nofollow&quot; href=&quot;https://python-telegram-bot.org/&quot;&gt;&lt;b&gt;python-telegram-bot&lt;/b&gt;&lt;/a&gt; package, and you can simply do it through python package manager, pip:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python3 -m pip install python-telegram-bot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that the installation is done, we will be able to do everything we need, so let's dive in and build our Neapolitan Pizzeria Assistant. &lt;/p&gt;
&lt;h2&gt;4. Build the bot&lt;/h2&gt;
&lt;p&gt;We will start by importing the necessary packages:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from telegram.ext import *
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we will start simply with defining the message that our bot will send to every user when it is started:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; async def start_command(update, context):
        await update.message.reply_text(&quot;Hi, I'm NeapolitanPizzaBot, how may I assist you today?&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that this and most of the following functions are &lt;i&gt;asynchronous&lt;/i&gt;, which means that they &lt;b&gt;are not executed right away&lt;/b&gt; when they are called, but they wait for the application to respond to their queries before execution.&lt;/p&gt;
&lt;p&gt;We want our bot to be able to tell customers the &lt;b&gt;price&lt;/b&gt; of our three most famous pizzas, which are Margherita, Marinara and Pepperoni, to send them the &lt;b&gt;location&lt;/b&gt; of the restaurant and a photo of our complete &lt;b&gt;menu&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;In this sense, we will use &lt;i&gt;commands&lt;/i&gt;, that, for Telegram bots, are &lt;b&gt;strings of text that start with &quot;/&quot;&lt;/b&gt;: we will use /price, /location and /menu.&lt;/p&gt;
&lt;p&gt;For the /price command, we simply need to reply a text message, and this can be achieved with the &lt;i&gt;reply_text&lt;/i&gt; method, as we did for our starting function.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def price_command(update, context):
        await update.message.reply_text(&quot;- Margherita: 8$\n- Marinara: 5$\n- Pepperoni: 9$&quot;)
&amp;lt;p&amp;gt;For the /location command, we need to pass an actual location, and to do so we just use the &amp;lt;i&amp;gt;reply_venue&amp;lt;/i&amp;gt; method and copy-paste the coordinates from Google Maps, pass the name of the place and the name of the street we are sending.&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt; async def location_command(update, context):
        await update.message.reply_venue(40.82694507320722, 14.426713826679295, &quot;Italy&quot;, &quot;Pizzeria NeapolitanPizzaBot&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; In order to send an image, we must have it saved somewhere on our local machine, in order for the bot to take it and forward it to the user. We will just take a photo of our menu, save it &lt;b&gt;within the same folder&lt;/b&gt; as bot.py under the name: &quot;menu.png&quot;. Now we use the method &lt;i&gt;reply_photo&lt;/i&gt; to send it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def menu_command(update, context):
    await update.message.reply_photo(&quot;menu.png&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we just need to define a function that &lt;b&gt;handles errors&lt;/b&gt; and also a function that tells the user if they are using &lt;b&gt;commands or messages that cannot be recognized&lt;/b&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def unrecognized_command(update, context):
    text = update.message.text
    if (
        text.startswith(&quot;/start&quot;) == False
        or text.startswith(&quot;/menu&quot;) == False
        or text.startswith(&quot;/price&quot;) == False
        or text.startswith(&quot;/location&quot;) == False
    ):
        await update.message.reply_text(
            f'I cannot understand the message:\n&quot;{text}&quot;\nAs my programmer did not insert it among the command I am set to respond (that are /price, /menu and /location): please check for misspelling/errors or contact the programmer if you feel anything is wrong/missing'
        )
    else:
        pass


async def error_handler(update, context: CallbackContext) -&amp;gt; None:
    await update.message.reply_text(&quot;Sorry, something went wrong.&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we are done with defining functions, we can actually write the portion of the code that will make the bot work:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if __name__ == &quot;__main__&quot;:
    print(&quot;Bot is high and running&quot;)
    application = Application.builder().token(TOKEN).build()
    # Commands
    application.add_handler(CommandHandler(&quot;start&quot;, start_command))
    application.add_handler(CommandHandler(&quot;menu&quot;, menu_command))
    application.add_handler(CommandHandler(&quot;price&quot;, price_command))
    application.add_handler(CommandHandler(&quot;location&quot;, location_command))
    application.add_handler(MessageHandler(filters.COMMAND, unrecognized_command))
    application.add_handler(MessageHandler(filters.TEXT, unrecognized_command))
    application.add_error_handler(error_handler)
    # Run bot
    application.run_polling(1.0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we associated &lt;b&gt;to each command its related function&lt;/b&gt;, and then we &lt;b&gt;filtered&lt;/b&gt; out all the commands and the texts without commands that cannot be recognized. If something goes wrong, the code is set to handle the error. Last but not least, we launch our application with the &lt;i&gt;run_polling&lt;/i&gt; method. &lt;/p&gt;
&lt;h2&gt;5. Run the bot&lt;/h2&gt;
&lt;p&gt;To run the bot, you can simply &lt;b&gt;launch it from your terminal&lt;/b&gt; typing:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python3 bot.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatively, if you want your bot to run independently from your machine, you can explore services such as &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.pythonanywhere.com/&quot;&gt;&lt;b&gt;pythonanywhere&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here is, in the meanwhile, an example chat for our bot&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=11949383436860980979&quot; alt=&quot;example_chat&quot;&gt;&lt;br&gt;
&lt;/p&gt;&lt;h2&gt;6. Conclusion&lt;/h2&gt;&lt;br&gt;
&lt;p&gt;To sum things up, you have build your &lt;b&gt;first python Telegram bot&lt;/b&gt;, that is &lt;b&gt;production-ready&lt;/b&gt; and will be really &lt;b&gt;useful&lt;/b&gt; for our Neapolitan Pizzeria. In this sense, we also explored how &lt;b&gt;basic python skills&lt;/b&gt; can be directly applied to &lt;b&gt;real projects&lt;/b&gt; and works. &lt;/p&gt;&lt;br&gt;
&lt;p&gt;In my next posts, I will explain how to &lt;b&gt;fine-grain&lt;/b&gt; our Telegram bots and, when we become sufficiently skilled at building them, we will switch to &lt;b&gt;Discord bots&lt;/b&gt;. Our ultimate goal will be to create an &lt;b&gt;AI-powered bot&lt;/b&gt;, and this will be the last episode of our bot-related post series... Stay tuned!&lt;/p&gt;
</description>
<category>Python</category>
<guid isPermaLink="true">https://coderlegion.com/252/create-a-python-telegram-bot-plain-simple-and-production-ready</guid>
<pubDate>Wed, 24 Apr 2024 06:49:46 +0000</pubDate>
</item>
</channel>
</rss>