19 β Guruhlarda ishlash¶
β¬ οΈ Oldingi: 18 β Yakuniy kapston: to'liq bot Β· π README Β· Keyingi: 20 β Guruh moderatsiyasi β‘οΈ
Bu bobda: kitobning VI qismi β real amaliyot boshlanadi. Shu paytgacha botimiz deyarli har doim shaxsiy (
private) suhbatda ishladi. Endi uni guruhga olib chiqamiz. O'rganamiz: Telegram suhbat turlari βprivate,group,supergroup,channelβ va nega guruh filtri doimgroupbilansupergroupni birga qamrashi kerak; botni guruhga qanday qo'shish; privacy mode (maxfiylik rejimi) β bot guruhda qaysi xabarlarni umuman ko'radi va uni@BotFather/setprivacyorqali yoqish/o'chirish, hamda nega bu eng ko'p "botim guruhda javob bermayapti" muammosining sababi;bot.chatType(...)filtri bilan handlerni faqat guruhga (yoki faqat shaxsiyga) yo'naltirish;ctx.getChatMember(userId)orqali foydalanuvchining.statusini olib a'zo/admin ekanini tekshirish (getChatAdministratorsbilan butun admin ro'yxatini olish);my_chat_memberbilan botning o'z statusi o'zgarganini (guruhga qo'shildi, admin bo'ldi, chiqarildi) ushlash va unichat_member(har qanday a'zo statusi) dan farqlash;new_chat_members/left_chat_memberxizmat xabarlari; va guruhda aynan kim yozganini (ctx.from) hamda qaysi guruhda (ctx.chat) ekanini ajratish. Moderatsiyaning o'zi β ban, mute, restrict β keyingi 20-bobda.Halollik eslatmasi: bu bobdagi handler routing (
bot.chatTypefiltri guruh/shaxsiyni to'g'ri ajratishi), a'zolik/admin tekshiruvi (getChatMemberning.statusini o'qib qaror qabul qilish),getChatAdministratorsro'yxati,my_chat_membero'tishlari (qo'shildi/admin bo'ldi/chiqarildi),new_chat_members/left_chat_member, hamdactx.fromvsctx.chatβ bularning hammasi offline, tokensiz ishga tushirilib tasdiqlandi: soxtaUpdate'larnibot.handleUpdate(...)ga uzatib, chiqayotgan API chaqiruvlarini transformer (bot.api.config.use(...)) bilan ushlab. TransformergetChatMemberso'roviga test uchun tayyorlanganChatMemberobyektini ("status" + "user") qaytaradi β Telegram serveri haqiqatan shu shaklda javob beradi. Natija: bob oxiridagi hisobotda 10/10 PASS. Jonli qism β botni real guruhga qo'shish, real a'zo statusini Telegram serveridan olish, haqiqiymy_chat_memberevent'lari va privacy rejimini guruhda his qilish β@BotFathertokeni va internet talab qiladi; bunday joylar "illustrativ" deb halol belgilangan.
1) Nega bu bob? Shaxsiydan guruhga¶
01-18 boblarda biz qurgan har bir bot bitta foydalanuvchi bilan yakkama-yakka gaplashardi: foydalanuvchi yozadi, bot javob beradi. Bu β botlarning katta qismi. Lekin Telegram'ning kuchi β guruhlar va kanallar: o'quv guruhi, jamoa chati, mahalla, mijozlar qo'llab-quvvatlash kanali. Guruh boti:
- a'zolarni kutib oladi ("Xush kelibsiz!"),
- buyruqlarga javob beradi (
/qoidalar,/statistika), - adminlarni oddiy a'zolardan ajratadi (faqat admin
/tozalaqila olsin), - guruhga qo'shilganda o'zini tanishtiradi,
- (20-bobda) spam va so'kishni moderatsiya qiladi.
Lekin guruh β shaxsiy suhbatdan jiddiy farq qiladi va aynan shu farqlar yangi boshlovchini chalg'itadi. Eng ko'p uchraydigan savol: "Botimni guruhga qo'shdim, lekin u xabarlarga javob bermayapti β nega?" Javob deyarli har doim ikkitadan biri: (a) privacy mode yoqilgan (bot oddiy gaplarni ko'rmaydi), yoki (b) handler private ga moslangan, guruhga emas. Shu bobda ikkalasini ham hal qilamiz.
Eslatma β aiogram bilan solishtirma. Python'da aiogram bilan xuddi shu bobni
F.chat.type.in_({ChatType.GROUP, ChatType.SUPERGROUP})filtri vabot.get_chat_member(...)bilan yozasiz. grammY'da bubot.chatType(["group","supergroup"])vactx.getChatMember(...)ga aylanadi β g'oya bir xil. Taqqoslash uchun ../tgbot-python/19-guruhlar.md.
2) Suhbat turlari: private, group, supergroup, channel¶
Har bir update'ning ctx.chat maydoni bor, uning ichida ctx.chat.type β suhbat turi. Telegram'da to'rt asosiy tur mavjud:
chat.type |
Nima | chat.id belgisi |
|---|---|---|
private |
Bir foydalanuvchi bilan shaxsiy suhbat | musbat (> 0), foydalanuvchi ID'siga teng |
group |
Oddiy (eski, kichik) guruh | manfiy (< 0) |
supergroup |
Katta guruh β admin huquqlari, mavzular (topics), ko'p a'zo | manfiy, odatda -100... bilan boshlanadi |
channel |
Kanal (bir tomonlama e'lon) β 22-bobda batafsil | manfiy, -100... |
Eng muhim amaliy nuqta: group va supergroup β aslida bir narsaning ikki bosqichi. Guruh kattalashsa, unda admin yoqilsa yoki tarix (history) ko'rinadigan qilinsa, Telegram uni avtomatik group dan supergroup ga aylantiradi β va shunda chat.id ham o'zgaradi. Demak siz "guruh" deganda har doim ikkalasini nazarda tutishingiz kerak. Faqat bittasini filtrlasangiz, bot ba'zi guruhlarda jim qolib qoladi.
Shuning uchun butun bu bobda guruh handlerlari uchun ikkala turni birga qamraymiz.
bot.chatType(...) β handlerni suhbat turiga bog'lash¶
grammY'da bot.chatType(...) β bu handlerni faqat tanlangan tur(lar)dagi suhbatlar uchun ishlatadigan filtr. Bitta tur uchun satr, bir nechta uchun massiv beriladi:
import { Bot } from "grammy";
const bot = new Bot(process.env.BOT_TOKEN);
// Faqat guruhlar (HAR IKKALA tur) uchun:
bot.chatType(["group", "supergroup"]).command("qoidalar", (ctx) =>
ctx.reply("Guruh qoidalari:\n1. Hurmat.\n2. Spam yo'q.\n3. Reklama yo'q.")
);
// Faqat shaxsiy suhbat uchun:
bot.chatType("private").command("start", (ctx) =>
ctx.reply("Salom! Meni guruhga qo'shing, men u yerda yordam beraman.")
);
// Faqat kanal postlari uchun (channel):
bot.chatType("channel").on("channel_post", (ctx) =>
ctx.reply("Kanal posti qabul qilindi.")
);
bot.chatType([...]) aslida bot.filter(...) ning qulay shakli: u ctx.chat?.type ni tekshiradi va mos kelsa, keyingi handlerni chaqiradi. U bot.command, bot.on, bot.hears β hammasi bilan zanjir orqali ishlaydi (yuqorida ko'rganimizdek .command(...) ni ulang).
Eslatma β
ctx.chatdoim bormi? Deyarli har doim β xabarlar, callback, my_chat_member dactx.chatmavjud. Lekininline_query(07-bob) kabi ba'zi update'lardactx.chatyo'q (foydalanuvchi hech qaysi chatda yozmagan).bot.chatType(...)bunday update'larni avtomatik o'tkazib yuboradi (mos kelmaydi) β shuning uchun u guruh handlerlari uchun xavfsiz "darvoza".
ctx.chat vs ctx.from: guruhda kim yozdi?¶
Shaxsiy suhbatda ctx.chat.id foydalanuvchi ID'siga teng edi, shuning uchun farqi sezilmasdi. Guruhda esa ular butunlay boshqa:
ctx.chatβ suhbatning o'zi: guruh (ctx.chat.idβ manfiy,ctx.chat.titleβ guruh nomi).ctx.fromβ xabarni yozgan odam:ctx.from.id,ctx.from.first_name,ctx.from.username.
bot.chatType(["group", "supergroup"]).on("message:text", (ctx) => {
return ctx.reply(
`Yozgan: ${ctx.from.first_name} (id ${ctx.from.id})\n` +
`Guruh: ${ctx.chat.title} (id ${ctx.chat.id})`,
// Guruhda javobni aniq xabarga "reply" qilish odob (kimsa yo'qotmasin):
{ reply_parameters: { message_id: ctx.msg.message_id } }
);
});
Guruhda javob berganda reply_parameters: { message_id: ctx.msg.message_id } qo'shish (05-bobda ko'rgan reply) yaxshi odat: ko'p odam yozayotgan chatda bot javobi qaysi xabarga tegishli ekani aniq bo'ladi. ctx.msg β joriy xabar uchun qulay yorliq (ctx.message yoki ctx.channelPost).
Diqqat β ID belgisiga ishonmang,
typega ishoning. "id manfiy bo'lsa guruh" qoidasi amalda ishlaydi, lekin mantiqnichat.idbelgisiga emas,chat.typega quring. Telegram kelajakda ID formatini o'zgartirsa hamtypebarqaror qoladi. Shuning uchun bizbot.chatType(...)ishlatamiz, "id < 0" ni emas.
3) Botni guruhga qo'shish¶
Bu β kod emas, sozlash qadami, lekin uni bilmasdan turib hech narsa ishlamaydi.
- Guruhni oching -> guruh nomini bosing -> Add members (a'zo qo'shish).
- Botingizni username (
@sizning_bot) bo'yicha qidiring va qo'shing. - (Ko'pincha kerak) Botni admin qiling: guruh sozlamalari -> Administrators -> Add admin -> botni tanlang. Faqat shunda bot xabar o'chira oladi, a'zoni cheklay oladi va h.k. (20-bob).
Bot guruhga qo'shilgan onda Telegram sizga my_chat_member update'ini yuboradi (4-bo'limda batafsil) β bu botning o'zini tanishtirishi uchun ideal moment.
Anti-eskirish β "Add to Group" tugmasi. Botingiz guruhga umuman qo'shila olishi uchun
@BotFatherda/setjoingroups-> Enable bo'lishi kerak (standart yoqilgan). Agar botni guruhga qo'sha olmasangiz, avval shuni tekshiring.bot.botInfo.can_join_groupsmaydoni bot guruhga qo'shila olishini ko'rsatadi.
4) Privacy mode β eng ko'p "nega ishlamayapti" sababi¶
Bu bo'lim β bu bobning eng muhim qismi. Yangi boshlovchining 9 ta "botim guruhda javob bermayapti" muammosidan 8 tasi shu yerda.
Privacy mode (maxfiylik rejimi) β Telegram'ning xavfsizlik xususiyati. U botning guruhda qaysi xabarlarni umuman olishini belgilaydi. Standart holatda u YOQILGAN.
Privacy YOQILGAN bo'lsa (standart)¶
Bot guruhda faqat shularni oladi:
- Buyruqlar β
/start,/qoidalar(aniq slash bilan boshlanadigan, bot uchun mo'ljallangan buyruqlar). - Mention β
@sizning_botdeb chaqirilgan xabarlar. - Reply β botning o'z xabariga javob (reply) qilingan xabarlar.
- Xizmat xabarlari β a'zo qo'shildi/chiqdi, sarlavha o'zgardi (
new_chat_members,left_chat_memberva h.k.).
Oddiy gaplarni (masalan "salom hammaga") bot ko'rmaydi β Telegram ularni botga umuman yubormaydi. Bu β maxfiylik: guruh a'zolari botning ularning har bir gapini eshitmasligiga ishonadi.
Privacy O'CHIRILGAN bo'lsa¶
Bot guruhdagi har bir xabarni oladi: oddiy gaplar, rasmlar, stikerlar β bari. Bu anti-spam, so'z filtri, statistika kabi botlar uchun kerak β ular hamma xabarni ko'rishi shart.
Qanday o'zgartirish kerak¶
@BotFather ga boring:
/setprivacy
-> botni tanlang
-> Disable (o'chirish β bot hamma xabarni ko'rsin)
yoki Enable (yoqish β standart, faqat buyruq/mention/reply)
Diqqat β o'zgartirgach botni qayta qo'shing! Privacy rejimini o'zgartirgach, u faqat botni guruhdan chiqarib, qayta qo'shganingizdan keyin kuchga kiradi (mavjud guruhlarda eski rejim qoladi). Bu β men bir necha soat boshim qotgan tuzoq: BotFather'da o'zgartirdim, lekin guruhda hech narsa o'zgarmadi.
Eslatma β admin qilish privacy bilan teng emas. Ko'pchilik o'ylaydiki, "botni admin qilsam, hamma xabarni ko'radi". Yo'q. Privacy mode va adminlik β ikki alohida narsa. Bot admin bo'lsa ham, privacy yoqilgan bo'lsa, oddiy gaplarni ko'rmaydi. (Maxsus istisno: admin bo'lgan botda Telegram ba'zan ko'proq ko'rsatadi, lekin bunga tayanmang β kerak bo'lsa privacy'ni aniq o'chiring.) Botingizning joriy holatini
bot.botInfo.can_read_all_group_messagesko'rsatadi:false= privacy yoqilgan,true= o'chirilgan.
Buni offline qanday "his qilamiz"?¶
Privacy mode β Telegram serveri tomonidagi filtr (qaysi update botga umuman yuborilishini hal qiladi). Shuning uchun uni offline test bilan to'liq takrorlab bo'lmaydi β bizning soxta update'larimiz allaqachon botga "yetib kelgan". Lekin biz uning oqibatini modellashtira olamiz: privacy yoqilgan deb tasavvur qilsak, bizning testimizga faqat buyruq/mention/reply update'larini uzatamiz; o'chirilgan deb hisoblasak β oddiy matn update'ini ham. Bobning kodi privacy'ning ikkala holatiga ham to'g'ri ishlaydi (buyruqlar har doim keladi).
Illustrativ: privacy mode'ni haqiqatan his qilish uchun botni real guruhga qo'shib, oddiy gap yozib ko'ring β bot jim qolsa, privacy yoqilgan. So'ng
@BotFatherda Disable qilib, botni qayta qo'shing β endi har gapni ko'radi. Bu qadamlar token + internet talab qiladi.
| Belgisi | Sabab | Yechim |
|---|---|---|
Bot guruhda oddiy gaplarga javob bermaydi, lekin /start ishlaydi |
Privacy mode yoqilgan (standart) | Anti-spam kerak bo'lsa: @BotFather -> /setprivacy -> Disable, so'ng botni qayta qo'shing |
/qoidalar ham ishlamaydi |
Handler private ga moslangan yoki chatType filtri noto'g'ri |
bot.chatType(["group","supergroup"]).command(...) ishlating; faqat private qilmang |
| Privacy'ni Disable qildim, baribir ko'rmayapti | O'zgartirishdan keyin bot qayta qo'shilmagan | Botni guruhdan chiqarib, qayta qo'shing |
Bot guruhda /buyruq@boshqa_bot ga ham javob beradi |
Buyruq boshqa botga atalgan | grammY @username ni o'qiydi; o'zingiznikiga moslang yoki ignoreCommandsOf mantig'ini qo'shing |
5) A'zo va admin tekshirish: getChatMember¶
Guruh botining yana bir asosiy ehtiyoji β "bu odam guruh a'zosimi?", "bu odam adminmi?". Buning uchun getChatMember ishlatamiz.
ctx.getChatMember(userId) Telegram'dan ChatMember obyektini qaytaradi. Uning eng muhim maydoni β .status:
.status |
Ma'nosi | A'zomi? | Adminmi? |
|---|---|---|---|
"creator" |
Guruh egasi (yaratuvchi) | ha | ha |
"administrator" |
Admin | ha | ha |
"member" |
Oddiy a'zo | ha | yo'q |
"restricted" |
Cheklangan a'zo (mute va h.k. β 20-bob) | ha (guruhda) | yo'q |
"left" |
Chiqib ketgan / hech qachon bo'lmagan | yo'q | yo'q |
"kicked" |
Ban qilingan (chetlatilgan) | yo'q | yo'q |
A'zolik tekshiruvi¶
bot.chatType(["group", "supergroup"]).command("tekshir", async (ctx) => {
const m = await ctx.getChatMember(ctx.from.id);
const azoMi = ["creator", "administrator", "member", "restricted"].includes(m.status);
await ctx.reply(azoMi ? `Siz a'zosiz (${m.status}).` : `Siz a'zo emassiz (${m.status}).`);
});
Admin tekshiruvi¶
Admin = creator yoki administrator. Buni qayta-qayta ishlatadigan kichik yordamchi yozamiz:
async function isAdmin(ctx, userId = ctx.from.id) {
const m = await ctx.getChatMember(userId);
return m.status === "creator" || m.status === "administrator";
}
bot.chatType(["group", "supergroup"]).command("admincmd", async (ctx) => {
if (!(await isAdmin(ctx))) {
return ctx.reply("Bu buyruq faqat adminlar uchun.");
}
await ctx.reply("Salom, admin! Boshqaruv paneli ochilmoqda...");
});
administrator statusidagi ChatMember obyektida qo'shimcha huquq maydonlari ham bo'ladi: can_delete_messages, can_restrict_members, can_pin_messages va h.k. Botning o'zi admin bo'lsa, qaysi amallarni qila olishini shu maydonlardan bilib olasiz (20-bobda ban/mute uchun kerak):
const me = await ctx.getChatMember(ctx.me.id); // ctx.me β bot haqida ma'lumot
if (me.status === "administrator" && me.can_restrict_members) {
// bot a'zolarni cheklay oladi (mute/ban)
}
Eslatma β
ctx.getChatMembervsbot.api.getChatMember.ctx.getChatMember(userId)joriy chat uchun ishlaydi (chat_id nictxdan oladi) β guruh ichida shuni ishlating. Boshqa chatni tekshirish kerak bo'lsa (masalan majburiy obuna: foydalanuvchi kanalga obuna bo'lganmi?) βbot.api.getChatMember(channelId, userId)ni ishlating, chat_id ni o'zingiz berasiz. Buni 22-bobda batafsil ko'ramiz.
Butun admin ro'yxati: getChatAdministrators¶
Bitta odamni emas, barcha adminlarni olish kerak bo'lsa:
bot.chatType(["group", "supergroup"]).command("adminlar", async (ctx) => {
const admins = await ctx.getChatAdministrators();
const ro = admins
.map((a) => `${a.user.first_name} (${a.status})`)
.join("\n");
await ctx.reply(`Guruh adminlari:\n${ro}`);
});
getChatAdministrators ChatMember obyektlari massivini qaytaradi (har biri creator yoki administrator). Bu β adminlar ro'yxatini bir marta olib keshlash uchun qulay (har xabarda getChatMember chaqirmaslik uchun β keyingi eslatmaga qarang).
Anti-eskirish β
getChatMemberni har xabarda chaqirmang. Har bir guruh xabari uchungetChatMemberchaqirish β Telegram API'ga ortiqcha so'rov (sekin + rate-limit xavfi). Faol guruhlarda adminlar ro'yxatinigetChatAdministratorsbilan bir marta olib, sessiyada yoki xotirada (Map) qisqa muddatga keshlang, vaqti-vaqti bilan yangilang.my_chat_member/chat_memberevent'lari (quyida) o'zgarishni bildiradi β keshni o'shanda yangilash mumkin.
6) my_chat_member vs chat_member: botning statusi o'zgardi¶
Guruh boti uchun muhim moment β botning o'zi guruhga qo'shilganda, admin bo'lganda yoki chiqarilganda xabar olish. Buning uchun Telegram ikkita o'xshash, lekin farqli update beradi:
| Update | Nima haqida | Privacy/allowed_updates |
|---|---|---|
my_chat_member |
Botning O'Z statusi o'zgardi (qo'shildi, admin bo'ldi, chiqarildi) | Har doim keladi β alohida sozlama kerak emas |
chat_member |
Har qanday a'zoning statusi o'zgardi (kimdir qo'shildi/chiqdi/admin bo'ldi) | allowed_updates ga aniq qo'shilishi kerak (standart kelmaydi) |
Bu farq juda muhim: my_chat_member "men bilan nima bo'ldi", chat_member esa "guruhdagi har kim bilan nima bo'ldi".
my_chat_member β bot qo'shildi / admin bo'ldi / chiqarildi¶
bot.on("my_chat_member", async (ctx) => {
const oldStatus = ctx.myChatMember.old_chat_member.status;
const newStatus = ctx.myChatMember.new_chat_member.status;
const oldinChiqib = oldStatus === "left" || oldStatus === "kicked";
const endiIchda = newStatus === "member" || newStatus === "administrator";
// 1) Bot guruhga QO'SHILDI (chetda edi -> endi ichida)
if (oldinChiqib && endiIchda) {
await ctx.reply(
"Salom! Meni guruhga qo'shganingiz uchun rahmat. " +
"/qoidalar bilan boshlang. Spamni tozalashim uchun meni admin qiling."
);
return;
}
// 2) Bot ADMIN bo'ldi -> huquqlarni o'qiymiz
if (oldStatus === "member" && newStatus === "administrator") {
const m = ctx.myChatMember.new_chat_member;
await ctx.reply(
`Endi men adminman. Xabar o'chirish: ${m.can_delete_messages ? "ha" : "yo'q"}, ` +
`a'zo cheklash: ${m.can_restrict_members ? "ha" : "yo'q"}.`
);
return;
}
// 3) Bot CHIQARILDI (ichida edi -> endi chetda)
if (newStatus === "left" || newStatus === "kicked") {
// Bu yerda reply YUBORIB BO'LMAYDI β bot endi guruhda yo'q!
console.log(`Bot ${ctx.chat.id} guruhdan chiqarildi.`);
return;
}
});
E'tibor bering:
ctx.myChatMemberβ update'ning o'zi (ChatMemberUpdated). Uningold_chat_membervanew_chat_membermaydonlari eski va yangi holatni beradi β biz o'tishni (transition) ularni solishtirib aniqlaymiz.- 3-holatda reply yuborilmaydi β bot guruhdan chiqarilgan, demak u yerga xabar yubora olmaydi (
403xato bo'ladi). Faqat log yozamiz yoki bazada belgilaymiz. my_chat_memberuchun hech qanday qo'shimcha sozlama kerak emas β u har doim keladi.
chat_member β boshqa a'zolar statusi (qo'shimcha sozlama kerak)¶
Agar siz boshqa a'zolarning statusi o'zgarganini (kim qo'shildi, kim chiqdi, kim admin bo'ldi) chat_member orqali kuzatmoqchi bo'lsangiz, u standart kelmaydi β uni allowed_updates ro'yxatiga aniq qo'shishingiz kerak:
// Polling bilan (15/17-bob): runner yoki bot.start ga allowed_updates beramiz
bot.start({
allowed_updates: ["message", "callback_query", "my_chat_member", "chat_member"],
});
// Webhook bilan (13-bob): setWebhook'ga beriladi
// await bot.api.setWebhook(url, { allowed_updates: ["message", "chat_member", ...] });
Diqqat β
chat_memberni unutib qo'yish. Bu β eng nozik gotcha:bot.on("chat_member", ...)yozasiz, lekin handler hech qachon ishlamaydi, chunkiallowed_updatesga"chat_member"qo'shmagansiz. grammY xato bermaydi β update shunchaki kelmaydi. Yodda tuting:my_chat_member(bot haqida) avtomatik keladi,chat_member(boshqalar haqida) β yo'q. Bu bobda biz asosanmy_chat_memberva xizmat xabarlari (new_chat_members) bilan ishlaymiz;chat_memberilg'or moderatsiya uchun (20-bob).Eslatma β
allowed_updatesga qo'shsangiz, hammasini sanang.allowed_updatesni bersangiz, u butun ro'yxatni almashtiradi (standart "hammasi"ni emas). Ya'ni["chat_member"]desangiz, endimessageham kelmaydi! Shuning uchun ro'yxatga botingiz ishlatadigan barcha turlarni kiriting (yuqoridagi misol kabi).
7) new_chat_members / left_chat_member: kutib olish va xayrlashish¶
Botning eng mashhur guruh xususiyati β yangi a'zoni kutib olish. Buni my_chat_member (bu bot haqida) bilan ADASHTIRMANG. Yangi odam qo'shilganda Telegram xizmat xabari yuboradi β uning message.new_chat_members maydoni bor (massiv, chunki bir vaqtda bir nechta odam qo'shilishi mumkin):
// Yangi a'zo(lar) qo'shildi -> kutib olamiz
bot.on("message:new_chat_members", async (ctx) => {
const yangilar = ctx.message.new_chat_members
.filter((u) => !u.is_bot) // botlarni kutib olmaymiz
.map((u) => u.first_name)
.join(", ");
if (yangilar) {
await ctx.reply(`Xush kelibsiz, ${yangilar}! Guruh qoidalari: /qoidalar`);
}
});
// A'zo chiqib ketdi / chiqarildi -> xayrlashamiz
bot.on("message:left_chat_member", async (ctx) => {
const odam = ctx.message.left_chat_member;
if (!odam.is_bot) {
await ctx.reply(`${odam.first_name} bizni tark etdi. Xayr!`);
}
});
Muhim nuqtalar:
new_chat_membersβ massiv. Bir foydalanuvchi bir necha kishini birdan qo'shsa, hammasi shu massivda keladi. Shuning uchunmap/filterbilan ishlaymiz.- Botlarni filtrlash β
u.is_botbilan. Aks holda boshqa bot qo'shilganda ham "Xush kelibsiz" deysiz (ko'pincha keraksiz). Botning o'zi qo'shilganda esa bu xizmat xabari emas, balkimy_chat_memberkeladi (yoki ikkalasi) β shuning uchun botni filtrlash xavfsiz. - Filtr sintaksisi β
bot.on("message:new_chat_members", ...)(04-bobdagi "filter query").:new_chat_membersaynan shu maydonli xabarlarni qamraydi.
Eslatma β
new_chat_membersvschat_member.new_chat_membersβ guruhdagi xizmat xabari (chatda ko'rinadi: "X guruhga qo'shildi"). U privacy yoqilgan bo'lsa ham keladi (xizmat xabarlari istisno).chat_memberesa β alohida update turi, ko'proq ma'lumot beradi (kim taklif qildi, eski/yangi status), lekinallowed_updatestalab qiladi. Oddiy "salom" boti uchunnew_chat_membersyetarli va soddaroq.
8) Hammasini birga: kichik guruh boti (Composer bilan)¶
Endi yuqoridagilarni 09-bobdagi Composer bilan bitta modulga yig'amiz β guruhga xos hamma narsa bir joyda, va bot.use(...) bilan asosiy botga ulanadi.
// guruh.js β guruh moduli (Composer)
import { Composer } from "grammy";
export const guruh = new Composer();
// Modul ichidagi HAMMA handler faqat guruhlarga (har ikki tur):
const g = guruh.chatType(["group", "supergroup"]);
// admin yordamchisi
async function isAdmin(ctx, userId = ctx.from.id) {
const m = await ctx.getChatMember(userId);
return m.status === "creator" || m.status === "administrator";
}
// /qoidalar β hammaga
g.command("qoidalar", (ctx) =>
ctx.reply("Qoidalar:\n1. Hurmat.\n2. Spam yo'q.\n3. Reklama yo'q.", {
reply_parameters: { message_id: ctx.msg.message_id },
})
);
// /adminlar β hozirgi adminlar ro'yxati
g.command("adminlar", async (ctx) => {
const admins = await ctx.getChatAdministrators();
await ctx.reply(
"Adminlar:\n" + admins.map((a) => `- ${a.user.first_name} (${a.status})`).join("\n")
);
});
// /tozala β FAQAT admin (illustrativ: haqiqiy tozalash 20-bobda)
g.command("tozala", async (ctx) => {
if (!(await isAdmin(ctx))) return; // oddiy a'zoga jim
await ctx.reply("Tozalash boshlandi (illustrativ β to'liq moderatsiya 20-bobda).");
});
// Yangi a'zoni kutib olish
g.on("message:new_chat_members", async (ctx) => {
const yangilar = ctx.message.new_chat_members
.filter((u) => !u.is_bot)
.map((u) => u.first_name)
.join(", ");
if (yangilar) await ctx.reply(`Xush kelibsiz, ${yangilar}! /qoidalar`);
});
// Bot guruhga qo'shilganda o'zini tanishtirish
guruh.on("my_chat_member", async (ctx) => {
const o = ctx.myChatMember.old_chat_member.status;
const n = ctx.myChatMember.new_chat_member.status;
const chiqdi = o === "left" || o === "kicked";
const kirdi = n === "member" || n === "administrator";
if (chiqdi && kirdi) {
await ctx.reply("Meni qo'shganingiz uchun rahmat! /qoidalar bilan boshlang.");
}
});
// bot.js β asosiy
import { Bot } from "grammy";
import { guruh } from "./guruh.js";
const bot = new Bot(process.env.BOT_TOKEN);
bot.use(guruh); // guruh modulini ulaymiz
// Shaxsiy /start ham qo'shamiz (guruh moduli private'ni qamramaydi):
bot.chatType("private").command("start", (ctx) =>
ctx.reply("Salom! Meni guruhga qo'shing.")
);
bot.start();
Diqqat β
my_chat_membernigga emas,guruhga uladik. E'tibor bering,my_chat_memberniguruh.on(...)ga (ya'ni butun Composer'ga) uladim,g(=guruh.chatType([...])) ga emas. Sababi:my_chat_memberupdate'idactx.chat.typeguruh bo'ladi, demakgham ushlaydi β lekin men buni aniq ko'rsatish uchun ataylabguruhga uladim. Amalda ikkalasi ham ishlaydi (chunki bot guruhga qo'shilganda chat turi guruh). Agar kanalda ham kuzatmoqchi bo'lsangiz,g(faqat group/supergroup) cheklamasin uchun butun Composer'ga ulang.
Bob bo'yicha qisqacha xulosa¶
| Vazifa | grammY usuli | Eslatma |
|---|---|---|
| Suhbat turini bilish | ctx.chat.type |
private/group/supergroup/channel |
| Faqat guruhga handler | bot.chatType(["group","supergroup"]) |
DOIM ikkala tur |
| Guruhda kim yozdi | ctx.from (odam) vs ctx.chat (guruh) |
reply: reply_parameters |
| Bot oddiy gaplarni ko'rmaydi | Privacy mode yoqilgan | /setprivacy -> Disable + qayta qo'shish |
| A'zo/admin tekshirish | ctx.getChatMember(id).status |
creator/administrator = admin |
| Boshqa chatda a'zolik | bot.api.getChatMember(chatId, id) |
majburiy obuna β 22-bob |
| Barcha adminlar | ctx.getChatAdministrators() |
keshlang, har xabarda chaqirmang |
| Bot statusi o'zgardi | bot.on("my_chat_member", ...) |
avtomatik keladi |
| Boshqa a'zo statusi | bot.on("chat_member", ...) |
allowed_updates SHART |
| Yangi/ketgan a'zo | message:new_chat_members / message:left_chat_member |
botlarni filtrlang |
Keyingi 20-bobda bu poydevor ustiga moderatsiyani quramiz: banChatMember (ban), restrictChatMember (mute), xabar o'chirish, anti-spam β endi bot guruhni nafaqat kuzatadi, balki boshqaradi. Majburiy obuna (foydalanuvchini kanalga obuna bo'lishga undash) β 22-bobda. Python ekvivalenti β ../tgbot-python/19-guruhlar.md.
Mashqlar¶
Quyidagi mashqlarning ko'pi offline tekshiriladi β guruh handlerini qurib, soxta
Update'larnibot.handleUpdate(...)ga uzatib, transformer (bot.api.config.use(...)) bilan chiqqan chaqiruvlarniassertqiling.getChatMemberni offline qilish uchun transformerda shu metodga testChatMemberobyektini ({ status, user }) qaytaring. Buyruq update'igaentities:[{type:"bot_command",offset:0,length:N}]qo'shishni unutmang, guruh uchunchat:{ id:-1001, type:"supergroup", title:"..." }, callback uchuncallback_query.data. Soxta update yasovchilar (mkGroupText,mkPrivateText,mkMyChatMember,mkNewMembers) bob oxiridagi_verify_19.mjsda bor β yechimlarda ularni qayta yozmaymiz.
Oson¶
/qoidalarfaqat guruhda.bot.chatType(["group","supergroup"]).command("qoidalar", ...)yozing: guruh qoidalarini chiqarsin. Offline: guruh/qoidalar-> javob keladi; shaxsiy/qoidalar-> javob kelmaydi.- Suhbat turini aytadigan bot.
/qayerbuyrug'ictx.chat.typeni qaytarsin (masalan "Bu yer: supergroup"). Offline: guruh va shaxsiyda turli javob kelishini tasdiqlang. - Kim yozdi. Guruhda har matnga
Yozgan: ${ctx.from.first_name}deb javob bering,reply_parametersbilan o'sha xabarga reply qiling. Offline: javob matnida ism borligini vareply_parameters.message_idto'g'ri ekanini tasdiqlang. - Botlarni kutib olmaslik.
new_chat_membershandleridais_botlarni filtrlang. Offline: bitta odam + bitta bot qo'shilgan update yuboring, salom matnida faqat odam ismi borligini tasdiqlang.
O'rta¶
- Admin darvozasi.
isAdmin(ctx)yordamchisini yozing (getChatMember-> creator/administrator)./admincmdni faqat adminlarga ruxsat bering, oddiy a'zoga "faqat adminlar" deb javob bering. Offline: admin (statusadministrator) "salom, admin", oddiy (statusmember) "faqat adminlar" olishini tasdiqlang. - A'zolik tekshiruvi.
/menazomanbuyrug'i foydalanuvchi a'zo ekanini (creator/administrator/member/restricted) yoki yo'qligini (left/kicked) aytsin. Offline: transformerdamember-> "a'zosiz" emas;kicked-> "a'zo emassiz" ekanini tasdiqlang. - Adminlar ro'yxati.
/adminlargetChatAdministratorsbilan barcha adminlarniism (status)ko'rinishida chiqarsin. Offline: transformer 2 admin (creator + administrator) qaytarsin, javobda ikkalasi ham borligini tasdiqlang. - Bot qo'shilganda salom.
my_chat_memberhandlerida botleft -> membero'tganda guruhga salom yuboring. Offline:mkMyChatMember("left","member")yuboring,sendMessagechaqirilganini va matnida salom borligini tasdiqlang.
Qiyin¶
- Bot admin bo'ldi -> huquqlarni e'lon qiling.
my_chat_memberdamember -> administratoro'tishni ushlang vanew_chat_member.can_delete_messagesqiymatini xabarda ko'rsating. Offline:mkMyChatMember("member","administrator")(huquqlar bilan) yuboring, "O'chirish: ha" chiqishini tasdiqlang. (Maslahat:mkMyChatMemberadministrator holatida huquq maydonlarini qo'shadi.) - Bot chiqarildi -> reply YO'Q.
member -> kickedo'tganda xabar yubormang (bot guruhda yo'q!), faqat ichki ro'yxatga (massiv) yozing. Offline:mkMyChatMember("member","kicked")yuboring, hech qandaysendMessagechaqirilmaganini va massivga yozilganini tasdiqlang. - Privacy-mantiqdan mustaqil buyruq. Botingizning guruh buyrug'i (
/qoidalar) privacy yoqilganda ham ishlashini ko'rsatish uchun: faqat buyruq va mention update'larini uzating (oddiy matnni emas β go'yo privacy yoqilgan)./qoidalarjavob bersin, oddiy "salom" matni esa handleringizga umuman bormasligini (sizdamessage:texthandleri bo'lsa, u privacy ON da kelmagan deb tasavvur qiling) tushuntiring. Offline:/qoidalar-> javob; oddiy matn update'ini umuman yubormang va izohlang (privacy serverda filtrlaydi). - A'zolikni keshlash.
getChatAdministratorsnatijasiniMap(chatId -> {admins, vaqt}) da keshlang, 60 soniyada bir marta yangilang.isAdminshu keshdan o'qisin. Offline: ikki marta/admincmd(bitta chatda) yuboring,getChatAdministratorstransformerda aynan bir marta chaqirilganini (kesh ishladi) tasdiqlang. - Yangi a'zoga shaxsiy ogohlantirish. Yangi a'zo qo'shilganda guruhga salom yozing va yangi a'zoga shaxsiy (
ctx.api.sendMessage(user.id, ...)) xabar yuborib ko'ring; lekin foydalanuvchi botni bloklagan bo'lsa (GrammyError403) butun handler yiqilmasin (try/catch). Offline: transformer shaxsiysendMessageda xato tashlasin (reject), guruh salomi baribir yuborilganini tasdiqlang.
Yechimlar
Quyidagi yechimlar bob oxiridagi
_verify_19.mjsnaqshi bilan offline ishga tushiriladi:makeBot(memberFor)(transformer +botInfo;memberFor(userId)testChatMemberstatusini beradi),mkGroupText/mkPrivateText/mkMyChatMember/mkNewMembers/mkLeftMembersoxta update yasovchilar vasentTexts(calls)yordamchisi. Qisqartirish uchun bu yordamchilar takrorlanmaydi β faqat o'zgartirilgan/qo'shilgan qism ko'rsatiladi.
1-mashq yechimi¶
bot.chatType(["group", "supergroup"]).command("qoidalar", (ctx) =>
ctx.reply("Qoidalar:\n1. Hurmat.\n2. Spam yo'q.\n3. Reklama yo'q.")
);
// Offline:
await bot.handleUpdate(mkGroupText("/qoidalar")); // -> javob keladi
await bot.handleUpdate(mkPrivateText("/qoidalar")); // -> javob KELMAYDI
const t = sentTexts(calls);
assert.equal(t.length, 1);
assert.ok(t[0].includes("Qoidalar"));
bot.chatType(["group","supergroup"]) shaxsiy suhbatni qamramaydi β shuning uchun private dagi /qoidalar handlerga umuman yetmaydi.
2-mashq yechimi¶
bot.command("qayer", (ctx) => ctx.reply(`Bu yer: ${ctx.chat.type}`));
// Offline:
await bot.handleUpdate(mkGroupText("/qayer")); // -> "Bu yer: supergroup"
await bot.handleUpdate(mkPrivateText("/qayer")); // -> "Bu yer: private"
const t = sentTexts(calls);
assert.equal(t[0], "Bu yer: supergroup");
assert.equal(t[1], "Bu yer: private");
ctx.chat.type har qanday suhbatda mavjud β guruhda supergroup, shaxsiyda private.
3-mashq yechimi¶
bot.chatType(["group", "supergroup"]).on("message:text", (ctx) =>
ctx.reply(`Yozgan: ${ctx.from.first_name}`, {
reply_parameters: { message_id: ctx.msg.message_id },
})
);
// Offline:
await bot.handleUpdate(mkGroupText("salom hammaga", { fromId: 42 }));
const c = calls.find((x) => x.method === "sendMessage");
assert.ok(c.payload.text.includes("Yozgan: Ali"));
assert.deepEqual(c.payload.reply_parameters, { message_id: 1 });
ctx.from β yozgan odam, ctx.chat β guruh. reply_parameters javobni aniq xabarga bog'laydi (05-bob).
4-mashq yechimi¶
bot.on("message:new_chat_members", async (ctx) => {
const yangilar = ctx.message.new_chat_members
.filter((u) => !u.is_bot)
.map((u) => u.first_name)
.join(", ");
if (yangilar) await ctx.reply(`Xush kelibsiz, ${yangilar}!`);
});
// Offline:
await bot.handleUpdate(mkNewMembers([
{ id: 100, is_bot: false, first_name: "Hasan" },
{ id: 12345, is_bot: true, first_name: "GuruhBot" }, // bot -> filtrlanadi
]));
const t = sentTexts(calls);
assert.equal(t[0], "Xush kelibsiz, Hasan!"); // botning ismi yo'q
filter((u) => !u.is_bot) botlarni chiqarib tashlaydi β faqat odamlarni kutib olamiz.
5-mashq yechimi¶
async function isAdmin(ctx, userId = ctx.from.id) {
const m = await ctx.getChatMember(userId);
return m.status === "creator" || m.status === "administrator";
}
bot.chatType(["group", "supergroup"]).command("admincmd", async (ctx) => {
if (!(await isAdmin(ctx))) return ctx.reply("Bu buyruq faqat adminlar uchun.");
return ctx.reply("Salom, admin!");
});
// Offline (memberFor: 999 -> administrator, 777 -> member):
const { bot, calls } = makeBot((uid) => ({ status: uid === 999 ? "administrator" : "member" }));
await bot.handleUpdate(mkGroupText("/admincmd", { fromId: 999 }));
await bot.handleUpdate(mkGroupText("/admincmd", { fromId: 777 }));
const t = sentTexts(calls);
assert.equal(t[0], "Salom, admin!");
assert.equal(t[1], "Bu buyruq faqat adminlar uchun.");
getChatMember ning .status ini transformer beradi; isAdmin uni creator/administrator bilan solishtiradi.
6-mashq yechimi¶
bot.chatType(["group", "supergroup"]).command("menazoman", async (ctx) => {
const m = await ctx.getChatMember(ctx.from.id);
const azoMi = ["creator", "administrator", "member", "restricted"].includes(m.status);
await ctx.reply(azoMi ? `Siz a'zosiz (${m.status}).` : `Siz a'zo emassiz (${m.status}).`);
});
// Offline:
const { bot, calls } = makeBot((uid) => ({ status: uid === 777 ? "member" : "kicked" }));
await bot.handleUpdate(mkGroupText("/menazoman", { fromId: 777 }));
await bot.handleUpdate(mkGroupText("/menazoman", { fromId: 888 }));
const t = sentTexts(calls);
assert.equal(t[0], "Siz a'zosiz (member).");
assert.equal(t[1], "Siz a'zo emassiz (kicked).");
A'zolik = ["creator","administrator","member","restricted"] to'plamida bo'lish; left/kicked β tashqarida.
7-mashq yechimi¶
bot.chatType(["group", "supergroup"]).command("adminlar", async (ctx) => {
const admins = await ctx.getChatAdministrators();
await ctx.reply(admins.map((a) => `${a.user.first_name} (${a.status})`).join("\n"));
});
// Offline (makeBot transformer getChatAdministrators -> [creator, administrator]):
await bot.handleUpdate(mkGroupText("/adminlar"));
const t = sentTexts(calls);
assert.ok(t[0].includes("Ega (creator)"));
assert.ok(t[0].includes("Admin (administrator)"));
getChatAdministrators ChatMember massivini qaytaradi; har birida user va status bor.
8-mashq yechimi¶
bot.on("my_chat_member", async (ctx) => {
const o = ctx.myChatMember.old_chat_member.status;
const n = ctx.myChatMember.new_chat_member.status;
if ((o === "left" || o === "kicked") && (n === "member" || n === "administrator")) {
await ctx.reply("Meni qo'shganingiz uchun rahmat!");
}
});
// Offline:
await bot.handleUpdate(mkMyChatMember("left", "member"));
const t = sentTexts(calls);
assert.ok(t[0].includes("rahmat"));
old_chat_member/new_chat_member statuslarini solishtirib "chetda edi -> ichida" o'tishini aniqlaymiz.
9-mashq yechimi¶
bot.on("my_chat_member", async (ctx) => {
const nm = ctx.myChatMember.new_chat_member;
if (ctx.myChatMember.old_chat_member.status === "member" && nm.status === "administrator") {
await ctx.reply(`Admin bo'ldim. O'chirish: ${nm.can_delete_messages ? "ha" : "yo'q"}`);
}
});
// Offline (mkMyChatMember administrator holatida can_delete_messages: true qo'shadi):
await bot.handleUpdate(mkMyChatMember("member", "administrator"));
const t = sentTexts(calls);
assert.ok(t[0].includes("O'chirish: ha"));
administrator statusidagi ChatMember huquq maydonlarini (can_delete_messages va h.k.) o'z ichiga oladi β botning imkoniyatini shulardan bilamiz.
10-mashq yechimi¶
const chiqarilgan = [];
bot.on("my_chat_member", (ctx) => {
const n = ctx.myChatMember.new_chat_member.status;
if (n === "left" || n === "kicked") {
chiqarilgan.push(ctx.chat.id); // reply YO'Q β bot guruhda emas
}
});
// Offline:
await bot.handleUpdate(mkMyChatMember("member", "kicked"));
assert.equal(chiqarilgan.length, 1);
assert.equal(chiqarilgan[0], -1001);
assert.equal(sentTexts(calls).length, 0); // hech qanday xabar yuborilmadi
Bot chiqarilgach unga xabar yuborib bo'lmaydi (403) β shuning uchun faqat ichki ro'yxatga yozamiz. Test sendMessage umuman chaqirilmaganini tasdiqlaydi.
11-mashq yechimi¶
bot.chatType(["group", "supergroup"]).command("qoidalar", (ctx) => ctx.reply("Qoidalar..."));
// (faraziy) oddiy matn handleri β privacy ON da bunga update KELMAYDI:
bot.chatType(["group", "supergroup"]).on("message:text", (ctx) => ctx.reply("matn ko'rdim"));
// Offline β privacy YOQILGAN holatni modellaymiz: faqat BUYRUQ uzatamiz,
// oddiy matn update'ini umuman yubormaymiz (serverda filtrlanadi):
await bot.handleUpdate(mkGroupText("/qoidalar"));
// await bot.handleUpdate(mkGroupText("salom")); // <- privacy ON: bu update KELMAS edi
const t = sentTexts(calls);
assert.equal(t.length, 1);
assert.ok(t[0].includes("Qoidalar"));
Privacy mode β server tomonidagi filtr: u oddiy matnni botga umuman yubormaydi. Offline testimizda buni "o'sha update'ni uzatmaslik" bilan modellaymiz. Buyruq esa privacy ON da ham keladi β shuning uchun /qoidalar har doim ishlaydi. Anti-spam kerak bo'lsa privacy'ni Disable qilasiz (4-bo'lim).
12-mashq yechimi¶
const adminKesh = new Map(); // chatId -> { admins, vaqt }
const KESH_MS = 60_000;
async function getAdmins(ctx) {
const id = ctx.chat.id;
const hozir = Date.now();
const bor = adminKesh.get(id);
if (bor && hozir - bor.vaqt < KESH_MS) return bor.admins;
const admins = await ctx.getChatAdministrators();
adminKesh.set(id, { admins, vaqt: hozir });
return admins;
}
async function isAdmin(ctx, userId = ctx.from.id) {
const admins = await getAdmins(ctx);
return admins.some((a) => a.user.id === userId);
}
bot.chatType(["group", "supergroup"]).command("admincmd", async (ctx) => {
await ctx.reply((await isAdmin(ctx)) ? "Admin!" : "Admin emas.");
});
// Offline (transformer getChatAdministrators -> [{user:{id:1},status:creator}, {user:{id:2},...}]):
await bot.handleUpdate(mkGroupText("/admincmd", { fromId: 1 }));
await bot.handleUpdate(mkGroupText("/admincmd", { fromId: 1 }));
const chaqirildi = calls.filter((c) => c.method === "getChatAdministrators").length;
assert.equal(chaqirildi, 1); // ikki buyruq, lekin API aynan BIR marta (kesh)
Kesh Map da chat bo'yicha saqlanadi; 60 soniya ichida ikkinchi /admincmd API'ga bormaydi. Bu β rate-limit'dan saqlanishning oddiy usuli (5-bo'limdagi anti-eskirish eslatmasi). Vaqtni conversation ichida ishlatmasangiz, oddiy Date.now() yetadi.
13-mashq yechimi¶
import { GrammyError } from "grammy";
bot.on("message:new_chat_members", async (ctx) => {
const odamlar = ctx.message.new_chat_members.filter((u) => !u.is_bot);
const ismlar = odamlar.map((u) => u.first_name).join(", ");
if (ismlar) await ctx.reply(`Xush kelibsiz, ${ismlar}!`); // guruhga
// har bir yangi odamga shaxsiy xabar (bloklagan bo'lsa yiqilmasin)
for (const u of odamlar) {
try {
await ctx.api.sendMessage(u.id, "Guruhga xush kelibsiz! /qoidalar bilan tanishing.");
} catch (e) {
if (!(e instanceof GrammyError)) throw e; // 403 (blok) ni yutamiz
}
}
});
// Offline: transformer shaxsiy chatga (musbat id) sendMessage'da reject qilsin:
// if (method === "sendMessage" && payload.chat_id > 0) return Promise.reject(new GrammyError(...));
// Guruh salomi (manfiy chat_id) baribir yuboriladi.
const guruhSalomi = sentTexts(calls).some((t) => t.includes("Xush kelibsiz"));
assert.ok(guruhSalomi); // shaxsiy xato guruh salomini yiqitmadi
Foydalanuvchi botni hech qachon ochmagan yoki bloklagan bo'lsa, unga shaxsiy yozib bo'lmaydi (403 -> GrammyError). try/catch bilan buni yutamiz, aks holda bitta odam tufayli butun kutib olish handleri yiqiladi (16-bobdagi xato boshqaruvi).
Yo'l davomi. Endi botingiz guruhda yashaydi: o'zini tanishtiradi, a'zolarni kutib oladi, kim admin-u kim oddiy a'zo ekanini biladi va privacy mode tuzog'ini chetlab o'tadi. Bu β guruh botining poydevori. 20-bobda shu poydevor ustiga moderatsiyani quramiz:
banChatMember,restrictChatMember(mute), xabar o'chirish, anti-flood va so'z filtri β bot endi guruhni boshqaradi. So'ng 22-bobda majburiy obuna (bot.api.getChatMemberbilan kanal a'zoligini tekshirish) va undan keyin Mini App dunyosiga o'tamiz.
Offline tekshirish hisoboti¶
Bobdagi guruh handlerlarining butun mantig'i _verify_19.mjs da uchma-uch ishga tushirildi (grammY 1.43.0, Node v24, ESM). Soxta Update'lar bot.handleUpdate(...) ga uzatildi; chiqayotgan API chaqiruvlari transformer (bot.api.config.use(...)) bilan ushlandi; getChatMember/getChatAdministrators so'rovlariga transformer test uchun tayyorlangan ChatMember obyektlarini qaytardi (Telegram serveri haqiqatan shu shaklda javob beradi). Tarmoqqa chiqilmadi, token ishlatilmadi.
PASS: t1: bot.chatType guruh/shaxsiy ajratadi (ctx.chat.title)
PASS: t2: getChatMember .status -> a'zo/emas (member/left/kicked)
PASS: t3: getChatMember admin tekshiruvi (creator/administrator vs member)
PASS: t4: getChatAdministrators ro'yxat (creator + administrator)
PASS: t5: my_chat_member bot qo'shildi (left->member) -> salom xabar
PASS: t6: my_chat_member admin bo'ldi -> huquqlarni o'qish (can_delete_messages)
PASS: t7: my_chat_member bot chiqarildi (member->kicked) -> log, reply yo'q
PASS: t8: new_chat_members (botni filtrlash) / left_chat_member
PASS: t9: ctx.from (yozgan) vs ctx.chat (guruh) + reply_parameters
PASS: t10: Composer guruh moduli + admin darvozasi (oddiy -> jim)
HAMMASI O'TDI: 10/10
Tasdiqlangan xulosalar: (1) bot.chatType(["group","supergroup"]) guruhlarni qamraydi va private ni o'tkazib yuboradi (aksincha ham); (2) ctx.getChatMember(id) qaytargan .status orqali a'zolik (member/restricted/creator/administrator = a'zo; left/kicked = emas) va adminlik (creator/administrator) to'g'ri aniqlanadi; (3) getChatAdministrators butun admin ro'yxatini beradi; (4) my_chat_member botning o'z statusi o'tishlarini (qo'shildi left->member, admin bo'ldi member->administrator huquqlar bilan, chiqarildi member->kicked β bu holatda xabar yuborilmaydi) to'g'ri ushlaydi; (5) new_chat_members/left_chat_member yangi/ketgan a'zolarni beradi va botlar is_bot bilan filtrlanadi; (6) ctx.from (yozgan odam) ctx.chat (guruh) dan ajratiladi va reply_parameters reply'ni biriktiradi; (7) Composer guruh moduli + admin darvozasi oddiy a'zoga jim qoladi. Jonli qism β botni real guruhga qo'shish, real a'zo statusini Telegram'dan olish, haqiqiy my_chat_member event'lari va privacy mode'ni guruhda his qilish β token + internet talab qiladi, shuning uchun illustrativ deb belgilangan.
β¬ οΈ Oldingi: 18 β Yakuniy kapston: to'liq bot Β· π README Β· Keyingi: 20 β Guruh moderatsiyasi β‘οΈ