Show the wait-ack only on a client's first relayed message.

Stops repeating the ack on every client message so the private chat is not cluttered after the first contact.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Artemii Peretiachenko 2026-08-29 19:00:21 +02:00
parent 897651abe9
commit 7588d74ceb
5 changed files with 39 additions and 9 deletions

View file

@ -7,8 +7,8 @@ DATABASE_PATH=data/bot.db
# (so UK keeps catalog strings and the services button stays in sync).
# WELCOME_TEXT_RU=Здравствуйте! Напишите ваш запрос.
# WELCOME_TEXT_UK=Вітаємо! Напишіть ваш запит.
# ACK_TEXT_RU=Спасибо, подождите ответ.
# ACK_TEXT_UK=Дякуємо, зачекайте на відповідь.
# ACK_TEXT_RU=Спасибо, дальше вам будет отвечать менеджер
# ACK_TEXT_UK=Дякуємо, далі вам буде відповідати менеджер.
# COOLDOWN_TEXT_RU=Подождите немного, не спамьте.
# COOLDOWN_TEXT_UK=Зачекайте трохи, не спамте.
CLIENT_COOLDOWN_SECONDS=5

View file

@ -42,7 +42,7 @@ python -m bot
| `MANAGER_GROUP_ID` | Id супергруппы с Topics |
| `DATABASE_PATH` | Путь к SQLite (по умолчанию `data/bot.db`) |
| `WELCOME_TEXT_RU` / `WELCOME_TEXT_UK` | Опционально: приветствие для языка |
| `ACK_TEXT_RU` / `ACK_TEXT_UK` | Опционально: текст после сообщения клиента |
| `ACK_TEXT_RU` / `ACK_TEXT_UK` | Опционально: текст после первого сообщения клиента |
| `COOLDOWN_TEXT_RU` / `COOLDOWN_TEXT_UK` | Опционально: ответ при кулдауне |
| `WELCOME_TEXT` / `ACK_TEXT` / `COOLDOWN_TEXT` | Legacy: только для RU (UK не перезаписывается) |
| `CLIENT_COOLDOWN_SECONDS` | Кулдаун клиента в секундах (по умолчанию `5`) |
@ -50,7 +50,7 @@ python -m bot
## Как это работает
1. Клиент: `/start` → выбор языка (RU/UK) → приветствие, запись в SQLite.
2. Клиент пишет сообщение → бот создаёт тему (если ещё нет) и копирует сообщение менеджерам → «Спасибо, подождите ответ».
2. Клиент пишет сообщение → бот создаёт тему (если ещё нет) и копирует сообщение менеджерам. На первое сообщение — «Спасибо, дальше вам будет отвечать менеджер»; дальше ack не повторяется.
3. Менеджер пишет в теме клиента → бот копирует содержимое клиенту в личку от своего имени.
4. Правки сообщений синхронизируются в обе стороны (текст и подписи).
5. Реакции синхронизируются в обе стороны.

View file

@ -15,6 +15,7 @@ CREATE TABLE IF NOT EXISTS clients (
last_name TEXT,
topic_id INTEGER UNIQUE,
language TEXT,
ack_sent INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
@ -44,6 +45,7 @@ class Client:
last_name: Optional[str]
topic_id: Optional[int]
language: Optional[str]
ack_sent: bool
created_at: str
updated_at: str
@ -81,6 +83,19 @@ class ClientRepository:
columns = {row[1] for row in rows}
if "language" not in columns:
await conn.execute("ALTER TABLE clients ADD COLUMN language TEXT")
if "ack_sent" not in columns:
await conn.execute(
"ALTER TABLE clients ADD COLUMN ack_sent INTEGER NOT NULL DEFAULT 0"
)
await conn.execute(
"""
UPDATE clients
SET ack_sent = 1
WHERE telegram_id IN (
SELECT DISTINCT client_telegram_id FROM message_links
)
"""
)
async def close(self) -> None:
if self._conn is not None:
@ -101,6 +116,7 @@ class ClientRepository:
last_name=row["last_name"],
topic_id=row["topic_id"],
language=row["language"],
ack_sent=bool(row["ack_sent"]),
created_at=row["created_at"],
updated_at=row["updated_at"],
)
@ -118,8 +134,8 @@ class ClientRepository:
"""
INSERT INTO clients (
telegram_id, username, first_name, last_name, topic_id, language,
created_at, updated_at
) VALUES (?, ?, ?, ?, NULL, NULL, ?, ?)
ack_sent, created_at, updated_at
) VALUES (?, ?, ?, ?, NULL, NULL, 0, ?, ?)
ON CONFLICT(telegram_id) DO UPDATE SET
username = excluded.username,
first_name = excluded.first_name,
@ -176,6 +192,18 @@ class ClientRepository:
)
await conn.commit()
async def mark_ack_sent(self, telegram_id: int) -> None:
conn = self._require_conn()
await conn.execute(
"""
UPDATE clients
SET ack_sent = 1, updated_at = ?
WHERE telegram_id = ?
""",
(_utcnow(), telegram_id),
)
await conn.commit()
async def set_language(self, telegram_id: int, language: str) -> None:
conn = self._require_conn()
await conn.execute(

View file

@ -174,6 +174,7 @@ async def client_message(
message: Message,
relay: RelayService,
cooldown: CooldownService,
repo: ClientRepository,
) -> None:
if message.from_user is None or not is_relayable_content(message):
return
@ -196,5 +197,6 @@ async def client_message(
return
cooldown.commit(message.from_user.id, message.media_group_id)
if decision.should_ack:
if decision.should_ack and not client.ack_sent:
await _answer_bot(message, client.language, "ack")
await repo.mark_ack_sent(message.from_user.id)

View file

@ -52,7 +52,7 @@ TEXTS: Dict[str, Dict[str, str]] = {
"Укажите что конкретно вам нужно сделать.\n\n"
"🔶 Все наши услуги посмотрите по кнопке ниже."
),
"ack": "Спасибо, подождите ответ.",
"ack": "Спасибо, дальше вам будет отвечать менеджер",
"cooldown": "Подождите немного, не спамьте.",
"send_failed": (
"Не удалось отправить сообщение менеджеру. Попробуйте позже."
@ -78,7 +78,7 @@ TEXTS: Dict[str, Dict[str, str]] = {
"Вкажіть, що саме вам потрібно зробити.\n\n"
"🔶 Всі наші послуги подивіться по кнопці нижче."
),
"ack": "Дякуємо, зачекайте на відповідь.",
"ack": "Дякуємо, далі вам буде відповідати менеджер.",
"cooldown": "Зачекайте трохи, не спамте.",
"send_failed": (
"Не вдалося надіслати повідомлення менеджеру. Спробуйте пізніше."