Skip to content

fix: исправлена надёжность загрузки файлов#94

Merged
love-apples merged 4 commits intolove-apples:mainfrom
bish-x:fix/upload-reliability
Apr 14, 2026
Merged

fix: исправлена надёжность загрузки файлов#94
love-apples merged 4 commits intolove-apples:mainfrom
bish-x:fix/upload-reliability

Conversation

@bish-x
Copy link
Copy Markdown
Contributor

@bish-x bish-x commented Apr 8, 2026

Описание

Загрузка файлов (upload reliability)

1. Убрано закрытие глобальной сессии бота при ошибке upload

Файл: utils/message.py

При загрузке VIDEO/AUDIO, если upload-сервер не возвращал token, вызывался await bot.session.close(), что закрывало глобальную HTTP-сессию бота. После этого bot.session оставался не None (закрытый объект), условие пересоздания в request() не срабатывало — все последующие API-запросы бота падали с RuntimeError: Session is closed до перезапуска процесса.

2. Исправлен MIME-type при загрузке файлов через upload_file

Файл: connection/base.py

Ранее MIME-type конструировался механически: f"{type.value}/{ext.lstrip('.')}", что давало некорректные значения для распространённых форматов (.jpgimage/jpg вместо image/jpeg и т.д.). Заменено на mimetypes.guess_type() с фоллбэком {type}/*.

3. Добавлен timeout для fallback upload-сессий

Файл: connection/base.py

Fallback ClientSession() создавался без timeout, что могло привести к бесконечному ожиданию. Добавлен timeout=bot.default_connection.timeout.

Сериализация (send_message / edit_message)

4. Исправлена сериализация format в SendMessage и EditMessage

Файлы: methods/send_message.py, methods/edit_message.py

format передавался как .value (строка из enum), но API ожидает уже десериализованное значение. Убран лишний .value.

5. Поле text в SendMessage теперь опционально

Файл: methods/send_message.py

json["text"] = self.text выставлялось безусловно. Теперь text добавляется в payload только если не None — это нужно для отправки медиа без текста.

6. Устранена передача parse_mode при наличии format

Файл: bot.py

В send_message и edit_message одновременно передавались format и parse_mode, что могло вызвать конфликт. Теперь parse_mode=None при наличии format.

Прочие улучшения

7. Bot.__repr__ с маскировкой токена

Файл: bot.py

Добавлен __repr__, возвращающий Bot(token='***') для безопасного логирования.

8. get_ids() в UserAdded и UserRemoved

Файлы: types/updates/user_added.py, types/updates/user_removed.py

get_ids() возвращал (chat_id, inviter_id) / (chat_id, admin_id), что некорректно для маршрутизации FSM-контекстов. Исправлено на (chat_id, user.user_id).

9. context.get_data() возвращает копию

Файл: context/context.py

MemoryContext.get_data() возвращал ссылку на внутренний _context, что позволяло мутировать состояние без блокировки. Теперь возвращается .copy().

10. Логирование вытеснения LRU-контекстов

Файл: dispatcher.py

При вытеснении контекста из LRU-кеша теперь логируется ключ вытесненного контекста.

Тестирование

  • Все существующие тесты проходят
  • Добавлены тесты для: mimetypes-фоллбэка, temp-сессии с timeout, UserAdded.get_ids(), UserRemoved.get_ids()
  • ruff check / ruff format — без замечаний

bish-x added 2 commits April 8, 2026 19:48
- format.value → format: убран .value, так как TextFormat наследует StrEnum
  и сериализуется как строка автоматически (send_message.py, edit_message.py)
- text: null не отправляется когда text is None (send_message.py)
- устранён двойной DeprecationWarning для parse_mode в bot.send_message
  и bot.edit_message — передаётся parse_mode=None после resolve_format
- Убрано закрытие глобальной сессии бота при ошибке upload VIDEO/AUDIO:
  session.close() закрывал основную сессию, после чего все последующие
  запросы падали с RuntimeError: Session is closed
- Исправлен MIME-type при загрузке файлов: заменён механический
  f"{type.value}/{ext.lstrip('.')}" на mimetypes.guess_type() для
  корректных значений (.jpg→image/jpeg, .mp3→audio/mpeg и т.д.)
- Добавлен timeout для fallback ClientSession при закрытой основной
  сессии: без него upload мог висеть бесконечно при зависании сервера
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

PR нацелен на повышение надёжности загрузки медиа/файлов и устойчивости работы HTTP-сессий при upload-ошибках в maxapi.

Changes:

  • Убрано закрытие глобальной bot.session при ошибке загрузки VIDEO/AUDIO (и обновлён тест).
  • Исправлено определение content_type при загрузке файла по пути через mimetypes.guess_type() и добавлен timeout для временных upload-сессий.
  • Дополнительно внесены изменения вне описанного upload-скоупа (IDs в update-событиях, сериализация format в send/edit message, копирование данных контекста, Bot.__repr__, лог вытеснения контекста).

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/test_utils/test_message_media_upload.py Обновление теста под новое поведение (не закрывать глобальную сессию при upload-ошибке).
maxapi/utils/message.py Убрано закрытие bot.session при отсутствии upload token для VIDEO/AUDIO.
maxapi/connection/base.py MIME-type через mimetypes.guess_type() и timeout для fallback ClientSession() при upload.
maxapi/types/updates/user_removed.py get_ids() теперь возвращает user.user_id вместо admin_id.
maxapi/types/updates/user_added.py get_ids() теперь возвращает user.user_id вместо inviter_id.
maxapi/methods/send_message.py Изменена сборка JSON (в т.ч. сериализация format, установка text).
maxapi/methods/edit_message.py Изменена сериализация format в JSON.
maxapi/dispatcher.py Добавлен debug-лог при LRU-вытеснении контекста.
maxapi/context/context.py get_data() возвращает копию контекста вместо исходного dict.
maxapi/bot.py Добавлен __repr__, изменена прокидка parse_mode в методы сообщений.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


if self.format is not None:
json["format"] = self.format.value
json["format"] = self.format
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

json["format"] now stores the TextFormat enum instance directly. In this codebase the JSON payload is expected to contain the string value (TextFormat.*.value), and current tests assert on that (see tests/test_format_parameter.py). Please serialize explicitly (e.g., use .value or str(self.format)) to keep the request payload stable and tests passing.

Suggested change
json["format"] = self.format
json["format"] = self.format.value

Copilot uses AI. Check for mistakes.
json["notify"] = self.notify
if self.format is not None:
json["format"] = self.format.value
json["format"] = self.format
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

json["format"] is set to the TextFormat enum instance rather than its string value. The surrounding code/tests expect the API payload to use the string form (e.g., .value), so this will break tests/test_format_parameter.py and may change the outgoing JSON unexpectedly. Serialize the enum explicitly before putting it into json.

Suggested change
json["format"] = self.format
json["format"] = self.format.value

Copilot uses AI. Check for mistakes.
Comment thread maxapi/bot.py
Comment on lines +203 to +205
def __repr__(self) -> str:
return "Bot(token='***')"

Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description focuses on upload reliability, but this PR also changes unrelated behaviors (e.g., adding a custom Bot.__repr__, changing format serialization in send/edit message, changing update get_ids results, context get_data returning a copy). Please either update the PR description to cover these changes or split them into a separate PR to keep review scope clear.

Copilot uses AI. Check for mistakes.
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 11, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

bish-x and others added 2 commits April 12, 2026 16:49
…_ids

Добавлены тесты для:
- mimetypes.guess_type() фоллбэк в upload_file (base.py)
- Создание вре��енной ClientSession с timeout при отсутствии сессии
- UserAdded.get_ids() и UserRemoved.get_ids() возвращающие user.user_id

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Подтянуты PR из upstream: love-apples#93 (FSM), love-apples#96 (download_file),
love-apples#101 (fetch user/chat), love-apples#105 (ClipboardButton), love-apples#109 (share payload),
love-apples#110 (webhook secret warning).

Конфликт в tests/test_types.py: принят upstream-стиль (явный
update_type, разнесённые assert). Сохранены доп. тесты
test_get_ids_ignores_inviter_id / test_get_ids_ignores_admin_id —
их purpose именно цель PR love-apples#94 (не путать inviter_id/admin_id с
user.user_id).
@bish-x
Copy link
Copy Markdown
Contributor Author

bish-x commented Apr 14, 2026

Конфликты разрешены, ветка перенесена на актуальный upstream/main (включая #93/#96/#101/#105/#109/#110).

Большая часть исходного scope PR уже въехала в main через другие PR: убран bot.session.close() в utils/message.py, фоллбэк mimetypes.guess_type и ClientTimeout в connection/base.py, get_ids() → user.user_id в user_added/user_removed, debug-лог LRU в dispatcher.py, get_data().copy() в context.py, Bot.__repr__ и дедуп DeprecationWarning для parse_mode в bot.py.

После мерджа осталось:

  • methods/send_message.py / methods/edit_message.pyjson["format"] = self.format (без .value, т.к. TextFormat = ParseMode = StrEnumjson.dumps корректно сериализует);
  • tests/test_types.py — два дополнительных теста на get_ids(), проверяющие что inviter_id/admin_id НЕ возвращаются вместо user.user_id;
  • tests/test_upload_file.py — покрытие mimetypes-фоллбэка и логики временной ClientSession в BaseConnection.upload_file.

Локально: pytest tests/ --ignore=tests/test_webhook515 passed, 14 skipped, ruff clean. Один merge-commit поверх трёх PR-коммитов.

@love-apples love-apples merged commit 552ca45 into love-apples:main Apr 14, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants