Mundarija (24)
- Bu darsda
- 1. Nega bu kerak?
- 2. Fayl yuborish
- 2.1 Uch yo'l
- 2.2 Kanon o'lchovi
- 2.3 file_id qoidalari
- 3. Albom: sendMediaGroup
- 4. Faylni yuklab olish: getFile
- 5. Hajm chegaralari
- 6. Formatlash
- 6.1 parse_mode
- 6.2 HTML ekranlash
- 6.3 Ekranlanmasa nima bo'ladi
- 6.4 Hujumchi nigohi
- 6.5 MarkdownV2
- 7. Ko'p uchraydigan xatolar
- 8. Mashqlar
- 1-mashq (oson): yo'lni tanlang
- 2-mashq (o'rta): izohni kesish
- 3-mashq (qiyin): albomni yig'ish
- 9. Loyiha qadami: tajriba va html() moduli
- 10. Real ishda
- Xulosa
- Manbalar
Media, fayllar va formatlash: Telegram botda rasm, hujjat va HTML
Qisqacha: Faylni Telegram'ga bir marta yuklang — javobda
file_idkeladi. Keyingi safar baytlarni emas, shu satrni yuboring: kanonda 300 KB rasm yuklash 30.8 ms,file_idbilan 2.4 ms bo'ldi. Bot API orqali 50 MB gacha yuborasiz va 20 MB gacha yuklab olasiz (getFile); kattaroq fayl uchun o'z local Bot API serveringiz kerak. Matnni HTML bilan formatlasangiz, foydalanuvchi matnidagi&,<,>ni ekranlang — aks holda Telegram xabarni rad etadi.
Bu darsda
- Fayl yuborishning uch yo'lini (yuklash,
file_id, URL) va ular farqini tushunasiz. sendMediaGroupbilan albom yuborasiz va uning cheklovlarini bilasiz.- Foydalanuvchi yuborgan faylni
getFilebilan yuklab olasiz. - 20 MB / 50 MB chegaralari va local Bot API server haqida bilasiz.
- HTML va MarkdownV2 formatlash, ekranlash va formatlash orqali hujumni tushunasiz.
Oldin bilishingiz kerak: Sessiya va ko'p qadamli dialoglar, Fayl yuklash: multipart va Multer, Fayl yuklash xavfsizligi.
Oldingi darslarda bot faqat matn yubordi. Bugun rasm, hujjat va formatlangan xabar bilan ishlaymiz. Bu dars — tajriba darsi: loyiha boti faqat matnli eslatma yuboradi, lekin HTML ekranlash unda bor va bugun uni chuqur ko'ramiz.
1. Nega bu kerak?
Bot ko'p hollarda fayl bilan ishlaydi:
- do'kon boti mahsulot rasmini yuboradi;
- buxgalteriya boti PDF hisobot yuboradi;
- foydalanuvchi chek rasmini yoki hujjatni botga yuboradi, bot uni saqlaydi.
Uchta savol tug'iladi: bir xil rasmni 10 000 foydalanuvchiga qanday tez yuborish? Foydalanuvchi yuborgan faylni qanday olish? Qancha hajmgacha mumkin?
O'xshatish: kutubxona. Kitobni birinchi marta topshirganingizda kutubxonachi unga inventar raqami yopishtiradi. Keyin "shu kitobni falonchiga bering" deyish uchun kitobni ko'tarib yurmaysiz — raqamni aytasiz. file_id — o'sha inventar raqami.
2. Fayl yuborish
2.1 Uch yo'l
Bot API'da sendPhoto, sendDocument, sendAudio, sendVideo, sendVoice metodlari faylni uch shaklda oladi:
| Shakl | Nima ketadi | Qachon |
|---|---|---|
| fayl baytlari | multipart/form-data (multipart) |
birinchi marta |
file_id satri |
qisqa satr | qayta yuborish |
| HTTP URL | havola, Telegram o'zi yuklaydi | ochiq internetdagi fayl |
grammY'da baytlar InputFile bilan beriladi — Buffer, fayl yo'li yoki oqim:
import { InputFile } from "grammy";
const m1 = await bot.api.sendPhoto(50,
new InputFile(rasmBuffer, "r.jpg"),
{ caption: "<b>birinchi</b>", parse_mode: "HTML" });
const fileId = m1.photo.at(-1).file_id;
await bot.api.sendPhoto(50, fileId, { caption: "file_id bilan" });m1.photo — massiv: Telegram rasmni bir nechta o'lchamda saqlaydi, .at(-1) — eng kattasi. Kanon mock'i bitta o'lcham qaytaradi; haqiqiy Telegram bir nechta qaytaradi (hujjat bo'yicha).
2.2 Kanon o'lchovi
Kurs tajribasi 300 KB rasmni avval multipart bilan, keyin file_id bilan yubordi:
yuklash (300 KB multipart) 30.8 ms; file_id bilan 2.4 ms; file_id: SOXTAALVQR06…
sendPhoto chaqiruvlarida photo parametri: string, string (1-si multipart fayl, 2-si satr)- Vaqt 13 barobar kam: baytlar qayta ketmadi. Bu — lokal mock, tarmoq yo'q. Haqiqiy internetda 300 KB'ni Telegram serveriga yuklash ancha uzoq, farq yanada katta bo'ladi.
SOXTA…— mock yaratgan id; haqiqiyfile_idboshqacha ko'rinadi va har ishga tushishda boshqa. Uni hech qachon "taxmin" qilmang.- Ikkinchi qator qiziq: ikkala chaqiruvda ham
photo— satr. Birinchisida grammY uniattach://<nom>shaklida yuboradi va fayl baytlarini multipart'ning alohida qismiga qo'yadi. Kanon mock'i dastlab buni bilmagan va xato bergan — tuzatilgan.
Millisekundlar har ishga tushishda boshqacha chiqadi. Aniq raqamni emas, farqning tartibini eslab qoling: file_id bilan yuborish o'n barobardan ko'p tez.
2.3 file_id qoidalari
Telegram hujjati bo'yicha:
file_id— faylni yuklab olish yoki qayta yuborish uchun. U botga xos: boshqa bot sizningfile_idingizni ishlata olmaydi.file_unique_id— bir fayl uchun vaqt o'tsa ham va turli botlarda ham bir xil. Lekin u bilan fayl yuborib yoki yuklab bo'lmaydi — faqat "bu o'sha fayl" deb solishtirish uchun.
Amaliy naqsh: mahsulot rasmini birinchi yuborganda file_id ni bazaga yozing. Keyin 10 000 foydalanuvchiga — faqat satr.
3. Albom: sendMediaGroup
Bir nechta rasmni bitta "albom" qilib yuborish:
const g = await bot.api.sendMediaGroup(50, [
{ type: "photo", media: fileId, caption: "albom" },
{ type: "photo", media: new InputFile(boshqaRasm, "b.jpg") },
]);Kanon:
sendMediaGroup: 2 xabar, bitta media_group_id: true
1 ta media: Bad Request: wrong number of messages to send- Natija — xabarlar massivi: albom Telegram uchun alohida xabarlar, ularni umumiy
media_group_idbirlashtiradi. - Hujjatga ko'ra albomda 2–10 element. Bitta element — xato (mock xato matni haqiqiy serverniki bilan solishtirilmagan).
file_idva yangi fayl bitta albomda aralashishi mumkin.
Bot qabul qilganda ham albom alohida update'lar bo'lib keladi — har rasm o'z message i bilan, bir xil media_group_id. Ularni birlashtirish — sizning vazifangiz (masalan, qisqa vaqt kutib, id bo'yicha yig'ish).
4. Faylni yuklab olish: getFile
Foydalanuvchi botga hujjat yubordi. Update'da faqat file_id bor, baytlar yo'q. Yuklab olish ikki qadam:
getFile(file_id)→file_path(Telegram serveridagi nisbiy yo'l).GET https://api.telegram.org/file/bot<token>/<file_path>— baytlar.
const f = await bot.api.getFile(fileId);
const javob = await fetch(
`https://api.telegram.org/file/bot${TOKEN}/${f.file_path}`,
);
const baytlar = Buffer.from(await javob.arrayBuffer());Kanon (mock manzili bilan):
getFile: {"file_path":"photos/file_1.jpg","file_size":307200} → yuklab olish 200 307200 bayt
21 MB getFile: Bad Request: file is too big (cloud Bot API limiti 20 MB; local Bot API server — 2000 MB, hujjat bo'yicha, sinalmagan)
noto'g'ri file_id: Bad Request: wrong file identifier/HTTP URL specifiedDiqqat: Yuklab olish havolasida bot tokeni bor. Uni logga yozmang va foydalanuvchiga ko'rsatmang. Faylni o'zingiz yuklab, o'z omboringizga (Object storage: S3 va MinIO) qo'ying va o'z havolangizni bering.
Foydalanuvchi faylidan keyingi qadam — Fayl yuklash xavfsizligi darsidagi hamma narsa: hujjat nomi va MIME turi mijozdan keladi, haqiqiy turni sehrli baytlardan aniqlang. Telegram faylni tekshirmaydi.
5. Hajm chegaralari
flowchart TD
F[Fayl] --> Y{Bot yuboradimi?}
Y -->|ha| H{50 MB dan kichikmi?}
H -->|ha| C[Bot API cloud]
H -->|yo'q| L[local Bot API server]
Y -->|yo'q, oladi| G{20 MB dan kichikmi?}
G -->|ha| C
G -->|yo'q| L
C --> I[file_id saqlanadi]
L --> IDiagrammaga qarang: yuborish va yuklab olishning chegarasi har xil. Telegram hujjati (2026-10-28 da o'qildi):
| Amal | Cloud Bot API | Local Bot API server |
|---|---|---|
| bot fayl yuboradi | 50 MB gacha | 2000 MB gacha |
| bot fayl yuklab oladi | 20 MB gacha | cheklovsiz |
| xabar matni | 1–4096 belgi | |
| rasm/hujjat izohi (caption) | 0–1024 belgi |
Matn chegaralari — "entity'lar tahlil qilingandan keyingi" belgilar, ya'ni <b> teglari hisobga kirmaydi.
Local Bot API server — Telegram'ning ochiq kodli serveri (tdlib/telegram-bot-api). Uni o'z serveringizda ishga tushirasiz, bot api.telegram.org o'rniga unga ulanadi (grammY'da apiRoot). Katta fayllar shu server orqali o'tadi. Bu kursda sinalmagan — faqat hujjat bo'yicha.
Kanon mock'i 21 MB faylni qabul qildi (50 MB dan kichik), lekin getFile da "file is too big" berdi — chegaralarning farqi aynan shu.
Tekshirib ko'ring: Foydalanuvchi botga 30 MB video yubordi. Bot uni qabul qilib, boshqa chatga qayta yubora oladimi? Yuklab ola oladimi?
Javob
Qayta yubora oladi — file_id bilan; baytlar Telegram ichida qoladi, hajm chegarasi tegmaydi. Yuklab ololmaydi — getFile 20 MB dan katta faylni rad etadi (kanon: 21 MB → "file is too big"). Yuklab olish kerak bo'lsa — local Bot API server.
6. Formatlash
6.1 parse_mode
Xabar matnida qalin, kursiv, havola, kod bo'lishi mumkin. Buni ikki "til"da yozish mumkin: parse_mode: "HTML" yoki "MarkdownV2". Uchinchi yo'l — entities maydoni (ofsetlar bilan), lekin u qo'lda noqulay.
HTML'da faqat bir nechta teg ruxsat etilgan: <b>, <i>, <u>, <s>, <a href>, <code>, <pre>, <blockquote>, <tg-spoiler> va yana bir nechtasi. <div>, <br>, <p> — yo'q; yangi qator — oddiy \n.
6.2 HTML ekranlash
Telegram hujjati: teg yoki HTML entity'ning qismi bo'lmagan barcha <, > va & belgilari mos entity bilan almashtirilishi kerak (<, >, &). Kanon html() funksiyasi aynan shu uchtasini ekranlaydi:
const html = (s) =>
s.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
console.log(html("1 < 2 & 3 > 2"));
console.log(html("<b>Ali</b>"));
console.log(`<b>${html("Hisobot <tayyorlash> & yuborish")}</b>`);
console.log(html("<"));Konsolda:
1 < 2 & 3 > 2
<b>Ali</b>
<b>Hisobot <tayyorlash> & yuborish</b>
&lt;&birinchi almashtiriladi. Aks holda<dan hosil bo'lgan<dagi&yana&ga aylanib,&lt;bo'lardi.- Oxirgi qator — matnda haqiqatan
<yozilgan bo'lsa, u o'zgarmasdan ko'rinadi:&lt;ekranda<bo'ladi. To'g'ri. - Uchinchi qator — naqsh: o'zingizning teglaringiz (
<b>) ekranlanmaydi, foydalanuvchi matni esa doimhtml()dan o'tadi.
Kanon html() qo'shtirnoqni (") ekranlamaydi — Telegram hujjati faqat uchta belgini talab qiladi. Lekin <a href="..."> atributiga foydalanuvchi matnini qo'ysangiz, " ham ekranlanishi kerak (" — hujjatda qo'llab-quvvatlanadigan entity). Kanon modulida atributga foydalanuvchi matni tushmaydi.
6.3 Ekranlanmasa nima bo'ladi
Kanon testi 1 < 2 matnini parse_mode: "HTML" bilan yuboradi va 400 kutadi. Biz shuni mock bilan alohida ishga tushirdik (ekranlangan variant bilan birga):
400 Bad Request: can't parse entities: Unsupported start tag at byte offset 2
yuborildi: <b>1 < 2</b>Xato matnining boshi (can't parse entities) — kanon testida tekshirilgan; bayt ofseti va qolgan so'zlar — mock'ning taxmini, haqiqiy server bilan solishtirilmagan. Muhimi: xabar umuman yuborilmadi. Foydalanuvchi vazifasiga < qo'ysa, unga eslatma kelmaydi — jim xato.
6.4 Hujumchi nigohi
Ekranlash nafaqat xatodan, balki formatlash orqali aldashdan ham himoya qiladi. Hujumchi vazifa nomini shunday yozadi:
<a href="https://example.com/soxta-kirish">Hisobingiz bloklandi, bu yerni bosing</a>Bot uni ekranlamasdan <b>Eslatma:</b> ${matn} ichiga qo'ysa, foydalanuvchi botning rasmiy xabarida chiroyli havola ko'radi — bot unga ishonadi. Bu fishing. html() dan o'tgan matn esa teglar bilan birga oddiy matn bo'lib ko'rinadi.
Bu — XSS g'oyasining chatdagi ko'rinishi: foydalanuvchi ma'lumoti va sizning belgilash tilingiz aralashmasin.
6.5 MarkdownV2
MarkdownV2 da maxsus belgilar ko'p. Telegram hujjati: _ * [ ] ( ) ~ \ > # + - = | { } . !belgilari matnda` bilan ekranlanishi kerak. Oddiy jumla Narx: 35 000 so'm. ham nuqta tufayli rad etiladi:
const MAXSUS = "_*[]()~`>#+-=|{}.!";
const md2 = (s) =>
[...s].map((c) => (MAXSUS.includes(c) || c === "\\")
? `\\${c}` : c).join("");
console.log(md2("Narx: 35 000 so'm."));
console.log(md2("1+1=2 (to'g'ri!)"));
console.log(md2("fayl_nomi.txt"));Konsolda:
Narx: 35 000 so'm\.
1\+1\=2 \(to'g'ri\!\)
fayl\_nomi\.txtUchta belgiga qarshi o'n sakkizta. Shuning uchun kanon HTML'ni tanladi: ekranlash sodda va xato qilish qiyin.
grammY'da formatlashning uchinchi yo'li ham bor — @grammyjs/parse-mode plagini (fmt shablonlari entities yasaydi). Kanon uni o'rnatmadi.
7. Ko'p uchraydigan xatolar
| Xato | Natija | To'g'risi |
|---|---|---|
| har safar faylni qayta yuklash | sekin, trafik | file_id ni bazada saqlash |
file_unique_id bilan yuborish |
xato | yuborish — file_id |
20 MB dan katta faylni getFile |
"file is too big" | local Bot API server |
| yuklab olish URL'ini logga yozish | token oqadi | faylni o'z omboringizga |
| foydalanuvchi matni ekranlanmagan | xabar ketmaydi yoki fishing | html() |
& ni oxirida almashtirish |
&lt; |
& birinchi |
| MarkdownV2 da nuqta | "can't parse entities" | HTML yoki to'liq ekranlash |
| albomda 1 ta yoki 11 ta element | xato | 2–10 |
8. Mashqlar
Mashqlar kurs/mashqlar/29/23-media/ da.
1-mashq (oson): yo'lni tanlang
Har holatda qaysi yo'l: yuklash, file_id, URL yoki local Bot API server?
- Har kuni ertalab 5 000 obunachiga bir xil reklama rasmi.
- Foydalanuvchi yuborgan 45 MB arxivni serverga saqlash.
- Ochiq saytdagi
https://example.com/menyu.jpgni bir marta yuborish. - Yangi yaratilgan PDF hisobotni birinchi marta yuborish.
Yechim
- Birinchi marta yuklash, keyin
file_id— 4 999 marta baytlarsiz. - Local Bot API server — cloud
getFile20 MB dan kattasini bermaydi. - URL — Telegram o'zi yuklaydi (hujjatdagi URL cheklovlari bilan).
- Yuklash (
InputFile) — fayl hali Telegram'da yo'q.
2-mashq (o'rta): izohni kesish
izohTayyorla(matn) yozing: foydalanuvchi matnini html() dan o'tkazadi va <b>Hisobot:</b> prefiksi bilan birlashtiradi. Ekranlashdan oldingi matn 1 000 belgidan uzun bo'lsa — 999 belgi + …. Chiqaring: natija uzunligi va oxirgi 12 belgisi; "a<b" uchun natija. Ishora: Telegram chegarani entity'lardan keyingi belgilarda o'lchaydi, ya'ni < bitta belgi; shuning uchun kesish ekranlashdan oldin qilinadi.
Yechim
const html = (s) =>
s.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
function izohTayyorla(matn) {
const qisqa = matn.length > 1000 ? `${matn.slice(0, 999)}…` : matn;
return `<b>Hisobot:</b> ${html(qisqa)}`;
}
const uzun = izohTayyorla("x<".repeat(800));
console.log(uzun.length, uzun.slice(-12));
console.log(izohTayyorla("a<b"));2513 x<x<x…
<b>Hisobot:</b> a<buzun.length — 2513: kesilgan 999 belgida 500 ta x va 499 ta < bor, har < ekranlangach 4 belgi (<) bo'ldi, ustiga … va 16 belgili prefiks. Telegram esa uni bitta < deb sanaydi: Hisobot: (9) + 1 000 = 1 009 belgi — 1024 ga sig'adi. Agar avval ekranlab, keyin kessak, < o'rtasidan kesilib, "can't parse entities" bo'lishi mumkin edi.
3-mashq (qiyin): albomni yig'ish
Bot alohida update'lar oladi: har birida { chat, media_group_id, file_id } (media_group_id bo'lmasligi ham mumkin). albomlarniYig(update'lar) funksiyasini yozing: bir media_group_id li rasmlarni bitta albomga, guruhsizlarini — alohida "yakka"ga yig'adi. Natija — { tur, chat, fayllar } massivi, kelish tartibida. Ishora: Map tartibni saqlaydi (Map).
Yechim
function albomlarniYig(updatelar) {
const natija = new Map();
let yakka = 0;
for (const u of updatelar) {
const kalit = u.media_group_id
? `${u.chat}:${u.media_group_id}`
: `yakka:${yakka++}`;
if (!natija.has(kalit)) {
natija.set(kalit, {
tur: u.media_group_id ? "albom" : "yakka",
chat: u.chat,
fayllar: [],
});
}
natija.get(kalit).fayllar.push(u.file_id);
}
return [...natija.values()];
}
const kelgan = [
{ chat: 7, media_group_id: "g1", file_id: "A" },
{ chat: 7, file_id: "B" },
{ chat: 7, media_group_id: "g1", file_id: "C" },
{ chat: 9, media_group_id: "g1", file_id: "D" },
];
for (const a of albomlarniYig(kelgan)) {
console.log(a.tur, a.chat, a.fayllar.join(","));
}albom 7 A,C
yakka 7 B
albom 9 DKalitga chat ham kiradi: media_group_id faqat bitta chat ichida ma'noga ega deb hisoblaymiz — begona chatdagi tasodifiy bir xil id albomni aralashtirmasin. Haqiqiy botda update'lar vaqt bo'yicha keladi, shuning uchun yig'ish qisqa kutish bilan qilinadi.
9. Loyiha qadami: tajriba va html() moduli
Bu dars — tajriba. vazifalar eslatma boti faqat matn yuboradi: vazifa nomi va muddati. Fayl biriktirish (vazifa ilovalari) ilovaning o'zida, Fayl yuklash darslaridagi modullar orqali. Shuning uchun sendPhoto, getFile loyihaga kirmadi — ehtiyoj yo'q.
Lekin formatlash loyihada bor. bot.ts dagi html() (formatlangan):
/** Telegram HTML parse_mode uchun: faqat `&`, `<`, `>`
* (hujjat talabi). */
export const html = (s: string): string =>
s.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");U uch joyda ishlatiladi: ro'yxat xabari (royxatXabari), dialog oxiridagi "Qo'shildi" va bildirishnoma yuboruvchisi. Kanon testi uni mock'ning HTML qoidasi bilan tekshiradi (formatlangan):
test("html va muddat yordamchilari", () => {
assert.equal(html("a < b & c > d"), "a < b & c > d");
assert.equal(htmlTekshir(`<b>${html("<script>")}</b>`), undefined);
assert.match(htmlTekshir("<b>a < b</b>") ?? "",
/can't parse entities/);
assert.match(htmlTekshir("<div>x</div>") ?? "",
/Unsupported start tag "div"/);
// ... muddatTahlil tekshiruvlari
});htmlTekshir — mock ichidagi Telegram HTML qoidasining nusxasi: undefined — xato yo'q. Ekranlangan <script> o'tdi, ekranlanmagan < va <div> rad etildi. Biz testlarni ishga tushirdik (Node 24.21.0): ✔ html va muddat yordamchilari, fayl bo'yicha 9/9.
Commit — bu darsda yangi kod yo'q, html() oldingi commit'larda bor.
10. Real ishda
- Do'kon botlari mahsulot rasmlarining
file_idsini bazada saqlaydi — katalog bir zumda yuboriladi. - Hujjat qabul qiluvchi botlar (ariza, chek) faylni darhol o'z omboriga ko'chiradi va tekshiradi.
- Katta media bilan ishlaydigan loyihalar (video kurs botlari) local Bot API server ishlatadi.
- Intervyuda: "file_id va file_unique_id farqi?", "Foydalanuvchi matnini HTML xabarga qanday xavfsiz qo'yasiz?"
Xulosa
- Fayl bir marta yuklanadi, keyin
file_idbilan: kanonda 30.8 → 2.4 ms. file_id— botga xos;file_unique_id— faqat solishtirish uchun.- Albom — 2–10 element, alohida xabarlar bitta
media_group_idbilan. - Cloud Bot API: yuborish 50 MB, yuklab olish 20 MB; kattasi — local Bot API server (sinalmagan).
- HTML'da foydalanuvchi matni
html()dan o'tadi:&birinchi; aks holda xabar ketmaydi yoki fishing.
Keyingi dars: Guruh, kanal botlari va inline rejim — bot guruhga qo'shilganini bilamiz, privacy mode'ni tushunamiz va istalgan chatdan inline so'rovga javob beramiz.
Manbalar
- Telegram Bot API:
sendPhoto,sendMediaGroup(2–10),getFile(20 MB),sendDocument(50 MB), formatlash qoidalari — https://core.telegram.org/bots/api#formatting-options (2026-10-28 da o'qildi) - Local Bot API server: https://github.com/tdlib/telegram-bot-api — sinalmagan
- grammY fayllar: https://grammy.dev/guide/files
- Kanon tajribasi (IlmHamroh, 2026-10-27): grammy 1.46.0, Node 24.21.0, lokal soxta Bot API serveri. Modul testlari — biz ishga tushirdik (2026-10-28).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!