Бот перестал реагировать на чужие группы и форвардить их сообщения
Сообщение в чате уведомлений оказалось форвардом из группы «Никитка играется с ботом», где тестовый бот был администратором: он видел сообщение прод-бота, считал отправителя посторонним и присылал access_denied с форвардом. Теперь чужие группы и каналы игнорируются полностью — и сообщения, и выходы, и callback-кнопки; личные чаты и основной чат работают как раньше.
This commit is contained in:
parent
e125d4bb0e
commit
058dfd6cc4
|
|
@ -158,7 +158,7 @@ make backup E=test # на домашнем сервере: ./scripts/clickho
|
||||||
|
|
||||||
## Версионирование
|
## Версионирование
|
||||||
|
|
||||||
- Единый источник версии — `common/version.py` (`__version__`), текущая — `0.5.0`.
|
- Единый источник версии — `common/version.py` (`__version__`), текущая — `0.5.1`.
|
||||||
- Схема — SemVer-подобная (`MAJOR.MINOR.PATCH`); git-тег = `v` + версия (`v0.1`).
|
- Схема — SemVer-подобная (`MAJOR.MINOR.PATCH`); git-тег = `v` + версия (`v0.1`).
|
||||||
- Docker-образы тегируются версией (Блок 2), события ClickHouse получают `bot_version` (Блок 3).
|
- Docker-образы тегируются версией (Блок 2), события ClickHouse получают `bot_version` (Блок 3).
|
||||||
- Выпуск: изменить `common/version.py` → коммит → аннотированный тег `vX.Y` → `git push --tags`.
|
- Выпуск: изменить `common/version.py` → коммит → аннотированный тег `vX.Y` → `git push --tags`.
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,9 @@ async def _send_fallback_welcome(event: ChatMemberUpdated, user: User) -> None:
|
||||||
@router.chat_member(ChatMemberUpdatedFilter(member_status_changed=LEAVE_TRANSITION))
|
@router.chat_member(ChatMemberUpdatedFilter(member_status_changed=LEAVE_TRANSITION))
|
||||||
async def goodbye_handler(event: ChatMemberUpdated) -> None:
|
async def goodbye_handler(event: ChatMemberUpdated) -> None:
|
||||||
"""Пользователь вышел или был исключён — удалить данные именно его."""
|
"""Пользователь вышел или был исключён — удалить данные именно его."""
|
||||||
|
if event.chat.id != get_settings().chat_id:
|
||||||
|
return
|
||||||
|
|
||||||
leaver = event.old_chat_member.user
|
leaver = event.old_chat_member.user
|
||||||
if leaver.is_bot:
|
if leaver.is_bot:
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ from aiogram.types import CallbackQuery, Message
|
||||||
from bot.keyboards import get_keyboard_donat, get_keyboard_main, get_keyboard_title
|
from bot.keyboards import get_keyboard_donat, get_keyboard_main, get_keyboard_title
|
||||||
from bot.services import access
|
from bot.services import access
|
||||||
from bot.services import shop as shop_service
|
from bot.services import shop as shop_service
|
||||||
|
from common.config import get_settings
|
||||||
from common.texts import system
|
from common.texts import system
|
||||||
from common.texts.render import render
|
from common.texts.render import render
|
||||||
from common.texts.store import get_text
|
from common.texts.store import get_text
|
||||||
|
|
@ -41,6 +42,15 @@ async def _render_chapter(chapter: str) -> tuple[str, object] | None:
|
||||||
|
|
||||||
@router.callback_query()
|
@router.callback_query()
|
||||||
async def callbacks_handler(callback: CallbackQuery) -> None:
|
async def callbacks_handler(callback: CallbackQuery) -> None:
|
||||||
|
settings = get_settings()
|
||||||
|
if (
|
||||||
|
callback.message is not None
|
||||||
|
and callback.message.chat.type != "private"
|
||||||
|
and callback.message.chat.id != settings.chat_id
|
||||||
|
):
|
||||||
|
await callback.answer()
|
||||||
|
return
|
||||||
|
|
||||||
answer_text = ""
|
answer_text = ""
|
||||||
args = (callback.data or "").split("_")
|
args = (callback.data or "").split("_")
|
||||||
action = args[0] if args else ""
|
action = args[0] if args else ""
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,12 @@ _ALLOWED_STATUSES = {
|
||||||
ChatMemberStatus.RESTRICTED,
|
ChatMemberStatus.RESTRICTED,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def is_allowed_chat(chat_id: int, chat_type: str, main_chat_id: int) -> bool:
|
||||||
|
"""Личные чаты и основной чат; чужие группы/каналы игнорируются полностью."""
|
||||||
|
return chat_type == "private" or chat_id == main_chat_id
|
||||||
|
|
||||||
|
|
||||||
#: TTL-кеш членства, чтобы не дёргать get_chat_member на каждое сообщение.
|
#: TTL-кеш членства, чтобы не дёргать get_chat_member на каждое сообщение.
|
||||||
_MEMBERSHIP_TTL = 300.0
|
_MEMBERSHIP_TTL = 300.0
|
||||||
_membership_cache: dict[int, tuple[float, bool]] = {}
|
_membership_cache: dict[int, tuple[float, bool]] = {}
|
||||||
|
|
@ -50,6 +56,10 @@ async def check_user(message: Message) -> bool:
|
||||||
if user is None:
|
if user is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Сообщения из посторонних групп не обрабатываем и не шлём по ним уведомления.
|
||||||
|
if not is_allowed_chat(message.chat.id, message.chat.type, settings.chat_id):
|
||||||
|
return False
|
||||||
|
|
||||||
if user.id in settings.admin_ids or await _is_member(message.bot, user.id):
|
if user.id in settings.admin_ids or await _is_member(message.bot, user.id):
|
||||||
await users_service.register_from_message(message)
|
await users_service.register_from_message(message)
|
||||||
remember_user(user.id, user.username)
|
remember_user(user.id, user.username)
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,4 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
__version__ = "0.5.0"
|
__version__ = "0.5.1"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
"""Тесты фильтра чатов: бот игнорирует чужие группы."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from bot.services.access import is_allowed_chat
|
||||||
|
|
||||||
|
|
||||||
|
def test_private_chat_is_allowed() -> None:
|
||||||
|
assert is_allowed_chat(123, "private", 100) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_main_chat_is_allowed() -> None:
|
||||||
|
assert is_allowed_chat(100, "supergroup", 100) is True
|
||||||
|
assert is_allowed_chat(100, "group", 100) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_foreign_group_is_ignored() -> None:
|
||||||
|
assert is_allowed_chat(200, "supergroup", 100) is False
|
||||||
|
assert is_allowed_chat(200, "group", 100) is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_foreign_channel_is_ignored() -> None:
|
||||||
|
assert is_allowed_chat(200, "channel", 100) is False
|
||||||
|
|
@ -7,4 +7,4 @@ from common import __version__
|
||||||
|
|
||||||
def test_version_is_set():
|
def test_version_is_set():
|
||||||
assert __version__
|
assert __version__
|
||||||
assert __version__ == "0.5.0"
|
assert __version__ == "0.5.1"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue