Бот в группах: права и события
Учим «Цыплёнка-помощника» жить не в личке, а в общем чате: понимать, что ему можно видеть, какие права ему нужны и как ловить, кто вошёл в группу, а кто из неё ушёл.
Группа для бота — это совсем другой мир, чем личный диалог: здесь много людей, бот по умолчанию полуслепой и почти бесправный, а вместо «привет» и «пока» прилетают особые события —
ChatMemberUpdated, по которым бот узнаёт, что состав участников изменился.
В уроке про хэндлеры сообщений наш «Цыплёнок-помощник» научился отвечать на любые сообщения в личке: ты ему «привет» — он тебе «привет». Но если ты добавишь того же бота в общий чат с друзьями или в беседу игрового клана, тебя ждёт сюрприз: бот вдруг как будто оглохнет и перестанет реагировать на половину сообщений. Сегодня разберёмся, почему так происходит и как это починить.
Зачем боту вообще уметь работать в группах
Представь чат твоего класса на сотню человек. Каждый день туда вступают новенькие, кто-то выходит, кто-то кидает спам со ссылками на «бесплатные подписки». Сидеть и вручную всех приветствовать и чистить — скучно. А бот может: новичка встретить тёплым «Привет, читай закреп!», спамера — молча выкинуть, а на команду /правила прислать список правил чата.
Или чат игрового клана: бот ведёт список состава, и как только кто-то вышел из беседы — автоматически вычёркивает его из таблицы рейдов. Никто не забыт, никто не лишний.
Всё это бот делает, реагируя на два типа происходящего в группе: на сообщения (как в личке, только теперь их пишут много людей) и на события состава — кто-то вошёл, кто-то вышел, кого-то сделали админом. Вот к чему мы придём в этом уроке — бот, который встречает новичков и провожает ушедших:
[Маша вступила в группу «9Б чат»]
🐤 Привет, Маша! Добро пожаловать в чат. Загляни в закреп — там всё важное.
[Петя вышел из группы]
🐤 Петя покинул чат. Будет не хватать! 👋
Результат: как только в группу заходит новый участник, бот пишет ему персональное приветствие по имени; когда кто-то выходит — бот прощается. Всё происходит само, без единой команды от людей.
Группа — это не личка: бот в наушниках с шумоподавлением
Чтобы понять главную странность ботов в группах, держи в голове такую картину. В личном диалоге бот — как собеседник один на один: ты говоришь — он слышит каждое слово. А в группе бот по умолчанию надевает наушники с сильным шумоподавлением: вокруг гудит сотня человек, и бот специально не вслушивается в общий гул. Он «снимает наушники» и слышит сообщение только в двух случаях: если оно начинается с команды (например /start или /правила) или если это ответ на его собственное сообщение либо упоминание через @имя_бота.
Это поведение называется режимом приватности (privacy mode). Telegram включает его всем ботам по умолчанию — и это забота о людях, а не вредность. Представь, если бы каждый бот в чате читал вообще все сообщения подряд: твою переписку с друзьями молча сканировали бы десятки чужих программ. Поэтому Telegram говорит: «По умолчанию бот глухой к обычной болтовне, видит только то, что адресовано лично ему».
Именно из-за режима приватности твой бот «оглох» в группе. Хэндлер на обычный текст, который отлично работал в личке, в группе просто не получает эти сообщения — они до него не доходят. И тут у тебя есть выбор из двух путей.
Путь первый: оставить приватность включённой
Если боту достаточно реагировать на команды (/start, /правила, /погода) и на прямые упоминания — ничего менять не надо. Это самый аккуратный и уважительный к людям вариант. Большинству ботов в группах именно это и нужно: их зовут командой, они отвечают. Болтовню людей бот при этом не видит — и хорошо.
Путь второй: выключить приватность
Если боту нужно видеть все сообщения — например, антиспам-бот должен проверять каждое сообщение на ссылки, — режим приватности придётся выключить. Делается это не в коде, а в настройках бота у BotFather: команда /setprivacy, выбираешь своего бота и ставишь Disable. Запомни две важные детали:
- После выключения приватности бота надо заново добавить в группу (или удалить и добавить снова) — старые группы продолжат работать по старому режиму.
- Выключай приватность только если это действительно нужно. Бот, который читает каждое слово в чате, — большая ответственность.
Права бота в группе: гость или администратор
Со «слухом» разобрались. Теперь про права. Когда бот заходит в группу, он сначала обычный участник — такой же, как любой человек. Он может писать сообщения и читать команды, но не может ничего «администраторского»: удалять чужие сообщения, банить, закреплять посты, приглашать по ссылке.
Это как разница между гостем на вечеринке и хозяином квартиры. Гость может болтать и есть печеньки, но не может выгнать кого-то за дверь или переставить мебель. Чтобы бот мог «переставлять мебель» — модерировать чат, — его нужно сделать администратором группы и выдать конкретные права. Делается это людьми, вручную, через настройки группы: «Администраторы → Добавить администратора → выбрать бота → отметить галочками, что ему можно».
Права выдаются по отдельности, и это удобно: можно дать боту ровно столько власти, сколько нужно, и ни капли больше. Вот основные права, которые тебе встретятся:
| Право | Что позволяет боту |
| Удалять сообщения | Стирать чужие сообщения — основа любого антиспама |
| Блокировать участников | Кикать и банить нарушителей |
| Закреплять сообщения | Прикалывать важный пост наверх чата |
| Приглашать по ссылке | Создавать ссылки-приглашения в группу |
Главное правило: сначала права, потом код. Можно написать идеальный хэндлер, который банит спамеров, но если боту не выдали право «Блокировать участников», Telegram просто откажет — и бот получит ошибку вместо результата. Если бот «ничего не делает» в группе, первым делом проверь, что он администратор и нужная галочка стоит.
События состава: кто вошёл и кто вышел
Переходим к самому интересному — к событиям. Когда в группе кто-то вступает, выходит, банится или получает права администратора, Telegram присылает боту особое обновление. В личке такого не бывает — это чисто «групповая» история. Называется это обновление ChatMemberUpdated — дословно «участник чата изменился».
Вспомни из самых первых уроков: Update (обновление) — это единица входящих данных от Telegram. Обычно это сообщение или нажатая кнопка. А ChatMemberUpdated — это просто ещё один вид обновления, только рассказывает он не про текст, а про то, как изменился статус какого-то участника: был «не в группе» — стал «участник», был «участник» — стал «вышел».
Удобнее всего думать об этом как о турникете на входе в клуб. Турникет не слышит, о чём болтают внутри, — он фиксирует только одно: человек прошёл внутрь или вышел наружу, и кто именно. ChatMemberUpdated — это и есть сигнал от такого турникета.
Что лежит внутри события
В каждом ChatMemberUpdated есть два ключевых поля — было и стало:
event.old_chat_member.status— каким был статус участника до события;event.new_chat_member.status— каким он стал после.
Статус — это просто строка: "member" (участник), "left" (вышел), "kicked" (забанен), "administrator" (админ), "creator" (создатель чата). Сравнивая «было» и «стало», бот понимает, что именно произошло. Человек был не в группе, а стал "member" — значит, вступил. Был "member", а стал "left" — значит, вышел сам.
Давай на чистом Python, без всякого Telegram, прочувствуем эту логику «было → стало». Напишем маленькую функцию, которая по двум статусам решает, что случилось:
def describe_change(old_status, new_status):
if old_status in ("left", "kicked") and new_status == "member":
return "вступил в группу"
if old_status == "member" and new_status == "left":
return "вышел из группы"
if old_status == "member" and new_status == "kicked":
return "был забанен"
return "что-то поменялось"
print("Маша:", describe_change("left", "member"))
print("Петя:", describe_change("member", "left"))
print("Спамер:", describe_change("member", "kicked"))
Вывод:
Маша: вступил в группу Петя: вышел из группы Спамер: был забанен
Видишь приём? Мы не гадаем по одному значению, а смотрим пару «откуда → куда». Это и есть сердце обработки ChatMemberUpdated: вся разница между «вступил» и «вышел» — в том, как изменилась пара статусов. Ровно эту же логику мы сейчас перенесём в бота.
Ловим событие в боте: my_chat_member и chat_member
В aiogram есть два разных хэндлера для событий состава, и их легко перепутать — поэтому разберём оба сразу.
@dp.my_chat_member()— срабатывает, когда меняется статус самого бота: бота добавили в группу, выгнали, сделали админом. «My» — «мой собственный» статус.@dp.chat_member()— срабатывает, когда меняется статус любого другого участника: вошёл человек, вышел человек. Именно это нам нужно для приветствий.
Начнём с того, что бот узнаёт о своём собственном попадании в группу. Это полезно: можно сразу поздороваться с чатом и рассказать, что бот умеет.
from aiogram.types import ChatMemberUpdated
from aiogram.filters import ChatMemberUpdatedFilter, IS_NOT_MEMBER, IS_MEMBER
@dp.my_chat_member(ChatMemberUpdatedFilter(IS_NOT_MEMBER >> IS_MEMBER))
async def bot_added(event: ChatMemberUpdated):
await event.answer(
"\ud83d\udc24 Привет всем! Я Цыплёнок-помощник. "
"Напишите /правила, чтобы узнать правила чата."
)
Результат: как только бота добавляют в группу, он сам пишет в этот чат приветственное сообщение и подсказывает команду.
Здесь появилась новая штука — фильтр перехода ChatMemberUpdatedFilter. Это удобный помощник aiogram, чтобы не сравнивать статусы руками. Запись IS_NOT_MEMBER >> IS_MEMBER читается буквально как стрелка: «было — не участник, стало — участник», то есть «зашёл». Стрелка >> здесь — это «перешёл из состояния в состояние». aiogram сам разберёт «было → стало» за нас, и хэндлер сработает только на нужный переход.
Теперь — главное блюдо. Ловим вступление и выход других участников и шлём персональное приветствие/прощание:
from aiogram.types import ChatMemberUpdated
from aiogram.filters import ChatMemberUpdatedFilter, JOIN_TRANSITION, LEAVE_TRANSITION
@dp.chat_member(ChatMemberUpdatedFilter(JOIN_TRANSITION))
async def on_user_join(event: ChatMemberUpdated):
name = event.new_chat_member.user.full_name
await event.answer(
f"\ud83d\udc24 Привет, {name}! Добро пожаловать в чат. "
"Загляни в закреп — там всё важное."
)
@dp.chat_member(ChatMemberUpdatedFilter(LEAVE_TRANSITION))
async def on_user_leave(event: ChatMemberUpdated):
name = event.new_chat_member.user.full_name
await event.answer(f"\ud83d\udc24 {name} покинул чат. Будет не хватать! \ud83d\udc4b")
Результат: когда в группу вступает новый человек, бот пишет «Привет, Маша! Добро пожаловать...» с его именем; когда участник выходит — «Петя покинул чат...». Ровно тот сценарий, что мы рисовали в начале урока.
Разберём по шагам:
JOIN_TRANSITIONиLEAVE_TRANSITION— готовые наборы переходов из aiogram.JOIN_TRANSITIONловит любой вариант «человек оказался в группе» (вступил сам, добавили, вернулся из бана), аLEAVE_TRANSITION— любой вариант ухода. Не надо помнить все строки статусов — библиотека уже собрала их за тебя.event.new_chat_member.user— это объект пользователя, которого касается событие. У него есть привычные поля:full_name(имя и фамилия),id,username.event.answer(...)— отправляет сообщение в тот же чат, где произошло событие. Удобно: не надо вручную вытаскивать id группы.
Обрати внимание: имя мы берём из new_chat_member — «нового» состояния — и для входа, и для выхода. Почему? Потому что new_chat_member — это всегда тот же самый человек, просто в его новом статусе (member при входе, left при выходе). Это один и тот же участник, а не разные люди.
Важная тонкость: chat_member надо включить отдельно
А вот и первая большая ловушка, на которой застревают почти все. Ты написал идеальный хэндлер @dp.chat_member, добавил бота в группу, кто-то вступил — а бот молчит. Код правильный, а тишина. В чём дело?
Дело в том, что Telegram по умолчанию не присылает боту обновления типа chat_member про других участников. Их надо явно запросить при запуске. Помнишь polling — способ, когда бот сам регулярно спрашивает Telegram «есть что новое?». При старте polling-а надо сказать: «и про смену состава тоже присылай». Делается это списком allowed_updates:
from aiogram.types import Update
async def main():
await dp.start_polling(
bot,
allowed_updates=dp.resolve_used_update_types(),
)
Результат: при запуске бот сам соберёт список всех типов обновлений, которые использует (включая chat_member, раз у нас есть такой хэндлер), и попросит Telegram присылать их все. После этого приветствия начнут работать.
Метод dp.resolve_used_update_types() — твой лучший друг: он сам пробегает по всем твоим хэндлерам и понимает, какие типы обновлений тебе нужны. Тебе не надо вручную перечислять строки — aiogram сделает это сам. Просто запомни правило: если используешь chat_member — передавай allowed_updates в start_polling, иначе событий о составе не будет.
Частые ошибки и подводные камни
1. «Бот не видит сообщения в группе» — это режим приватности
Самая частая жалоба новичка: «в личке всё работает, а в группе бот игнорит обычный текст». Это не баг — это режим приватности, включённый по умолчанию. Бот видит только команды и упоминания. Решений два: либо переводи общение в группе на команды (/правила вместо «правила»), либо выключай приватность через BotFather командой /setprivacy — и обязательно передобавь бота в группу после этого.
2. Забыл allowed_updates — и chat_member молчит
Хэндлер @dp.chat_member написан верно, но приветствий нет. Почти всегда причина одна: при запуске не передали allowed_updates, и Telegram просто не шлёт эти события. Самый надёжный способ — всегда стартовать polling с allowed_updates=dp.resolve_used_update_types(). Тогда aiogram сам не забудет включить нужные типы.
3. Путаешь my_chat_member и chat_member
Эти два хэндлера легко перепутать, и тогда бот «здоровается сам с собой». Запомни намертво: my_chat_member — про самого бота (его добавили/выгнали/сделали админом), chat_member — про остальных людей (вошёл/вышел участник). Для приветствия новичков нужен именно chat_member.
4. Бот не может банить или удалять — нет прав
Код для бана написан, а Telegram возвращает ошибку вроде «not enough rights». Причина не в коде: боту просто не выдали права администратора. Сначала сделай бота админом в настройках группы и поставь нужные галочки («Блокировать участников», «Удалять сообщения»), и только потом проверяй код. Помни правило: сначала права, потом код.
5. Берёшь имя из old_chat_member вместо new_chat_member
При обработке выхода новички иногда лезут в old_chat_member, думая «человек же был, возьму из старого состояния». Это лишнее: и old_chat_member, и new_chat_member описывают одного и того же участника, просто в разных статусах. Поле user в них одинаковое, поэтому имя удобнее всегда брать из new_chat_member.user.full_name — и для входа, и для выхода, чтобы не путаться.
Мини-практика: бот-вахтёр клана
Теперь твой ход. Прокачай «Цыплёнка-помощника» до бота-вахтёра для чата игрового клана:
- Когда новый человек вступает (
JOIN_TRANSITION) — поприветствуй его по имени и попроси написать/ник, чтобы добавиться в состав. Не забудь включитьallowed_updatesпри запуске, иначе событие не дойдёт! - Когда участник выходит (
LEAVE_TRANSITION) — напиши прощание и добавь подсказку администратору: «не забудь убрать его из таблицы рейдов». - Сделай команду
/состав, которая работает даже при включённом режиме приватности (это же команда!) и присылает заглушку-список из трёх ников.
Когда заработает — попробуй усложнить. Добавь хэндлер @dp.my_chat_member с фильтром на переход в админы: пусть бот, когда его сделали администратором, радостно пишет «Спасибо за права! Теперь я могу следить за порядком». Подсказка: для этого пригодится фильтр перехода вроде ... >> IS_ADMIN. А ещё подумай, как с помощью SQLite (мы проходили базу в модуле про хранение) реально вести список состава, а не заглушку.
Итоги
Сегодня «Цыплёнок-помощник» переехал из уютной лички в шумную группу и научился там жить. Главное, что стоит унести с собой:
- Группа — не личка. По умолчанию работает режим приватности: бот видит только команды и упоминания, а не всю болтовню. Выключается у BotFather через
/setprivacy— но только если это правда нужно. - Сначала права, потом код. Модерировать (банить, удалять, закреплять) бот может, только если его сделали администратором и выдали конкретные галочки-права.
- События состава — это
ChatMemberUpdated. У него есть «было» (old_chat_member) и «стало» (new_chat_member); по паре статусов бот понимает, вошёл человек или вышел. - Два хэндлера:
my_chat_member— про самого бота,chat_member— про остальных участников. Для приветствий нужен второй. - Не забудь
allowed_updatesпри запуске polling-а, иначеchat_memberпросто не придёт. Спасаетdp.resolve_used_update_types().
В следующем уроке мы используем выданные боту права по-настоящему и научим «Цыплёнка» модерировать чат: ловить спам, удалять сообщения со ссылками и аккуратно банить нарушителей. Турникет на входе мы поставили — пора учить бота наводить порядок и внутри. До встречи!