Telegram Bot — davomi
Muammodan boshlaylik
Oldingi darsda oddiy botni yozdik. Lekin real bot murakkabroq:
- Foydalanuvchi ro'yxatdan o'tishi kerak
- Bot holat (state) ni eslab qolishi kerak: "siz hozir mahsulot qo'shish jarayonidasiz"
- Webhook orqali ishlash kerak (polling production da emas)
Bu darsda shu narsalarni ko'ramiz.
Wizard Scene — ko'p bosqichli suhbat
Foydalanuvchi mahsulot qo'shmoqda. Bot unga ketma-ket savollar beradi:
Bot: Mahsulot nomini yozing
Foydalanuvchi: iPhone 15
Bot: Narxini yozing
Foydalanuvchi: 12000000
Bot: Mahsulot qo'shildi! ✅
Bu Wizard Scene — ko'p bosqichli so'roq.
O'rnatish
npm install nestjs-telegraf telegraf
Wizard Scene yaratish
// bot/scenes/add-product.scene.ts
import { Wizard, WizardStep, Ctx, Message } from 'nestjs-telegraf';
import { WizardContext } from 'telegraf/typings/scenes';
@Wizard('add-product') // ← scene nomi
export class AddProductScene {
// 1-qadam: nom so'rash
@WizardStep(1)
async step1(@Ctx() ctx: WizardContext) {
await ctx.reply('Mahsulot nomini yozing:');
ctx.wizard.next(); // ← keyingi qadamga o'tish
}
// 2-qadam: nom saqlash, narx so'rash
@WizardStep(2)
async step2(
@Ctx() ctx: WizardContext,
@Message('text') name: string,
) {
ctx.wizard.state['name'] = name; // ← nomni eslab qolish
await ctx.reply('Narxini yozing (so\'mda):');
ctx.wizard.next();
}
// 3-qadam: narx saqlash, tugatish
@WizardStep(3)
async step3(
@Ctx() ctx: WizardContext,
@Message('text') price: string,
) {
const name = ctx.wizard.state['name'];
const priceNum = Number(price);
if (isNaN(priceNum)) {
await ctx.reply('Narx raqam bo\'lishi kerak. Qayta yozing:');
return; // ← qadamni o'zgartirmaymiz
}
// Mahsulot saqlash
await this.productsService.create({ name, price: priceNum });
await ctx.reply(`✅ "${name}" — ${priceNum.toLocaleString()} so'm qo'shildi!`);
await ctx.scene.leave(); // ← sceneni tugatish
}
}
Scene ni modulga qo'shish
// bot/bot.module.ts
import { TelegrafModule } from 'nestjs-telegraf';
@Module({
imports: [
TelegrafModule.forFeature([AddProductScene]),
],
providers: [BotUpdate, AddProductScene],
})
export class BotModule {}
Scene ni ishga tushirish
// bot/bot.update.ts
@Update()
export class BotUpdate {
@Hears('➕ Mahsulot qo\'shish')
async addProduct(@Ctx() ctx: Context) {
await (ctx as any).scene.enter('add-product'); // ← scenega kirish
}
}
Foydalanuvchini Telegram ID bilan bog'lash
Foydalanuvchi bot ga yozganida uning Telegram ID si bo'ladi. Buni database dagi foydalanuvchi bilan bog'lash kerak.
// users/user.entity.ts
@Entity()
export class User {
@Column({ nullable: true, unique: true })
telegramId: number;
}
// bot/bot.update.ts
@Start()
async start(@Ctx() ctx: Context) {
const telegramId = ctx.from.id;
const name = ctx.from.first_name;
// Foydalanuvchi bormi?
let user = await this.usersService.findByTelegramId(telegramId);
if (!user) {
// Yangi foydalanuvchi — ro'yxatdan o'tkazish
user = await this.usersService.createFromTelegram({
telegramId,
name,
});
await ctx.reply(`Xush kelibsiz, ${name}! Siz ro'yxatdan o'tdingiz.`);
} else {
await ctx.reply(`Xush kelibsiz qaytib, ${name}!`);
}
}
Webhook sozlash (Production)
Development da polling yaxshi. Production da — webhook.
// app.module.ts
TelegrafModule.forRoot({
token: process.env.TELEGRAM_BOT_TOKEN,
launchOptions: {
webhook: {
domain: 'https://yourdomain.com', // ← HTTPS kerak!
path: '/telegram/webhook',
},
},
})
Webhook faqat HTTPS bilan ishlaydi. Lokalni test qilish uchun ngrok ishlating:
ngrok http 3000
# → https://abc123.ngrok.io
Xabar formatlash
// HTML formatlash
await ctx.replyWithHTML(
'<b>Buyurtma:</b> #123\n' +
'<i>Holat:</i> Yetkazilmoqda\n' +
'<code>Track ID: UZ123456</code>'
);
// Markdown
await ctx.replyWithMarkdown(
'*Buyurtma:* #123\n' +
'_Holat:_ Yetkazilmoqda'
);
// Rasm bilan
await ctx.replyWithPhoto(
{ url: 'https://example.com/product.jpg' },
{ caption: 'iPhone 15 Pro' }
);
Bot Middleware — Guard
Faqat ro'yxatdan o'tgan foydalanuvchilarga ruxsat:
// bot/guards/auth.guard.ts
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { TelegrafExecutionContext } from 'nestjs-telegraf';
@Injectable()
export class TelegramAuthGuard implements CanActivate {
constructor(private usersService: UsersService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const ctx = TelegrafExecutionContext.create(context).getContext();
const telegramId = ctx.from?.id;
if (!telegramId) return false;
const user = await this.usersService.findByTelegramId(telegramId);
return !!user;
}
}
@Hears('/profile')
@UseGuards(TelegramAuthGuard)
async profile(@Ctx() ctx: Context) {
// Faqat ro'yxatdan o'tganlar ko'radi
}
Tez-tez uchraydigan xatolar
1. Scene ishlamayapti
Sababi: Scene TelegrafModule.forFeature([Scene]) ga qo'shilmagan.
Yechim: Module da import va provider ga qo'shing.
2. Webhook SSL xatosi
Error: HTTPS required for webhook
Sababi: HTTP ishlatyapsiz. Yechim: Nginx + SSL, yoki ngrok bilan test qiling.
3. Bir xabar ikki marta qayta ishlanmoqda Sababi: Polling va webhook bir vaqtda ishlamoqda. Yechim: Faqat bittasini ishlating.
Xulosa
Eslab qoling:
@Wizard()— ko'p bosqichli suhbatctx.wizard.state— bosqichlar orasida ma'lumot saqlashctx.scene.leave()— sceneni tugatish- Telegram ID orqali foydalanuvchini database bilan bog'lang
- Production da webhook, development da polling
Keyingi dars: 51-dars: To'lov tizimlari — Click / Payme / Stripe