package main import ( "context" "errors" "strconv" "strings" "fergalla.com/dockerbot/internal/apperrors" "github.com/go-telegram/bot" "github.com/go-telegram/bot/models" ) func (appCtx *App) handleDockerLogs(ctx context.Context, b *bot.Bot, chatID int64, params []string) { if len(params) == 0 { appCtx.sendMessage( ctx, b, chatID, appCtx.T("e.container.missing_name", map[string]string{ "example": "logs", "opts": " --tail 10", }), true, nil, ) return } container, tail := parseLogsParams(params) text, keyboard, err := appCtx.renderContainerLogs(ctx, container, 0, tail) if err != nil { appCtx.logger.Error( "Failed to render container logs", "module", "Bot", "container", container, "tail", tail, "error", err, ) if errors.Is(err, apperrors.ErrContainerNotFound) { appCtx.sendMessage(ctx, b, chatID, appCtx.T("e.container.not_found"), true, nil) } else { appCtx.sendMessage(ctx, b, chatID, appCtx.T("e.container.logs"), true, nil) } return } appCtx.sendMessage(ctx, b, chatID, text, true, keyboard) } func parseLogsParams(params []string) (container string, tail int) { const defaultTail = 0 const maxTail = 100 if len(params) == 0 { return "", defaultTail } container = params[0] tail = defaultTail for i := 1; i < len(params); i++ { if params[i] == "--tail" && i+1 < len(params) { if v, err := strconv.Atoi(params[i+1]); err == nil { tail = v } i++ // saltar valor } } // clamp if tail <= 0 { tail = defaultTail } if tail > maxTail { tail = maxTail } return } func (appCtx *App) callbackDockerLogs(ctx context.Context, b *bot.Bot, update *models.Update) { if update.CallbackQuery == nil { return } cb := update.CallbackQuery parts := strings.Split(cb.Data, ":") if len(parts) < 2 { return } container := parts[1] pageIndex := 0 if len(parts) >= 3 { if p, err := strconv.Atoi(parts[2]); err == nil { pageIndex = p } } tail := 0 // default if len(parts) >= 4 { if t, err := strconv.Atoi(parts[3]); err == nil { tail = t } } text, keyboard, err := appCtx.renderContainerLogs(ctx, container, pageIndex, tail) if err != nil { appCtx.logger.Error("Logs render generator failed", "error", err) b.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{ CallbackQueryID: cb.ID, Text: appCtx.T("e.container.logs"), ShowAlert: true, }) return } b.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{ CallbackQueryID: cb.ID, }) _, err = b.EditMessageText(ctx, &bot.EditMessageTextParams{ ChatID: cb.Message.Message.Chat.ID, MessageID: cb.Message.Message.ID, Text: text, ParseMode: "html", ReplyMarkup: keyboard, }) if err != nil { appCtx.logger.Warn("Failed to render logs", "error", err) return } } func (appCtx *App) callbackDockerLogsRefresh(ctx context.Context, b *bot.Bot, update *models.Update) { if update.CallbackQuery == nil { return } cb := update.CallbackQuery parts := strings.Split(cb.Data, ":") if len(parts) < 2 { return } container := parts[1] pageIndex := 0 if len(parts) >= 3 { if p, err := strconv.Atoi(parts[2]); err == nil { pageIndex = p } } tail := 0 // default if len(parts) >= 4 { if t, err := strconv.Atoi(parts[3]); err == nil { tail = t } } // 🎨 render logs actualizado text, keyboard, err := appCtx.renderContainerLogs(ctx, container, pageIndex, tail) if err != nil { var msg string if errors.Is(err, apperrors.ErrContainerNotFound) { msg = appCtx.T("e.container.not_found") } else { msg = appCtx.T("e.container.logs") } b.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{ CallbackQueryID: cb.ID, Text: msg, ShowAlert: true, }) return } // ✅ feedback UX b.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{ CallbackQueryID: cb.ID, Text: "🔄 " + appCtx.T("ui.updated"), ShowAlert: false, }) // 🔄 actualizar mensaje _, err = b.EditMessageText(ctx, &bot.EditMessageTextParams{ ChatID: cb.Message.Message.Chat.ID, MessageID: cb.Message.Message.ID, Text: text, ParseMode: "html", ReplyMarkup: keyboard, }) if err != nil { appCtx.logger.Warn( "Failed to refresh logs", "module", "Bot", "container", container, "error", err, ) return } } func (appCtx *App) callbackDockerBackDetail(ctx context.Context, b *bot.Bot, update *models.Update) { if update.CallbackQuery == nil { return } cb := update.CallbackQuery parts := strings.Split(cb.Data, ":") if len(parts) < 2 { return } container := parts[1] pageIndex := 0 if len(parts) >= 3 { if p, err := strconv.Atoi(parts[2]); err == nil { pageIndex = p } } text, keyboard, err := appCtx.renderContainerDetail(ctx, container, pageIndex) if err != nil { b.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{ CallbackQueryID: cb.ID, Text: appCtx.T("e.container.info"), ShowAlert: true, }) return } b.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{ CallbackQueryID: cb.ID, }) _, err = b.EditMessageText(ctx, &bot.EditMessageTextParams{ ChatID: cb.Message.Message.Chat.ID, MessageID: cb.Message.Message.ID, Text: text, ParseMode: "html", ReplyMarkup: keyboard, }) if err != nil { appCtx.logger.Warn("Failed to go back to detail", "error", err) return } }