Добавлено экранирование Markdown в сообщениях бота и обработчик кнопки для очистки истории диалога. Обновлено приветственное сообщение с инструкциями по форматированию.
This commit is contained in:
parent
d2af1e23de
commit
c9feb61ceb
102
bot.py
102
bot.py
@ -4,6 +4,7 @@ import logging
|
||||
import json
|
||||
from io import BytesIO
|
||||
from typing import Dict, List, Any, Optional
|
||||
import re
|
||||
|
||||
from aiogram import Bot, Dispatcher, types, F
|
||||
from aiogram.filters import Command
|
||||
@ -11,6 +12,7 @@ from aiogram.types import Message
|
||||
from dotenv import load_dotenv
|
||||
from ollama import AsyncClient
|
||||
import asyncpg
|
||||
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||
|
||||
# Настройка логирования
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
@ -151,6 +153,50 @@ async def keep_typing(chat_id: int):
|
||||
logging.error(f"Ошибка в keep_typing: {e}")
|
||||
|
||||
|
||||
def escape_markdown(text: str) -> str:
|
||||
"""Экранирует специальные символы Markdown, если они не являются частью форматирования"""
|
||||
# Регулярное выражение для поиска уже отформатированного текста
|
||||
md_patterns = [
|
||||
r'`[^`]+`', # Код
|
||||
r'\*\*[^*]+\*\*', # Жирный
|
||||
r'\*[^*]+\*', # Курсив
|
||||
r'_[^_]+_', # Курсив (альтернативный)
|
||||
r'__[^_]+__', # Жирный (альтернативный)
|
||||
r'\[[^\]]+\]\([^)]+\)', # Ссылки
|
||||
]
|
||||
|
||||
# Собираем все совпадения с шаблонами
|
||||
matches = []
|
||||
for pattern in md_patterns:
|
||||
matches.extend([(m.start(), m.end()) for m in re.finditer(pattern, text)])
|
||||
|
||||
# Сортируем по началу совпадения
|
||||
matches.sort()
|
||||
|
||||
# Если нет совпадений, просто экранируем все специальные символы
|
||||
if not matches:
|
||||
return re.sub(r'([_*\[\]()~`>#+-=|{}.!\\])', r'\\\1', text)
|
||||
|
||||
# Иначе экранируем только те символы, которые не входят в уже отформатированный текст
|
||||
result = ""
|
||||
last_pos = 0
|
||||
|
||||
for start, end in matches:
|
||||
# Экранируем символы до начала форматирования
|
||||
if start > last_pos:
|
||||
result += re.sub(r'([_*\[\]()~`>#+-=|{}.!\\])', r'\\\1', text[last_pos:start])
|
||||
|
||||
# Добавляем отформатированный текст без изменений
|
||||
result += text[start:end]
|
||||
last_pos = end
|
||||
|
||||
# Экранируем символы после последнего форматирования
|
||||
if last_pos < len(text):
|
||||
result += re.sub(r'([_*\[\]()~`>#+-=|{}.!\\])', r'\\\1', text[last_pos:])
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@dp.message(Command("start"))
|
||||
async def cmd_start(message: Message):
|
||||
"""Обработчик команды /start"""
|
||||
@ -165,7 +211,22 @@ async def cmd_start(message: Message):
|
||||
# Если есть, сохраняем их в кэше
|
||||
user_messages[user_id] = messages
|
||||
|
||||
await message.answer("Привет! Я бот на базе Gemma. Отправьте мне сообщение или фото, и я отвечу вам.")
|
||||
welcome_message = (
|
||||
"Привет! Я бот на базе Gemma. Отправьте мне сообщение или фото, и я отвечу вам.\n\n"
|
||||
"Поддерживается *Markdown* форматирование:\n"
|
||||
"• *жирный текст* (звездочки)\n"
|
||||
"• _курсив_ (нижнее подчеркивание)\n"
|
||||
"• `код` (обратные кавычки)\n"
|
||||
"• [ссылки](https://example.com)\n\n"
|
||||
"Используйте /clear для очистки контекста диалога."
|
||||
)
|
||||
|
||||
# Создаем инлайн-кнопку для очистки истории
|
||||
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||
[InlineKeyboardButton(text="🗑 Очистить историю сообщений", callback_data="clear_history")]
|
||||
])
|
||||
|
||||
await message.answer(welcome_message, parse_mode=types.ParseMode.MARKDOWN, reply_markup=keyboard)
|
||||
|
||||
|
||||
@dp.message(Command("clear"))
|
||||
@ -245,8 +306,14 @@ async def handle_photo(message: Message):
|
||||
# Сохраняем ответ в БД
|
||||
await save_message_to_db(user_id, assistant_message)
|
||||
|
||||
# Отправляем ответ пользователю
|
||||
await message.answer(answer)
|
||||
# Создаем инлайн-кнопку для очистки истории
|
||||
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||
[InlineKeyboardButton(text="🗑 Очистить историю сообщений", callback_data="clear_history")]
|
||||
])
|
||||
|
||||
# Отправляем ответ пользователю с Markdown-форматированием и кнопкой
|
||||
safe_answer = escape_markdown(answer)
|
||||
await message.answer(safe_answer, parse_mode=types.ParseMode.MARKDOWN, reply_markup=keyboard)
|
||||
|
||||
except Exception as e:
|
||||
# Останавливаем задачу обновления статуса печати в случае ошибки
|
||||
@ -305,8 +372,14 @@ async def handle_text(message: Message):
|
||||
# Сохраняем ответ в БД
|
||||
await save_message_to_db(user_id, assistant_message)
|
||||
|
||||
# Отправляем ответ пользователю
|
||||
await message.answer(answer)
|
||||
# Создаем инлайн-кнопку для очистки истории
|
||||
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||
[InlineKeyboardButton(text="🗑 Очистить историю сообщений", callback_data="clear_history")]
|
||||
])
|
||||
|
||||
# Отправляем ответ пользователю с Markdown-форматированием и кнопкой
|
||||
safe_answer = escape_markdown(answer)
|
||||
await message.answer(safe_answer, parse_mode=types.ParseMode.MARKDOWN, reply_markup=keyboard)
|
||||
|
||||
except Exception as e:
|
||||
# Останавливаем задачу обновления статуса печати в случае ошибки
|
||||
@ -324,6 +397,25 @@ async def handle_other(message: Message):
|
||||
await message.answer("Я могу обрабатывать только текст и фотографии. Пожалуйста, отправьте текст или одну фотографию.")
|
||||
|
||||
|
||||
# Добавляем обработчик нажатия на кнопку очистки истории
|
||||
@dp.callback_query(F.data == "clear_history")
|
||||
async def clear_history_callback(callback: types.CallbackQuery):
|
||||
"""Обработчик кнопки очистки истории"""
|
||||
user_id = callback.from_user.id
|
||||
|
||||
# Очищаем контекст в памяти
|
||||
user_messages[user_id] = []
|
||||
|
||||
# Очищаем контекст в БД
|
||||
await clear_messages_from_db(user_id)
|
||||
|
||||
# Уведомляем пользователя
|
||||
await callback.answer("Контекст диалога очищен!")
|
||||
|
||||
# Отправляем сообщение в чат
|
||||
await callback.message.answer("Контекст диалога очищен. Вы можете начать новый разговор.")
|
||||
|
||||
|
||||
async def main():
|
||||
"""Запуск бота"""
|
||||
# Инициализируем подключение к базе данных
|
||||
|
||||
Loading…
Reference in New Issue
Block a user