feat: OCR y Whisper como herramientas opcionales por canal en uMind
Cada canal (Telegram/WhatsApp) de un agente ahora puede activar, de forma independiente, que los audios entrantes se transcriban con el Whisper ASR propio y que a las imágenes entrantes se les extraiga texto con el servicio OCR propio, antes de pasarle el mensaje al agente. Antes esos mensajes se ignoraban en silencio. - UmindCanal gana usar_whisper_audio/usar_ocr_imagenes (default off). - WhatsApp: se descarga el media vía Graph API (resolución de URL + fetch con el mismo access_token del canal) y se enruta a Whisper/OCR según type. - Telegram: se descarga el archivo vía getFile + CDN de archivos del bot, mismo enrutamiento para voice/audio/photo. - Panel: checkboxes en el alta de canal y toggles inline por canal ya creado, en la tab Canales del agente. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
d493d6dee6
commit
84506a98e2
@@ -15,9 +15,16 @@ type umindTgChat struct {
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
type umindTgFileRef struct {
|
||||
FileID string `json:"file_id"`
|
||||
}
|
||||
|
||||
type umindTgMessage struct {
|
||||
Chat umindTgChat `json:"chat"`
|
||||
Text string `json:"text"`
|
||||
Chat umindTgChat `json:"chat"`
|
||||
Text string `json:"text"`
|
||||
Voice *umindTgFileRef `json:"voice"`
|
||||
Audio *umindTgFileRef `json:"audio"`
|
||||
Photo []umindTgFileRef `json:"photo"`
|
||||
}
|
||||
|
||||
type umindTgUpdate struct {
|
||||
@@ -39,12 +46,23 @@ func UmindTelegramWebhook(c *fiber.Ctx) error {
|
||||
if err := c.BodyParser(&update); err != nil || update.Message == nil {
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
texto := strings.TrimSpace(update.Message.Text)
|
||||
if texto == "" {
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
msg := update.Message
|
||||
|
||||
if err := services.ProcesarMensajeTelegramUmind(canal, update.Message.Chat.ID, texto); err != nil {
|
||||
switch {
|
||||
case msg.Voice != nil && msg.Voice.FileID != "":
|
||||
err = services.ProcesarMediaTelegramUmind(canal, msg.Chat.ID, msg.Voice.FileID, "audio")
|
||||
case msg.Audio != nil && msg.Audio.FileID != "":
|
||||
err = services.ProcesarMediaTelegramUmind(canal, msg.Chat.ID, msg.Audio.FileID, "audio")
|
||||
case len(msg.Photo) > 0:
|
||||
err = services.ProcesarMediaTelegramUmind(canal, msg.Chat.ID, msg.Photo[len(msg.Photo)-1].FileID, "image")
|
||||
default:
|
||||
texto := strings.TrimSpace(msg.Text)
|
||||
if texto == "" {
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
err = services.ProcesarMensajeTelegramUmind(canal, msg.Chat.ID, texto)
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("[UMIND_TELEGRAM] canal %d: %v", canal.ID, err)
|
||||
models.RegistrarEventoUmind(canal.AgenteID, "error", "canal_telegram", "Error procesando un mensaje de Telegram", err.Error())
|
||||
}
|
||||
@@ -53,12 +71,18 @@ func UmindTelegramWebhook(c *fiber.Ctx) error {
|
||||
|
||||
// ─── WhatsApp ────────────────────────────────────────────────────────────────
|
||||
|
||||
type umindWaMediaRef struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type umindWaMessage struct {
|
||||
From string `json:"from"`
|
||||
Type string `json:"type"`
|
||||
Text struct {
|
||||
Body string `json:"body"`
|
||||
} `json:"text"`
|
||||
Image umindWaMediaRef `json:"image"`
|
||||
Audio umindWaMediaRef `json:"audio"`
|
||||
}
|
||||
|
||||
type umindWaValue struct {
|
||||
@@ -130,10 +154,28 @@ func UmindWhatsAppWebhook(c *fiber.Ctx) error {
|
||||
for _, entry := range payload.Entry {
|
||||
for _, change := range entry.Changes {
|
||||
for _, msg := range change.Value.Messages {
|
||||
if msg.Type != "text" || strings.TrimSpace(msg.Text.Body) == "" {
|
||||
var err error
|
||||
switch msg.Type {
|
||||
case "text":
|
||||
texto := strings.TrimSpace(msg.Text.Body)
|
||||
if texto == "" {
|
||||
continue
|
||||
}
|
||||
err = services.ProcesarMensajeWhatsAppUmind(canal, msg.From, texto)
|
||||
case "audio":
|
||||
if msg.Audio.ID == "" {
|
||||
continue
|
||||
}
|
||||
err = services.ProcesarMediaWhatsAppUmind(canal, msg.From, msg.Audio.ID, "audio")
|
||||
case "image":
|
||||
if msg.Image.ID == "" {
|
||||
continue
|
||||
}
|
||||
err = services.ProcesarMediaWhatsAppUmind(canal, msg.From, msg.Image.ID, "image")
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if err := services.ProcesarMensajeWhatsAppUmind(canal, msg.From, strings.TrimSpace(msg.Text.Body)); err != nil {
|
||||
if err != nil {
|
||||
log.Printf("[UMIND_WHATSAPP] canal %d: %v", canal.ID, err)
|
||||
models.RegistrarEventoUmind(canal.AgenteID, "error", "canal_whatsapp", "Error procesando un mensaje de WhatsApp", err.Error())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user