diff --git a/README.md b/README.md index 9f983a3..1ccfefa 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ - Команда разбана: `/РАЗБАН` (тоже можно с `!`). - Обе команды работают только как `reply` на сообщение нужного пользователя. - Список ID хранится в `data/blocked_users.json` и сохраняется между перезапусками. +- При бане дополнительно чистятся последние сообщения этого пользователя в текущем чате. ## Установка 1. Установите Python 3.10+. @@ -35,8 +36,9 @@ ## Как пользоваться 1. В группе ответьте на сообщение пользователя командой `/ВБАННАХУЙ!`. 2. Юзербот покажет короткую анимацию и добавит ID пользователя в автоудаление. -3. Новые сообщения этого пользователя в группах будут удаляться только у вас. -4. Чтобы отключить автоудаление, ответьте на его сообщение командой `/РАЗБАН`. +3. После бана бот также попробует удалить недавние сообщения этого пользователя в текущем чате. +4. Новые сообщения этого пользователя в группах будут удаляться только у вас. +5. Чтобы отключить автоудаление, ответьте на его сообщение командой `/РАЗБАН`. ## Ограничения - Удаление "только у вас" работает в формате `best effort` и зависит от ограничений Telegram API. diff --git a/bot.py b/bot.py index e90e41b..7a1c07b 100644 --- a/bot.py +++ b/bot.py @@ -7,11 +7,13 @@ from typing import Optional from dotenv import load_dotenv from telethon import TelegramClient, events +from telethon.errors import RPCError from telethon.tl.custom.message import Message BAN_COMMAND = "/вбаннахуй" UNBAN_COMMAND = "/разбан" DATA_PATH = Path("data/blocked_users.json") +RECENT_CLEANUP_LIMIT = 50 ANIMATION_FRAMES = [ "Запускаю анти-режим.", @@ -127,6 +129,60 @@ async def animate_ban(message: Message, target_user_id: int, is_new: bool) -> No logging.exception("Не удалось отправить финальный статус анимации.") +async def delete_message_best_effort( + client: TelegramClient, message: Message +) -> bool: + try: + await message.delete(revoke=False) + return True + except Exception as exc: + logging.debug( + "Удаление через message.delete(revoke=False) не сработало: %s", exc + ) + + try: + target = message.input_chat if message.input_chat else message.chat_id + await client.delete_messages(target, [message.id], revoke=False) + return True + except Exception as exc: + logging.debug( + "Удаление через client.delete_messages(..., revoke=False) не сработало: %s", + exc, + ) + + try: + await message.delete() + return True + except RPCError as exc: + logging.warning( + "Не удалось удалить сообщение %s в чате %s: %s", + message.id, + message.chat_id, + exc, + ) + except Exception: + logging.exception( + "Неожиданная ошибка удаления сообщения %s в чате %s.", + message.id, + message.chat_id, + ) + + return False + + +async def cleanup_recent_messages_from_user( + client: TelegramClient, chat_id: int, user_id: int, limit: int = RECENT_CLEANUP_LIMIT +) -> int: + deleted_count = 0 + async for message in client.iter_messages(chat_id, from_user=user_id, limit=limit): + if message.id is None: + continue + if await delete_message_best_effort(client, message): + deleted_count += 1 + + return deleted_count + + def load_settings() -> tuple[int, str, str]: load_dotenv() api_id_raw = os.getenv("API_ID", "").strip() @@ -181,6 +237,15 @@ async def main() -> None: status_message = await event.reply("Подготовка...") added = await blocklist.add(target_user_id) await animate_ban(status_message, target_user_id, added) + cleaned = await cleanup_recent_messages_from_user( + client, + event.chat_id, + target_user_id, + ) + if cleaned > 0: + await event.reply( + f"Дополнительно очищено последних сообщений в этом чате: {cleaned}." + ) return removed = await blocklist.remove(target_user_id) @@ -196,7 +261,7 @@ async def main() -> None: @client.on(events.NewMessage(incoming=True)) async def auto_delete_target_messages(event: events.NewMessage.Event) -> None: - if not event.is_group: + if event.is_private: return sender_id = event.sender_id @@ -206,11 +271,10 @@ async def main() -> None: if not await blocklist.contains(int(sender_id)): return - try: - await client.delete_messages(event.chat_id, [event.id], revoke=False) - except Exception: - logging.exception( - "Не удалось удалить сообщение %s от пользователя %s в чате %s.", + deleted = await delete_message_best_effort(client, event.message) + if not deleted: + logging.warning( + "Пропуск удаления: сообщение %s от пользователя %s в чате %s.", event.id, sender_id, event.chat_id,