Permite adjuntar archivos a tareas desde Telegram
Antes solo facturas y documentos de proyecto consumían el adjunto
pendiente de Telegram; crear_tarea/asignar_tarea lo ignoraban. Agrega
adjuntar_documento_tarea: si el usuario manda un archivo junto con una
instrucción de tarea ("asigna esto a Natalia"), el agente crea/resuelve
la tarea y guarda el archivo como comentario adjunto, visible igual que
un adjunto subido desde /app/tareas.
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
@@ -65,6 +68,53 @@ func AsignarTarea(id uint, asignadoID *uint) (*models.Tarea, error) {
|
||||
return full, nil
|
||||
}
|
||||
|
||||
// AdjuntarDocumentoTarea guarda como comentario de una tarea el archivo (PDF/foto)
|
||||
// que el usuario acaba de enviar por Telegram en este chat. Reutiliza el mismo
|
||||
// formato de "archivos" (JSON) que ya usa el comentario web (AddComentario), para
|
||||
// que el archivo aparezca igual en /app/tareas sin importar de dónde se subió.
|
||||
func AdjuntarDocumentoTarea(chatID int64, tareaID uint, comentario string) (*models.TareaComentario, error) {
|
||||
if tareaID == 0 {
|
||||
return nil, fmt.Errorf("id de tarea requerido")
|
||||
}
|
||||
tarea, err := models.GetTareaByID(tareaID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tarea no encontrada: %w", err)
|
||||
}
|
||||
att, ok := PopTelegramAttachment(chatID)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no hay ningún documento pendiente en este chat; pide al usuario que lo envíe de nuevo")
|
||||
}
|
||||
if comentario == "" {
|
||||
comentario = fmt.Sprintf("Archivo adjunto vía Telegram: %s", att.OriginalName)
|
||||
}
|
||||
|
||||
dir := fmt.Sprintf("uploads/tareas/%d", tareaID)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
_ = os.Remove(att.Path)
|
||||
return nil, fmt.Errorf("no se pudo crear el directorio: %w", err)
|
||||
}
|
||||
ext := filepath.Ext(att.OriginalName)
|
||||
finalPath := filepath.Join(dir, fmt.Sprintf("%d_telegram%s", time.Now().UnixMilli(), ext))
|
||||
if err := os.Rename(att.Path, finalPath); err != nil {
|
||||
return nil, fmt.Errorf("no se pudo guardar el archivo: %w", err)
|
||||
}
|
||||
|
||||
archivosJSON, _ := json.Marshal([]map[string]string{
|
||||
{"nombre": att.OriginalName, "ruta": finalPath},
|
||||
})
|
||||
|
||||
c := &models.TareaComentario{
|
||||
TareaID: tareaID,
|
||||
Contenido: comentario,
|
||||
Archivos: string(archivosJSON),
|
||||
}
|
||||
if err := models.CreateComentario(c); err != nil {
|
||||
return nil, fmt.Errorf("no se pudo guardar el comentario: %w", err)
|
||||
}
|
||||
go NotificarTareaComentario(tarea, comentario)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func cambioAsignado(a, b *uint) bool {
|
||||
if a == nil && b == nil {
|
||||
return false
|
||||
|
||||
@@ -362,6 +362,11 @@ func agentTools() []agentTool {
|
||||
"id": num("ID de la tarea"),
|
||||
"asignado_id": num("ID del usuario responsable (usa listar_usuarios si no lo sabes)"),
|
||||
}, []string{"id", "asignado_id"})),
|
||||
tool("adjuntar_documento_tarea", "Guarda como comentario de una tarea el archivo (PDF/foto) que el usuario acaba de enviar por Telegram en este chat. Úsala cuando el usuario mande un archivo junto con instrucciones sobre una tarea (nueva o existente). Solo funciona si hay un archivo adjunto pendiente. Si la tarea es nueva, primero créala con crear_tarea y usa el id que devuelve.",
|
||||
obj(map[string]agentToolParam{
|
||||
"id": num("ID de la tarea a la que se adjunta el archivo"),
|
||||
"comentario": str("Comentario que acompaña al archivo (opcional)"),
|
||||
}, []string{"id"})),
|
||||
tool("actualizar_estado_tarea", "Cambia el estado de una tarea. Los valores deben coincidir con las columnas del tablero Kanban del dashboard.",
|
||||
obj(map[string]agentToolParam{
|
||||
"id": num("ID de la tarea"),
|
||||
@@ -1044,6 +1049,13 @@ func runTool(chatID int64, name string, a map[string]interface{}) (interface{},
|
||||
}
|
||||
return map[string]interface{}{"ok": true, "id": t.ID, "asignado_id": asignadoID}, nil
|
||||
|
||||
case "adjuntar_documento_tarea":
|
||||
c, err := AdjuntarDocumentoTarea(chatID, uint(getInt("id", 0)), getStr("comentario"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return map[string]interface{}{"ok": true, "comentario_id": c.ID, "tarea_id": c.TareaID}, nil
|
||||
|
||||
case "actualizar_estado_tarea":
|
||||
id := uint(getInt("id", 0))
|
||||
estado := getStr("estado")
|
||||
@@ -1497,9 +1509,10 @@ COMPORTAMIENTO:
|
||||
- Cuando el mensaje empiece con "[Documento adjunto recibido: ...]", el usuario acaba de enviar un PDF o foto por Telegram y puedes leerlo directamente (está adjunto a este mismo mensaje, no es solo un nombre de archivo). Decide primero de qué se trata el archivo antes de guardarlo:
|
||||
· Si es una factura → primero determina la dirección leyendo quién emite y quién recibe: si el emisor/vendedor es "U-Site S.A.S BIC" (o similar) y el destinatario es un cliente, es una factura de VENTA → identifica cliente, monto y número, resuelve el cliente_id con listar_clientes, y llama a adjuntar_factura. Si es al revés — un proveedor (AWS, Coolify, un hosting, cualquier tercero) le está facturando A U-Site — es una factura de COMPRA → llama a adjuntar_factura_compra con el nombre de ese proveedor, no busques cliente_id. Ante la duda, mira a nombre de quién sale la plata: si U-Site cobra, es venta; si U-Site paga, es compra
|
||||
· Si es un documento de un proyecto (contrato, orden de servicio, entregable, etc.) → identifica a qué proyecto corresponde (usa listar_proyectos si hace falta) y llama a adjuntar_documento_proyecto
|
||||
Si logras identificar los datos necesarios (del documento o de lo que escribió el usuario), guarda directamente sin pedir confirmación — solo pregunta si de verdad no hay forma de determinar el cliente/proyecto o falta un dato imprescindible. No uses estas tools si el mensaje no menciona ningún documento adjunto
|
||||
· Si el mensaje habla de una tarea (ej: "asigna esto a Natalia", "esta es la tarea para el diseño") → usa adjuntar_documento_tarea. Si la tarea todavía no existe, créala primero con crear_tarea (con asignado_id si corresponde) y luego llama a adjuntar_documento_tarea con el id que te devolvió; si ya existe, resuélvela con listar_tareas
|
||||
Si logras identificar los datos necesarios (del documento o de lo que escribió el usuario), guarda directamente sin pedir confirmación — solo pregunta si de verdad no hay forma de determinar el cliente/proyecto/tarea o falta un dato imprescindible. No uses estas tools si el mensaje no menciona ningún documento adjunto
|
||||
- Para registrar un proyecto nuevo con sus fases (el usuario puede describirlo en texto o mandarlo en un documento), primero usa crear_proyecto y luego llama a crear_fase_proyecto una vez por cada fase que corresponda — no hace falta preguntar confirmación entre cada fase
|
||||
- Para asignar una tarea a alguien, resuelve el nombre con listar_usuarios y usa asignado_id en crear_tarea (si es nueva) o asignar_tarea (si ya existe) — la persona asignada recibe una notificación
|
||||
- Para asignar una tarea a alguien, resuelve el nombre con listar_usuarios y usa asignado_id en crear_tarea (si es nueva) o asignar_tarea (si ya existe) — la persona asignada recibe una notificación. Si además el usuario mandó un archivo junto con la instrucción de asignar, después de crear/asignar la tarea usa también adjuntar_documento_tarea para guardar ese archivo en la tarea
|
||||
|
||||
COMANDOS ESPECIALES (el usuario puede escribirlos):
|
||||
- /reset — olvidar el historial de esta conversación
|
||||
|
||||
Reference in New Issue
Block a user