telegram
This commit is contained in:
@@ -822,6 +822,12 @@ func PortalTelegramValidar(c *fiber.Ctx) error {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "No autenticado"})
|
||||
}
|
||||
|
||||
// Si ya está vinculado (por ejemplo, vía webhook), responder éxito de inmediato.
|
||||
full, err := models.GetPortalUserByID(u.ID)
|
||||
if err == nil && full != nil && strings.TrimSpace(full.TelegramChatID) != "" {
|
||||
return c.JSON(fiber.Map{"ok": true, "chat_id": full.TelegramChatID})
|
||||
}
|
||||
|
||||
// Recuperar el token vigente del usuario
|
||||
tkn, err := models.GetTelegramPortalTokenByUser(u.ID)
|
||||
if err != nil || tkn == nil {
|
||||
@@ -829,11 +835,15 @@ func PortalTelegramValidar(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
configs, _ := models.GetAllTelegramConfigs()
|
||||
hadWebhookConflict := false
|
||||
for _, cfg := range configs {
|
||||
if !cfg.Activo || cfg.BotToken == "" {
|
||||
continue
|
||||
}
|
||||
chatID, found := searchTokenInUpdates(cfg.BotToken, tkn.Token)
|
||||
chatID, found, reason := searchTokenInUpdates(cfg.BotToken, tkn.Token)
|
||||
if reason == "webhook_conflict" {
|
||||
hadWebhookConflict = true
|
||||
}
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
@@ -848,22 +858,36 @@ func PortalTelegramValidar(c *fiber.Ctx) error {
|
||||
return c.JSON(fiber.Map{"ok": true, "chat_id": fmt.Sprintf("%d", chatID)})
|
||||
}
|
||||
|
||||
// Revalidar por si el webhook hizo la vinculación mientras corría esta petición.
|
||||
full, err = models.GetPortalUserByID(u.ID)
|
||||
if err == nil && full != nil && strings.TrimSpace(full.TelegramChatID) != "" {
|
||||
return c.JSON(fiber.Map{"ok": true, "chat_id": full.TelegramChatID})
|
||||
}
|
||||
|
||||
if hadWebhookConflict {
|
||||
return c.Status(200).JSON(fiber.Map{
|
||||
"ok": false,
|
||||
"error": "El bot está en modo webhook. Envía /vincular " + tkn.Token + " al bot y espera unos segundos; luego vuelve a verificar.",
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(200).JSON(fiber.Map{"ok": false, "error": "No se encontró el mensaje. Asegúrate de enviar /vincular " + tkn.Token + " al bot."})
|
||||
}
|
||||
|
||||
// searchTokenInUpdates llama getUpdates y busca un mensaje que contenga el token.
|
||||
// Retorna el chat_id y true si lo encuentra.
|
||||
func searchTokenInUpdates(botToken, token string) (int64, bool) {
|
||||
func searchTokenInUpdates(botToken, token string) (int64, bool, string) {
|
||||
apiURL := fmt.Sprintf("https://api.telegram.org/bot%s/getUpdates?limit=100", botToken)
|
||||
resp, err := http.Get(apiURL) //nolint:noctx
|
||||
if err != nil {
|
||||
log.Printf("[searchTokenInUpdates] error getUpdates: %v", err)
|
||||
return 0, false
|
||||
return 0, false, "network_error"
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result struct {
|
||||
OK bool `json:"ok"`
|
||||
OK bool `json:"ok"`
|
||||
Description string `json:"description"`
|
||||
Result []struct {
|
||||
Message struct {
|
||||
Chat struct {
|
||||
@@ -873,8 +897,18 @@ func searchTokenInUpdates(botToken, token string) (int64, bool) {
|
||||
} `json:"message"`
|
||||
} `json:"result"`
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil || !result.OK {
|
||||
return 0, false
|
||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||
return 0, false, "decode_error"
|
||||
}
|
||||
if !result.OK {
|
||||
desc := strings.ToLower(strings.TrimSpace(result.Description))
|
||||
if strings.Contains(desc, "can't use getupdates method while webhook is active") {
|
||||
return 0, false, "webhook_conflict"
|
||||
}
|
||||
if desc != "" {
|
||||
log.Printf("[searchTokenInUpdates] Telegram API !ok: %s", result.Description)
|
||||
}
|
||||
return 0, false, "api_not_ok"
|
||||
}
|
||||
|
||||
upperToken := strings.ToUpper(token)
|
||||
@@ -882,8 +916,8 @@ func searchTokenInUpdates(botToken, token string) (int64, bool) {
|
||||
txt := strings.ToUpper(strings.TrimSpace(upd.Message.Text))
|
||||
if strings.Contains(txt, upperToken) && upd.Message.Chat.ID != 0 {
|
||||
log.Printf("[searchTokenInUpdates] token=%s chatID=%d", token, upd.Message.Chat.ID)
|
||||
return upd.Message.Chat.ID, true
|
||||
return upd.Message.Chat.ID, true, ""
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
return 0, false, ""
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"mime/multipart"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@@ -492,25 +491,10 @@ func DownloadDocumento(c *fiber.Ctx) error {
|
||||
if !strings.HasPrefix(clean, "uploads/") {
|
||||
return c.Status(403).JSON(fiber.Map{"error": "Acceso denegado"})
|
||||
}
|
||||
nombre := item.OriginalName
|
||||
if nombre == "" {
|
||||
nombre = item.Nombre
|
||||
}
|
||||
c.Set("Content-Disposition", attachmentDisposition(nombre))
|
||||
c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, item.OriginalName))
|
||||
return c.SendFile(clean)
|
||||
}
|
||||
|
||||
// attachmentDisposition genera un header Content-Disposition con fallback ASCII y encoding RFC 5987.
|
||||
func attachmentDisposition(name string) string {
|
||||
safe := strings.Map(func(r rune) rune {
|
||||
if r > 127 || r == '"' || r == '\\' || r == '/' || r == '\n' || r == '\r' {
|
||||
return '_'
|
||||
}
|
||||
return r
|
||||
}, name)
|
||||
return fmt.Sprintf(`attachment; filename="%s"; filename*=UTF-8''%s`, safe, url.PathEscape(name))
|
||||
}
|
||||
|
||||
// ─── Tickets (admin) ──────────────────────────────────────────────────────────
|
||||
|
||||
func GetTickets(c *fiber.Ctx) error {
|
||||
|
||||
Reference in New Issue
Block a user