Tarkibga o'tish

19 β€” Guruhlarda ishlash

⬅️ Oldingi: 18 β€” Yakuniy kapston: to'liq bot Β· 🏠 README Β· Keyingi: 20 β€” Guruh moderatsiyasi ➑️


Bu bobda: Hozirgacha botimiz asosan shaxsiy chatda (private) ishladi β€” bitta odam botga yozadi, bot javob beradi. Endi botni guruhga olib chiqamiz. Avval chat turlarini ajratamiz: private, group, supergroup (va keyingi boblarda channel) β€” ularning farqi va id'lari qanday. So'ng eng muhim, boshlovchini ko'p chalg'itadigan narsa β€” privacy mode (@BotFather /setprivacy): standart holatda bot guruhda HAR xabarni emas, faqat buyruq, reply va mention'larni ko'radi; nega ba'zan bot "indamay turadi" β€” javob shu yerda. Keyin guruhga xos buyruqlar yozamiz (/rules, /id) va ularni chat turi bo'yicha filtrlaymiz, ya'ni shaxsiy chatda bir, guruhda boshqacha ishlaydi. Markaziy texnik mavzu β€” getChatMember($chatId, $userId): u ChatMember qaytaradi, undagi ->status (ChatMemberStatus enum: CREATOR, ADMINISTRATOR, MEMBER, RESTRICTED, LEFT, KICKED) orqali foydalanuvchi a'zomi, adminmi yoki chiqib ketganmi β€” buni biz qo'lda aniqlaymiz va admin-only buyruqlarni shunga qarab himoyalaymiz. Nihoyat onMyChatMember bilan bot guruhga qo'shilgan / admin qilingan / chiqarilgan lahzalarini ushlaymiz va guruh ichidagi bot huquqlarini tushunamiz. Ban/restrict/pin kabi haqiqiy moderatsiya amallari β€” keyingi, 20-bobda.

Halol eslatma: Bobning BARCHA mantig'i β€” chat-turi filtri (isGroup), a'zolik/admin tekshiruvi (isMember/isAdmin orqali getChatMember status'ini o'qish) va onMyChatMember routing'i β€” Nutgram::fake() (FakeNutgram) bilan tokensiz, tarmoqsiz haqiqatan ishga tushirib tekshirilgan: getChatMember javobini willReceive([...]) bilan mock qilib, admin/a'zo/chiqqan tarmoqlarini sinadik β€” 9 ta PHPUnit testi, 16 ta assert, hammasi o'tdi (Nutgram 4.46, PHP 8.4, PHPUnit 12). Jonli qism (bot guruhda real xabar yuborishi, real pin/ban, telefonda guruhga qo'shish) β€” illustrativ deb belgilangan; hech qayerda soxta "bot guruhga yozdi" yozilmagan.


Chat turlari: private, group, supergroup

Har bir update'da bot qaysi chatdan kelganini biladi. $bot->chat() Chat obyektini qaytaradi, undagi ->type esa ChatType enum (SergiX44\Nutgram\Telegram\Properties\ChatType):

type Nima id ko'rinishi
ChatType::PRIVATE Shaxsiy chat (bitta odam ↔ bot) musbat, foydalanuvchi id'siga teng
ChatType::GROUP Oddiy guruh (kichik, ~200 a'zogacha) manfiy, masalan -123456789
ChatType::SUPERGROUP Katta guruh (moderatsiya, topiclar) -100 bilan boshlanadi, masalan -1001234567890
ChatType::CHANNEL Kanal (bir tomonlama) -100... β€” 21-bob

Guruh turlari va privacy mode

Eng birinchi bilish kerak bo'lgan amaliy buyruq β€” /id: u joriy chatning turini va id'sini qaytaradi. Bu siz uchun keyinchalik (admin guruhini .envga yozish, broadcast qilish) kerak bo'ladi:

<?php
use SergiX44\Nutgram\Nutgram;
use SergiX44\Nutgram\Telegram\Properties\ChatType;

$bot->onCommand('id', function (Nutgram $bot) {
    $type = $bot->chat()?->type;
    // ChatType enum -> string qiymati ('private' / 'group' / 'supergroup')
    $typeStr = $type instanceof ChatType ? $type->value : (string) $type;

    $bot->sendMessage("Chat turi: {$typeStr}\nChat id: {$bot->chatId()}\nSizning id: {$bot->userId()}");
});

group β†’ supergroup. Telegram oddiy groupni, a'zo ko'paygach yoki admin sozlama o'zgartirgach, avtomatik supergroupga aylantiradi. Bunda chat id ham o'zgaradi (-123456 β†’ -100...). Shu sababli guruh id'sini doim eng so'nggi update'dan oling, eskini "qotirib" saqlamang. Real moderatsiya (ban, restrict, pin, topiclar) faqat supergroupda to'liq ishlaydi.

PHP eslatma: Bu bob siz PHP'ni bilasiz deb faraz qiladi β€” enum, match, in_array, null-safe ?-> (../php/README.md). Biz Telegram/Nutgram'ga xos narsalarni tushuntiramiz, PHP asoslarini qayta o'rgatmaymiz.


Privacy mode β€” nega bot guruhda "indamaydi"?

Bu β€” boshlovchilar duch keladigan eng katta "nega ishlamayapti" muammosi. Botni guruhga qo'shasiz, oddiy xabar yozasiz β€” bot javob bermaydi. Sabab: privacy mode.

Standart holatda (privacy ENABLED) bot guruhda HAR xabarni emas, faqat quyidagilarni ko'radi:

  • buyruqlar: /start, /rules (va /rules@mening_botim ko'rinishi);
  • botning xabariga reply qilingan xabarlar;
  • bot @mention qilingan xabarlar;
  • service xabarlar (a'zo qo'shildi/chiqdi, va h.k.).

Oddiy "salom hammaga" tipidagi xabarlarni bot ko'rmaydi. Bu β€” maxfiylik va samaradorlik uchun maxsus qilingan: aks holda har bir guruh xabari botga yuborilib, ortiqcha yuk va maxfiylik muammosi bo'lardi.

Privacy holatini @BotFather orqali o'zgartirasiz:

@BotFather -> /setprivacy -> botni tanlang -> Disable yoki Enable

  Enable   (standart) β€” bot faqat buyruq/reply/mention'ni ko'radi
  Disable           β€” bot guruhdagi HAMMA xabarni ko'radi

Privacy'ni Disable qilish kerak bo'ladigan holatlar: anti-spam, har xabarni tekshiruvchi moderatsiya, kalit so'zga reaksiya. Aks holda Enable qoldiring β€” bu xavfsizroq va tezroq.

Muhim nozik nuqta: privacy sozlamasini o'zgartirgach, u eski guruhlarda darhol kuchga kirmaydi β€” botni guruhdan chiqarib, qayta qo'shish kerak. Yana: agar bot guruhda admin bo'lsa, u privacy holatidan qat'i nazar HAMMA xabarni ko'radi. Ya'ni "botni admin qilish" ham privacy'ni amalda o'chiradi.

Privacy ENABLED bo'lsa ham buyruqlar har doim yetib keladi, shuning uchun guruh botini odatda buyruqlar asosida quramiz:

<?php
use SergiX44\Nutgram\Nutgram;

// Privacy ENABLED bo'lsa ham bu yetib keladi (chunki bu buyruq):
$bot->onCommand('rules', function (Nutgram $bot) {
    $bot->sendMessage('Guruh qoidalari:\n1. Hurmatli bo\'ling.\n2. Spam yo\'q.');
});

// Bu esa privacy ENABLED holatda guruhda ODATDA yetib KELMAYDI
// (faqat botga reply / @mention bo'lsa keladi):
$bot->onText('/salom (.+)/', function (Nutgram $bot, string $kim) {
    $bot->sendMessage("Salom, {$kim}!");
});

Halol eslatma: privacy mode'ni kodda sinab bo'lmaydi β€” u @BotFather sozlamasi va jonli Telegram serverida hal bo'ladi (Telegram qaysi update'ni botga yuborishini hal qiladi). Biz buni illustrativ deb belgilaymiz. Kodimiz esa to'g'ri: yetib kelgan har bir update'ni biz to'g'ri yo'naltiramiz β€” buni offline tekshiramiz.


Buyruqni chat turiga qarab filtrlash

Bir xil buyruq shaxsiy chatda va guruhda turlicha ishlashi mumkin β€” yoki umuman faqat bittasida ishlashi kerak. Buni handler ichida $bot->chat()?->type ni tekshirib qilamiz. Avval qulay yordamchi:

<?php
use SergiX44\Nutgram\Nutgram;
use SergiX44\Nutgram\Telegram\Properties\ChatType;

/** Joriy update guruh yoki supergruhdanmi? */
function isGroup(Nutgram $bot): bool
{
    $type = $bot->chat()?->type;
    return $type === ChatType::GROUP || $type === ChatType::SUPERGROUP;
}

Endi buyruqni guruhga "qulflaymiz":

<?php
$bot->onCommand('rules', function (Nutgram $bot) {
    if (!isGroup($bot)) {
        $bot->sendMessage('Bu buyruq faqat guruhda ishlaydi. Botni guruhga qo\'shing.');
        return;
    }
    $bot->sendMessage('Guruh qoidalari: hurmatli bo\'ling, spam yo\'q.');
});

Yoki aksincha β€” buyruq faqat shaxsiy chatda ishlasin (masalan, sozlamalar menyusi):

<?php
$bot->onCommand('sozlama', function (Nutgram $bot) {
    if (isGroup($bot)) {
        $bot->sendMessage('Sozlamalarni men bilan shaxsiy chatda o\'zgartiring.');
        return;
    }
    // ... shaxsiy chat menyusi
});

Middleware bilan ham bo'ladi. Agar guruh-only filtrni ko'p handlerga qo'llamoqchi bo'lsangiz, 9-bobdagi middleware'ga ajrating: $bot->middleware(function (Nutgram $bot, $next) { if (!isGroup($bot)) return; $next($bot); }) β€” yoki butun guruh handlerlarini $bot->group(...) ichiga oling. Bu bobda soddalik uchun handler ichida tekshiramiz.

getChatMember status oqimi: a'zo va admin tekshiruvi


A'zolik va adminni tekshirish: getChatMember

Telegram bizga "bu odam guruh a'zosimi?" degan savolga to'g'ridan-to'g'ri javob beradigan flag bermaydi. Buning o'rniga getChatMember($chatId, $userId) ni chaqiramiz β€” u shu chatdagi shu foydalanuvchining ChatMember obyektini qaytaradi. Eng muhim maydon β€” ->status, u ChatMemberStatus enum (SergiX44\Nutgram\Telegram\Properties\ChatMemberStatus):

ChatMemberStatus Ma'no A'zomi? Adminmi?
CREATOR Guruh egasi (creator) βœ… βœ…
ADMINISTRATOR Tayinlangan admin βœ… βœ…
MEMBER Oddiy a'zo βœ… ❌
RESTRICTED Cheklangan a'zo (hali guruhda) βœ… ❌
LEFT Chiqib ketgan ❌ ❌
KICKED Ban qilingan (chiqarilgan) ❌ ❌

Buning ustiga ikkita yordamchi quramiz:

<?php
use SergiX44\Nutgram\Nutgram;
use SergiX44\Nutgram\Telegram\Properties\ChatMemberStatus;

/** Foydalanuvchi guruhda admin (creator yoki administrator)mi? */
function isAdmin(Nutgram $bot, int $chatId, int $userId): bool
{
    $member = $bot->getChatMember($chatId, $userId);
    return in_array($member?->status, [
        ChatMemberStatus::CREATOR,
        ChatMemberStatus::ADMINISTRATOR,
    ], strict: true);
}

/** Foydalanuvchi guruhda (a'zo)mi β€” chiqib ketmaganmi/banlanmaganmi? */
function isMember(Nutgram $bot, int $chatId, int $userId): bool
{
    $member = $bot->getChatMember($chatId, $userId);
    return in_array($member?->status, [
        ChatMemberStatus::CREATOR,
        ChatMemberStatus::ADMINISTRATOR,
        ChatMemberStatus::MEMBER,
        ChatMemberStatus::RESTRICTED,
    ], strict: true);
}

in_array(..., strict: true) β€” enum'larni qat'iy (===) solishtiradi; bu enum bilan to'g'ri usul. $member?->status β€” null-safe: agar getChatMember null qaytarsa (xato), tekshiruv false bo'ladi.

Admin-only buyruqni himoyalash

Endi guruhda faqat admin bajara oladigan buyruq yozamiz. getChatMember chaqiruvchini ($bot->userId()) o'sha guruhda ($bot->chatId()) tekshiradi:

<?php
$bot->onCommand('pin', function (Nutgram $bot) {
    if (!isGroup($bot)) {
        $bot->sendMessage('Bu buyruq faqat guruhda ishlaydi.');
        return;
    }
    if (!isAdmin($bot, $bot->chatId(), $bot->userId())) {
        $bot->sendMessage('Faqat adminlar bu buyruqdan foydalanadi.');
        return;
    }
    // Haqiqiy pin amali β€” keyingi bobda; hozir illustrativ:
    $bot->sendMessage('Xabar mahkamlandi (illustrativ β€” real pin uchun bot admin bo\'lishi va pinMessage chaqirilishi kerak).');
});

Tezlik nuqtai nazaridan. Har buyruqda getChatMember β€” bu alohida Telegram API so'rovi (tarmoq). Faol guruhda buni cachelash mantiqan to'g'ri: admin ro'yxati kam o'zgaradi, shuning uchun natijani qisqa muddatga (masalan, 5 daqiqa) saqlash mumkin β€” $bot->setGlobalData($key, $val, ttl: 300) (Nutgram'ning o'rnatilgan global storage'i, TTL bilan) yoki PSR-16 cache (11-bob). Bu bobda biz har safar so'raymiz (soddalik uchun), lekin production'da cache'ni eslang.

getChatMemberCount. A'zolar soni kerak bo'lsa, alohida metod bor: $bot->getChatMemberCount($chatId) β€” bitta son qaytaradi. Bu butun ro'yxatni olib bermaydi (Telegram botga to'liq a'zolar ro'yxatini bermaydi β€” faqat har bir a'zoni id bo'yicha alohida so'rash mumkin).


Bot guruhga qo'shilganda: onMyChatMember

Botning guruhdagi o'z maqomi o'zgarganda (qo'shildi, admin qilindi, oddiy a'zoga tushirildi, chiqarildi) Telegram my_chat_member update yuboradi. Uni onMyChatMember bilan ushlaymiz. Bu β€” guruhga qo'shilganda salomlashish, sozlamalar yozish yoki "menga admin huquqi bering" deb so'rash uchun ideal joy.

Handler ichida $bot->chatMember() ChatMemberUpdated obyektini qaytaradi (u my_chat_memberni ham, chat_memberni ham qoplaydi). Undagi maydonlar:

  • ->chat β€” qaysi guruh (Chat);
  • ->old_chat_member->status β€” oldingi maqom;
  • ->new_chat_member->status β€” yangi maqom;
  • ->from β€” kim o'zgartirdi (odatda guruh admini).

onMyChatMember: botning guruhdagi maqomi o'zgarishi

<?php
use SergiX44\Nutgram\Nutgram;
use SergiX44\Nutgram\Telegram\Properties\ChatMemberStatus;

$bot->onMyChatMember(function (Nutgram $bot) {
    $upd = $bot->chatMember();              // ChatMemberUpdated
    if ($upd === null) {
        return;
    }

    $newStatus = $upd->new_chat_member->status;
    // status string bo'lib kelgan ekstremal holatga moslab enum'ga keltiramiz:
    $newStatus = $newStatus instanceof ChatMemberStatus
        ? $newStatus
        : ChatMemberStatus::tryFrom((string) $newStatus);

    $chatId = $upd->chat->id;

    match (true) {
        // Botni guruhga oddiy a'zo qilib qo'shishdi:
        $newStatus === ChatMemberStatus::MEMBER => $bot->sendMessage(
            'Salom! Meni qo\'shganingiz uchun rahmat. /rules bilan qoidalarni ko\'ring.',
            chat_id: $chatId,
        ),
        // Botga admin huquqlari berildi:
        $newStatus === ChatMemberStatus::ADMINISTRATOR => $bot->sendMessage(
            'Admin huquqlari berildi. Endi guruhni moderatsiya qila olaman.',
            chat_id: $chatId,
        ),
        // Botni chiqarib yuborishdi / banlashdi β€” log/tozalash (xabar yuborib bo'lmaydi):
        in_array($newStatus, [ChatMemberStatus::LEFT, ChatMemberStatus::KICKED], true) => null,
        default => null,
    };
});

Nega chat_id: ni aniq beramiz? onMyChatMember ichida joriy "javob beriladigan" chat har doim ham to'g'ri aniqlanmaydi (bu message emas, maqom o'zgarishi update'i). Shuning uchun sendMessage(...)da chatni $upd->chat->id dan aniq uzatamiz β€” bu ishonchli.

Foydalanuvchilar guruhga qo'shilganda? onMyChatMember β€” botning O'ZI haqida. Boshqa foydalanuvchilarning maqomi o'zgarishini kuzatish uchun onChatMember ishlatiladi (bot guruhda admin bo'lishi va Telegram'da chat_member update'i yoqilgan bo'lishi kerak). Yangi a'zoni kutib olish (welcome), ketganni kuzatish β€” 20-bobda.


Bot guruh adminmi va qanday huquqlarga ega?

Bot guruhni moderatsiya qilishi (xabar o'chirish, pin, ban) uchun u admin bo'lishi va kerakli huquqlarga ega bo'lishi shart. Buni botning o'zini getChatMember'da tekshirib bilamiz β€” qaytgan ChatMemberAdministrator obyektida can_delete_messages, can_restrict_members, can_pin_messages kabi bool maydonlar bor:

<?php
use SergiX44\Nutgram\Nutgram;
use SergiX44\Nutgram\Telegram\Properties\ChatMemberStatus;
use SergiX44\Nutgram\Telegram\Types\Chat\ChatMemberAdministrator;

/** Botning o'zi guruhda restrict (ban/cheklash) qila oladimi? */
function botCanRestrict(Nutgram $bot, int $chatId): bool
{
    $botId  = $bot->getMe()?->id;            // botning user id'si
    $member = $bot->getChatMember($chatId, $botId);

    if ($member?->status !== ChatMemberStatus::ADMINISTRATOR) {
        return false;                         // umuman admin emas
    }
    // Admin bo'lsa ham, aniq huquq kerak:
    return $member instanceof ChatMemberAdministrator
        && $member->can_restrict_members === true;
}

getChatMember ChatMemberning aniq pastki turini qaytaradi: admin uchun β€” ChatMemberAdministrator (huquqlar maydonlari bilan), oddiy a'zo uchun β€” ChatMemberMember, va h.k. Shuning uchun huquqni o'qishdan oldin instanceof ChatMemberAdministrator bilan tekshiramiz.

Halol eslatma: botning huquqlarini jonli guruh belgilaydi β€” uni @BotFather/Telegram admin paneli orqali berasiz. Bu yerdagi kod to'g'ri (getChatMember + instanceof + huquq maydoni), lekin jonli ban/pin amallari 20-bobning mavzusi va ular real bot adminligini talab qiladi β€” illustrativ.


Offline test: routing va a'zolik tekshiruvi (FakeNutgram)

Endi yuqoridagi mantiqning hammasini tokensiz, tarmoqsiz tekshiramiz. Hiyla: getChatMember ham Telegram API so'rovi β€” FakeNutgram'da uning javobini willReceive([...]) bilan oldindan "qo'yamiz", guruh xabarini esa hearMessage([... 'chat' => [...]]) bilan yuboramiz. onMyChatMember uchun hearUpdateType(UpdateType::MY_CHAT_MEMBER, [...]).

Muhim nozik nuqta (test). Handler ichida avval getChatMember chaqirilsa, u FakeNutgram tarixida 0-o'rinni egallaydi, sendMessage esa 1-o'rinda bo'ladi. Shuning uchun assertReplyText('...', index: 1) deb aniq indeks beramiz β€” aks holda assert getChatMemberni tekshirib, xato beradi. Bu (16-bobdan) β€” har bir bot chaqiruvi tarixga yoziladi.

<?php
namespace App\Tests;

use PHPUnit\Framework\TestCase;
use SergiX44\Nutgram\Nutgram;
use SergiX44\Nutgram\Telegram\Properties\UpdateType;

final class GroupTest extends TestCase
{
    private function bot(): Nutgram
    {
        $bot = Nutgram::fake();
        registerHandlers($bot);   // /id, /rules, /pin, onMyChatMember β€” yuqoridagi kod
        return $bot;
    }

    public function test_rules_guruhda_ishlaydi(): void
    {
        $bot = $this->bot();
        $bot->hearMessage([
            'text' => '/rules',
            'chat' => ['id' => -100123, 'type' => 'group', 'title' => 'Test'],
        ])->reply();
        $bot->assertReplyText('Guruh qoidalari: hurmatli bo\'ling.');
    }

    public function test_rules_shaxsiy_chatda_rad_etiladi(): void
    {
        $bot = $this->bot();
        $bot->hearMessage([
            'text' => '/rules',
            'chat' => ['id' => 555, 'type' => 'private'],
        ])->reply();
        $bot->assertReplyText('Bu buyruq faqat guruhda ishlaydi.');
    }

    public function test_pin_admin_uchun_ishlaydi(): void
    {
        $bot = $this->bot();
        // getChatMember -> administrator (mock):
        $bot->willReceive([
            'status' => 'administrator',
            'user' => ['id' => 42, 'is_bot' => false, 'first_name' => 'Ali'],
            'can_be_edited' => false, 'is_anonymous' => false,
            'can_manage_chat' => true, 'can_delete_messages' => true,
            'can_manage_video_chats' => true, 'can_restrict_members' => true,
            'can_promote_members' => false, 'can_change_info' => true,
            'can_invite_users' => true,
        ]);
        $bot->hearMessage([
            'from' => ['id' => 42, 'is_bot' => false, 'first_name' => 'Ali'],
            'text' => '/pin',
            'chat' => ['id' => -100123, 'type' => 'supergroup', 'title' => 'Test'],
        ])->reply();
        // 0 = getChatMember, 1 = sendMessage:
        $bot->assertReplyText('Xabar mahkamlandi (illustrativ).', index: 1);
    }

    public function test_pin_oddiy_azo_uchun_rad_etiladi(): void
    {
        $bot = $this->bot();
        $bot->willReceive([
            'status' => 'member',
            'user' => ['id' => 77, 'is_bot' => false, 'first_name' => 'Vali'],
        ]);
        $bot->hearMessage([
            'from' => ['id' => 77, 'is_bot' => false, 'first_name' => 'Vali'],
            'text' => '/pin',
            'chat' => ['id' => -100123, 'type' => 'supergroup', 'title' => 'Test'],
        ])->reply();
        $bot->assertReplyText('Faqat adminlar bu buyruqdan foydalanadi.', index: 1);
    }

    public function test_bot_guruhga_qoshilganda_salomlashadi(): void
    {
        $bot = $this->bot();
        $bot->hearUpdateType(UpdateType::MY_CHAT_MEMBER, [
            'chat' => ['id' => -100999, 'type' => 'supergroup', 'title' => 'Yangi guruh'],
            'from' => ['id' => 1, 'is_bot' => false, 'first_name' => 'Egasi'],
            'date' => 1703892479,
            'old_chat_member' => [
                'status' => 'left',
                'user' => ['id' => 9, 'is_bot' => true, 'first_name' => 'MyBot'],
            ],
            'new_chat_member' => [
                'status' => 'member',
                'user' => ['id' => 9, 'is_bot' => true, 'first_name' => 'MyBot'],
            ],
        ])->reply();
        $bot->assertReplyText('Salom! Meni qo\'shganingiz uchun rahmat. /rules bilan qoidalarni ko\'ring.');
    }
}

Probe muhitida (Nutgram::fake() + PHPUnit) ishga tushirdik:

php vendor/bin/phpunit --bootstrap vendor/autoload.php tests/GroupTest.php
PHPUnit 12.5.29 by Sebastian Bergmann and contributors.
.........                                                           9 / 9 (100%)
Time: 00:00.071, Memory: 8.00 MB
OK (9 tests, 16 assertions)

Bu haqiqiy natija: chat-turi filtri, getChatMember status orqali admin/a'zo tekshiruvi va onMyChatMember routing'i tokensiz ishladi. Jonli qism (bot guruhda real xabar yuborishi, real pin/ban) β€” illustrativ.


Mashqlar

Oson

  1. /whereami buyrug'i yozing: agar chat private bo'lsa "Siz men bilan shaxsiy chatdasiz", aks holda "Biz '{guruh nomi}' guruhidamiz" deb javob bersin ($bot->chat()?->title).
  2. isGroup($bot) yordamchisini ishlating va /kun buyrug'ini faqat guruhda ishlaydigan qiling; shaxsiy chatda muloyim rad javobi bersin.
  3. ChatMemberStatus enum qiymatlarini sanab bering: qaysilari "a'zo" deb hisoblanadi, qaysilari yo'q? Jadval ko'rinishida yozing.
  4. Privacy mode ENABLED holatda guruhda bot qaysi xabarlarni ko'radi? 3 ta misol keltiring.
  5. getChatMemberCount($chatId) bilan /azolar buyrug'ini yozing β€” guruhdagi a'zolar sonini qaytarsin.
  6. /id buyrug'iga $bot->userId() ni ham qo'shing, shunda foydalanuvchi o'z Telegram id'sini ham ko'rsin.

O'rta

  1. isAdmin($bot, $chatId, $userId) yordamchisini yozing va /eslatma <matn> buyrug'ini faqat adminlar guruhga eslatma yuborishi mumkin qilib himoyalang.
  2. onMyChatMember yozing: bot guruhga admin qilib qo'shilsa "Tayyorman", oddiy a'zo qilib qo'shilsa "Iltimos, meni admin qiling" desin (new_chat_member->status ni tekshiring).
  3. FakeNutgram testi yozing: /eslatma ni oddiy a'zo yuborganda rad etilishini tekshiring (willReceive(['status' => 'member', ...]) + assertReplyText(..., index: 1)).
  4. botCanRestrict($bot, $chatId) ni getMe() va instanceof ChatMemberAdministrator bilan yozing; bot admin emas bo'lsa false qaytishini test qiling.
  5. Chat turini match bilan ifodalaydigan chatTuriNomi(ChatType $t): string funksiyasi yozing (private β†’ "Shaxsiy chat", group/supergroup β†’ "Guruh", channel β†’ "Kanal").
  6. Admin tekshiruvini middlewarega (9-bob) ajrating: faqat guruh adminlari o'tadigan EnsureGroupAdmin middleware yozing.

Qiyin

  1. Admin cache: getChatMember natijasini setGlobalData/getGlobalData (TTL bilan) yoki PSR-16 bilan 5 daqiqaga cache'lang, shunda har buyruqda tarmoqqa chiqilmasin. Kalitni "admin:{$chatId}:{$userId}" ko'rinishida tuzing. FakeNutgram bilan birinchi chaqiruv getChatMember qilishini, ikkinchisi cache'dan o'qishini tekshiring (assertCalled('getChatMember', 1)).
  2. onMyChatMember to'liq jurnal: bot guruhga qo'shilgan/chiqarilgan har bir hodisani (old β†’ new status, guruh nomi, kim o'zgartirdi) PDO jadvaliga (10-bob) yozadigan handler quring; :memory: SQLite bilan offline test yozing.
  3. /admins ro'yxati: guruh adminlarini ko'rsatadigan buyruq yozing. Diqqat β€” Telegram botga to'liq adminlar ro'yxatini getChatAdministrators orqali beradi (alohida metod); uni nutgram.dev hujjatidan tekshirib, qaytgan massivni formatlang va FakeNutgram'da willReceive([...]) bilan sinab ko'ring.
Yechimlar

Oson 1:

<?php
use SergiX44\Nutgram\Telegram\Properties\ChatType;

$bot->onCommand('whereami', function (\SergiX44\Nutgram\Nutgram $bot) {
    if ($bot->chat()?->type === ChatType::PRIVATE) {
        $bot->sendMessage('Siz men bilan shaxsiy chatdasiz.');
        return;
    }
    $bot->sendMessage("Biz '" . ($bot->chat()?->title ?? 'noma\'lum') . "' guruhidamiz.");
});

Oson 2: isGroup (matnda berilgan) bilan:

<?php
$bot->onCommand('kun', function (\SergiX44\Nutgram\Nutgram $bot) {
    if (!isGroup($bot)) {
        $bot->sendMessage('Bu buyruq faqat guruhda ishlaydi.');
        return;
    }
    $bot->sendMessage('Bugun ' . date('d.m.Y') . '.');
});

Oson 3:

Status A'zomi?
CREATOR βœ…
ADMINISTRATOR βœ…
MEMBER βœ…
RESTRICTED βœ… (hali guruhda)
LEFT ❌ (chiqib ketgan)
KICKED ❌ (banlangan)

Oson 4: Privacy ENABLED holatda bot ko'radi: (1) buyruqlar β€” /start, /rules; (2) botga reply qilingan xabar; (3) botni @mention qilgan xabar (yana service xabarlar β€” a'zo qo'shildi/chiqdi).

Oson 5:

<?php
$bot->onCommand('azolar', function (\SergiX44\Nutgram\Nutgram $bot) {
    if (!isGroup($bot)) { $bot->sendMessage('Faqat guruhda.'); return; }
    $n = $bot->getChatMemberCount($bot->chatId());
    $bot->sendMessage("Guruhda {$n} ta a'zo bor.");
});

Oson 6:

<?php
$bot->onCommand('id', function (\SergiX44\Nutgram\Nutgram $bot) {
    $t = $bot->chat()?->type;
    $ts = $t instanceof \SergiX44\Nutgram\Telegram\Properties\ChatType ? $t->value : (string) $t;
    $bot->sendMessage("Chat: {$ts} ({$bot->chatId()})\nSizning id: {$bot->userId()}");
});

O'rta 1: isAdmin matnda berilgan.

<?php
$bot->onText('/eslatma (.+)/', function (\SergiX44\Nutgram\Nutgram $bot, string $matn) {
    if (!isGroup($bot)) { $bot->sendMessage('Faqat guruhda.'); return; }
    if (!isAdmin($bot, $bot->chatId(), $bot->userId())) {
        $bot->sendMessage('Faqat adminlar eslatma yubora oladi.');
        return;
    }
    $bot->sendMessage("πŸ“Œ Eslatma: {$matn}");
});

O'rta 2:

<?php
use SergiX44\Nutgram\Telegram\Properties\ChatMemberStatus;

$bot->onMyChatMember(function (\SergiX44\Nutgram\Nutgram $bot) {
    $upd = $bot->chatMember();
    if ($upd === null) return;
    $new = $upd->new_chat_member->status;
    $new = $new instanceof ChatMemberStatus ? $new : ChatMemberStatus::tryFrom((string) $new);
    if ($new === ChatMemberStatus::ADMINISTRATOR) {
        $bot->sendMessage('Tayyorman.', chat_id: $upd->chat->id);
    } elseif ($new === ChatMemberStatus::MEMBER) {
        $bot->sendMessage('Iltimos, meni admin qiling.', chat_id: $upd->chat->id);
    }
});

O'rta 3:

<?php
public function test_eslatma_azo_uchun_rad_etiladi(): void
{
    $bot = Nutgram::fake();
    registerHandlers($bot);
    $bot->willReceive([
        'status' => 'member',
        'user' => ['id' => 77, 'is_bot' => false, 'first_name' => 'Vali'],
    ]);
    $bot->hearMessage([
        'from' => ['id' => 77, 'is_bot' => false, 'first_name' => 'Vali'],
        'text' => '/eslatma salom',
        'chat' => ['id' => -100123, 'type' => 'supergroup', 'title' => 'T'],
    ])->reply();
    $bot->assertReplyText('Faqat adminlar eslatma yubora oladi.', index: 1);
}

O'rta 4: botCanRestrict matnda berilgan. Test:

<?php
public function test_bot_admin_emas_restrict_qila_olmaydi(): void
{
    $bot = Nutgram::fake();
    $bot->willReceive(['id' => 9, 'is_bot' => true, 'first_name' => 'MyBot']); // getMe
    $bot->willReceive([
        'status' => 'member',
        'user' => ['id' => 9, 'is_bot' => true, 'first_name' => 'MyBot'],
    ]); // getChatMember
    $this->assertFalse(botCanRestrict($bot, -100123));
}

O'rta 5:

<?php
use SergiX44\Nutgram\Telegram\Properties\ChatType;

function chatTuriNomi(ChatType $t): string
{
    return match ($t) {
        ChatType::PRIVATE => 'Shaxsiy chat',
        ChatType::GROUP, ChatType::SUPERGROUP => 'Guruh',
        ChatType::CHANNEL => 'Kanal',
        default => 'Boshqa',
    };
}

O'rta 6:

<?php
use SergiX44\Nutgram\Nutgram;

$ensureGroupAdmin = function (Nutgram $bot, $next) {
    if (!isGroup($bot)) { $bot->sendMessage('Faqat guruhda.'); return; }
    if (!isAdmin($bot, $bot->chatId(), $bot->userId())) {
        $bot->sendMessage('Faqat adminlar.');
        return;
    }
    $next($bot);
};

$bot->onCommand('eslatma', function (Nutgram $bot) {
    $bot->sendMessage('Eslatma yuborildi (admin tasdiqlandi).');
})->middleware($ensureGroupAdmin);

Qiyin 1 (eskiz):

<?php
use SergiX44\Nutgram\Nutgram;
use SergiX44\Nutgram\Telegram\Properties\ChatMemberStatus;

function isAdminCached(Nutgram $bot, int $chatId, int $userId): bool
{
    $key = "admin:{$chatId}:{$userId}";
    $cached = $bot->getGlobalData($key);     // null bo'lsa hali cache'da yo'q
    if ($cached !== null) {
        return (bool) $cached;
    }
    $m = $bot->getChatMember($chatId, $userId);
    $isAdmin = in_array($m?->status, [ChatMemberStatus::CREATOR, ChatMemberStatus::ADMINISTRATOR], true);

    // TTL = 300 soniya (5 daqiqa) β€” keyin avtomatik eskiradi:
    $bot->setGlobalData($key, $isAdmin ? 1 : 0, ttl: 300);
    return $isAdmin;
}
Test: birinchi chaqiruvdan keyin assertCalled('getChatMember', 1), ikkinchisi cache'dan kelishini tekshiring (yana 1 marta β€” jami 1).

Qiyin 2 (eskiz): onMyChatMember ichida $upd->old_chat_member->status, $upd->new_chat_member->status, $upd->chat->title, $upd->from->id ni oling va PDO prepare(...)->execute([...]) bilan bot_events jadvaliga yozing. Test :memory: SQLite'da hearUpdateType(UpdateType::MY_CHAT_MEMBER, [...])->reply() dan keyin SELECT COUNT(*) ni tekshiradi.

Qiyin 3 (eskiz): Nutgram'da $bot->getChatAdministrators($chatId) β€” ChatMember[] qaytaradi (creator + adminlar). Har biridan ->user->first_name va ->status ni oling, ro'yxatni HTML formatda yuboring. FakeNutgram: willReceive([['status' => 'creator', 'user' => [...]], ['status' => 'administrator', 'user' => [...]]]) (massivlar massivi) bilan mock qiling. Aniq imzoni nutgram.dev/docs dan tasdiqlang.


⬅️ Oldingi: 18 β€” Yakuniy kapston: to'liq bot Β· 🏠 README Β· Keyingi: 20 β€” Guruh moderatsiyasi ➑️