mirror of
https://github.com/marcogll/talia_bot.git
synced 2026-01-13 13:25:19 +00:00
feat: implement IMAP confirmation loop for print flow
Adds a confirmation loop to the printing feature. - Creates a new `imap_listener.py` module to check for confirmation emails from the printer. - Updates `main.py` to use a JSON payload in the email subject, containing a unique `job_id` and the `telegram_id` of the user. - After sending a print job, the bot now waits for a specified time and then checks the IMAP inbox for a matching confirmation. - Notifies the user via Telegram whether the print job was successful or if a confirmation was not received. - Updates `flows.json` with a clearer message for the user during the print process.
This commit is contained in:
@@ -41,9 +41,11 @@ from talia_bot.modules.vikunja import get_projects, add_comment_to_task, update_
|
||||
from talia_bot.db import setup_database
|
||||
from talia_bot.modules.flow_engine import FlowEngine
|
||||
from talia_bot.modules.transcription import transcribe_audio
|
||||
import uuid
|
||||
from talia_bot.modules.llm_engine import analyze_client_pitch
|
||||
from talia_bot.modules.calendar import create_event
|
||||
from talia_bot.modules.mailer import send_email_with_attachment
|
||||
from talia_bot.modules.imap_listener import check_for_confirmation
|
||||
from talia_bot.config import ADMIN_ID, VIKUNJA_INBOX_PROJECT_ID
|
||||
|
||||
from talia_bot.scheduler import schedule_daily_summary
|
||||
@@ -313,11 +315,21 @@ async def handle_flow_resolution(update: Update, context: ContextTypes.DEFAULT_T
|
||||
|
||||
elif resolution_type == "resolution_email_sent":
|
||||
file_info = collected_data.get("UPLOAD_FILE")
|
||||
user_id = update.effective_user.id
|
||||
|
||||
if isinstance(file_info, dict):
|
||||
file_id = file_info.get("file_id")
|
||||
file_name = file_info.get("file_name")
|
||||
|
||||
if file_id and file_name:
|
||||
job_id = str(uuid.uuid4())
|
||||
subject_data = {
|
||||
"job_id": job_id,
|
||||
"telegram_id": user_id,
|
||||
"filename": file_name
|
||||
}
|
||||
subject = f"DATA:{json.dumps(subject_data)}"
|
||||
|
||||
file_obj = await context.bot.get_file(file_id)
|
||||
file_buffer = io.BytesIO()
|
||||
await file_obj.download_to_memory(file_buffer)
|
||||
@@ -326,9 +338,21 @@ async def handle_flow_resolution(update: Update, context: ContextTypes.DEFAULT_T
|
||||
success = await send_email_with_attachment(
|
||||
file_content=file_buffer.getvalue(),
|
||||
filename=file_name,
|
||||
subject=f"Print Job: {file_name}"
|
||||
subject=subject
|
||||
)
|
||||
if not success:
|
||||
|
||||
if success:
|
||||
final_message = f"Recibido. 📨\n\nTu trabajo de impresión ha sido enviado (Job ID: {job_id}). Te notificaré cuando la impresora confirme que ha sido impreso."
|
||||
|
||||
# Esperar y verificar la confirmación
|
||||
await asyncio.sleep(60) # Espera de 60 segundos
|
||||
confirmation_data = await asyncio.to_thread(check_for_confirmation, job_id)
|
||||
|
||||
if confirmation_data:
|
||||
await context.bot.send_message(chat_id=user_id, text=f"✅ ¡Éxito! Tu archivo '{file_name}' ha sido impreso correctamente.")
|
||||
else:
|
||||
await context.bot.send_message(chat_id=user_id, text=f"⚠️ El trabajo de impresión para '{file_name}' fue enviado, pero no he recibido una confirmación de la impresora. Por favor, verifica la bandeja de la impresora.")
|
||||
else:
|
||||
final_message = "❌ Hubo un error al enviar el archivo a la impresora."
|
||||
else:
|
||||
final_message = "❌ No se encontró la información del archivo."
|
||||
|
||||
79
talia_bot/modules/imap_listener.py
Normal file
79
talia_bot/modules/imap_listener.py
Normal file
@@ -0,0 +1,79 @@
|
||||
# talia_bot/modules/imap_listener.py
|
||||
import imaplib
|
||||
import email
|
||||
import json
|
||||
import logging
|
||||
from email.header import decode_header
|
||||
|
||||
from talia_bot.config import IMAP_SERVER, IMAP_USER, IMAP_PASSWORD
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def check_for_confirmation(job_id: str):
|
||||
"""
|
||||
Checks for a print confirmation email via IMAP.
|
||||
Returns the parsed data from the email subject if a confirmation is found, else None.
|
||||
"""
|
||||
if not all([IMAP_SERVER, IMAP_USER, IMAP_PASSWORD]):
|
||||
logger.error("IMAP settings are not fully configured.")
|
||||
return None
|
||||
|
||||
try:
|
||||
mail = imaplib.IMAP4_SSL(IMAP_SERVER)
|
||||
mail.login(IMAP_USER, IMAP_PASSWORD)
|
||||
mail.select("inbox")
|
||||
|
||||
# Buscar correos no leídos del remitente específico
|
||||
status, messages = mail.search(None, '(UNSEEN FROM "noreply@print.epsonconnect.com")')
|
||||
if status != "OK":
|
||||
logger.error("Failed to search for emails.")
|
||||
mail.logout()
|
||||
return None
|
||||
|
||||
for num in messages[0].split():
|
||||
status, data = mail.fetch(num, "(RFC822)")
|
||||
if status != "OK":
|
||||
continue
|
||||
|
||||
msg = email.message_from_bytes(data[0][1])
|
||||
|
||||
# Decodificar el asunto del correo
|
||||
subject, encoding = decode_header(msg["Subject"])[0]
|
||||
if isinstance(subject, bytes):
|
||||
subject = subject.decode(encoding if encoding else "utf-8")
|
||||
|
||||
# Buscar la línea que contiene el asunto original
|
||||
body = ""
|
||||
if msg.is_multipart():
|
||||
for part in msg.walk():
|
||||
if part.get_content_type() == "text/plain":
|
||||
body = part.get_payload(decode=True).decode()
|
||||
break
|
||||
else:
|
||||
body = msg.get_payload(decode=True).decode()
|
||||
|
||||
for line in body.splitlines():
|
||||
if line.strip().startswith("Subject:"):
|
||||
original_subject = line.strip()[len("Subject:"):].strip()
|
||||
# El asunto está encapsulado en `DATA:{...}`
|
||||
if original_subject.startswith("DATA:"):
|
||||
try:
|
||||
json_data_str = original_subject[len("DATA:"):].strip()
|
||||
job_data = json.loads(json_data_str)
|
||||
|
||||
if job_data.get("job_id") == job_id:
|
||||
logger.info(f"Confirmation found for job_id: {job_id}")
|
||||
# Marcar el correo como leído
|
||||
mail.store(num, '+FLAGS', '\\Seen')
|
||||
mail.logout()
|
||||
return job_data
|
||||
except (json.JSONDecodeError, KeyError) as e:
|
||||
logger.warning(f"Could not parse job data from subject: {original_subject}. Error: {e}")
|
||||
continue
|
||||
|
||||
mail.logout()
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to check email via IMAP: {e}")
|
||||
return None
|
||||
Reference in New Issue
Block a user