USER
Исправь данную ошибку:
No error handlers are registered, logging exception.
Traceback (most recent call last):
File "d:\Visual Studio\project\GPTTgBOT\.venv\Lib\site-packages elegram\ext\_jobqueue.py", line 777, in _run
await self.callback(context)
File "d:\Visual Studio\project\GPTTgBOT empCodeRunnerFile.py", line 45, in process_accumulated_message
update_copy.message.text = full_message
^^^^^^^^^^^^^^^^^^^^^^^^
File "d:\Visual Studio\project\GPTTgBOT\.venv\Lib\site-packages elegram\_telegramobject.py", line 161, in __setattr__
raise AttributeError(
AttributeError: Attribute `text` of class `Message` can't be set!
Код:
from collections import defaultdict
import re
import logging
from telegram import Update, ForceReply
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes, MessageHandler, filters
from gradio_client import Client
import time
import copy
TOKEN = "7626716371:AAFTR6OHq2JJXwmo7BPUaN3Ef3Av4eNBOKY"
# Включение логирования
logger = logging.getLogger(__name__)
# Инициализация клиента Gradio
try:
logger.info("Initializing Gradio client...")
client = Client("yuntian-deng/o1")
logger.info("Gradio client initialized successfully")
except Exception as e:
logger.error(f"Failed to initialize Gradio client: {e}", exc_info=True)
# Словарь для хранения истории чатов для каждого пользователя
chat_histories = {}
# разрешённые ID даша стёпа дениз
ALLOWED_USER_IDS = [5197267693, 1956293525, 424323538]
# Словарь для хранения частичных сообщений
partial_messages = defaultdict(dict)
# Максимальный интервал между сообщениями в секундах
MAX_INTERVAL = 5 # Вы можете настроить это значение
async def process_accumulated_message(context: ContextTypes.DEFAULT_TYPE):
user_id = context.job.data['user_id']
if user_id in partial_messages:
full_message = partial_messages[user_id]['text']
# Удаляем накопленное сообщение из словаря
del partial_messages[user_id]
# Создаем глубокую копию объекта update
update_copy = copy.deepcopy(context.job.data['update'])
update_copy.message.text = full_message
# Вызываем вашу функцию handle_message с полным сообщением
await handle_message(update_copy, context)
async def message_collector(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id
message_text = update.message.text
current_time = time.time()
# Добавляем или обновляем частичное сообщение пользователя
if user_id in partial_messages:
partial_messages[user_id]['text'] += message_text
partial_messages[user_id]['last_time'] = current_time
else:
partial_messages[user_id] = {'text': message_text, 'last_time': current_time}
# Отменяем предыдущую задачу обработки сообщений для этого пользователя, если она есть
if 'jobs' not in context.chat_data:
context.chat_data['jobs'] = {}
if user_id in context.chat_data['jobs']:
context.chat_data['jobs'][user_id].schedule_removal()
# Планируем новую задачу обработки через MAX_INTERVAL секунд
job = context.job_queue.run_once(
process_accumulated_message,
when=MAX_INTERVAL,
data={'user_id': user_id, 'update': update}
)
context.chat_data['jobs'][user_id] = job
def markdown_to_telegram_html(text):
# Заголовки
text = re.sub(r'^# (.+)$', r'<b></b>', text, flags=re.MULTILINE)
text = re.sub(r'^## (.+)$', r'<b></b>', text, flags=re.MULTILINE)
text = re.sub(r'^### (.+)$', r'<b></b>', text, flags=re.MULTILINE)
# Жирный курсивный текст
text = re.sub(r'\*\*\*(.*?)\*\*\*', r'<b><i></i></b>', text)
# Жирный текст
text = re.sub(r'\*\*(.*?)\*\*', r'<b></b>', text)
# Курсивный текст
text = re.sub(r'\*(.*?)\*', r'<i></i>', text)
# Зачёркнутый текст
text = re.sub(r'~~(.*?)~~', r'<s></s>', text)
# Горизонтальная линия (заменяем на символы, поддерживаемые Telegram)
text = re.sub(r'^---$', r'――――――――――', text, flags=re.MULTILINE)
# Ссылки
text = re.sub(r'\[(.*?)\]\((.*?)\)', r'<a href=""></a>', text)
# Блоки кода (многострочные)
text = re.sub(r'```(?:\w+
)?([\s\S]+?)```', r'<pre></pre>', text)
# Однострочный код
text = re.sub(r'`(.*?)`', r'<code></code>', text)
# Списки (упрощенно)
text = re.sub(r'^-\s(.+)$', r'• ', text, flags=re.MULTILINE)
text = re.sub(r'^\d+\.\s(.+)$', r' ', text, flags=re.MULTILINE)
# Удаление неподдерживаемых элементов
#text = re.sub(r'!\[.*?\]\(.*?\)', '', text) # Изображения
#text = re.sub(r'^\|.*\|.*$', '', text, flags=re.MULTILINE) # Таблицы
#text = re.sub(r':[a-z_+-]+:', '', text) # Эмодзи
#text = re.sub(r'[\^~](.+?)[\^~]', r'', text) # Подстрочный и надстрочный текст
#text = re.sub(r'\$\$.*?\$\$', '', text, flags=re.DOTALL) # Формулы TeX
#text = re.sub(r'\[\d+\]:\s*http.*$', '', text, flags=re.MULTILINE) # Сноски
#text = re.sub(r'^> (.+)$', r'', text, flags=re.MULTILINE) # Цитаты
# Удаление неподдерживаемых HTML-тегов
def remove_unsupported_html_tags(text):
allowed_tags = ['a', 'b', 'strong', 'i', 'em', 'u', 'ins', 's', 'strike', 'del', 'code', 'pre']
# Исправленное регулярное выражение
pattern = r'</?(?!(?:' + '|'.join(allowed_tags) + r'))([a-zA-Z]+)(?:\s[^>]*)?>'
text = re.sub(pattern, '', text)
return text
text = remove_unsupported_html_tags(text)
return text.strip()
# Команда /start
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
user = update.effective_user
# Проверка, разрешен ли доступ пользователю
if user.id not in ALLOWED_USER_IDS:
await update.message.reply_text("Извините, у вас нет доступа к этому боту.")
return
await update.message.reply_html(
rf"Привет, {user.mention_html()}! Как я могу вам помочь?",
reply_markup=ForceReply(selective=True),
)
# Обработка текстовых сообщений
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
user_id = update.effective_user.id
# Проверка, разрешен ли доступ пользователю
if user_id not in ALLOWED_USER_IDS:
await update.message.reply_text("Извините, у вас нет доступа к этому боту.")
return
user_message = update.message.text
# Получение истории чата пользователя или инициализация новой
history = chat_histories.get(user_id, [])
try:
# Вызов API /predict
result = client.predict(
inputs=user_message,
top_p=1,
temperature=1,
chat_counter=len(history),
chatbot=history,
api_name="/predict"
)
# Разбор результата
chatbot_output, chat_counter, status_code, reset_text = result
# Извлечение последнего ответа бота
if chatbot_output and len(chatbot_output) > 0:
last_message = chatbot_output[-1]
if isinstance(last_message, list) and len(last_message) == 2:
bot_reply = last_message[1]
else:
bot_reply = "Произошла непредвиденная ошибка."
else:
bot_reply = "Произошла непредвиденная ошибка."
# Обновление истории чата
history.append((user_message, bot_reply))
chat_histories[user_id] = history
# Отправка ответа пользователю
def tokenize(message):
tokens = []
pos = 0
length = len(message)
while pos < length:
if message[pos] == '<':
# Это тег
end_tag_pos = message.find('>', pos)
if end_tag_pos == -1:
# Неполный тег, считаем остальное текстом
tokens.append(('text', message[pos:]))
break
else:
token_content = message[pos+1:end_tag_pos]
# Проверяем, является ли это закрывающим тегом
if token_content.startswith('/'):
tag_name = token_content[1:].split()[0]
tokens.append(('end_tag', tag_name, '<'+token_content+'>'))
else:
# Тег может содержать атрибуты
tag_name = token_content.split()[0]
tokens.append(('start_tag', tag_name, '<'+token_content+'>'))
pos = end_tag_pos + 1
else:
# Это текст
next_tag_pos = message.find('<', pos)
if next_tag_pos == -1:
# Больше нет тегов
tokens.append(('text', message[pos:]))
break
else:
tokens.append(('text', message[pos:next_tag_pos]))
pos = next_tag_pos
return tokens
def generate_closing_tags(open_tags):
closing_tags = ''
for start_tag in reversed(open_tags):
# Получаем имя тега из стартового тега
tag_content = start_tag[1:-1].strip()
if ' ' in tag_content:
tag_name = tag_content.split(' ',1)[0]
else:
tag_name = tag_content
closing_tags += '</{}>'.format(tag_name)
return closing_tags
async def send_large_message(message, update):
MAX_MESSAGE_LENGTH = 4000
tokens = tokenize(message)
open_tags = []
current_part = ''
current_length = 0
parts = []
for token in tokens:
if token[0] == 'start_tag':
tag_name = token[1]
tag_string = token[2]
token_length = len(tag_string)
if current_length + token_length > MAX_MESSAGE_LENGTH:
# Закрываем теги и начинаем новую часть
closing_tags = generate_closing_tags(open_tags)
current_part += closing_tags
parts.append(current_part)
current_part = ''
current_length = 0
# Открываем теги снова
opening_tags = ''.join(open_tags)
current_part += opening_tags
current_length += len(opening_tags)
current_part += tag_string
current_length += token_length
open_tags.append(tag_string)
elif token[0] == 'end_tag':
tag_name = token[1]
tag_string = token[2]
token_length = len(tag_string)
if current_length + token_length > MAX_MESSAGE_LENGTH:
# Закрываем теги и начинаем новую часть
closing_tags = generate_closing_tags(open_tags)
current_part += closing_tags
parts.append(current_part)
current_part = ''
current_length = 0
# Открываем теги снова
opening_tags = ''.join(open_tags)
current_part += opening_tags
current_length += len(opening_tags)
current_part += tag_string
current_length += token_length
if open_tags:
open_tags.pop()
elif token[0] == 'text':
text_content = token[1]
text_length = len(text_content)
pos = 0
while pos < text_length:
remaining_space = MAX_MESSAGE_LENGTH - current_length
if remaining_space <= 0:
# Закрываем теги и начинаем новую часть
closing_tags = generate_closing_tags(open_tags)
current_part += closing_tags
parts.append(current_part)
current_part = ''
current_length = 0
# Открываем теги снова
opening_tags = ''.join(open_tags)
current_part += opening_tags
current_length += len(opening_tags)
remaining_space = MAX_MESSAGE_LENGTH - current_length
take_length = min(remaining_space, text_length - pos)
current_part += text_content[pos:pos+take_length]
current_length += take_length
pos += take_length
else:
pass
# Закрываем оставшиеся открытые теги и добавляем последнюю часть
if open_tags:
closing_tags = generate_closing_tags(open_tags)
current_part += closing_tags
if current_part:
parts.append(current_part)
# Отправляем части сообщения
for part in parts:
await update.message.reply_text(part, parse_mode='HTML')
send_bot_reply = markdown_to_telegram_html(bot_reply)
print(send_bot_reply)
await send_large_message(send_bot_reply, update)
except Exception as e:
logger.error(f"Ошибка при обработке сообщения: {e}", exc_info=True)
await update.message.reply_text("Извините, произошла ошибка при обработке вашего запроса.", parse_mode='HTML')
# Команда /reset для сброса истории чата
async def reset_chat(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
user_id = update.effective_user.id
chat_histories[user_id] = []
await update.message.reply_text("Чат сброшен. Начнем с чистого листа!", parse_mode='HTML')
async def get_my_id(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
user_id = update.effective_user.id
await update.message.reply_text(f"Ваш Telegram ID: {user_id}")
def main():
# Замените 'YOUR_TELEGRAM_BOT_TOKEN' на токен вашего бота
application = ApplicationBuilder().token(TOKEN).build()
# Регистрируем обработчики команд и сообщений
application.add_handler(CommandHandler("start", start))
application.add_handler(CommandHandler("clear", reset_chat))
application.add_handler(CommandHandler("myid", get_my_id)) # Добавьте эту строку
message_handler = MessageHandler(filters.TEXT & ~filters.COMMAND, message_collector)
application.add_handler(message_handler)
# Запуск бота
application.run_polling()
if __name__ == '__main__':
main()
ASSISTANT
Ошибка, которую вы получаете, связана с тем, что атрибут `text` объекта `Message` из библиотеки `python-telegram-bot` является доступным только для чтения и не может быть изменен после создания объекта. Поэтому попытка установить `update_copy.message.text = full_message` вызывает `AttributeError`.
**Причина ошибки:**
Атрибуты объектов `Message` в библиотеке `python-telegram-bot` являются неизменяемыми после их создания. Это сделано для обеспечения целостности данных и предотвращения непреднамеренных изменений исходных сообщений.
**Как исправить:**
Вместо того чтобы пытаться изменить `update_copy.message.text`, вы можете передать накопленное сообщение `full_message` непосредственно в функцию `handle_message` в качестве дополнительного аргумента. Для этого нужно немного изменить сигнатуру функции `handle_message` и обеспечить, чтобы она использовала переданный текст сообщения вместо `update.message.text`, если он предоставлен.
**Шаги для исправления ошибки:**
1. **Измените сигнатуру функции `handle_message`:**
Добавьте дополнительный параметр `user_message=None` к функции `handle_message`. Это позволит передавать собственный текст сообщения в функцию.
```python
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE, user_message=None) -> None:
```
2. **Обновите функцию `handle_message` для использования переданного текста сообщения:**
Внутри функции проверьте, предоставлен ли `user_message`. Если нет, используйте стандартное `update.message.text`.
```python
if user_message is None:
user_message = update.message.text
```
3. **Измените функцию `process_accumulated_message`:**
В этой функции вам не нужно создавать копию объекта `update` и пытаться изменить `message.text`. Вместо этого просто передайте `full_message` в `handle_message` как `user_message`.
```python
async def process_accumulated_message(context: ContextTypes.DEFAULT_TYPE):
user_id = context.job.data['user_id']
if user_id in partial_messages:
full_message = partial_messages[user_id]['text']
# Удаляем накопленное сообщение из словаря
del partial_messages[user_id]
# Вызываем функцию handle_message с полным сообщением
await handle_message(context.job.data['update'], context, user_message=full_message)
```
**Обновленный код:**
```python
from collections import defaultdict
import re
import logging
from telegram import Update, ForceReply
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes, MessageHandler, filters
from gradio_client import Client
import time
import copy
TOKEN = "7626716371:AAFTR6OHq2JJXwmo7BPUaN3Ef3Av4eNBOKY"
# Включение логирования
logger = logging.getLogger(__name__)
# Инициализация клиента Gradio
try:
logger.info("Initializing Gradio client...")
client = Client("yuntian-deng/o1")
logger.info("Gradio client initialized successfully")
except Exception as e:
logger.error(f"Failed to initialize Gradio client: {e}", exc_info=True)
# Словарь для хранения истории чатов для каждого пользователя
chat_histories = {}
# Разрешённые ID: Даша, Стёпа, Дениз
ALLOWED_USER_IDS = [5197267693, 1956293525, 424323538]
# Словарь для хранения частичных сообщений
partial_messages = defaultdict(dict)
# Максимальный интервал между сообщениями в секундах
MAX_INTERVAL = 5 # Вы можете настроить это значение
async def process_accumulated_message(context: ContextTypes.DEFAULT_TYPE):
user_id = context.job.data['user_id']
if user_id in partial_messages:
full_message = partial_messages[user_id]['text']
# Удаляем накопленное сообщение из словаря
del partial_messages[user_id]
# Вызываем функцию handle_message с полным сообщением
await handle_message(context.job.data['update'], context, user_message=full_message)
async def message_collector(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id
message_text = update.message.text
current_time = time.time()
# Добавляем или обновляем частичное сообщение пользователя
if user_id in partial_messages:
partial_messages[user_id]['text'] += message_text
partial_messages[user_id]['last_time'] = current_time
else:
partial_messages[user_id] = {'text': message_text, 'last_time': current_time}
# Отменяем предыдущую задачу обработки сообщений для этого пользователя, если она есть
if 'jobs' not in context.chat_data:
context.chat_data['jobs'] = {}
if user_id in context.chat_data['jobs']:
context.chat_data['jobs'][user_id].schedule_removal()
# Планируем новую задачу обработки через MAX_INTERVAL секунд
job = context.job_queue.run_once(
process_accumulated_message,
when=MAX_INTERVAL,
data={'user_id': user_id, 'update': update}
)
context.chat_data['jobs'][user_id] = job
def markdown_to_telegram_html(text):
# Ваш код преобразования Markdown в HTML для Telegram...
# (оставим без изменений для краткости)
return text.strip()
# Команда /start
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
user = update.effective_user
# Проверка, разрешен ли доступ пользователю
if user.id not in ALLOWED_USER_IDS:
await update.message.reply_text("Извините, у вас нет доступа к этому боту.")
return
await update.message.reply_html(
rf"Привет, {user.mention_html()}! Как я могу вам помочь?",
reply_markup=ForceReply(selective=True),
)
# Обработка текстовых сообщений
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE, user_message=None) -> None:
user_id = update.effective_user.id
# Проверка, разрешен ли доступ пользователю
if user_id not in ALLOWED_USER_IDS:
await update.message.reply_text("Извините, у вас нет доступа к этому боту.")
return
if user_message is None:
user_message = update.message.text
# Получение истории чата пользователя или инициализация новой
history = chat_histories.get(user_id, [])
try:
# Вызов API /predict
result = client.predict(
inputs=user_message,
top_p=1,
temperature=1,
chat_counter=len(history),
chatbot=history,
api_name="/predict"
)
# Разбор результата
chatbot_output, chat_counter, status_code, reset_text = result
# Извлечение последнего ответа бота
if chatbot_output and len(chatbot_output) > 0:
last_message = chatbot_output[-1]
if isinstance(last_message, list) and len(last_message) == 2:
bot_reply = last_message[1]
else:
bot_reply = "Произошла непредвиденная ошибка."
else:
bot_reply = "Произошла непредвиденная ошибка."
# Обновление истории чата
history.append((user_message, bot_reply))
chat_histories[user_id] = history
# Отправка ответа пользователю
# (Ваш код отправки больших сообщений без изменений)
send_bot_reply = markdown_to_telegram_html(bot_reply)
print(send_bot_reply)
await send_large_message(send_bot_reply, update)
except Exception as e:
logger.error(f"Ошибка при обработке сообщения: {e}", exc_info=True)
await update.message.reply_text("Извините, произошла ошибка при обработке вашего запроса.", parse_mode='HTML')
# Команда /reset для сброса истории чата
async def reset_chat(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
user_id = update.effective_user.id
chat_histories[user_id] = []
await update.message.reply_text("Чат сброшен. Начнем с чистого листа!", parse_mode='HTML')
async def get_my_id(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
user_id = update.effective_user.id
await update.message.reply_text(f"Ваш Telegram ID: {user_id}")
def main():
# Замените 'YOUR_TELEGRAM_BOT_TOKEN' на токен вашего бота
application = ApplicationBuilder().token(TOKEN).build()
# Регистрируем обработчики команд и сообщений
application.add_handler(CommandHandler("start", start))
application.add_handler(CommandHandler("clear", reset_chat))
application.add_handler(CommandHandler("myid", get_my_id)) # Добавьте эту строку
message_handler = MessageHandler(filters.TEXT & ~filters.COMMAND, message_collector)
application.add_handler(message_handler)
# Запуск бота
application.run_polling()
if __name__ == '__main__':
main()
```
**Объяснение изменений:**
- **Функция `handle_message` теперь принимает дополнительный аргумент `user_message`:** Это позволяет нам передавать накопленное сообщение напрямую, без необходимости изменять `update.message.text`.
- **В функции `handle_message`, если `user_message` не предоставлено, оно устанавливается из `update.message.text`:** Это обеспечивает обратную совместимость и позволяет функции работать как с обычными сообщениями, так и с накопленными.
- **В функции `process_accumulated_message` мы больше не пытаемся изменить `update.message.text`:** Вместо этого просто вызываем `handle_message`, передавая `full_message` как `user_message`.
**Дополнительные рекомендации:**
- **Избегайте изменения объектов, которые предоставляются библиотекой `python-telegram-bot`:** Многие из них являются неизменяемыми или изменения в них могут привести к непредвиденным ошибкам.
- **Используйте контекст или дополнительные аргументы для передачи данных между функциями:** Это более безопасный и устойчивый подход.
- **Следите за тем, чтобы все функции получали необходимые данные без необходимости изменять объекты библиотеки.**
Теперь ваш бот должен работать правильно, обрабатывая накопленные сообщения без возникновения ошибки `AttributeError`.