mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-02-21 20:01:47 +00:00
Revert "Fix poll delivery session usage and tidy poll creation prompts"
This commit is contained in:
@@ -3,8 +3,7 @@ import logging
|
||||
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
|
||||
from typing import Optional
|
||||
|
||||
from aiogram import Bot, Dispatcher, F, types
|
||||
from aiogram.exceptions import MessageNotModified, TelegramBadRequest
|
||||
from aiogram import Dispatcher, F, types
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@@ -41,65 +40,6 @@ class PollCreationStates(StatesGroup):
|
||||
waiting_for_questions = State()
|
||||
|
||||
|
||||
def _get_creation_header(texts) -> str:
|
||||
return texts.t("ADMIN_POLLS_CREATION_HEADER", "🗳️ <b>Создание опроса</b>")
|
||||
|
||||
|
||||
def _format_creation_prompt(texts, body: str, error: str | None = None) -> str:
|
||||
header = _get_creation_header(texts)
|
||||
body_content = body.strip()
|
||||
if body_content.startswith(header):
|
||||
body_content = body_content[len(header) :].lstrip("\n")
|
||||
|
||||
sections = [header]
|
||||
if error:
|
||||
sections.append(error)
|
||||
if body_content:
|
||||
sections.append(body_content)
|
||||
|
||||
return "\n\n".join(sections)
|
||||
|
||||
|
||||
async def _delete_user_message(message: types.Message) -> None:
|
||||
try:
|
||||
await message.delete()
|
||||
except TelegramBadRequest as error:
|
||||
logger.debug("Failed to delete poll creation input: %s", error)
|
||||
|
||||
|
||||
async def _update_creation_message(
|
||||
bot: Bot,
|
||||
chat_id: int,
|
||||
message_id: int | None,
|
||||
text: str,
|
||||
*,
|
||||
reply_markup: types.InlineKeyboardMarkup | None = None,
|
||||
parse_mode: str | None = None,
|
||||
) -> int:
|
||||
if message_id:
|
||||
try:
|
||||
await bot.edit_message_text(
|
||||
text=text,
|
||||
chat_id=chat_id,
|
||||
message_id=message_id,
|
||||
reply_markup=reply_markup,
|
||||
parse_mode=parse_mode,
|
||||
)
|
||||
return message_id
|
||||
except MessageNotModified:
|
||||
return message_id
|
||||
except TelegramBadRequest as error:
|
||||
logger.debug("Failed to edit poll creation prompt: %s", error)
|
||||
|
||||
new_message = await bot.send_message(
|
||||
chat_id=chat_id,
|
||||
text=text,
|
||||
reply_markup=reply_markup,
|
||||
parse_mode=parse_mode,
|
||||
)
|
||||
return new_message.message_id
|
||||
|
||||
|
||||
def _build_polls_keyboard(polls: list[Poll], language: str) -> types.InlineKeyboardMarkup:
|
||||
texts = get_texts(language)
|
||||
keyboard: list[list[types.InlineKeyboardButton]] = []
|
||||
@@ -337,6 +277,7 @@ async def start_poll_creation(
|
||||
texts = get_texts(db_user.language)
|
||||
await state.clear()
|
||||
await state.set_state(PollCreationStates.waiting_for_title)
|
||||
await state.update_data(questions=[])
|
||||
|
||||
await callback.message.edit_text(
|
||||
texts.t(
|
||||
@@ -345,11 +286,6 @@ async def start_poll_creation(
|
||||
),
|
||||
parse_mode="HTML",
|
||||
)
|
||||
await state.update_data(
|
||||
questions=[],
|
||||
prompt_message_id=callback.message.message_id,
|
||||
prompt_chat_id=callback.message.chat.id,
|
||||
)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@@ -361,66 +297,31 @@ async def process_poll_title(
|
||||
state: FSMContext,
|
||||
db: AsyncSession,
|
||||
):
|
||||
texts = get_texts(db_user.language)
|
||||
data = await state.get_data()
|
||||
prompt_message_id = data.get("prompt_message_id")
|
||||
prompt_chat_id = data.get("prompt_chat_id", message.chat.id)
|
||||
|
||||
user_input = (message.text or "").strip()
|
||||
|
||||
if user_input == "/cancel":
|
||||
if message.text == "/cancel":
|
||||
await state.clear()
|
||||
await _delete_user_message(message)
|
||||
cancel_text = texts.t("ADMIN_POLLS_CREATION_CANCELLED", "❌ Создание опроса отменено.")
|
||||
await _update_creation_message(
|
||||
message.bot,
|
||||
prompt_chat_id,
|
||||
prompt_message_id,
|
||||
cancel_text,
|
||||
await message.answer(
|
||||
get_texts(db_user.language).t("ADMIN_POLLS_CREATION_CANCELLED", "❌ Создание опроса отменено."),
|
||||
reply_markup=get_admin_communications_submenu_keyboard(db_user.language),
|
||||
parse_mode="HTML",
|
||||
)
|
||||
return
|
||||
|
||||
await _delete_user_message(message)
|
||||
|
||||
if not user_input:
|
||||
error_text = texts.t(
|
||||
"ADMIN_POLLS_CREATION_TITLE_EMPTY",
|
||||
"❌ Заголовок не может быть пустым. Попробуйте снова.",
|
||||
)
|
||||
prompt_body = texts.t(
|
||||
"ADMIN_POLLS_CREATION_TITLE_PROMPT",
|
||||
"🗳️ <b>Создание опроса</b>\n\nВведите заголовок опроса:",
|
||||
)
|
||||
new_message_id = await _update_creation_message(
|
||||
message.bot,
|
||||
prompt_chat_id,
|
||||
prompt_message_id,
|
||||
_format_creation_prompt(texts, prompt_body, error_text),
|
||||
parse_mode="HTML",
|
||||
)
|
||||
await state.update_data(prompt_message_id=new_message_id, prompt_chat_id=prompt_chat_id)
|
||||
title = message.text.strip()
|
||||
if not title:
|
||||
await message.answer("❌ Заголовок не может быть пустым. Попробуйте снова.")
|
||||
return
|
||||
|
||||
await state.update_data(title=user_input)
|
||||
await state.update_data(title=title)
|
||||
await state.set_state(PollCreationStates.waiting_for_description)
|
||||
|
||||
prompt_body = (
|
||||
texts = get_texts(db_user.language)
|
||||
await message.answer(
|
||||
texts.t(
|
||||
"ADMIN_POLLS_CREATION_DESCRIPTION_PROMPT",
|
||||
"Введите описание опроса. HTML разрешён.\nОтправьте /skip, чтобы пропустить.",
|
||||
)
|
||||
+ f"\n\n{get_html_help_text()}"
|
||||
)
|
||||
new_message_id = await _update_creation_message(
|
||||
message.bot,
|
||||
prompt_chat_id,
|
||||
prompt_message_id,
|
||||
_format_creation_prompt(texts, prompt_body),
|
||||
+ f"\n\n{get_html_help_text()}",
|
||||
parse_mode="HTML",
|
||||
)
|
||||
await state.update_data(prompt_message_id=new_message_id, prompt_chat_id=prompt_chat_id)
|
||||
|
||||
|
||||
@admin_required
|
||||
@@ -432,74 +333,36 @@ async def process_poll_description(
|
||||
db: AsyncSession,
|
||||
):
|
||||
texts = get_texts(db_user.language)
|
||||
data = await state.get_data()
|
||||
prompt_message_id = data.get("prompt_message_id")
|
||||
prompt_chat_id = data.get("prompt_chat_id", message.chat.id)
|
||||
|
||||
user_input = message.text or ""
|
||||
|
||||
if user_input == "/cancel":
|
||||
if message.text == "/cancel":
|
||||
await state.clear()
|
||||
await _delete_user_message(message)
|
||||
cancel_text = texts.t("ADMIN_POLLS_CREATION_CANCELLED", "❌ Создание опроса отменено.")
|
||||
await _update_creation_message(
|
||||
message.bot,
|
||||
prompt_chat_id,
|
||||
prompt_message_id,
|
||||
cancel_text,
|
||||
await message.answer(
|
||||
texts.t("ADMIN_POLLS_CREATION_CANCELLED", "❌ Создание опроса отменено."),
|
||||
reply_markup=get_admin_communications_submenu_keyboard(db_user.language),
|
||||
parse_mode="HTML",
|
||||
)
|
||||
return
|
||||
|
||||
await _delete_user_message(message)
|
||||
|
||||
if user_input == "/skip":
|
||||
description: Optional[str] = None
|
||||
description: Optional[str]
|
||||
if message.text == "/skip":
|
||||
description = None
|
||||
else:
|
||||
description = user_input.strip()
|
||||
description = message.text.strip()
|
||||
is_valid, error_message = validate_html_tags(description)
|
||||
if not is_valid:
|
||||
error_text = texts.t(
|
||||
"ADMIN_POLLS_CREATION_INVALID_HTML",
|
||||
"❌ Ошибка в HTML: {error}",
|
||||
).format(error=error_message)
|
||||
prompt_body = (
|
||||
texts.t(
|
||||
"ADMIN_POLLS_CREATION_DESCRIPTION_PROMPT",
|
||||
"Введите описание опроса. HTML разрешён.\nОтправьте /skip, чтобы пропустить.",
|
||||
)
|
||||
+ f"\n\n{get_html_help_text()}"
|
||||
await message.answer(
|
||||
texts.t("ADMIN_POLLS_CREATION_INVALID_HTML", "❌ Ошибка в HTML: {error}").format(error=error_message)
|
||||
)
|
||||
new_message_id = await _update_creation_message(
|
||||
message.bot,
|
||||
prompt_chat_id,
|
||||
prompt_message_id,
|
||||
_format_creation_prompt(texts, prompt_body, error_text),
|
||||
parse_mode="HTML",
|
||||
)
|
||||
await state.update_data(prompt_message_id=new_message_id, prompt_chat_id=prompt_chat_id)
|
||||
return
|
||||
|
||||
await state.update_data(description=description)
|
||||
await state.set_state(PollCreationStates.waiting_for_reward)
|
||||
|
||||
prompt_body = texts.t(
|
||||
"ADMIN_POLLS_CREATION_REWARD_PROMPT",
|
||||
(
|
||||
"Укажите награду за прохождение опроса (в рублях).\n"
|
||||
"0 — без награды. Можно использовать дробные значения.\n"
|
||||
"Например: 0, 0.5, 10"
|
||||
),
|
||||
await message.answer(
|
||||
texts.t(
|
||||
"ADMIN_POLLS_CREATION_REWARD_PROMPT",
|
||||
"Введите сумму награды в рублях. Отправьте 0 чтобы отключить награду.",
|
||||
)
|
||||
)
|
||||
new_message_id = await _update_creation_message(
|
||||
message.bot,
|
||||
prompt_chat_id,
|
||||
prompt_message_id,
|
||||
_format_creation_prompt(texts, prompt_body),
|
||||
parse_mode="HTML",
|
||||
)
|
||||
await state.update_data(prompt_message_id=new_message_id, prompt_chat_id=prompt_chat_id)
|
||||
|
||||
|
||||
def _parse_reward_amount(message_text: str) -> int | None:
|
||||
@@ -525,50 +388,18 @@ async def process_poll_reward(
|
||||
db: AsyncSession,
|
||||
):
|
||||
texts = get_texts(db_user.language)
|
||||
data = await state.get_data()
|
||||
prompt_message_id = data.get("prompt_message_id")
|
||||
prompt_chat_id = data.get("prompt_chat_id", message.chat.id)
|
||||
|
||||
user_input = message.text or ""
|
||||
|
||||
if user_input == "/cancel":
|
||||
if message.text == "/cancel":
|
||||
await state.clear()
|
||||
await _delete_user_message(message)
|
||||
cancel_text = texts.t("ADMIN_POLLS_CREATION_CANCELLED", "❌ Создание опроса отменено.")
|
||||
await _update_creation_message(
|
||||
message.bot,
|
||||
prompt_chat_id,
|
||||
prompt_message_id,
|
||||
cancel_text,
|
||||
await message.answer(
|
||||
texts.t("ADMIN_POLLS_CREATION_CANCELLED", "❌ Создание опроса отменено."),
|
||||
reply_markup=get_admin_communications_submenu_keyboard(db_user.language),
|
||||
parse_mode="HTML",
|
||||
)
|
||||
return
|
||||
|
||||
await _delete_user_message(message)
|
||||
|
||||
reward_kopeks = _parse_reward_amount(user_input)
|
||||
reward_kopeks = _parse_reward_amount(message.text)
|
||||
if reward_kopeks is None:
|
||||
error_text = texts.t(
|
||||
"ADMIN_POLLS_CREATION_REWARD_INVALID",
|
||||
"❌ Некорректная сумма. Попробуйте ещё раз.",
|
||||
)
|
||||
prompt_body = texts.t(
|
||||
"ADMIN_POLLS_CREATION_REWARD_PROMPT",
|
||||
(
|
||||
"Укажите награду за прохождение опроса (в рублях).\n"
|
||||
"0 — без награды. Можно использовать дробные значения.\n"
|
||||
"Например: 0, 0.5, 10"
|
||||
),
|
||||
)
|
||||
new_message_id = await _update_creation_message(
|
||||
message.bot,
|
||||
prompt_chat_id,
|
||||
prompt_message_id,
|
||||
_format_creation_prompt(texts, prompt_body, error_text),
|
||||
parse_mode="HTML",
|
||||
)
|
||||
await state.update_data(prompt_message_id=new_message_id, prompt_chat_id=prompt_chat_id)
|
||||
await message.answer(texts.t("ADMIN_POLLS_CREATION_REWARD_INVALID", "❌ Некорректная сумма. Попробуйте ещё раз."))
|
||||
return
|
||||
|
||||
reward_enabled = reward_kopeks > 0
|
||||
@@ -587,14 +418,7 @@ async def process_poll_reward(
|
||||
"Отправьте /done, когда вопросы будут добавлены."
|
||||
),
|
||||
)
|
||||
new_message_id = await _update_creation_message(
|
||||
message.bot,
|
||||
prompt_chat_id,
|
||||
prompt_message_id,
|
||||
_format_creation_prompt(texts, prompt),
|
||||
parse_mode="HTML",
|
||||
)
|
||||
await state.update_data(prompt_message_id=new_message_id, prompt_chat_id=prompt_chat_id)
|
||||
await message.answer(prompt)
|
||||
|
||||
|
||||
@admin_required
|
||||
@@ -606,52 +430,21 @@ async def process_poll_question(
|
||||
db: AsyncSession,
|
||||
):
|
||||
texts = get_texts(db_user.language)
|
||||
data = await state.get_data()
|
||||
prompt_message_id = data.get("prompt_message_id")
|
||||
prompt_chat_id = data.get("prompt_chat_id", message.chat.id)
|
||||
|
||||
user_input = message.text or ""
|
||||
|
||||
if user_input == "/cancel":
|
||||
if message.text == "/cancel":
|
||||
await state.clear()
|
||||
await _delete_user_message(message)
|
||||
cancel_text = texts.t("ADMIN_POLLS_CREATION_CANCELLED", "❌ Создание опроса отменено.")
|
||||
await _update_creation_message(
|
||||
message.bot,
|
||||
prompt_chat_id,
|
||||
prompt_message_id,
|
||||
cancel_text,
|
||||
await message.answer(
|
||||
texts.t("ADMIN_POLLS_CREATION_CANCELLED", "❌ Создание опроса отменено."),
|
||||
reply_markup=get_admin_communications_submenu_keyboard(db_user.language),
|
||||
parse_mode="HTML",
|
||||
)
|
||||
return
|
||||
|
||||
await _delete_user_message(message)
|
||||
|
||||
if user_input == "/done":
|
||||
if message.text == "/done":
|
||||
data = await state.get_data()
|
||||
questions = data.get("questions", [])
|
||||
if not questions:
|
||||
error_text = texts.t(
|
||||
"ADMIN_POLLS_CREATION_NEEDS_QUESTION",
|
||||
"❌ Добавьте хотя бы один вопрос.",
|
||||
await message.answer(
|
||||
texts.t("ADMIN_POLLS_CREATION_NEEDS_QUESTION", "❌ Добавьте хотя бы один вопрос."),
|
||||
)
|
||||
prompt = texts.t(
|
||||
"ADMIN_POLLS_CREATION_QUESTION_PROMPT",
|
||||
(
|
||||
"Введите вопрос и варианты ответов.\n"
|
||||
"Каждая строка — отдельный вариант.\n"
|
||||
"Первая строка — текст вопроса.\n"
|
||||
"Отправьте /done, когда вопросы будут добавлены."
|
||||
),
|
||||
)
|
||||
new_message_id = await _update_creation_message(
|
||||
message.bot,
|
||||
prompt_chat_id,
|
||||
prompt_message_id,
|
||||
_format_creation_prompt(texts, prompt, error_text),
|
||||
parse_mode="HTML",
|
||||
)
|
||||
await state.update_data(prompt_message_id=new_message_id, prompt_chat_id=prompt_chat_id)
|
||||
return
|
||||
|
||||
title = data.get("title")
|
||||
@@ -669,80 +462,47 @@ async def process_poll_question(
|
||||
questions=questions,
|
||||
)
|
||||
|
||||
await state.clear()
|
||||
|
||||
reward_text = _format_reward_text(poll, db_user.language)
|
||||
final_text = texts.t(
|
||||
"ADMIN_POLLS_CREATION_FINISHED",
|
||||
"✅ Опрос «{title}» создан. Вопросов: {count}. {reward}",
|
||||
).format(
|
||||
title=html.escape(poll.title),
|
||||
count=len(poll.questions),
|
||||
reward=reward_text,
|
||||
)
|
||||
polls_keyboard = _build_polls_keyboard(await list_polls(db), db_user.language)
|
||||
await _update_creation_message(
|
||||
message.bot,
|
||||
prompt_chat_id,
|
||||
prompt_message_id,
|
||||
final_text,
|
||||
reply_markup=polls_keyboard,
|
||||
await message.answer(
|
||||
texts.t(
|
||||
"ADMIN_POLLS_CREATION_FINISHED",
|
||||
"✅ Опрос «{title}» создан. Вопросов: {count}. {reward}",
|
||||
).format(
|
||||
title=poll.title,
|
||||
count=len(poll.questions),
|
||||
reward=reward_text,
|
||||
),
|
||||
reply_markup=_build_polls_keyboard(await list_polls(db), db_user.language),
|
||||
parse_mode="HTML",
|
||||
)
|
||||
await state.clear()
|
||||
return
|
||||
|
||||
lines = [line.strip() for line in user_input.splitlines() if line.strip()]
|
||||
lines = [line.strip() for line in message.text.splitlines() if line.strip()]
|
||||
if len(lines) < 3:
|
||||
error_text = texts.t(
|
||||
"ADMIN_POLLS_CREATION_MIN_OPTIONS",
|
||||
"❌ Нужен вопрос и минимум два варианта ответа.",
|
||||
await message.answer(
|
||||
texts.t(
|
||||
"ADMIN_POLLS_CREATION_MIN_OPTIONS",
|
||||
"❌ Нужен вопрос и минимум два варианта ответа.",
|
||||
)
|
||||
)
|
||||
prompt = texts.t(
|
||||
"ADMIN_POLLS_CREATION_QUESTION_PROMPT",
|
||||
(
|
||||
"Введите вопрос и варианты ответов.\n"
|
||||
"Каждая строка — отдельный вариант.\n"
|
||||
"Первая строка — текст вопроса.\n"
|
||||
"Отправьте /done, когда вопросы будут добавлены."
|
||||
),
|
||||
)
|
||||
new_message_id = await _update_creation_message(
|
||||
message.bot,
|
||||
prompt_chat_id,
|
||||
prompt_message_id,
|
||||
_format_creation_prompt(texts, prompt, error_text),
|
||||
parse_mode="HTML",
|
||||
)
|
||||
await state.update_data(prompt_message_id=new_message_id, prompt_chat_id=prompt_chat_id)
|
||||
return
|
||||
|
||||
question_text = lines[0]
|
||||
options = lines[1:]
|
||||
data = await state.get_data()
|
||||
questions = data.get("questions", [])
|
||||
questions.append({"text": question_text, "options": options})
|
||||
await state.update_data(questions=questions)
|
||||
|
||||
prompt = texts.t(
|
||||
"ADMIN_POLLS_CREATION_QUESTION_PROMPT",
|
||||
(
|
||||
"Введите вопрос и варианты ответов.\n"
|
||||
"Каждая строка — отдельный вариант.\n"
|
||||
"Первая строка — текст вопроса.\n"
|
||||
"Отправьте /done, когда вопросы будут добавлены."
|
||||
),
|
||||
)
|
||||
confirmation = texts.t(
|
||||
"ADMIN_POLLS_CREATION_ADDED_QUESTION",
|
||||
"Вопрос добавлен: «{question}». Добавьте следующий вопрос или отправьте /done.",
|
||||
).format(question=html.escape(question_text))
|
||||
body = f"{confirmation}\n\n{prompt}"
|
||||
new_message_id = await _update_creation_message(
|
||||
message.bot,
|
||||
prompt_chat_id,
|
||||
prompt_message_id,
|
||||
_format_creation_prompt(texts, body),
|
||||
await message.answer(
|
||||
texts.t(
|
||||
"ADMIN_POLLS_CREATION_ADDED_QUESTION",
|
||||
"Вопрос добавлен: «{question}». Добавьте следующий вопрос или отправьте /done.",
|
||||
).format(question=question_text),
|
||||
parse_mode="HTML",
|
||||
)
|
||||
await state.update_data(prompt_message_id=new_message_id, prompt_chat_id=prompt_chat_id)
|
||||
|
||||
|
||||
async def _render_poll_details(poll: Poll, language: str) -> str:
|
||||
@@ -929,7 +689,7 @@ async def confirm_poll_send(
|
||||
|
||||
await callback.message.edit_text(
|
||||
result_text,
|
||||
reply_markup=_build_poll_details_keyboard(poll_id, db_user.language),
|
||||
reply_markup=_build_poll_details_keyboard(poll.id, db_user.language),
|
||||
parse_mode="HTML",
|
||||
)
|
||||
await callback.answer()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import asyncio
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import Iterable
|
||||
|
||||
from aiogram import Bot
|
||||
@@ -23,16 +22,7 @@ from app.localization.texts import get_texts
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PollSnapshot:
|
||||
id: int
|
||||
title: str
|
||||
description: str | None
|
||||
reward_enabled: bool
|
||||
reward_amount_kopeks: int
|
||||
|
||||
|
||||
def _build_poll_invitation_text(poll: PollSnapshot, user: User) -> str:
|
||||
def _build_poll_invitation_text(poll: Poll, user: User) -> str:
|
||||
texts = get_texts(user.language)
|
||||
|
||||
lines: list[str] = [f"🗳️ <b>{poll.title}</b>"]
|
||||
@@ -76,14 +66,6 @@ async def send_poll_to_users(
|
||||
poll: Poll,
|
||||
users: Iterable[User],
|
||||
) -> dict:
|
||||
poll_info = PollSnapshot(
|
||||
id=poll.id,
|
||||
title=poll.title,
|
||||
description=poll.description,
|
||||
reward_enabled=poll.reward_enabled,
|
||||
reward_amount_kopeks=poll.reward_amount_kopeks,
|
||||
)
|
||||
|
||||
sent = 0
|
||||
failed = 0
|
||||
skipped = 0
|
||||
@@ -92,7 +74,7 @@ async def send_poll_to_users(
|
||||
existing_response = await db.execute(
|
||||
select(PollResponse.id).where(
|
||||
and_(
|
||||
PollResponse.poll_id == poll_info.id,
|
||||
PollResponse.poll_id == poll.id,
|
||||
PollResponse.user_id == user.id,
|
||||
)
|
||||
)
|
||||
@@ -102,7 +84,7 @@ async def send_poll_to_users(
|
||||
continue
|
||||
|
||||
response = PollResponse(
|
||||
poll_id=poll_info.id,
|
||||
poll_id=poll.id,
|
||||
user_id=user.id,
|
||||
)
|
||||
db.add(response)
|
||||
@@ -110,7 +92,7 @@ async def send_poll_to_users(
|
||||
try:
|
||||
await db.flush()
|
||||
|
||||
text = _build_poll_invitation_text(poll_info, user)
|
||||
text = _build_poll_invitation_text(poll, user)
|
||||
keyboard = build_start_keyboard(response.id, user.language)
|
||||
|
||||
await bot.send_message(
|
||||
@@ -130,7 +112,7 @@ async def send_poll_to_users(
|
||||
failed += 1
|
||||
logger.error(
|
||||
"❌ Ошибка отправки опроса %s пользователю %s: %s",
|
||||
poll_info.id,
|
||||
poll.id,
|
||||
user.telegram_id,
|
||||
error,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user