feat: подпись «Оператор поддержки» на ответе оператора

Ответ из топика приходил пользователю без подписи, и было непонятно, кто
пишет — бот или человек.

- Текст уходит одним сообщением: «Оператор поддержки:\n<текст>».
- У медиа copy_message не позволяет подменить caption, поэтому подпись
  отправляется отдельным сообщением перед копией; caption медиа, если был,
  попадает в то же сообщение.
- Подпись задаётся SUPPORT_OPERATOR_SIGNATURE (default «Оператор поддержки»).
- Заметки с префиксом // по-прежнему не уходят пользователю.

Тесты: 236 OK (6 новых — текст, медиа с caption, медиа без caption,
переопределение подписи через env).

VERSION: 0.1.0+56 -> 0.1.0+57
This commit is contained in:
hermes committed 2026-10-11 19:59:34 +02:00
1 parent 3dd2924d5e
commit 6938c1d0f3
5 files changed
+109 -10

No files matched your search

+57
View File
@@ -345,6 +345,63 @@ class SupportStartTextTests(unittest.TestCase):
self.assertNotIn("оператор", rendered.lower())
class OperatorSignatureTests(unittest.IsolatedAsyncioTestCase):
"""Ответ оператора приходит пользователю с подписью «Оператор поддержки:»."""
def test_default_signature(self):
with patch.dict("os.environ", {"SUPPORT_OPERATOR_SIGNATURE": ""}, clear=False):
self.assertEqual(support.operator_signature(), "Оператор поддержки")
def test_signature_from_env(self):
with patch.dict("os.environ", {"SUPPORT_OPERATOR_SIGNATURE": "Саша"}, clear=False):
self.assertEqual(support.operator_signature(), "Саша")
async def test_text_reply_gets_signature_prefix(self):
bot = AsyncMock()
message = SimpleNamespace(text="Проверяю платёж", caption=None,
chat=SimpleNamespace(id=-100123))
await support._deliver_operator_reply(bot, message, 42)
bot.send_message.assert_awaited_once()
kwargs = bot.send_message.await_args.kwargs
self.assertEqual(kwargs["chat_id"], 42)
self.assertEqual(kwargs["text"], "Оператор поддержки:\nПроверяю платёж")
bot.copy_message.assert_not_awaited()
async def test_media_with_caption_sends_signature_then_copies(self):
bot = AsyncMock()
message = SimpleNamespace(text=None, caption="Скриншот платежа",
chat=SimpleNamespace(id=-100123), message_id=77)
await support._deliver_operator_reply(bot, message, 42)
self.assertEqual(
bot.send_message.await_args.kwargs["text"],
"Оператор поддержки:\nСкриншот платежа",
)
bot.copy_message.assert_awaited_once_with(
chat_id=42, from_chat_id=-100123, message_id=77
)
async def test_media_without_caption_sends_bare_signature_then_copies(self):
bot = AsyncMock()
message = SimpleNamespace(text=None, caption=None,
chat=SimpleNamespace(id=-100123), message_id=78)
await support._deliver_operator_reply(bot, message, 42)
self.assertEqual(bot.send_message.await_args.kwargs["text"], "Оператор поддержки:")
bot.copy_message.assert_awaited_once_with(
chat_id=42, from_chat_id=-100123, message_id=78
)
async def test_signature_env_override_is_used(self):
bot = AsyncMock()
message = SimpleNamespace(text="Готово", caption=None,
chat=SimpleNamespace(id=-100123))
with patch.dict("os.environ", {"SUPPORT_OPERATOR_SIGNATURE": "Саша"}, clear=False):
await support._deliver_operator_reply(bot, message, 42)
self.assertEqual(bot.send_message.await_args.kwargs["text"], "Саша:\nГотово")
class StartClosesSupportCaseTests(_TempDbCase):
"""/start — возврат в лобби, поэтому обращение поддержки должно закрываться.