Приветствие снова читается одним блоком: фото с короткой подписью и одно сообщение
Первая строка welcome.text уходит подписью к фото, остальное вместе с text2 и text3 — одним сообщением с кнопкой. Так текст не разбит на три части и не упирается в лимит подписи к фото (1024 символа). В редакторе welcome.text появилась подсказка, что первая строка — подпись. Если понадобится больше гибкости, следующим шагом можно сделать последовательность сообщений с картинками.
This commit is contained in:
parent
8ae4e8bb6c
commit
34b6574e43
|
|
@ -151,7 +151,7 @@ make backup E=test # на домашнем сервере: ./scripts/clickho
|
||||||
|
|
||||||
## Версионирование
|
## Версионирование
|
||||||
|
|
||||||
- Единый источник версии — `common/version.py` (`__version__`), текущая — `0.4.14`.
|
- Единый источник версии — `common/version.py` (`__version__`), текущая — `0.4.15`.
|
||||||
- Схема — SemVer-подобная (`MAJOR.MINOR.PATCH`); git-тег = `v` + версия (`v0.1`).
|
- Схема — SemVer-подобная (`MAJOR.MINOR.PATCH`); git-тег = `v` + версия (`v0.1`).
|
||||||
- Docker-образы тегируются версией (Блок 2), события ClickHouse получают `bot_version` (Блок 3).
|
- Docker-образы тегируются версией (Блок 2), события ClickHouse получают `bot_version` (Блок 3).
|
||||||
- Выпуск: изменить `common/version.py` → коммит → аннотированный тег `vX.Y` → `git push --tags`.
|
- Выпуск: изменить `common/version.py` → коммит → аннотированный тег `vX.Y` → `git push --tags`.
|
||||||
|
|
@ -180,6 +180,13 @@ git tag -a vX.Y -m "Release vX.Y"
|
||||||
git push origin master --tags
|
git push origin master --tags
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Приветствие новых участников
|
||||||
|
|
||||||
|
Бот отправляет фото с короткой персональной подписью (первая строка шаблона `welcome.text`),
|
||||||
|
а затем одно сообщение со всем остальным текстом (`welcome.text` без первой строки +
|
||||||
|
`welcome.text2` + `welcome.text3`) и кнопкой на канал. Так приветствие остаётся одним
|
||||||
|
смысловым блоком, но не упирается в лимит подписи к фото (1024 символа).
|
||||||
|
|
||||||
## Пасхальное событие «Яички»
|
## Пасхальное событие «Яички»
|
||||||
|
|
||||||
`/яички` работает только в сезон: по умолчанию с **1 апреля по православную Пасху**
|
`/яички` работает только в сезон: по умолчанию с **1 апреля по православную Пасху**
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,12 @@
|
||||||
<p class="mt-1 text-xs text-slate-500">
|
<p class="mt-1 text-xs text-slate-500">
|
||||||
Лимит Telegram — {{ max_length }} символов. Теги подставляет рендерер бота; неизвестный тег — ошибка.
|
Лимит Telegram — {{ max_length }} символов. Теги подставляет рендерер бота; неизвестный тег — ошибка.
|
||||||
</p>
|
</p>
|
||||||
|
{% if template.key == 'welcome.text' %}
|
||||||
|
<p class="mt-2 rounded border border-sky-200 bg-sky-50 p-2 text-xs text-sky-800">
|
||||||
|
Первая строка этого шаблона уходит подписью к фото, остальное — одним сообщением
|
||||||
|
вместе с <span class="font-mono">welcome.text2</span> и <span class="font-mono">welcome.text3</span>.
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
|
|
|
||||||
|
|
@ -52,18 +52,39 @@ async def welcome_handler(event: ChatMemberUpdated) -> None:
|
||||||
|
|
||||||
builder = InlineKeyboardBuilder()
|
builder = InlineKeyboardBuilder()
|
||||||
builder.button(text=welcome.MSG_WELCOME_BUTTON_CAPTION, url=welcome.MSG_WELCOME_BUTTON_URL)
|
builder.button(text=welcome.MSG_WELCOME_BUTTON_CAPTION, url=welcome.MSG_WELCOME_BUTTON_URL)
|
||||||
|
markup = builder.as_markup()
|
||||||
|
|
||||||
await event.bot.send_photo(
|
caption, body = welcome.split_welcome(
|
||||||
chat_id=event.chat.id,
|
await get_text("welcome.text"),
|
||||||
photo=FSInputFile("images/welcome_image.jpg"),
|
await get_text("welcome.text2"),
|
||||||
caption=render(await get_text("welcome.text"), {"username": user.username}),
|
await get_text("welcome.text3"),
|
||||||
)
|
)
|
||||||
await event.bot.send_message(
|
caption = render(caption, {"username": user.username})
|
||||||
chat_id=event.chat.id,
|
body = render(body, {"username": user.username})
|
||||||
text=await get_text("welcome.text2"),
|
|
||||||
reply_markup=builder.as_markup(),
|
# Подпись к фото ограничена 1024 символами; если не влезла — уйдёт в тело.
|
||||||
)
|
if len(caption) > 1024:
|
||||||
await event.bot.send_message(chat_id=event.chat.id, text=await get_text("welcome.text3"))
|
body = caption + (f"\n\n{body}" if body else "")
|
||||||
|
caption = ""
|
||||||
|
|
||||||
|
if body:
|
||||||
|
await event.bot.send_photo(
|
||||||
|
chat_id=event.chat.id,
|
||||||
|
photo=FSInputFile("images/welcome_image.jpg"),
|
||||||
|
caption=caption or None,
|
||||||
|
)
|
||||||
|
await event.bot.send_message(
|
||||||
|
chat_id=event.chat.id,
|
||||||
|
text=body,
|
||||||
|
reply_markup=markup,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await event.bot.send_photo(
|
||||||
|
chat_id=event.chat.id,
|
||||||
|
photo=FSInputFile("images/welcome_image.jpg"),
|
||||||
|
caption=caption or None,
|
||||||
|
reply_markup=markup,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.chat_member(ChatMemberUpdatedFilter(member_status_changed=LEAVE_TRANSITION))
|
@router.chat_member(ChatMemberUpdatedFilter(member_status_changed=LEAVE_TRANSITION))
|
||||||
|
|
|
||||||
|
|
@ -36,3 +36,18 @@ MSG_WELCOME_TEXT3 = """❗️<b>По игре</b>:
|
||||||
MSG_WELCOME_BUTTON_CAPTION = "Канал"
|
MSG_WELCOME_BUTTON_CAPTION = "Канал"
|
||||||
|
|
||||||
MSG_WELCOME_BUTTON_URL = "https://t.me/+DQk8MyN2y6QzNTM6"
|
MSG_WELCOME_BUTTON_URL = "https://t.me/+DQk8MyN2y6QzNTM6"
|
||||||
|
|
||||||
|
|
||||||
|
def split_welcome(*parts: str) -> tuple[str, str]:
|
||||||
|
"""Разделить приветствие на подпись к фото и одно общее сообщение.
|
||||||
|
|
||||||
|
Первая строка первого шаблона становится подписью (персональное приветствие),
|
||||||
|
всё остальное (включая ``text2``/``text3``) — одним сообщением по порядку.
|
||||||
|
"""
|
||||||
|
if not parts:
|
||||||
|
return "", ""
|
||||||
|
|
||||||
|
caption, _, rest = parts[0].partition("\n")
|
||||||
|
body_parts = [rest.rstrip(), *parts[1:]]
|
||||||
|
body = "\n\n".join(part.strip() for part in body_parts if part and part.strip())
|
||||||
|
return caption.strip(), body
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,4 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
__version__ = "0.4.14"
|
__version__ = "0.4.15"
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,4 @@ from common import __version__
|
||||||
|
|
||||||
def test_version_is_set():
|
def test_version_is_set():
|
||||||
assert __version__
|
assert __version__
|
||||||
assert __version__ == "0.4.14"
|
assert __version__ == "0.4.15"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
"""Тесты сборки приветствия: подпись к фото + одно общее сообщение."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from common.texts.welcome import split_welcome
|
||||||
|
|
||||||
|
|
||||||
|
def test_first_line_is_caption_rest_joins_body() -> None:
|
||||||
|
caption, body = split_welcome(
|
||||||
|
"💠{username}, добро пожаловать в Dispel Evil!\n\n❗️Правила клана\n1. …",
|
||||||
|
"Часть 2",
|
||||||
|
"Часть 3",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert caption == "💠{username}, добро пожаловать в Dispel Evil!"
|
||||||
|
assert body == "❗️Правила клана\n1. …\n\nЧасть 2\n\nЧасть 3"
|
||||||
|
|
||||||
|
|
||||||
|
def test_without_newline_all_text_is_caption() -> None:
|
||||||
|
caption, body = split_welcome("Привет", "Второй", "Третий")
|
||||||
|
|
||||||
|
assert caption == "Привет"
|
||||||
|
assert body == "Второй\n\nТретий"
|
||||||
|
|
||||||
|
|
||||||
|
def test_empty_parts_are_skipped() -> None:
|
||||||
|
caption, body = split_welcome("Привет\n\nТело", "", " ")
|
||||||
|
|
||||||
|
assert caption == "Привет"
|
||||||
|
assert body == "Тело"
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_parts() -> None:
|
||||||
|
assert split_welcome() == ("", "")
|
||||||
Loading…
Reference in New Issue