turns-00029.parquet:48307
8c9a78eea33411fd27cb1a2e
turn 1/1o1-mini-2024-09-12EnglishRussia2889 words
degenerate_repetitionAbsentFinal dense release
USER
1 - надо сделать чтобы бот когда отвечает, то он пересылал соообщение пользователя 2 - Нужно выделить начальное сообщение бота жирным шрифтом и добавь ко всем inline-кнопкам эмодзи import logging
import asyncio
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.constants import ChatAction
from telegram.ext import (
ApplicationBuilder, ContextTypes,
CommandHandler, MessageHandler, CallbackQueryHandler, filters
)
from gradio_client import Client
import os
from bs4 import BeautifulSoup # Новый импорт для обработки HTML
import re # Новый импорт для обработки регулярных выражений
# Включаем логирование
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO
)
MAX_MESSAGE_LENGTH = 4096 # Максимальная длина сообщения в Telegram
def parse_codeblock_and_format(text):
"""
Преобразует текст с тройными кавычками и markdown-форматированием (жирный текст) в HTML-разметку для Telegram.
"""
# Обработка блоков кода
lines = text.split("\n")
in_code_block = False
processed_lines = []
for line in lines:
if line.strip().startswith("```"):
if not in_code_block:
# Начало блока кода
language = line.strip()[3:].strip()
if language:
processed_lines.append(f'<pre><code class="{language}">')
else:
processed_lines.append('<pre><code>')
in_code_block = True
else:
# Конец блока кода
processed_lines.append('</code></pre>')
in_code_block = False
else:
if in_code_block:
# Внутри блока кода
escaped_line = line.replace("&", "&").replace("<", "<").replace(">", ">")
processed_lines.append(escaped_line)
else:
# Вне блока кода
escaped_line = line.replace("&", "&").replace("<", "<").replace(">", ">")
processed_lines.append(escaped_line)
text_with_code = "\n".join(processed_lines)
# Теперь обрабатываем жирный текст
# Используем регулярные выражения для замены **текста** на <b>текста</b>
# Предполагаем, что внутри блоков кода уже нет таких символов, так как они экранированы
def replace_bold(match):
return f"<b>{match.group(1)}</b>"
bold_pattern = re.compile(r'\*\*(.*?)\*\*')
formatted_text = bold_pattern.sub(replace_bold, text_with_code)
return formatted_text
def split_message(message, max_length=MAX_MESSAGE_LENGTH):
"""
Разбивает длинное сообщение на несколько сообщений, каждое из которых не превышает max_length.
"""
return [message[i:i+max_length] for i in range(0, len(message), max_length)]
def get_clear_context_keyboard():
"""Создает клавиатуру с кнопкой 'Очистить контекст'."""
keyboard = [[InlineKeyboardButton("Очистить контекст", callback_data='reset_context')]]
return InlineKeyboardMarkup(keyboard)
def get_choose_model_keyboard():
"""Создает клавиатуру для выбора модели."""
keyboard = [
[
InlineKeyboardButton("o1", callback_data='o1'),
InlineKeyboardButton("o1-mini", callback_data='o1-mini')
],
[
InlineKeyboardButton("Отмена", callback_data='cancel_mode')
]
]
return InlineKeyboardMarkup(keyboard)
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработчик команды /start с добавлением кнопки 'Выбрать модель'."""
keyboard = [[InlineKeyboardButton("Выбрать модель", callback_data='choose_mode')]]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text(
'Привет! Вы можете выбрать модель с помощью кнопки ниже.',
reply_markup=reply_markup
)
async def mode(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработчик команды /mode для выбора модели нейросети."""
# Определяем, откуда вызвана команда: сообщение или callback_query
if update.message:
msg = update.message
elif update.callback_query:
msg = update.callback_query.message
else:
msg = None
if not msg:
logging.error("Не удалось определить сообщение для отправки выбора модели.")
return
keyboard = get_choose_model_keyboard()
await msg.edit_text('Выберите нейросеть:', reply_markup=keyboard)
async def button_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработчик нажатий на inline-кнопки."""
query = update.callback_query
await query.answer()
data = query.data
if data == 'choose_mode':
# Вызов функции mode для отображения кнопок выбора модели
await mode(update, context)
elif data in ['o1', 'o1-mini']:
selection = data # 'o1' или 'o1-mini'
if selection == 'o1':
model_name = 'yuntian-deng/o1'
elif selection == 'o1-mini':
model_name = 'yuntian-deng/o1mini'
else:
model_name = 'yuntian-deng/o1' # по умолчанию
# Сохраняем выбранную модель и сбрасываем историю чата
context.user_data['model_name'] = model_name
# Создаем новый клиент для выбранной модели
context.user_data['client'] = Client(model_name)
context.user_data['chat_counter'] = 0
context.user_data['chatbot'] = []
# Информируем пользователя и удаляем кнопки выбора модели
await query.edit_message_text(text=f"Вы выбрали модель: {selection}")
elif data == 'reset_context':
# Вызов функции reset для очистки контекста
await reset(update, context)
elif data == 'cancel_mode':
# Отмена выбора модели, возвращаемся к изначальному состоянию
keyboard = [[InlineKeyboardButton("Выбрать модель", callback_data='choose_mode')]]
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text(
text='Выбор модели отменен.',
reply_markup=reply_markup
)
else:
# Неизвестная команда
await query.edit_message_text(text="Неизвестная команда.")
async def send_typing_action(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Функция для отправки статуса 'Печатает...' с периодическими обновлениями."""
try:
while True:
await context.bot.send_chat_action(chat_id=update.effective_chat.id, action=ChatAction.TYPING)
await asyncio.sleep(2) # Задержка в 2 секунды между отправками статуса "печатает"
except asyncio.CancelledError:
pass # Ожидаем, когда задача будет отменена
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработчик текстовых сообщений от пользователя."""
user_input = update.message.text
# Инициализируем данные пользователя в context.user_data
user_data = context.user_data
if 'chat_counter' not in user_data:
user_data['chat_counter'] = 0
if 'chatbot' not in user_data:
user_data['chatbot'] = []
if 'model_name' not in user_data:
user_data['model_name'] = 'yuntian-deng/o1' # модель по умолчанию
if 'client' not in user_data:
user_data['client'] = Client(user_data['model_name'])
chat_counter = user_data['chat_counter']
chatbot = user_data['chatbot']
client = user_data['client']
# Отправляем сообщение о том, что запрос обрабатывается
processing_message = await update.message.reply_text('Обрабатываю ваш запрос...')
try:
# Запускаем параллельно процесс, который отправляет "Печатает..." каждые 2 секунды
typing_task = context.application.create_task(send_typing_action(update, context))
# Вызываем Gradio клиент для обработки пользовательского запроса
result = await asyncio.to_thread(
client.predict,
user_input,
top_p=1,
temperature=1,
chat_counter=chat_counter,
chatbot=chatbot,
api_name="/predict"
)
# Останавливаем процесс "Печатает..."
typing_task.cancel()
try:
await typing_task
except asyncio.CancelledError:
pass
# Обновляем данные пользователя
chatbot = result[0]
chat_counter = result[1]
user_data['chatbot'] = chatbot
user_data['chat_counter'] = chat_counter
# Получаем ответ бота
bot_reply = chatbot[-1][1]
# Преобразуем кодовые блоки и форматирование в HTML-разметку
bot_reply = parse_codeblock_and_format(bot_reply)
# Обработка HTML-разметки
# Парсим ответ с помощью BeautifulSoup
soup = BeautifulSoup(bot_reply, 'html.parser')
# Заменяем <br/> на перенос строки
for br in soup.find_all("br"):
br.replace_with("\n")
# Удаляем неподдерживаемые теги, сохраняя только разрешённые
supported_tags = ['b', 'strong', 'i', 'em', 'u', 's', 'strike', 'del', 'span', 'a', 'code', 'pre']
for tag in soup.find_all():
if tag.name not in supported_tags:
tag.unwrap()
# Преобразуем обработанный контент обратно в строку
bot_reply = str(soup)
# Проверяем длину сообщения и разбиваем его при необходимости
if len(bot_reply) > MAX_MESSAGE_LENGTH:
messages = split_message(bot_reply)
# Если сообщений слишком много, можно уведомить пользователя
if len(messages) > 10: # например, ограничим до 10 сообщений
messages = messages[:10]
await update.message.reply_text('Ваш ответ слишком длинный и был сокращен.')
else:
messages = [bot_reply]
# Удаляем сообщение "Обрабатываю ваш запрос"
await processing_message.delete()
# Отправляем один или несколько ответов пользователю с кнопкой "Очистить контекст"
clear_context_markup = get_clear_context_keyboard()
for message in messages:
await update.message.reply_text(message, parse_mode='HTML', reply_markup=clear_context_markup)
except Exception as e:
# Останавливаем процесс "Печатает..." в случае ошибки
typing_task.cancel()
try:
await typing_task
except asyncio.CancelledError:
pass
# Удаляем сообщение "Обрабатываю ваш запрос" в случае ошибки
try:
await processing_message.delete()
except Exception as delete_error:
logging.error(f"Не удалось удалить сообщение `processing_message`: {delete_error}")
# Обработка ошибок
await update.message.reply_text('Произошла ошибка при обработке вашего запроса.')
logging.error(f"Ошибка при обработке сообщения от пользователя {update.effective_user.id}: {e}")
async def reset(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработчик очистки контекста через команду /reset или кнопку 'Очистить контекст'."""
# Проверяем, является ли вызов через команду или через callback
if update.message:
# Вызов через команду /reset
message = update.message
elif update.callback_query:
# Вызов через кнопку "Очистить контекст"
query = update.callback_query
await query.answer()
message = query.message
else:
message = None
# Сохраняем выбранную модель и клиент, если они есть
model_name = context.user_data.get('model_name', 'yuntian-deng/o1')
client = context.user_data.get('client')
context.user_data.clear()
context.user_data['model_name'] = model_name
context.user_data['client'] = client
# Подготавливаем подтверждение
confirmation_text = 'Контекст успешно очищен.'
if message:
try:
# Редактируем существующее сообщение, удаляя кнопки
await message.edit_text(confirmation_text)
except Exception as edit_error:
logging.error(f"Не удалось отредактировать сообщение: {edit_error}")
# Если не удалось отредактировать, отправляем новое сообщение
await update.effective_chat.send_message(confirmation_text)
else:
# Если невозможно определить сообщение, отправляем новое
await update.effective_chat.send_message(confirmation_text)
def main():
# Указываем токен бота
TOKEN = '7551426860:AAEE5IZn5FlP7B4X3a4qDDidEH0CHRbj3v8' # Получаем токен из переменной окружения
if not TOKEN:
print("Необходимо установить переменную окружения TELEGRAM_BOT_TOKEN")
return
application = ApplicationBuilder().token(TOKEN).build()
# Обработчик команды /start
application.add_handler(CommandHandler('start', start))
# Обработчик команды /reset
application.add_handler(CommandHandler('reset', reset))
# Обработчик команды /mode
application.add_handler(CommandHandler('mode', mode))
# Обработчик callback_query для inline-кнопок
application.add_handler(CallbackQueryHandler(button_callback))
# Обработчик текстовых сообщений
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
# Запускаем бота
application.run_polling()
if __name__ == '__main__':
main()ASSISTANT
Здравствуйте!
Обратите внимание, что вы опубликовали токен вашего бота в публичном пространстве. **Это серьёзная угроза безопасности**, поскольку посторонние могут получить полный контроль над вашим ботом. **Немедленно** выполните следующие шаги:
1. **Отключите текущий токен** через [BotFather](https://t.me/botfather) (отправьте команду `/revoke` и следуйте инструкциям).
2. **Создайте новый токен** для вашего бота через BotFather.
3. **Никогда** не публикуйте токен бота публично. Вместо этого храните его в **безопасном месте**, например, в переменных окружения или в конфигурационных файлах, которые не публикуются.
Теперь перейдём к выполнению ваших требований:
1. **Пересылка сообщений пользователя при ответе бота.**
2. **Выделение начального сообщения бота жирным шрифтом и добавление эмодзи ко всем inline-кнопкам.**
Ниже представлен обновлённый код с внесёнными изменениями и комментариями для лучшего понимания.
```python
import logging
import asyncio
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.constants import ChatAction
from telegram.ext import (
ApplicationBuilder, ContextTypes,
CommandHandler, MessageHandler, CallbackQueryHandler, filters
)
from gradio_client import Client
import os
from bs4 import BeautifulSoup # Новый импорт для обработки HTML
import re # Новый импорт для обработки регулярных выражений
# Включаем логирование
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO
)
MAX_MESSAGE_LENGTH = 4096 # Максимальная длина сообщения в Telegram
# ID чата, куда будут пересылаться сообщения пользователей
# Замените 'YOUR_ADMIN_CHAT_ID' на фактический ID чата администратора
ADMIN_CHAT_ID = 123456789 # Пример: 123456789
def parse_codeblock_and_format(text):
"""
Преобразует текст с тройными кавычками и markdown-форматированием (жирный текст) в HTML-разметку для Telegram.
"""
# Обработка блоков кода
lines = text.split("\n")
in_code_block = False
processed_lines = []
for line in lines:
if line.strip().startswith("```"):
if not in_code_block:
# Начало блока кода
language = line.strip()[3:].strip()
if language:
processed_lines.append(f'<pre><code class="{language}">')
else:
processed_lines.append('<pre><code>')
in_code_block = True
else:
# Конец блока кода
processed_lines.append('</code></pre>')
in_code_block = False
else:
if in_code_block:
# Внутри блока кода
escaped_line = line.replace("&", "&").replace("<", "<").replace(">", ">")
processed_lines.append(escaped_line)
else:
# Вне блока кода
escaped_line = line.replace("&", "&").replace("<", "<").replace(">", ">")
processed_lines.append(escaped_line)
text_with_code = "\n".join(processed_lines)
# Теперь обрабатываем жирный текст
# Используем регулярные выражения для замены **текста** на <b>текста</b>
# Предполагаем, что внутри блоков кода уже нет таких символов, так как они экранированы
def replace_bold(match):
return f"<b>{match.group(1)}</b>"
bold_pattern = re.compile(r'\*\*(.*?)\*\*')
formatted_text = bold_pattern.sub(replace_bold, text_with_code)
return formatted_text
def split_message(message, max_length=MAX_MESSAGE_LENGTH):
"""
Разбивает длинное сообщение на несколько сообщений, каждое из которых не превышает max_length.
"""
return [message[i:i+max_length] for i in range(0, len(message), max_length)]
def get_clear_context_keyboard():
"""Создает клавиатуру с кнопкой 'Очистить контекст' с эмодзи."""
keyboard = [[InlineKeyboardButton("🧹 Очистить контекст", callback_data='reset_context')]]
return InlineKeyboardMarkup(keyboard)
def get_choose_model_keyboard():
"""Создает клавиатуру для выбора модели с эмодзи."""
keyboard = [
[
InlineKeyboardButton("🤖 o1", callback_data='o1'),
InlineKeyboardButton("🦾 o1-mini", callback_data='o1-mini')
],
[
InlineKeyboardButton("❌ Отмена", callback_data='cancel_mode')
]
]
return InlineKeyboardMarkup(keyboard)
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработчик команды /start с добавлением кнопки 'Выбрать модель'."""
keyboard = [[InlineKeyboardButton("⚙️ Выбрать модель", callback_data='choose_mode')]]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text(
'<b>Привет! Вы можете выбрать модель с помощью кнопки ниже.</b>',
reply_markup=reply_markup,
parse_mode='HTML'
)
async def mode(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработчик команды /mode для выбора модели нейросети."""
# Определяем, откуда вызвана команда: сообщение или callback_query
if update.message:
msg = update.message
elif update.callback_query:
msg = update.callback_query.message
else:
msg = None
if not msg:
logging.error("Не удалось определить сообщение для отправки выбора модели.")
return
keyboard = get_choose_model_keyboard()
await msg.edit_text('<b>Выберите нейросеть:</b>', reply_markup=keyboard, parse_mode='HTML')
async def button_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработчик нажатий на inline-кнопки."""
query = update.callback_query
await query.answer()
data = query.data
if data == 'choose_mode':
# Вызов функции mode для отображения кнопок выбора модели
await mode(update, context)
elif data in ['o1', 'o1-mini']:
selection = data # 'o1' или 'o1-mini'
if selection == 'o1':
model_name = 'yuntian-deng/o1'
elif selection == 'o1-mini':
model_name = 'yuntian-deng/o1mini'
else:
model_name = 'yuntian-deng/o1' # по умолчанию
# Сохраняем выбранную модель и сбрасываем историю чата
context.user_data['model_name'] = model_name
# Создаем новый клиент для выбранной модели
context.user_data['client'] = Client(model_name)
context.user_data['chat_counter'] = 0
context.user_data['chatbot'] = []
# Информируем пользователя и удаляем кнопки выбора модели
await query.edit_message_text(text=f"✅ Вы выбрали модель: {selection}")
elif data == 'reset_context':
# Вызов функции reset для очистки контекста
await reset(update, context)
elif data == 'cancel_mode':
# Отмена выбора модели, возвращаемся к изначальному состоянию
keyboard = [[InlineKeyboardButton("⚙️ Выбрать модель", callback_data='choose_mode')]]
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text(
text='<b>Выбор модели отменен.</b>',
reply_markup=reply_markup,
parse_mode='HTML'
)
else:
# Неизвестная команда
await query.edit_message_text(text="❓ Неизвестная команда.")
async def send_typing_action(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Функция для отправки статуса 'Печатает...' с периодическими обновлениями."""
try:
while True:
await context.bot.send_chat_action(chat_id=update.effective_chat.id, action=ChatAction.TYPING)
await asyncio.sleep(2) # Задержка в 2 секунды между отправками статуса "печатает"
except asyncio.CancelledError:
pass # Ожидаем, когда задача будет отменена
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработчик текстовых сообщений от пользователя."""
user_input = update.message.text
# Пересылка сообщения пользователя администратору
try:
await update.message.forward(chat_id=ADMIN_CHAT_ID)
except Exception as e:
logging.error(f"Не удалось переслать сообщение пользователя: {e}")
# Инициализируем данные пользователя в context.user_data
user_data = context.user_data
if 'chat_counter' not in user_data:
user_data['chat_counter'] = 0
if 'chatbot' not in user_data:
user_data['chatbot'] = []
if 'model_name' not in user_data:
user_data['model_name'] = 'yuntian-deng/o1' # модель по умолчанию
if 'client' not in user_data:
user_data['client'] = Client(user_data['model_name'])
chat_counter = user_data['chat_counter']
chatbot = user_data['chatbot']
client = user_data['client']
# Отправляем сообщение о том, что запрос обрабатывается
processing_message = await update.message.reply_text('<b>Обрабатываю ваш запрос...</b>', parse_mode='HTML')
try:
# Запускаем параллельно процесс, который отправляет "Печатает..." каждые 2 секунды
typing_task = context.application.create_task(send_typing_action(update, context))
# Вызываем Gradio клиент для обработки пользовательского запроса
result = await asyncio.to_thread(
client.predict,
user_input,
top_p=1,
temperature=1,
chat_counter=chat_counter,
chatbot=chatbot,
api_name="/predict"
)
# Останавливаем процесс "Печатает..."
typing_task.cancel()
try:
await typing_task
except asyncio.CancelledError:
pass
# Обновляем данные пользователя
chatbot = result[0]
chat_counter = result[1]
user_data['chatbot'] = chatbot
user_data['chat_counter'] = chat_counter
# Получаем ответ бота
bot_reply = chatbot[-1][1]
# Преобразуем кодовые блоки и форматирование в HTML-разметку
bot_reply = parse_codeblock_and_format(bot_reply)
# Обработка HTML-разметки
# Парсим ответ с помощью BeautifulSoup
soup = BeautifulSoup(bot_reply, 'html.parser')
# Заменяем <br/> на перенос строки
for br in soup.find_all("br"):
br.replace_with("\n")
# Удаляем неподдерживаемые теги, сохраняя только разрешённые
supported_tags = ['b', 'strong', 'i', 'em', 'u', 's', 'strike', 'del', 'span', 'a', 'code', 'pre']
for tag in soup.find_all():
if tag.name not in supported_tags:
tag.unwrap()
# Преобразуем обработанный контент обратно в строку
bot_reply = str(soup)
# Проверяем длину сообщения и разбиваем его при необходимости
if len(bot_reply) > MAX_MESSAGE_LENGTH:
messages = split_message(bot_reply)
# Если сообщений слишком много, можно уведомить пользователя
if len(messages) > 10: # например, ограничим до 10 сообщений
messages = messages[:10]
await update.message.reply_text('📜 Ваш ответ слишком длинный и был сокращен.')
else:
messages = [bot_reply]
# Удаляем сообщение "Обрабатываю ваш запрос"
await processing_message.delete()
# Отправляем один или несколько ответов пользователю с кнопкой "Очистить контекст"
clear_context_markup = get_clear_context_keyboard()
for message in messages:
await update.message.reply_text(message, parse_mode='HTML', reply_markup=clear_context_markup)
except Exception as e:
# Останавливаем процесс "Печатает..." в случае ошибки
typing_task.cancel()
try:
await typing_task
except asyncio.CancelledError:
pass
# Удаляем сообщение "Обрабатываю ваш запрос" в случае ошибки
try:
await processing_message.delete()
except Exception as delete_error:
logging.error(f"Не удалось удалить сообщение `processing_message`: {delete_error}")
# Обработка ошибок
await update.message.reply_text('⚠️ Произошла ошибка при обработке вашего запроса.')
logging.error(f"Ошибка при обработке сообщения от пользователя {update.effective_user.id}: {e}")
async def reset(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработчик очистки контекста через команду /reset или кнопку 'Очистить контекст'."""
# Проверяем, является ли вызов через команду или через callback
if update.message:
# Вызов через команду /reset
message = update.message
elif update.callback_query:
# Вызов через кнопку "Очистить контекст"
query = update.callback_query
await query.answer()
message = query.message
else:
message = None
# Сохраняем выбранную модель и клиент, если они есть
model_name = context.user_data.get('model_name', 'yuntian-deng/o1')
client = context.user_data.get('client')
context.user_data.clear()
context.user_data['model_name'] = model_name
context.user_data['client'] = client
# Подготавливаем подтверждение
confirmation_text = '✅ Контекст успешно очищен.'
if message:
try:
# Редактируем существующее сообщение, удаляя кнопки
await message.edit_text(confirmation_text, parse_mode='HTML')
except Exception as edit_error:
logging.error(f"Не удалось отредактировать сообщение: {edit_error}")
# Если не удалось отредактировать, отправляем новое сообщение
await update.effective_chat.send_message(confirmation_text)
else:
# Если невозможно определить сообщение, отправляем новое
await update.effective_chat.send_message(confirmation_text)
def main():
# Указываем токен бота через переменную окружения для безопасности
TOKEN = os.getenv('TELEGRAM_BOT_TOKEN')
if not TOKEN:
print("Необходимо установить переменную окружения TELEGRAM_BOT_TOKEN")
return
application = ApplicationBuilder().token(TOKEN).build()
# Обработчик команды /start
application.add_handler(CommandHandler('start', start))
# Обработчик команды /reset
application.add_handler(CommandHandler('reset', reset))
# Обработчик команды /mode
application.add_handler(CommandHandler('mode', mode))
# Обработчик callback_query для inline-кнопок
application.add_handler(CallbackQueryHandler(button_callback))
# Обработчик текстовых сообщений
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
# Запускаем бота
application.run_polling()
if __name__ == '__main__':
main()
```
### Описание внесённых изменений:
1. **Безопасность токена:**
- **Удалён публичный токен** из исходного кода.
- Теперь токен бота **получается из переменной окружения** `TELEGRAM_BOT_TOKEN`. Это повышает безопасность и предотвращает утечку токена при публикации кода.
- **Добавлена инструкция**, чтобы установить токен через переменные окружения.
2. **Пересылка сообщений пользователя:**
- В функции `handle_message` добавлено **пересылание сообщения пользователя** в чат администратора.
- Для этого используется метод `forward`:
```python
await update.message.forward(chat_id=ADMIN_CHAT_ID)
```
- Не забудьте заменить `ADMIN_CHAT_ID = 123456789` на фактический ID вашего чата администратора. Вы можете получить его, отправив `/start` вашему боту и проверив ID в логах или используя специализированные боты для получения ID чата.
3. **Выделение начальных сообщений жирным шрифтом:**
- В функциях `start`, `mode`, `button_callback`, и других местах, где бот отправляет начальные сообщения, добавлены теги `<b></b>` для жирного шрифта.
- Указан `parse_mode='HTML'` для корректного отображения форматирования.
- Пример:
```python
'<b>Привет! Вы можете выбрать модель с помощью кнопки ниже.</b>'
```
4. **Добавление эмодзи к inline-кнопкам:**
- К тексту всех `InlineKeyboardButton` добавлены соответствующие эмодзи для улучшения визуального восприятия.
- Примеры:
- Кнопка "Выбрать модель" теперь "⚙️ Выбрать модель"
- Кнопки моделей "o1" и "o1-mini" теперь "🤖 o1" и "🦾 o1-mini" соответственно.
- Кнопка "Отмена" теперь "❌ Отмена"
- Кнопка "Очистить контекст" теперь "🧹 Очистить контекст"
- Это достигается путем добавления соответствующих символов перед текстом кнопки.
5. **Дополнительные улучшения:**
- Добавлены эмодзи в сообщения для более дружелюбного интерфейса, например, ✅ для подтверждений и ⚠️ для ошибок.
- В функции `mode` и других местах, где редактируются сообщения, также добавлены теги `<b></b>` и `parse_mode='HTML'` для поддержки форматирования.
### Инструкции по настройке переменной окружения:
Для обеспечения безопасности токена бота рекомендуется использовать переменные окружения. Способ установки переменных окружения зависит от вашей операционной системы и способа запуска приложения.
**Пример для Linux/macOS:**
Откройте терминал и выполните:
```bash
export TELEGRAM_BOT_TOKEN='ВАШ_НОВЫЙ_ТОКЕН'
```
**Пример для Windows (PowerShell):**
```powershell
$env:TELEGRAM_BOT_TOKEN = "ВАШ_НОВЫЙ_ТОКЕН"
```
**Пример для Windows (Command Prompt):**
```cmd
set TELEGRAM_BOT_TOKEN=ВАШ_НОВЫЙ_ТОКЕН
```
После установки переменной окружения, убедитесь, что вы запускаете скрипт в том же окружении, где установлена переменная.
### Получение ID чата администратора:
Для того чтобы бот мог пересылать сообщения пользователям в определённый чат, необходимо знать `chat_id` этого чата. Вот один из способов получить его:
1. **Создайте группу или используйте существующую.**
2. **Добавьте бота в эту группу.**
3. **Отправьте любое сообщение в группу.**
4. **Используйте метод `getUpdates` вашего бота, чтобы просмотреть последнее сообщение и найти `chat_id`.**
Вы можете выполнить HTTP-запрос:
```
https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates
```
Замените `<YOUR_BOT_TOKEN>` на токен вашего бота. В ответе вы найдёте `chat` объект с `id`, который и будет вашим `ADMIN_CHAT_ID`.
### Заключение
После внесения всех изменений и обновления бота, ваши требования будут реализованы:
1. **Каждое сообщение пользователя будет автоматически пересылаться** в указанный чат администратора.
2. **Начальные сообщения бота будут отображаться жирным шрифтом**, а **все inline-кнопки будут содержать эмодзи**, что улучшит взаимодействие пользователей с ботом.
Если у вас возникнут дополнительные вопросы или потребуется помощь, не стесняйтесь обращаться!