fix: unsilence all error returns from SQL Exec, Bot API calls, and migrations

This commit is contained in:
2026-06-24 20:03:24 +03:30
parent 4165839477
commit e7e4dc7bfe
8 changed files with 227 additions and 193 deletions

View File

@@ -4,11 +4,11 @@ import (
"context"
"database/sql"
"fmt"
"log/slog"
"strconv"
"strings"
"time"
bot "github.com/go-telegram/bot"
"github.com/go-telegram/bot/models"
"worktimeBot/internal/db"
@@ -21,7 +21,7 @@ func (h *Handler) handleHistoryMsg(ctx context.Context, msg *models.Message) {
// historyCallback opens or refreshes the calendar view (inline).
func (h *Handler) historyCallback(ctx context.Context, chatID int64, msgID int, callbackID string) {
defer h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
defer h.answerCb(ctx, callbackID)
h.editCalendar(ctx, chatID, msgID, 0, 0)
}
@@ -53,7 +53,7 @@ func (h *Handler) handleEditMsg(ctx context.Context, msg *models.Message) {
// handleHistoryCallback routes calendar-related callback data to the right handler.
func (h *Handler) handleHistoryCallback(ctx context.Context, chatID int64, msgID int, callbackID, data string) {
defer h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
defer h.answerCb(ctx, callbackID)
user, err := h.getOrCreateUser(chatID)
if err != nil {
return
@@ -256,10 +256,15 @@ func (h *Handler) editCalendar(ctx context.Context, chatID int64, msgID int, yea
if err == nil {
for rows.Next() {
var d string
rows.Scan(&d)
if err := rows.Scan(&d); err != nil {
slog.Error("failed to scan day row", "error", err)
continue
}
hasEvent[d] = true
}
rows.Close()
if err := rows.Close(); err != nil {
slog.Error("failed to close rows", "error", err)
}
}
kbRows := [][]models.InlineKeyboardButton{}
@@ -325,9 +330,7 @@ func (h *Handler) editCalendarYearPicker(ctx context.Context, chatID int64, msgI
backData := fmt.Sprintf("cal_years_back_%d_%d", refY, refM)
navSuffix := fmt.Sprintf("_%d_%d", refY, refM)
kb := buildYearPicker("cal_", backData, navSuffix, centerYear)
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
ChatID: chatID, MessageID: msgID, Text: "Select year:", ReplyMarkup: &kb,
})
h.editText(ctx, chatID, msgID, "Select year:", &kb)
}
// editDayView opens an existing message as a day view.
@@ -416,12 +419,17 @@ func (h *Handler) editEventType(ctx context.Context, chatID int64, msgID int, us
{Text: "Back", CallbackData: fmt.Sprintf("back_day_%d", eventID)},
})
kb := models.InlineKeyboardMarkup{InlineKeyboard: kbRows}
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{ChatID: chatID, MessageID: msgID, Text: text, ReplyMarkup: &kb})
h.editText(ctx, chatID, msgID, text, &kb)
}
// setEventWorkType updates an event's work type and returns to the day view.
func (h *Handler) setEventWorkType(ctx context.Context, chatID int64, msgID int, user *db.User, eventID, workTypeID int64, loc *time.Location) {
h.DB.DB().Exec("UPDATE events SET work_type_id=? WHERE id=?", workTypeID, eventID)
_, err := h.DB.DB().Exec("UPDATE events SET work_type_id=? WHERE id=?", workTypeID, eventID)
if err != nil {
slog.Error("failed to update event work type", "event_id", eventID, "error", err)
h.sendDayView(ctx, chatID, msgID, user, "", loc, false)
return
}
event, err := h.getEventByID(user.ID, eventID)
if err != nil {
return
@@ -467,7 +475,7 @@ func (h *Handler) editEventTime(ctx context.Context, chatID int64, msgID int, us
func(hh int) string { return fmt.Sprintf("edittm_%d_%02d", eventID, hh) },
fmt.Sprintf("back_day_%d", eventID),
)
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{ChatID: chatID, MessageID: msgID, Text: "Select hour:", ReplyMarkup: &kb})
h.editText(ctx, chatID, msgID, "Select hour:", &kb)
}
// editEventTimeMin shows a minute picker (15-min intervals) after hour selection.
@@ -476,7 +484,7 @@ func (h *Handler) editEventTimeMin(ctx context.Context, chatID int64, msgID int,
func(mm int) string { return fmt.Sprintf("edittm_%d_%02d_%02d", eventID, hh, mm) },
fmt.Sprintf("edit_time_%d", eventID),
)
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{ChatID: chatID, MessageID: msgID, Text: fmt.Sprintf("Select minute for hour %02d:", hh), ReplyMarkup: &kb})
h.editText(ctx, chatID, msgID, fmt.Sprintf("Select minute for hour %02d:", hh), &kb)
}
// editEventTimeSet applies a new time to an event, checking for overlaps.
@@ -528,19 +536,24 @@ func (h *Handler) editEventTimeSet(ctx context.Context, chatID int64, msgID int,
}
for i := 1; i < len(sorted); i++ {
if sorted[i].typ == sorted[i-1].typ {
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{ChatID: chatID, MessageID: msgID, Text: "Edit rejected: overlapping events. Two consecutive events must be different types (in/out)."})
h.editText(ctx, chatID, msgID, "Edit rejected: overlapping events. Two consecutive events must be different types (in/out).", nil)
return
}
}
}
h.DB.DB().Exec("UPDATE events SET occurred_at=? WHERE id=?", newTime.Unix(), eventID)
_, err = h.DB.DB().Exec("UPDATE events SET occurred_at=? WHERE id=?", newTime.Unix(), eventID)
if err != nil {
slog.Error("failed to update event occurred_at", "event_id", eventID, "error", err)
h.sendDayView(ctx, chatID, msgID, user, date, loc, false)
return
}
h.sendDayView(ctx, chatID, msgID, user, date, loc, false)
}
// acknowledgeCallback sends an empty acknowledgement for a callback query.
func (h *Handler) acknowledgeCallback(ctx context.Context, chatID int64, msgID int) {
h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: fmt.Sprintf("cb_%d_%d", chatID, msgID)})
h.answerCb(ctx, fmt.Sprintf("cb_%d_%d", chatID, msgID))
}
// promptNote asks the user to type a note for the event, then re-renders the day view.
@@ -560,10 +573,7 @@ func (h *Handler) promptNote(ctx context.Context, chatID int64, msgID int, user
{{Text: "Cancel", CallbackData: fmt.Sprintf("note_cancel_%d", eventID)}},
},
}
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
ChatID: chatID, MessageID: msgID,
Text: fmt.Sprintf("Send the note for event at %s:", t), ReplyMarkup: &kb,
})
h.editText(ctx, chatID, msgID, fmt.Sprintf("Send the note for event at %s:", t), &kb)
}
// deleteEventPrompt asks the user to confirm event deletion.
@@ -576,7 +586,7 @@ func (h *Handler) deleteEventPrompt(ctx context.Context, chatID int64, msgID int
},
},
}
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{ChatID: chatID, MessageID: msgID, Text: "Delete this event?", ReplyMarkup: &kb})
h.editText(ctx, chatID, msgID, "Delete this event?", &kb)
}
// deleteEventConfirm deletes an event and warns if the timeline becomes invalid.
@@ -588,7 +598,12 @@ func (h *Handler) deleteEventConfirm(ctx context.Context, chatID int64, msgID in
t := time.Unix(event.OccurredAt, 0).In(loc)
date := t.Format("2006-01-02")
h.DB.DB().Exec("DELETE FROM events WHERE id=?", eventID)
_, err = h.DB.DB().Exec("DELETE FROM events WHERE id=?", eventID)
if err != nil {
slog.Error("failed to delete event", "event_id", eventID, "error", err)
h.sendDayView(ctx, chatID, msgID, user, date, loc, false)
return
}
// Warn if consecutive events now have the same type
events, err := h.DB.EventsForDayByDate(user.ID, date)
@@ -641,9 +656,7 @@ func (h *Handler) addEventTime(ctx context.Context, chatID int64, msgID int, use
func(hh int) string { return fmt.Sprintf("addtm_%s_%s_%02d", eventType, date, hh) },
"cal_day_"+date,
)
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
ChatID: chatID, MessageID: msgID, Text: fmt.Sprintf("Select hour for %s event:", eventType), ReplyMarkup: &kb,
})
h.editText(ctx, chatID, msgID, fmt.Sprintf("Select hour for %s event:", eventType), &kb)
}
// addEventTimeMin shows a minute picker (15-min intervals) after hour selection for adding an event.
@@ -652,9 +665,7 @@ func (h *Handler) addEventTimeMin(ctx context.Context, chatID int64, msgID int,
func(mm int) string { return fmt.Sprintf("addtm_%s_%s_%02d_%02d", eventType, date, hh, mm) },
"add"+eventType+"_"+date,
)
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
ChatID: chatID, MessageID: msgID, Text: fmt.Sprintf("Select minute for hour %02d:", hh), ReplyMarkup: &kb,
})
h.editText(ctx, chatID, msgID, fmt.Sprintf("Select minute for hour %02d:", hh), &kb)
}
// addEventDo creates a new event at the specified time and returns to the day view.

View File

@@ -341,7 +341,7 @@ func (h *Handler) sendExportDirect(ctx context.Context, chatID int64, user *db.U
// exportCallback opens the export month picker.
func (h *Handler) exportCallback(ctx context.Context, chatID int64, msgID int, callbackID string) {
defer h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
defer h.answerCb(ctx, callbackID)
user, err := h.getOrCreateUser(chatID)
if err != nil {
return
@@ -399,9 +399,9 @@ func (h *Handler) editExportMonthPicker(ctx context.Context, chatID int64, msgID
kb := models.InlineKeyboardMarkup{InlineKeyboard: kbRows}
if msgID == 0 {
h.Bot.SendMessage(ctx, &bot.SendMessageParams{ChatID: chatID, Text: "Select month to export:", ReplyMarkup: &kb})
h.sendWithKB(ctx, chatID, "Select month to export:", kb)
} else {
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{ChatID: chatID, MessageID: msgID, Text: "Select month to export:", ReplyMarkup: &kb})
h.editText(ctx, chatID, msgID, "Select month to export:", &kb)
}
}
@@ -411,9 +411,7 @@ func (h *Handler) editExportYearPicker(ctx context.Context, chatID int64, msgID
backData := fmt.Sprintf("exp_years_back_%d", refY)
navSuffix := fmt.Sprintf("_%d", refY)
kb := buildYearPicker("exp_", backData, navSuffix, centerYear)
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
ChatID: chatID, MessageID: msgID, Text: "Select year:", ReplyMarkup: &kb,
})
h.editText(ctx, chatID, msgID, "Select year:", &kb)
}
// formatMonthTitle returns the localized month name and year for a given calendar.
@@ -430,7 +428,7 @@ func formatMonthTitle(cal string, year, month int) string {
// handleExportCallback routes export inline button presses (year nav, month select).
func (h *Handler) handleExportCallback(ctx context.Context, chatID int64, msgID int, callbackID, data string) {
defer h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
defer h.answerCb(ctx, callbackID)
user, err := h.getOrCreateUser(chatID)
if err != nil {
return
@@ -474,18 +472,12 @@ func (h *Handler) handleExportCallback(ctx context.Context, chatID int64, msgID
if n, _ := fmt.Sscanf(data, "exp_do_%d_%d", &y, &m); n == 2 {
last, err := h.DB.GetLastEvent(user.ID)
if err == nil && last != nil && last.EventType == "in" {
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
ChatID: chatID,
MessageID: msgID,
Text: "You are currently clocked in. Please clock out first, then try again.",
ReplyMarkup: &models.InlineKeyboardMarkup{
InlineKeyboard: [][]models.InlineKeyboardButton{
{
{Text: "Back to Menu", CallbackData: "back_menu"},
},
},
kb := models.InlineKeyboardMarkup{
InlineKeyboard: [][]models.InlineKeyboardButton{
{{Text: "Back to Menu", CallbackData: "back_menu"}},
},
})
}
h.editText(ctx, chatID, msgID, "You are currently clocked in. Please clock out first, then try again.", &kb)
return
}
@@ -505,34 +497,22 @@ func (h *Handler) handleExportCallback(ctx context.Context, chatID int64, msgID
data, err := GenerateMonthlyReport(h.DB, user.ID, user.Timezone, user.ExportAccent, user.Calendar, y, m, start, end)
if err != nil {
slog.Error("generate report", "error", err)
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
ChatID: chatID,
MessageID: msgID,
Text: "Error generating report",
ReplyMarkup: &models.InlineKeyboardMarkup{
InlineKeyboard: [][]models.InlineKeyboardButton{
{
{Text: "Back to Menu", CallbackData: "back_menu"},
},
},
kb := models.InlineKeyboardMarkup{
InlineKeyboard: [][]models.InlineKeyboardButton{
{{Text: "Back to Menu", CallbackData: "back_menu"}},
},
})
}
h.editText(ctx, chatID, msgID, "Error generating report", &kb)
return
}
slog.Info("report generated", "bytes", len(data))
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
ChatID: chatID,
MessageID: msgID,
Text: "Report ready:",
ReplyMarkup: &models.InlineKeyboardMarkup{
InlineKeyboard: [][]models.InlineKeyboardButton{
{
{Text: "Back to Menu", CallbackData: "back_menu"},
},
},
kb := models.InlineKeyboardMarkup{
InlineKeyboard: [][]models.InlineKeyboardButton{
{{Text: "Back to Menu", CallbackData: "back_menu"}},
},
})
}
h.editText(ctx, chatID, msgID, "Report ready:", &kb)
if _, err := h.Bot.SendDocument(ctx, &bot.SendDocumentParams{
ChatID: chatID,

View File

@@ -87,18 +87,18 @@ func (h *Handler) handleActionMsg(ctx context.Context, msg *models.Message, fn a
return
}
kb := mainKeyboard()
h.Bot.SendMessage(ctx, &bot.SendMessageParams{ChatID: msg.Chat.ID, Text: text, ReplyMarkup: kb})
h.sendWithKB(ctx, msg.Chat.ID, text, kb)
}
// handleActionCallback runs an action function and edits the callback message with the result.
func (h *Handler) handleActionCallback(ctx context.Context, chatID int64, msgID int, callbackID string, fn actionFunc) {
defer h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
defer h.answerCb(ctx, callbackID)
text, err := fn(chatID)
if err != nil {
text = err.Error()
}
kb := backKeyboard()
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{ChatID: chatID, MessageID: msgID, Text: text, ReplyMarkup: kb})
h.editText(ctx, chatID, msgID, text, &kb)
}
// HandleMessage routes incoming text messages to the appropriate handler.
@@ -126,7 +126,9 @@ func (h *Handler) HandleMessage(ctx context.Context, msg *models.Message) {
loc := loadLocation(user.Timezone)
event, err := h.getEventByID(user.ID, pn.eventID)
if err == nil {
h.DB.SetEventNote(pn.eventID, msg.Text)
if err := h.DB.SetEventNote(pn.eventID, msg.Text); err != nil {
slog.Error("failed to save event note", "event_id", pn.eventID, "error", err)
}
date := time.Unix(event.OccurredAt, 0).In(loc).Format("2006-01-02")
h.sendDayView(ctx, msg.Chat.ID, 0, user, date, loc, true)
}
@@ -172,16 +174,16 @@ func (h *Handler) HandleMessage(ctx context.Context, msg *models.Message) {
// HandleCallback routes inline callback queries to the appropriate handler.
func (h *Handler) HandleCallback(ctx context.Context, cb *models.CallbackQuery) {
if len(h.AllowedUsers) > 0 && !h.AllowedUsers[cb.Message.Message.Chat.ID] {
h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: cb.ID, Text: "Unauthorized"})
h.answerCbWithText(ctx, cb.ID, "Unauthorized")
return
}
user, err := h.getOrCreateUser(cb.Message.Message.Chat.ID)
if err != nil {
h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: cb.ID})
h.answerCb(ctx, cb.ID)
return
}
if h.isRateLimited(user.ID) {
h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: cb.ID})
h.answerCb(ctx, cb.ID)
return
}
slog.Info("callback", "chat_id", cb.Message.Message.Chat.ID, "data", cb.Data)
@@ -200,7 +202,7 @@ func (h *Handler) HandleCallback(ctx context.Context, cb *models.CallbackQuery)
case "export":
h.exportCallback(ctx, chatID, msgID, cb.ID)
case "noop":
h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: cb.ID})
h.answerCb(ctx, cb.ID)
case "dayoff":
h.handleActionCallback(ctx, chatID, msgID, cb.ID, h.dayOff)
case "settings":
@@ -276,7 +278,7 @@ func (h *Handler) handleStart(ctx context.Context, msg *models.Message) {
}
slog.Info("start", "user_id", user.ID, "chat_id", msg.Chat.ID)
kb := mainKeyboard()
h.Bot.SendMessage(ctx, &bot.SendMessageParams{ChatID: msg.Chat.ID, Text: h.buildStatusText(msg.Chat.ID), ReplyMarkup: kb})
h.sendWithKB(ctx, msg.Chat.ID, h.buildStatusText(msg.Chat.ID), kb)
}
// handleHelp shows the list of available commands.
@@ -367,20 +369,20 @@ func (h *Handler) handleWorkTypeMsg(ctx context.Context, msg *models.Message) {
return
}
text, kb := h.buildWorkTypeKeyboard(user, day)
h.Bot.SendMessage(ctx, &bot.SendMessageParams{ChatID: msg.Chat.ID, Text: text, ReplyMarkup: kb})
h.sendWithKB(ctx, msg.Chat.ID, text, kb)
}
// workTypeCallback shows the work type picker (inline edit).
func (h *Handler) workTypeCallback(ctx context.Context, chatID int64, msgID int, callbackID string) {
defer h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
defer h.answerCb(ctx, callbackID)
user, day, err := h.getUserToday(chatID)
if err != nil {
kb := backKeyboard()
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{ChatID: chatID, MessageID: msgID, Text: "Error loading work types", ReplyMarkup: kb})
h.editText(ctx, chatID, msgID, "Error loading work types", &kb)
return
}
text, kb := h.buildWorkTypeKeyboard(user, day)
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{ChatID: chatID, MessageID: msgID, Text: text, ReplyMarkup: kb})
h.editText(ctx, chatID, msgID, text, &kb)
}
// buildWorkTypeKeyboard returns the work type selection keyboard.
@@ -407,7 +409,7 @@ func (h *Handler) buildWorkTypeKeyboard(user *db.User, day *db.Day) (string, mod
// selectWorkType updates the day's work type from a callback.
func (h *Handler) selectWorkType(ctx context.Context, chatID int64, msgID int, callbackID, idStr string) {
defer h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
defer h.answerCb(ctx, callbackID)
_, day, err := h.getUserToday(chatID)
if err != nil {
return
@@ -425,7 +427,7 @@ func (h *Handler) selectWorkType(ctx context.Context, chatID int64, msgID int, c
return
}
kb := backKeyboard()
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{ChatID: chatID, MessageID: msgID, Text: fmt.Sprintf("Work type set to: %s", wt.Name), ReplyMarkup: kb})
h.editText(ctx, chatID, msgID, fmt.Sprintf("Work type set to: %s", wt.Name), &kb)
}
// mainKeyboard returns the persistent inline menu.
@@ -472,7 +474,41 @@ func formatDuration(seconds int64) string {
// sendText sends a plain text message to the given chat.
func (h *Handler) sendText(ctx context.Context, chatID int64, text string) {
h.Bot.SendMessage(ctx, &bot.SendMessageParams{ChatID: chatID, Text: text})
if _, err := h.Bot.SendMessage(ctx, &bot.SendMessageParams{ChatID: chatID, Text: text}); err != nil {
slog.Warn("send text failed", "chat_id", chatID, "error", err)
}
}
// editText edits an existing message, logging errors.
func (h *Handler) editText(ctx context.Context, chatID int64, msgID int, text string, kb *models.InlineKeyboardMarkup) {
p := &bot.EditMessageTextParams{ChatID: chatID, MessageID: msgID, Text: text}
if kb != nil {
p.ReplyMarkup = kb
}
if _, err := h.Bot.EditMessageText(ctx, p); err != nil {
slog.Warn("edit text failed", "chat_id", chatID, "msg_id", msgID, "error", err)
}
}
// sendWithKB sends a message with an inline keyboard, logging errors.
func (h *Handler) sendWithKB(ctx context.Context, chatID int64, text string, kb models.InlineKeyboardMarkup) {
if _, err := h.Bot.SendMessage(ctx, &bot.SendMessageParams{ChatID: chatID, Text: text, ReplyMarkup: kb}); err != nil {
slog.Warn("send with keyboard failed", "chat_id", chatID, "error", err)
}
}
// answerCb answers a callback query, logging errors.
func (h *Handler) answerCb(ctx context.Context, callbackID string) {
if _, err := h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID}); err != nil {
slog.Debug("answer callback failed", "error", err)
}
}
// answerCbWithText answers a callback query with a toast message, logging errors.
func (h *Handler) answerCbWithText(ctx context.Context, callbackID, text string) {
if _, err := h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID, Text: text}); err != nil {
slog.Debug("answer callback with text failed", "error", err)
}
}
// navMonth returns the (prevY, prevM, nextY, nextM) for month navigation.
@@ -493,8 +529,12 @@ func navMonth(year, month int) (prevY, prevM, nextY, nextM int) {
// sendOrEdit sends a new message or edits an existing one depending on msgID.
func (h *Handler) sendOrEdit(ctx context.Context, chatID int64, msgID int, text string, kb *models.InlineKeyboardMarkup) {
if msgID == 0 {
h.Bot.SendMessage(ctx, &bot.SendMessageParams{ChatID: chatID, Text: text, ReplyMarkup: kb})
if kb != nil {
h.sendWithKB(ctx, chatID, text, *kb)
} else {
h.sendText(ctx, chatID, text)
}
} else {
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{ChatID: chatID, MessageID: msgID, Text: text, ReplyMarkup: kb})
h.editText(ctx, chatID, msgID, text, kb)
}
}

View File

@@ -7,7 +7,6 @@ import (
"time"
bot "github.com/go-telegram/bot"
"github.com/go-telegram/bot/models"
"worktimeBot/internal/db"
)
@@ -56,32 +55,9 @@ func (h *Handler) buildStatusText(chatID int64) string {
// backToMenu returns to the main menu from any sub-menu.
func (h *Handler) backToMenu(ctx context.Context, chatID int64, msgID int, callbackID string) {
defer h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
text := h.buildStatusText(chatID)
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
ChatID: chatID,
MessageID: msgID,
Text: text,
ReplyMarkup: &models.InlineKeyboardMarkup{
InlineKeyboard: [][]models.InlineKeyboardButton{
{
{Text: "Clock In", CallbackData: "clockin"},
{Text: "Clock Out", CallbackData: "clockout"},
},
{
{Text: "Work Type", CallbackData: "worktype"},
{Text: "Day Off", CallbackData: "dayoff"},
},
{
{Text: "Report", CallbackData: "report"},
{Text: "Export", CallbackData: "export"},
},
{
{Text: "Settings", CallbackData: "settings"},
},
},
},
})
defer h.answerCb(ctx, callbackID)
kb := mainKeyboard()
h.editText(ctx, chatID, msgID, h.buildStatusText(chatID), &kb)
}
// buildDailyReport builds a formatted daily report string for a given user/day.
@@ -164,6 +140,12 @@ func (h *Handler) SendDailyReport(ctx context.Context, userID int64, chatID int6
return
}
text := h.buildDailyReport(user, day, events)
h.Bot.SendMessage(ctx, &bot.SendMessageParams{ChatID: chatID, Text: text})
h.DB.MarkReportSent(user.ID, today)
_, err = h.Bot.SendMessage(ctx, &bot.SendMessageParams{ChatID: chatID, Text: text})
if err != nil {
slog.Error("daily report: send message failed", "chat_id", chatID, "error", err)
return
}
if err := h.DB.MarkReportSent(user.ID, today); err != nil {
slog.Error("daily report: mark sent failed", "chat_id", chatID, "error", err)
}
}

View File

@@ -7,7 +7,6 @@ import (
"strings"
"time"
bot "github.com/go-telegram/bot"
"github.com/go-telegram/bot/models"
"worktimeBot/internal/db"
@@ -59,7 +58,7 @@ func (h *Handler) handleAccentMsg(ctx context.Context, msg *models.Message) {
return
}
text, kb := h.buildAccentKeyboard(user)
h.Bot.SendMessage(ctx, &bot.SendMessageParams{ChatID: msg.Chat.ID, Text: text, ReplyMarkup: &kb})
h.sendWithKB(ctx, msg.Chat.ID, text, kb)
}
// handleSetTimezone validates and updates the user's IANA timezone.
@@ -98,18 +97,13 @@ func (h *Handler) handleSetTimezone(ctx context.Context, msg *models.Message) {
// settingsCallback shows the settings inline menu.
func (h *Handler) settingsCallback(ctx context.Context, chatID int64, msgID int, callbackID string) {
h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
h.answerCb(ctx, callbackID)
user, err := h.getOrCreateUser(chatID)
if err != nil {
return
}
text, kb := h.buildSettingsKeyboard(user)
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
ChatID: chatID,
MessageID: msgID,
Text: text,
ReplyMarkup: &kb,
})
h.editText(ctx, chatID, msgID, text, &kb)
}
// buildSettingsKeyboard returns the settings menu text and inline keyboard.
@@ -132,23 +126,18 @@ func (h *Handler) buildSettingsKeyboard(user *db.User) (string, models.InlineKey
// accentCallback shows the accent color picker (inline edit).
func (h *Handler) accentCallback(ctx context.Context, chatID int64, msgID int, callbackID string) {
h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
h.answerCb(ctx, callbackID)
user, err := h.getOrCreateUser(chatID)
if err != nil {
return
}
text, kb := h.buildAccentKeyboard(user)
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
ChatID: chatID,
MessageID: msgID,
Text: text,
ReplyMarkup: &kb,
})
h.editText(ctx, chatID, msgID, text, &kb)
}
// timezoneCallback shows the current timezone and instructions to change it.
func (h *Handler) timezoneCallback(ctx context.Context, chatID int64, msgID int, callbackID string) {
h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
h.answerCb(ctx, callbackID)
user, err := h.getOrCreateUser(chatID)
if err != nil {
return
@@ -159,12 +148,7 @@ func (h *Handler) timezoneCallback(ctx context.Context, chatID int64, msgID int,
{{Text: "Back to Settings", CallbackData: "back_settings"}},
},
}
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
ChatID: chatID,
MessageID: msgID,
Text: text,
ReplyMarkup: &kb,
})
h.editText(ctx, chatID, msgID, text, &kb)
}
// buildAccentKeyboard returns the accent color picker keyboard.
@@ -197,7 +181,7 @@ func (h *Handler) buildAccentKeyboard(user *db.User) (string, models.InlineKeybo
// selectAccent sets the user's export accent color after validation.
func (h *Handler) selectAccent(ctx context.Context, chatID int64, msgID int, callbackID, name string) {
h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
h.answerCb(ctx, callbackID)
user, err := h.getOrCreateUser(chatID)
if err != nil {
return
@@ -215,17 +199,12 @@ func (h *Handler) selectAccent(ctx context.Context, chatID int64, msgID int, cal
{{Text: "Back to Settings", CallbackData: "back_settings"}},
},
}
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
ChatID: chatID,
MessageID: msgID,
Text: fmt.Sprintf("Accent set to: %s", name),
ReplyMarkup: &kb,
})
h.editText(ctx, chatID, msgID, fmt.Sprintf("Accent set to: %s", name), &kb)
}
// reportTimeCallback shows the current report time setting.
func (h *Handler) reportTimeCallback(ctx context.Context, chatID int64, msgID int, callbackID string) {
h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
h.answerCb(ctx, callbackID)
user, err := h.getOrCreateUser(chatID)
if err != nil {
return
@@ -236,44 +215,29 @@ func (h *Handler) reportTimeCallback(ctx context.Context, chatID int64, msgID in
{{Text: "Back to Settings", CallbackData: "back_settings"}},
},
}
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
ChatID: chatID,
MessageID: msgID,
Text: text,
ReplyMarkup: &kb,
})
h.editText(ctx, chatID, msgID, text, &kb)
}
// backToSettings returns to the main settings menu.
func (h *Handler) backToSettings(ctx context.Context, chatID int64, msgID int, callbackID string) {
h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
h.answerCb(ctx, callbackID)
user, err := h.getOrCreateUser(chatID)
if err != nil {
return
}
text, kb := h.buildSettingsKeyboard(user)
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
ChatID: chatID,
MessageID: msgID,
Text: text,
ReplyMarkup: &kb,
})
h.editText(ctx, chatID, msgID, text, &kb)
}
// calTypeCallback shows the calendar type selector.
func (h *Handler) calTypeCallback(ctx context.Context, chatID int64, msgID int, callbackID string) {
h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
h.answerCb(ctx, callbackID)
user, err := h.getOrCreateUser(chatID)
if err != nil {
return
}
text, kb := h.buildCalendarKeyboard(user)
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
ChatID: chatID,
MessageID: msgID,
Text: text,
ReplyMarkup: &kb,
})
h.editText(ctx, chatID, msgID, text, &kb)
}
// buildCalendarKeyboard returns the calendar type selector keyboard.
@@ -302,7 +266,7 @@ func (h *Handler) buildCalendarKeyboard(user *db.User) (string, models.InlineKey
// selectCalendar sets the user's calendar after validation.
func (h *Handler) selectCalendar(ctx context.Context, chatID int64, msgID int, callbackID, name string) {
h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
h.answerCb(ctx, callbackID)
valid := map[string]bool{"gregorian": true, "jalali": true, "hijri": true}
if !valid[name] {
return
@@ -316,10 +280,5 @@ func (h *Handler) selectCalendar(ctx context.Context, chatID int64, msgID int, c
return
}
text, kb := h.buildSettingsKeyboard(user)
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
ChatID: chatID,
MessageID: msgID,
Text: text,
ReplyMarkup: &kb,
})
h.editText(ctx, chatID, msgID, text, &kb)
}