Update bot.py

parent cd5806ce
...@@ -13,6 +13,8 @@ import os ...@@ -13,6 +13,8 @@ import os
from dotenv import load_dotenv from dotenv import load_dotenv
import unicodedata import unicodedata
from aiogram.filters import Command from aiogram.filters import Command
import logging
from aiogram import types
# Список слов фраз - указателей спама # Список слов фраз - указателей спама
SPAM_KEYWORDS = [ SPAM_KEYWORDS = [
...@@ -38,7 +40,7 @@ SPAM_KEYWORDS = [ ...@@ -38,7 +40,7 @@ SPAM_KEYWORDS = [
"лотерея", "быстрый выигрыш", "выигрыш", "приз", "деньги", "халява", "прибыль", "заработок", "зп", "подработка", "легкий заработок", "лотерея", "быстрый выигрыш", "выигрыш", "приз", "деньги", "халява", "прибыль", "заработок", "зп", "подработка", "легкий заработок",
"мгновенно", "доход", "доход онлайн", "как заработать", "схема заработка", "мгновенно", "доход", "доход онлайн", "как заработать", "схема заработка",
"пассивный доход", "быстро заработать", "богатство", "всего за", "инвестировать", "инвестиция", "пассивный доход", "быстро заработать", "богатство", "всего за", "инвестировать", "инвестиция",
"биткоин", "криптовалюта", "forex", "деньги на карта", "18+", "дивиденд", "процент", "депозит", "выплата", "невероятный доход", "статус","гарантированный доход", "секрет богатство", "без риск", "биткоин", "криптовалюта", "forex", "деньги на карта", "СБП", "18+", "дивиденд", "процент", "депозит", "выплата", "невероятный доход", "статус","гарантированный доход", "секрет богатство", "без риск",
# Психология, манипуляции и самопомощь # Психология, манипуляции и самопомощь
"гипноз", "секрет уверенность", "как стать успешный", "формула успех", "психотренинг", "управление человек", "контроль над разум", "манипуляция", "техника влияния", "мгновенный успех", "гипноз", "секрет уверенность", "как стать успешный", "формула успех", "психотренинг", "управление человек", "контроль над разум", "манипуляция", "техника влияния", "мгновенный успех",
...@@ -81,8 +83,14 @@ SPAM_KEYWORDS = [ ...@@ -81,8 +83,14 @@ SPAM_KEYWORDS = [
"🎁", "🔥", "💸", "💰", "💲", "💎", "🤑", "🤩", "⚡", "⭐", "💥", "✅", "🏆", "✋", "💖", "💝", "⚔️", "🥇", "💯", "💳", "🔞", "🥳", "😱" "🎁", "🔥", "💸", "💰", "💲", "💎", "🤑", "🤩", "⚡", "⭐", "💥", "✅", "🏆", "✋", "💖", "💝", "⚔️", "🥇", "💯", "💳", "🔞", "🥳", "😱"
] ]
# Настройка логирования
log_level = os.getenv('LOG_LEVEL', 'DEBUG').upper() # Считываем уровень логирования из .env (по умолчанию DEBUG)
logging.basicConfig(level=log_level, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
def preprocess_spam_keywords(keywords): def preprocess_spam_keywords(keywords):
"""Приводит ключевые слова к нормализованной форме.""" """Приводит ключевые слова к нормализованной форме."""
logger.debug("preprocess_spam_keywords: keyword conversion")
return set(' '.join(normalize_text_to_infinitive(keyword)) for keyword in keywords) return set(' '.join(normalize_text_to_infinitive(keyword)) for keyword in keywords)
# Загрузка переменных из файла .env # Загрузка переменных из файла .env
...@@ -102,22 +110,31 @@ dp.include_router(router) ...@@ -102,22 +110,31 @@ dp.include_router(router)
active_users = {} active_users = {}
morph = MorphAnalyzer() morph = MorphAnalyzer()
def has_mixed_layout(text): def has_mixed_layout(text):
""" """
Проверяет, содержит ли текст слова, написанные на смешанной раскладке. Проверяет, содержит ли текст слова, написанные на смешанной раскладке.
Например: "пр1в3т", "teст", "нeт". Например: "пр1в3т", "teст", "нeт".
""" """
cyrillic_letters = "абвгдеёжзийклмнопрстуфхцчшщъыьэюя" try:
latin_letters = "abcdefghijklmnopqrstuvwxyz"
words = text.split() cyrillic_letters = "абвгдеёжзийклмнопрстуфхцчшщъыьэюя"
for word in words: latin_letters = "abcdefghijklmnopqrstuvwxyz"
has_cyrillic = any(char in cyrillic_letters for char in word.lower())
has_latin = any(char in latin_letters for char in word.lower()) words = text.split()
if has_cyrillic and has_latin: for word in words:
return True # Найдено слово со смешанной раскладкой has_cyrillic = any(char in cyrillic_letters for char in word.lower())
return False has_latin = any(char in latin_letters for char in word.lower())
if has_cyrillic and has_latin:
logger.info("Word with mixed layout: %s", word)
return True # Найдено слово со смешанной раскладкой
logger.info("No word with mixed layout.")
return False
except Exception as e:
logger.error("Checking error: %s", e)
return False
# Регулярное выражение для распознавания эмодзи # Регулярное выражение для распознавания эмодзи
emoji_pattern = re.compile("[" emoji_pattern = re.compile("["
...@@ -141,6 +158,7 @@ def normalize_text_to_infinitive(text): ...@@ -141,6 +158,7 @@ def normalize_text_to_infinitive(text):
""" """
if not text: # Проверяем, что текст не None или пустой if not text: # Проверяем, что текст не None или пустой
logger.warning("No text for normalisation")
return [] return []
# Приведение текста к единой форме Unicode (NFKC) # Приведение текста к единой форме Unicode (NFKC)
...@@ -166,17 +184,21 @@ def normalize_text_to_infinitive(text): ...@@ -166,17 +184,21 @@ def normalize_text_to_infinitive(text):
normalized_words.append(morph.parse(word)[0].normal_form) normalized_words.append(morph.parse(word)[0].normal_form)
except Exception as e: except Exception as e:
# Если слово не удалось обработать, добавляем его как есть # Если слово не удалось обработать, добавляем его как есть
logger.error("Normalisation error '%s': %s", word, e)
normalized_words.append(word) normalized_words.append(word)
return normalized_words return normalized_words
def extract_emojis(text): def extract_emojis(text):
#return text.join(c for c in text if unicodedata.category(c).startswith('So')) try:
return ''.join(c for c in text if emoji.is_emoji(c)) return ''.join(c for c in text if emoji.is_emoji(c))
except Exception as e:
logger.error("Extracting emojis error: %s", e)
return ''
def is_spam(text): def is_spam(text):
if text is None: # Проверяем, что текст не None if text is None: # Проверяем, что текст не None
logger.warning("No text")
return False return False
# Нормализуем текст # Нормализуем текст
...@@ -208,6 +230,8 @@ def is_spam(text): ...@@ -208,6 +230,8 @@ def is_spam(text):
spam_count += 1 spam_count += 1
matched_keywords.append(keyword) # Добавляем совпавшее слово в список matched_keywords.append(keyword) # Добавляем совпавшее слово в список
logger.debug(f"Spam-words found in text: {', '.join(matched_keywords)}")
spam_count += emoji_count # Учитываем только спам-эмодзи spam_count += emoji_count # Учитываем только спам-эмодзи
# Возвращаем True, если найдено два или более совпадений # Возвращаем True, если найдено два или более совпадений
...@@ -230,6 +254,7 @@ async def welcome_new_user(event: ChatMemberUpdated): ...@@ -230,6 +254,7 @@ async def welcome_new_user(event: ChatMemberUpdated):
try: try:
# Отправляем приветственное сообщение # Отправляем приветственное сообщение
logger.info(f"Sending welcome message to {new_user.first_name} ({new_user.id}) in chat {event.chat.id}")
welcome_message = await bot.send_message( welcome_message = await bot.send_message(
chat_id=event.chat.id, chat_id=event.chat.id,
text=f"Здравствуйте, {new_user.first_name}! Добро пожаловать в нашу группу!\n\n" text=f"Здравствуйте, {new_user.first_name}! Добро пожаловать в нашу группу!\n\n"
...@@ -246,9 +271,10 @@ async def welcome_new_user(event: ChatMemberUpdated): ...@@ -246,9 +271,10 @@ async def welcome_new_user(event: ChatMemberUpdated):
"user_answered": False, "user_answered": False,
"chat_id": event.chat.id, "chat_id": event.chat.id,
} }
logger.info(f"User {new_user.first_name} ({new_user.id}) added to active users.")
except Exception as e: except Exception as e:
print(f"Failed sending a message to the group {event.chat.id}. Ошибка: {e}") logger.error(f"Failed sending a message to the group {event.chat.id}. Error: {e}")
# Ожидание ответа пользователя # Ожидание ответа пользователя
await check_answer(new_user.id) await check_answer(new_user.id)
...@@ -265,10 +291,11 @@ async def check_answer(user_id): ...@@ -265,10 +291,11 @@ async def check_answer(user_id):
# Удаляем пользователя из группы # Удаляем пользователя из группы
await bot.ban_chat_member(chat_id=user_data["chat_id"], user_id=user_id) await bot.ban_chat_member(chat_id=user_data["chat_id"], user_id=user_id)
logger.info(f"User {user_id} has been banned due to not answering in time.")
except AiogramError as e: except AiogramError as e:
print(f"User deletion error {user_id}. Error: {e}") logger.error(f"User deletion error {user_id}. Error: {e}")
except Exception as e: except Exception as e:
print(f"User deletion error {user_id}. Error: {e}") logger.error(f"User deletion error {user_id}. Error: {e}")
async def cleanup_messages(user_data): async def cleanup_messages(user_data):
"""Удаление всех сообщений пользователя и бота.""" """Удаление всех сообщений пользователя и бота."""
...@@ -278,7 +305,7 @@ async def cleanup_messages(user_data): ...@@ -278,7 +305,7 @@ async def cleanup_messages(user_data):
try: try:
await bot.delete_message(chat_id=user_data["chat_id"], message_id=message_id) await bot.delete_message(chat_id=user_data["chat_id"], message_id=message_id)
except Exception as e: except Exception as e:
print(f"Bot message deletion error. Error: {e}") logger.error(f"Bot message deletion error. Error: {e}")
# Удаляем сообщения пользователя # Удаляем сообщения пользователя
for user_message in user_data["user_messages"]: for user_message in user_data["user_messages"]:
...@@ -286,9 +313,9 @@ async def cleanup_messages(user_data): ...@@ -286,9 +313,9 @@ async def cleanup_messages(user_data):
if user_message: if user_message:
await bot.delete_message(chat_id=user_data["chat_id"], message_id=user_message.message_id) await bot.delete_message(chat_id=user_data["chat_id"], message_id=user_message.message_id)
except Exception as e: except Exception as e:
print(f"User message deletion error. Error: {e}") logger.error(f"User message deletion error. Error: {e}")
except Exception as e: except Exception as e:
print(f"Message deletion error. Error: {e}") logger.error(f"Message deletion error. Error: {e}")
@router.message() @router.message()
async def handle_message(message: Message): async def handle_message(message: Message):
...@@ -302,7 +329,7 @@ async def handle_message(message: Message): ...@@ -302,7 +329,7 @@ async def handle_message(message: Message):
await message.delete() await message.delete()
return return
except AiogramError as e: except AiogramError as e:
print(f"User status verification error {message.from_user.id}. Error: {e}") logger.error(f"User status verification error {message.from_user.id}. Error: {e}")
text = message.text or message.caption or "" text = message.text or message.caption or ""
text = " ".join(normalize_text_to_infinitive(text)) # Убедимся, что это строка text = " ".join(normalize_text_to_infinitive(text)) # Убедимся, что это строка
...@@ -313,7 +340,7 @@ async def handle_message(message: Message): ...@@ -313,7 +340,7 @@ async def handle_message(message: Message):
await message.delete() await message.delete()
return return
except Exception as e: except Exception as e:
print(f"Error deleting a message with a mixed layout. Error: {e}") logger.error(f"Error deleting a message with a mixed layout. Error: {e}")
# Проверка на мультимедиа (фото/видео) и его подпись # Проверка на мультимедиа (фото/видео) и его подпись
if (message.photo or message.video or message.document) and text.strip() and is_spam(text): if (message.photo or message.video or message.document) and text.strip() and is_spam(text):
...@@ -321,7 +348,7 @@ async def handle_message(message: Message): ...@@ -321,7 +348,7 @@ async def handle_message(message: Message):
await message.delete() await message.delete()
return return
except Exception as e: except Exception as e:
print(f"Error deleting a multimedia message. Error: {e}") logger.error(f"Error deleting a multimedia message. Error: {e}")
# Проверка на кнопки/ссылки # Проверка на кнопки/ссылки
if message.reply_markup and hasattr(message.reply_markup, 'inline_keyboard'): if message.reply_markup and hasattr(message.reply_markup, 'inline_keyboard'):
...@@ -331,7 +358,7 @@ async def handle_message(message: Message): ...@@ -331,7 +358,7 @@ async def handle_message(message: Message):
await message.delete() await message.delete()
return return
except Exception as e: except Exception as e:
print(f"Error when deleting a message with a button. Error: {e}") logger.error(f"Error when deleting a message with a button. Error: {e}")
# Проверка на спам # Проверка на спам
if is_spam(message.text): if is_spam(message.text):
...@@ -339,7 +366,7 @@ async def handle_message(message: Message): ...@@ -339,7 +366,7 @@ async def handle_message(message: Message):
await message.delete() await message.delete()
return return
except Exception as e: except Exception as e:
print(f"Error when trying to delete spam messages. Error: {e}") logger.error(f"Error when trying to delete spam messages. Error: {e}")
user_data = active_users.get(message.from_user.id) user_data = active_users.get(message.from_user.id)
...@@ -356,18 +383,19 @@ async def handle_message(message: Message): ...@@ -356,18 +383,19 @@ async def handle_message(message: Message):
user_data["message_ids"].append(correct_message.message_id) # Добавляем в список ID сообщений user_data["message_ids"].append(correct_message.message_id) # Добавляем в список ID сообщений
# Удаляем сообщения после правильного ответа # Удаляем сообщения после правильного ответа
await cleanup_messages(user_data) await cleanup_messages(user_data)
logger.info(f"User {message.from_user.id} answered correctly with {user_answer}. Correct answer.")
else: else:
# Ответ неправильный, добавляем новое сообщение бота # Ответ неправильный, добавляем новое сообщение бота
incorrect_message = await message.reply("Неправильный ответ. Попробуйте еще раз.") incorrect_message = await message.reply("Неправильный ответ. Попробуйте еще раз.")
user_data["message_ids"].append(incorrect_message.message_id) user_data["message_ids"].append(incorrect_message.message_id)
logger.warning(f"User {message.from_user.id} answered incorrectly with {user_answer}. Correct answer was {user_data['correct_answer']}.")
else: else:
# Если ответ не число # Если ответ не число
invalid_message = await message.reply("Пожалуйста, отправьте числовой ответ.") invalid_message = await message.reply("Пожалуйста, отправьте числовой ответ.")
user_data["message_ids"].append(invalid_message.message_id) user_data["message_ids"].append(invalid_message.message_id)
logger.warning(f"User {message.from_user.id} sent an invalid answer: {message.text}")
from aiogram import types
# Обработчик сообщений # Обработчик сообщений
@dp.message(lambda message: message.reply_to_message and '/spam' in message.text.lower()) @dp.message(lambda message: message.reply_to_message and '/spam' in message.text.lower())
async def delete_spam(message: types.Message): async def delete_spam(message: types.Message):
...@@ -377,14 +405,15 @@ async def delete_spam(message: types.Message): ...@@ -377,14 +405,15 @@ async def delete_spam(message: types.Message):
# Удаляем сообщение, на которое был дан ответ # Удаляем сообщение, на которое был дан ответ
await message.reply_to_message.delete() await message.reply_to_message.delete()
except Exception as e: except Exception as e:
print(f"Deleting error: {e}") logger.error("Deleting error: %s", e)
async def main(): async def main():
"""Запуск бота.""" """Запуск бота."""
print("bot is running") logger.info("bot is running")
await dp.start_polling(bot) await dp.start_polling(bot)
if __name__ == "__main__": if __name__ == "__main__":
SPAM_KEYWORDS = preprocess_spam_keywords(SPAM_KEYWORDS) SPAM_KEYWORDS = preprocess_spam_keywords(SPAM_KEYWORDS)
asyncio.run(main()) asyncio.run(main())
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment