Initial commit
This commit is contained in:
260
services/instructions.py
Normal file
260
services/instructions.py
Normal file
@@ -0,0 +1,260 @@
|
||||
from database.db import get_connection
|
||||
from database.repository import (
|
||||
get_terminal_steps,
|
||||
set_instruction_progress,
|
||||
get_instruction_progress,
|
||||
clear_instruction_progress,
|
||||
find_instructions,
|
||||
)
|
||||
from keyboards.factory import pause_keyboard, back_to_main, build_keyboard
|
||||
from services.log_export import log_event
|
||||
|
||||
PAUSE_PROMPT = "Помогла ли эта часть?"
|
||||
PAUSE_YES = {"да, помогло", "да помогло", "да"}
|
||||
PAUSE_NO = {"нет, дальше", "нет дальше", "нет"}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
# PINPAD — простой ответ
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
async def send_pinpad_error(message, code: str, keyboard=None):
|
||||
"""
|
||||
Отправляет простой ответ для ошибок пинпада:
|
||||
причина + что делать
|
||||
"""
|
||||
with get_connection() as conn:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"SELECT reason, action FROM pinpad_errors WHERE code = ?",
|
||||
(code,)
|
||||
)
|
||||
row = cur.fetchone()
|
||||
|
||||
if not row:
|
||||
return False # сигнал, что ошибки нет
|
||||
|
||||
reason, action = row
|
||||
log_event(message.from_id, "pinpad_error", str(code))
|
||||
action_text = (action or "").strip()
|
||||
if action_text.lower().startswith("terminal:"):
|
||||
query = action_text.split(":", 1)[1].strip()
|
||||
if query:
|
||||
found = find_instructions(query.lower())
|
||||
if len(found) == 1:
|
||||
instruction_id, _ = found[0]
|
||||
await send_terminal_instruction(
|
||||
message,
|
||||
instruction_id,
|
||||
keyboard=keyboard or back_to_main(),
|
||||
)
|
||||
return True
|
||||
if len(found) > 1:
|
||||
buttons = [{"title": title} for _, title in found]
|
||||
kb = build_keyboard(buttons, back=True)
|
||||
await message.answer(
|
||||
"🔎 Найдено несколько вариантов. Выберите нужный:",
|
||||
keyboard=kb,
|
||||
)
|
||||
return True
|
||||
|
||||
await message.answer(
|
||||
f"❗ Ошибка пинпада {code}\n\n"
|
||||
f"📌 Причина:\n{reason}\n\n"
|
||||
f"❌ Не удалось найти инструкцию по ключу: {query or 'не задан'}",
|
||||
keyboard=keyboard or back_to_main(),
|
||||
)
|
||||
return True
|
||||
|
||||
await message.answer(
|
||||
f"❗ Ошибка пинпада {code}\n\n"
|
||||
f"📌 Причина:\n{reason}\n\n"
|
||||
f"✅ Что делать:\n{action}",
|
||||
keyboard=keyboard
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
# TERMINAL — подробная инструкция
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
async def _send_text(target, text: str, keyboard=None):
|
||||
if hasattr(target, "answer"):
|
||||
await target.answer(text, keyboard=keyboard)
|
||||
else:
|
||||
await target.send_message(text, keyboard=keyboard)
|
||||
|
||||
|
||||
async def _send_attachment(target, attachment: str):
|
||||
if hasattr(target, "answer"):
|
||||
await target.answer(attachment=attachment)
|
||||
else:
|
||||
await target.send_message(attachment=attachment)
|
||||
|
||||
|
||||
def _parse_goto(value: str):
|
||||
parts = (value or "").strip().split(":", 1)
|
||||
if not parts[0].isdigit():
|
||||
return None
|
||||
instr_id = int(parts[0])
|
||||
step_index = 0
|
||||
if len(parts) > 1:
|
||||
if not parts[1].isdigit():
|
||||
return None
|
||||
step_num = int(parts[1])
|
||||
if step_num > 0:
|
||||
step_index = step_num - 1
|
||||
return instr_id, step_index
|
||||
|
||||
|
||||
async def _send_terminal_from_index(target, instruction_id: int, start_index: int, keyboard=None, max_hops: int = 5):
|
||||
"""
|
||||
Отправляет пошаговую инструкцию терминала:
|
||||
несколько сообщений подряд (text / image)
|
||||
"""
|
||||
steps = get_terminal_steps(instruction_id)
|
||||
|
||||
if not steps:
|
||||
await _send_text(target, "❌ Для этой инструкции нет шагов.", keyboard=keyboard)
|
||||
return
|
||||
|
||||
for idx, (step_type, content) in enumerate(steps[start_index:], start=start_index):
|
||||
if step_type == "text":
|
||||
await _send_text(target, content, keyboard=keyboard)
|
||||
elif step_type == "image":
|
||||
await _send_attachment(target, content)
|
||||
elif step_type == "pause":
|
||||
await _send_text(target, PAUSE_PROMPT, keyboard=pause_keyboard())
|
||||
pause_at_end = (idx == len(steps) - 1)
|
||||
set_instruction_progress(
|
||||
target.from_id if hasattr(target, "from_id") else target.user_id,
|
||||
instruction_id,
|
||||
idx + 1,
|
||||
pause_at_end
|
||||
)
|
||||
return
|
||||
elif step_type == "goto":
|
||||
if max_hops <= 0:
|
||||
await _send_text(target, "❌ Превышено число переходов между инструкциями.", keyboard=keyboard)
|
||||
return
|
||||
parsed = _parse_goto(content)
|
||||
if not parsed:
|
||||
await _send_text(target, f"❌ Некорректный goto: {content}", keyboard=keyboard)
|
||||
return
|
||||
next_id, next_index = parsed
|
||||
user_id = target.from_id if hasattr(target, "from_id") else target.user_id
|
||||
log_event(user_id, "terminal_instruction", str(next_id))
|
||||
clear_instruction_progress(target.from_id if hasattr(target, "from_id") else target.user_id)
|
||||
await _send_terminal_from_index(
|
||||
target,
|
||||
next_id,
|
||||
next_index,
|
||||
keyboard=keyboard,
|
||||
max_hops=max_hops - 1,
|
||||
)
|
||||
return
|
||||
|
||||
clear_instruction_progress(target.from_id if hasattr(target, "from_id") else target.user_id)
|
||||
|
||||
|
||||
async def send_terminal_instruction(message, instruction_id: int, keyboard=None):
|
||||
log_event(message.from_id, "terminal_instruction", str(instruction_id))
|
||||
await _send_terminal_from_index(message, instruction_id, 0, keyboard)
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
# УНИВЕРСАЛЬНЫЙ ХЕЛПЕР (по желанию)
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
async def send_instruction(message, instruction_id: int, keyboard=None):
|
||||
"""
|
||||
Алиас, чтобы использовать одно имя в коде
|
||||
"""
|
||||
await send_terminal_instruction(message, instruction_id, keyboard)
|
||||
|
||||
|
||||
async def handle_pause_response(message) -> bool:
|
||||
if not message.text:
|
||||
return False
|
||||
|
||||
progress = get_instruction_progress(message.from_id)
|
||||
if not progress:
|
||||
return False
|
||||
|
||||
text = message.text.strip().lower()
|
||||
instruction_id, next_step, pause_at_end = progress
|
||||
|
||||
if text in PAUSE_YES:
|
||||
clear_instruction_progress(message.from_id)
|
||||
await message.answer("Отлично, остановил инструкцию.", keyboard=back_to_main())
|
||||
return True
|
||||
|
||||
if text in PAUSE_NO:
|
||||
if pause_at_end:
|
||||
clear_instruction_progress(message.from_id)
|
||||
await message.answer(
|
||||
"Похоже, инструкция не помогла. Заполните, пожалуйста, форму:\n"
|
||||
"https://example.com/google-form",
|
||||
keyboard=back_to_main()
|
||||
)
|
||||
return True
|
||||
|
||||
steps = get_terminal_steps(instruction_id)
|
||||
if next_step >= len(steps):
|
||||
clear_instruction_progress(message.from_id)
|
||||
await message.answer(
|
||||
"Похоже, инструкция не помогла. Заполните, пожалуйста, форму:\n"
|
||||
"https://example.com/google-form",
|
||||
keyboard=back_to_main()
|
||||
)
|
||||
return True
|
||||
|
||||
await _send_terminal_from_index(message, instruction_id, next_step, keyboard=back_to_main())
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
async def handle_pause_event(event) -> bool:
|
||||
payload = event.get_payload_json() if hasattr(event, "get_payload_json") else None
|
||||
if not payload or payload.get("pause") not in ("yes", "no"):
|
||||
return False
|
||||
|
||||
progress = get_instruction_progress(event.user_id)
|
||||
if not progress:
|
||||
return False
|
||||
|
||||
instruction_id, next_step, pause_at_end = progress
|
||||
|
||||
if payload["pause"] == "yes":
|
||||
clear_instruction_progress(event.user_id)
|
||||
await event.send_message("Отлично, всё работает.", keyboard=back_to_main())
|
||||
return True
|
||||
|
||||
if pause_at_end:
|
||||
clear_instruction_progress(event.user_id)
|
||||
await event.send_message(
|
||||
"Похоже, инструкция не помогла. Заполните, пожалуйста, форму:\n"
|
||||
"https://example.com/google-form",
|
||||
keyboard=back_to_main()
|
||||
)
|
||||
return True
|
||||
|
||||
steps = get_terminal_steps(instruction_id)
|
||||
if next_step >= len(steps):
|
||||
clear_instruction_progress(event.user_id)
|
||||
await event.send_message(
|
||||
"Похоже, инструкция не помогла. Заполните, пожалуйста, форму:\n"
|
||||
"https://example.com/google-form",
|
||||
keyboard=back_to_main()
|
||||
)
|
||||
return True
|
||||
|
||||
await _send_terminal_from_index(
|
||||
event,
|
||||
instruction_id,
|
||||
next_step,
|
||||
keyboard=back_to_main()
|
||||
)
|
||||
return True
|
||||
Reference in New Issue
Block a user