fix(ux): Corrigir timezone e adicionar variações humanas nas mensagens

PORQUE FOI FEITA ESSA ALTERAÇÃO?
Melhoria de UX. Correção de dois problemas identificados no teste:
1. Timezone: Bot usava UTC mostrando 'Boa noite' às 16h do Brasil
2. Formatação: Markdown literal (**text**) não era renderizado no Telegram

QUAIS TESTES FORAM FEITOS?
- pytest tests/test_onboarding.py: 12 testes passaram
- Teste manual no Telegram confirmando timezone e formatação HTML

A ALTERAÇÃO GEROU UM NOVO TESTE?
Não, testes existentes cobrem a lógica. Alterações foram:
- Uso de zoneinfo com America/Sao_Paulo
- parse_mode='HTML' em todas as mensagens
- 3 variações de boas-vindas (random.choice)
- 3 variações de confirmação de cadastro (agradecimento natural)
This commit is contained in:
João Pedro Toledo Goncalves 2026-02-02 16:30:17 -03:00
parent 8005c0c6a3
commit 14865c049f
2 changed files with 72 additions and 23 deletions

View File

@ -6,15 +6,64 @@ No LLM required - pure Python logic + database lookups.
"""
import logging
from typing import Optional, Dict, Any
import random
from typing import Optional, Dict, Any, List
from dataclasses import dataclass, asdict
from datetime import datetime, timezone
from enum import Enum
from zoneinfo import ZoneInfo
from src.clients import get_qdrant_client
logger = logging.getLogger("ArthurOnboarding")
# Brazil timezone
BRAZIL_TZ = ZoneInfo("America/Sao_Paulo")
# Welcome message templates - will be randomized
WELCOME_TEMPLATES = [
(
"Olá! {greeting}. 👋\n\n"
"Essa parece ser a primeira vez que nos falamos. "
"Pode me confirmar seu <b>Nome Completo</b> e <b>Empresa</b>, por favor?\n\n"
"Exemplo: <i>João Silva, iT Guys</i>"
),
(
"{greeting}! 👋\n\n"
"Prazer em conhecê-lo! Para que eu possa te atender melhor, "
"me conta: qual é o seu <b>nome</b> e de qual <b>empresa</b> você fala?\n\n"
"Pode responder assim: <i>Maria Santos, Empresa XYZ</i>"
),
(
"E aí! {greeting}. 😊\n\n"
"Ainda não temos seu cadastro por aqui. "
"Me passa seu <b>nome completo</b> e a <b>empresa</b> que você representa?\n\n"
"Formato: <i>Nome Sobrenome, Sua Empresa</i>"
),
]
# Registration confirmation templates
REGISTRATION_SUCCESS_TEMPLATES = [
(
"Perfeito, <b>{first_name}</b>! Obrigado pelas informações. ✅\n\n"
"Você está vinculado à empresa <b>{company}</b>.\n"
"Agora pode me descrever problemas técnicos que eu analisarei.\n\n"
"Como posso te ajudar hoje?"
),
(
"Show, <b>{first_name}</b>! Cadastro feito com sucesso. ✅\n\n"
"Te encontrei aqui como parte da <b>{company}</b>.\n"
"Estou pronto pra ajudar com qualquer questão técnica!\n\n"
"O que você precisa hoje?"
),
(
"Muito obrigado, <b>{first_name}</b>! Tudo certo. ✅\n\n"
"Seu perfil está associado à <b>{company}</b>.\n"
"Pode contar comigo para análises técnicas e suporte.\n\n"
"Em que posso ajudar?"
),
]
class UserStatus(Enum):
"""Status of a user in the system."""
@ -81,13 +130,13 @@ class OnboardingManager:
def get_time_greeting(self) -> str:
"""
Returns appropriate greeting based on current time.
Returns appropriate greeting based on current time in Brazil.
- 05:00 - 11:59 -> "Bom dia"
- 12:00 - 17:59 -> "Boa tarde"
- 18:00 - 04:59 -> "Boa noite"
"""
now = datetime.now()
now = datetime.now(BRAZIL_TZ)
hour = now.hour
if 5 <= hour < 12:
@ -169,7 +218,7 @@ class OnboardingManager:
Start the registration flow for unknown user.
Returns:
Greeting message asking for name and company.
Greeting message asking for name and company (randomized).
"""
greeting = self.get_time_greeting()
self._pending_registrations[telegram_id] = {
@ -177,12 +226,9 @@ class OnboardingManager:
"started_at": datetime.now(timezone.utc)
}
return (
f"Olá! {greeting}. 👋\n\n"
"Essa parece ser a primeira vez que nos falamos. "
"Pode me confirmar seu **Nome Completo** e **Empresa**, por favor?\n\n"
"Exemplo: _João Silva, iT Guys_"
)
# Pick a random welcome template
template = random.choice(WELCOME_TEMPLATES)
return template.format(greeting=greeting)
def is_pending_registration(self, telegram_id: str) -> bool:
"""Check if user is in the middle of registration."""

View File

@ -7,6 +7,7 @@ Integrates onboarding flow for new user identification.
import os
import re
import random
import logging
from typing import List, Optional
from dataclasses import dataclass
@ -14,7 +15,7 @@ from telegram import Update
from telegram.ext import ApplicationBuilder, ContextTypes, CommandHandler, MessageHandler, filters
from src.agents.dispatcher import get_dispatcher
from src.agents.onboarding import get_onboarding_manager, UserStatus
from src.agents.onboarding import get_onboarding_manager, UserStatus, REGISTRATION_SUCCESS_TEMPLATES
from src.agents.non_client_handler import get_non_client_handler
from src.clients.financial_client import get_financial_client
@ -91,12 +92,13 @@ class TelegramListener:
await update.message.reply_text(
f"👋 {greeting}, {name}!\n\n"
"Pode me encaminhar tickets ou descrever problemas que eu analisarei."
"Pode me encaminhar tickets ou descrever problemas que eu analisarei.",
parse_mode="HTML"
)
else:
# Start onboarding
welcome_msg = self._onboarding.start_registration(telegram_id)
await update.message.reply_text(welcome_msg)
await update.message.reply_text(welcome_msg, parse_mode="HTML")
async def _handle_message(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
if not await self._check_auth(update):
@ -117,7 +119,7 @@ class TelegramListener:
if not is_known:
# Start onboarding flow
welcome_msg = self._onboarding.start_registration(telegram_id)
await update.message.reply_text(welcome_msg)
await update.message.reply_text(welcome_msg, parse_mode="HTML")
return
# Get user context for personalized response
@ -144,8 +146,9 @@ class TelegramListener:
if not match:
await update.message.reply_text(
"📝 Por favor, informe no formato:\n"
"_Nome Completo, Empresa_\n\n"
"Exemplo: João Silva, iT Guys"
"<i>Nome Completo, Empresa</i>\n\n"
"Exemplo: João Silva, iT Guys",
parse_mode="HTML"
)
return
@ -168,17 +171,16 @@ class TelegramListener:
first_name = name.split()[0]
if tenant_id:
# Client user - welcome!
# Client user - welcome with random template!
template = random.choice(REGISTRATION_SUCCESS_TEMPLATES)
await update.message.reply_text(
f"✅ Cadastro realizado! {greeting}, {first_name}!\n\n"
f"Você está vinculado à empresa **{company}**.\n"
"Agora pode me descrever problemas técnicos que eu analisarei.\n\n"
"Como posso te ajudar hoje?"
template.format(first_name=first_name, company=company),
parse_mode="HTML"
)
else:
# Non-client - explain limitations
response = self._non_client.generate_response(name, company)
await update.message.reply_text(response)
await update.message.reply_text(response, parse_mode="HTML")
async def _handle_non_client_message(self, update: Update, user_ctx: dict):
"""Handle message from non-client user (passive messenger mode)."""
@ -221,7 +223,8 @@ class TelegramListener:
# Send acknowledgment
await update.message.reply_text(
f"👋 {greeting}, {first_name}!\n"
"🔍 Analisando sua solicitação..."
"🔍 Analisando sua solicitação...",
parse_mode="HTML"
)
# Dispatch to Arthur