refactor: migrate from go-telegram-bot-api/v5 to go-telegram/bot

- Replaced tgbotapi with go-telegram/bot (zero-dependency, context-aware, Bot API 10.0)
- All handler methods now accept context.Context; threading ctx through all sends/edits
- Changed from function-based (NewMessage/NewEditMessageText/NewInlineKeyboardMarkup) to struct-param API (SendMessageParams/EditMessageTextParams/InlineKeyboardMarkup)
- Added colored buttons: DEL buttons in calendar use Style: 'danger' (red)
- Both polling and webhook modes preserved with new library patterns
- Context-based shutdown (signal.NotifyContext) replaces stop channel
This commit is contained in:
2026-06-24 14:56:50 +03:30
parent fb2d0ef7d1
commit 49c0e82bac
9 changed files with 581 additions and 580 deletions

View File

@@ -1,55 +1,58 @@
package bot
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
bot "github.com/go-telegram/bot"
"github.com/go-telegram/bot/models"
"worktimeBot/internal/db"
)
// handleHistoryMsg shows the calendar view (new message).
func (h *Handler) handleHistoryMsg(msg *tgbotapi.Message) {
h.sendCalendar(msg.Chat.ID, 0, 0, 0)
func (h *Handler) handleHistoryMsg(ctx context.Context, msg *models.Message) {
h.sendCalendar(ctx, msg.Chat.ID, 0, 0, 0)
}
// historyCallback opens or refreshes the calendar view (inline).
func (h *Handler) historyCallback(chatID int64, msgID int, callbackID string) {
defer h.Bot.Request(tgbotapi.NewCallback(callbackID, ""))
h.editCalendar(chatID, msgID, 0, 0)
func (h *Handler) historyCallback(ctx context.Context, chatID int64, msgID int, callbackID string) {
defer h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
h.editCalendar(ctx, chatID, msgID, 0, 0)
}
// handleEditMsg parses a /edit YYYY-MM-DD command and shows that day's events.
func (h *Handler) handleEditMsg(msg *tgbotapi.Message) {
date := msg.CommandArguments()
func (h *Handler) handleEditMsg(ctx context.Context, msg *models.Message) {
date := strings.TrimSpace(strings.TrimPrefix(msg.Text, "/edit"))
if date == "" {
h.sendText(msg.Chat.ID, "Usage: /edit YYYY-MM-DD (your calendar type)")
h.sendText(ctx, msg.Chat.ID, "Usage: /edit YYYY-MM-DD (your calendar type)")
return
}
if len(date) != 10 || date[4] != '-' || date[7] != '-' {
h.sendText(msg.Chat.ID, "Invalid date. Use: /edit YYYY-MM-DD (your calendar type)")
h.sendText(ctx, msg.Chat.ID, "Invalid date. Use: /edit YYYY-MM-DD (your calendar type)")
return
}
_, m, d := parseGregorianDateKey(date)
if m < 1 || m > 12 || d < 1 || d > 31 {
h.sendText(msg.Chat.ID, "Invalid date.")
h.sendText(ctx, msg.Chat.ID, "Invalid date.")
return
}
user, err := h.getOrCreateUser(msg.Chat.ID)
if err != nil {
h.sendText(msg.Chat.ID, "Error loading profile")
h.sendText(ctx, msg.Chat.ID, "Error loading profile")
return
}
loc := loadLocation(user.Timezone)
date = userDateToGregorian(date, user.Calendar)
h.sendDayView(msg.Chat.ID, 0, user, date, loc, true)
h.sendDayView(ctx, msg.Chat.ID, 0, user, date, loc, true)
}
// handleHistoryCallback routes calendar-related callback data to the right handler.
func (h *Handler) handleHistoryCallback(chatID int64, msgID int, callbackID, data string) {
defer h.Bot.Request(tgbotapi.NewCallback(callbackID, ""))
func (h *Handler) handleHistoryCallback(ctx context.Context, chatID int64, msgID int, callbackID, data string) {
defer h.Bot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{CallbackQueryID: callbackID})
user, err := h.getOrCreateUser(chatID)
if err != nil {
return
@@ -57,7 +60,7 @@ func (h *Handler) handleHistoryCallback(chatID int64, msgID int, callbackID, dat
loc := loadLocation(user.Timezone)
if data == "history" {
h.editCalendar(chatID, msgID, 0, 0)
h.editCalendar(ctx, chatID, msgID, 0, 0)
return
}
@@ -67,70 +70,70 @@ func (h *Handler) handleHistoryCallback(chatID int64, msgID int, callbackID, dat
// Navigate to previous month
if n, _ := fmt.Sscanf(data, "cal_prev_%d_%d", &y, &m); n == 2 {
h.editCalendar(chatID, msgID, y, m)
h.editCalendar(ctx, chatID, msgID, y, m)
return
}
// Navigate to next month
if n, _ := fmt.Sscanf(data, "cal_next_%d_%d", &y, &m); n == 2 {
h.editCalendar(chatID, msgID, y, m)
h.editCalendar(ctx, chatID, msgID, y, m)
return
}
// Open a specific day
if n, _ := fmt.Sscanf(data, "cal_day_%s", &date); n == 1 && len(date) == 10 {
h.editDayView(chatID, msgID, user, date, loc)
h.editDayView(ctx, chatID, msgID, user, date, loc)
return
}
// Back to day view from event editing
if n, _ := fmt.Sscanf(data, "back_day_%d", &eid); n == 1 {
h.backToDayView(chatID, msgID, user, eid, loc)
h.backToDayView(ctx, chatID, msgID, user, eid, loc)
return
}
// Set event work type
var wtid int64
if n, _ := fmt.Sscanf(data, "settype_%d_%d", &eid, &wtid); n == 2 {
h.setEventWorkType(chatID, msgID, user, eid, wtid, loc)
h.setEventWorkType(ctx, chatID, msgID, user, eid, wtid, loc)
return
}
// Show work type picker for an event
if n, _ := fmt.Sscanf(data, "edit_type_%d", &eid); n == 1 {
h.editEventType(chatID, msgID, user, eid, loc)
h.editEventType(ctx, chatID, msgID, user, eid, loc)
return
}
// Delete event confirmation prompt
if n, _ := fmt.Sscanf(data, "delete_%d", &eid); n == 1 {
h.deleteEventPrompt(chatID, msgID, user, eid, loc)
h.deleteEventPrompt(ctx, chatID, msgID, user, eid, loc)
return
}
// Confirm event deletion
if n, _ := fmt.Sscanf(data, "delconf_%d", &eid); n == 1 {
h.deleteEventConfirm(chatID, msgID, user, eid, loc)
h.deleteEventConfirm(ctx, chatID, msgID, user, eid, loc)
return
}
// Show hour picker for event time
if n, _ := fmt.Sscanf(data, "edit_time_%d", &eid); n == 1 {
h.editEventTime(chatID, msgID, user, eid, loc)
h.editEventTime(ctx, chatID, msgID, user, eid, loc)
return
}
// Set event time (hour+minute)
var hh, mm int
if n, _ := fmt.Sscanf(data, "edittm_%d_%d_%d", &eid, &hh, &mm); n == 3 {
h.editEventTimeSet(chatID, msgID, user, eid, hh, mm, loc)
h.editEventTimeSet(ctx, chatID, msgID, user, eid, hh, mm, loc)
return
}
// Show minute picker (hour already chosen)
if n, _ := fmt.Sscanf(data, "edittm_%d_%d", &eid, &hh); n == 2 {
h.editEventTimeMin(chatID, msgID, user, eid, hh, loc)
h.editEventTimeMin(ctx, chatID, msgID, user, eid, hh, loc)
return
}
}
// sendCalendar sends a calendar view as a new message.
func (h *Handler) sendCalendar(chatID int64, msgID int, year, month int) {
h.editCalendar(chatID, msgID, year, month)
func (h *Handler) sendCalendar(ctx context.Context, chatID int64, msgID int, year, month int) {
h.editCalendar(ctx, chatID, msgID, year, month)
}
// editCalendar renders a monthly calendar grid with event indicators and navigation.
func (h *Handler) editCalendar(chatID int64, msgID int, year, month int) {
func (h *Handler) editCalendar(ctx context.Context, chatID int64, msgID int, year, month int) {
user, err := h.getOrCreateUser(chatID)
if err != nil {
return
@@ -172,20 +175,20 @@ func (h *Handler) editCalendar(chatID int64, msgID int, year, month int) {
rows.Close()
}
kbRows := [][]tgbotapi.InlineKeyboardButton{}
kbRows := [][]models.InlineKeyboardButton{}
// Weekday header row
headerRow := []tgbotapi.InlineKeyboardButton{}
headerRow := []models.InlineKeyboardButton{}
for _, wn := range cm.weekDays {
headerRow = append(headerRow, tgbotapi.NewInlineKeyboardButtonData(wn, "noop"))
headerRow = append(headerRow, models.InlineKeyboardButton{Text: wn, CallbackData: "noop"})
}
kbRows = append(kbRows, headerRow)
// Day cells
row := []tgbotapi.InlineKeyboardButton{}
row := []models.InlineKeyboardButton{}
for i, d := range cm.days {
if d.dayNum == 0 {
row = append(row, tgbotapi.NewInlineKeyboardButtonData(" ", "noop"))
row = append(row, models.InlineKeyboardButton{Text: " ", CallbackData: "noop"})
} else {
label := fmt.Sprintf("%d", d.dayNum)
if d.date == todayKey {
@@ -193,12 +196,12 @@ func (h *Handler) editCalendar(chatID int64, msgID int, year, month int) {
} else if hasEvent[d.date] {
label = fmt.Sprintf("%d*", d.dayNum)
}
row = append(row, tgbotapi.NewInlineKeyboardButtonData(label, "cal_day_"+d.date))
row = append(row, models.InlineKeyboardButton{Text: label, CallbackData: "cal_day_" + d.date})
}
if len(row) == 7 || i == len(cm.days)-1 {
// Pad the last row to 7 columns so buttons render evenly
for len(row) < 7 {
row = append(row, tgbotapi.NewInlineKeyboardButtonData(" ", "noop"))
row = append(row, models.InlineKeyboardButton{Text: " ", CallbackData: "noop"})
}
kbRows = append(kbRows, row)
row = nil
@@ -208,31 +211,31 @@ func (h *Handler) editCalendar(chatID int64, msgID int, year, month int) {
prevY, prevM, nextY, nextM := navMonth(year, month)
// Navigation row
kbRows = append(kbRows, tgbotapi.NewInlineKeyboardRow(
tgbotapi.NewInlineKeyboardButtonData("<", fmt.Sprintf("cal_prev_%d_%d", prevY, prevM)),
tgbotapi.NewInlineKeyboardButtonData("Today", "history"),
tgbotapi.NewInlineKeyboardButtonData(">", fmt.Sprintf("cal_next_%d_%d", nextY, nextM)),
))
kbRows = append(kbRows, []models.InlineKeyboardButton{
{Text: "<", CallbackData: fmt.Sprintf("cal_prev_%d_%d", prevY, prevM)},
{Text: "Today", CallbackData: "history"},
{Text: ">", CallbackData: fmt.Sprintf("cal_next_%d_%d", nextY, nextM)},
})
kbRows = append(kbRows, tgbotapi.NewInlineKeyboardRow(
tgbotapi.NewInlineKeyboardButtonData("Back to Menu", "back_menu"),
))
kbRows = append(kbRows, []models.InlineKeyboardButton{
{Text: "Back to Menu", CallbackData: "back_menu"},
})
kb := tgbotapi.NewInlineKeyboardMarkup(kbRows...)
h.sendOrEdit(chatID, msgID, text, &kb)
kb := models.InlineKeyboardMarkup{InlineKeyboard: kbRows}
h.sendOrEdit(ctx, chatID, msgID, text, &kb)
}
// editDayView opens an existing message as a day view.
func (h *Handler) editDayView(chatID int64, msgID int, user *db.User, date string, loc *time.Location) {
h.sendDayView(chatID, msgID, user, date, loc, false)
func (h *Handler) editDayView(ctx context.Context, chatID int64, msgID int, user *db.User, date string, loc *time.Location) {
h.sendDayView(ctx, chatID, msgID, user, date, loc, false)
}
// sendDayView displays all events for a given date with inline edit/delete buttons.
func (h *Handler) sendDayView(chatID int64, msgID int, user *db.User, date string, loc *time.Location, isNewMsg bool) {
func (h *Handler) sendDayView(ctx context.Context, chatID int64, msgID int, user *db.User, date string, loc *time.Location, isNewMsg bool) {
events, err := h.DB.EventsForDayByDate(user.ID, date)
if err != nil {
if isNewMsg {
h.sendText(chatID, "Error loading events")
h.sendText(ctx, chatID, "Error loading events")
}
return
}
@@ -240,17 +243,17 @@ func (h *Handler) sendDayView(chatID int64, msgID int, user *db.User, date strin
text := fmt.Sprintf("Date: %s", formatDateForCalendar(date, user.Calendar))
if len(events) == 0 {
text += "\nNo events for this day."
kb := tgbotapi.NewInlineKeyboardMarkup(
tgbotapi.NewInlineKeyboardRow(
tgbotapi.NewInlineKeyboardButtonData("Back to Calendar", "history"),
),
)
h.sendOrEdit(chatID, msgID, text, &kb)
kb := models.InlineKeyboardMarkup{
InlineKeyboard: [][]models.InlineKeyboardButton{
{{Text: "Back to Calendar", CallbackData: "history"}},
},
}
h.sendOrEdit(ctx, chatID, msgID, text, &kb)
return
}
text += "\n\nEvents:"
kbRows := [][]tgbotapi.InlineKeyboardButton{}
kbRows := [][]models.InlineKeyboardButton{}
for _, e := range events {
t := time.Unix(e.OccurredAt, 0).In(loc).Format("15:04")
label := "IN"
@@ -269,100 +272,94 @@ func (h *Handler) sendDayView(chatID int64, msgID int, user *db.User, date strin
}
text += fmt.Sprintf("\n%s %s%s%s", label, t, wt, note)
kbRows = append(kbRows, tgbotapi.NewInlineKeyboardRow(
tgbotapi.NewInlineKeyboardButtonData(fmt.Sprintf("Time %s", t), fmt.Sprintf("edit_time_%d", e.ID)),
tgbotapi.NewInlineKeyboardButtonData("Type", fmt.Sprintf("edit_type_%d", e.ID)),
tgbotapi.NewInlineKeyboardButtonData("DEL", fmt.Sprintf("delete_%d", e.ID)),
))
kbRows = append(kbRows, []models.InlineKeyboardButton{
{Text: fmt.Sprintf("Time %s", t), CallbackData: fmt.Sprintf("edit_time_%d", e.ID)},
{Text: "Type", CallbackData: fmt.Sprintf("edit_type_%d", e.ID)},
{Text: "DEL", CallbackData: fmt.Sprintf("delete_%d", e.ID), Style: "danger"},
})
}
kbRows = append(kbRows, tgbotapi.NewInlineKeyboardRow(
tgbotapi.NewInlineKeyboardButtonData("Back to Calendar", "history"),
))
kb := tgbotapi.NewInlineKeyboardMarkup(kbRows...)
h.sendOrEdit(chatID, msgID, text, &kb)
kbRows = append(kbRows, []models.InlineKeyboardButton{
{Text: "Back to Calendar", CallbackData: "history"},
})
kb := models.InlineKeyboardMarkup{InlineKeyboard: kbRows}
h.sendOrEdit(ctx, chatID, msgID, text, &kb)
}
// editEventType shows a work type picker for a specific event.
func (h *Handler) editEventType(chatID int64, msgID int, user *db.User, eventID int64, loc *time.Location) {
func (h *Handler) editEventType(ctx context.Context, chatID int64, msgID int, user *db.User, eventID int64, loc *time.Location) {
wts, err := h.DB.GetWorkTypes()
if err != nil {
return
}
text := "Select new work type:"
kbRows := [][]tgbotapi.InlineKeyboardButton{}
kbRows := [][]models.InlineKeyboardButton{}
for _, wt := range wts {
kbRows = append(kbRows, tgbotapi.NewInlineKeyboardRow(
tgbotapi.NewInlineKeyboardButtonData(wt.Name, fmt.Sprintf("settype_%d_%d", eventID, wt.ID)),
))
kbRows = append(kbRows, []models.InlineKeyboardButton{
{Text: wt.Name, CallbackData: fmt.Sprintf("settype_%d_%d", eventID, wt.ID)},
})
}
kbRows = append(kbRows, tgbotapi.NewInlineKeyboardRow(
tgbotapi.NewInlineKeyboardButtonData("Back", fmt.Sprintf("back_day_%d", eventID)),
))
kb := tgbotapi.NewInlineKeyboardMarkup(kbRows...)
edit := tgbotapi.NewEditMessageText(chatID, msgID, text)
edit.ReplyMarkup = &kb
h.Bot.Send(edit)
kbRows = append(kbRows, []models.InlineKeyboardButton{
{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})
}
// setEventWorkType updates an event's work type and returns to the day view.
func (h *Handler) setEventWorkType(chatID int64, msgID int, user *db.User, eventID, workTypeID int64, loc *time.Location) {
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)
event, err := h.getEventByID(user.ID, eventID)
if err != nil {
return
}
t := time.Unix(event.OccurredAt, 0).In(loc)
h.sendDayView(chatID, msgID, user, t.Format("2006-01-02"), loc, false)
h.sendDayView(ctx, chatID, msgID, user, t.Format("2006-01-02"), loc, false)
}
// editEventTime shows an hour picker for changing an event's time.
func (h *Handler) editEventTime(chatID int64, msgID int, user *db.User, eventID int64, loc *time.Location) {
func (h *Handler) editEventTime(ctx context.Context, chatID int64, msgID int, user *db.User, eventID int64, loc *time.Location) {
text := "Select hour:"
kbRows := [][]tgbotapi.InlineKeyboardButton{}
hourRow := []tgbotapi.InlineKeyboardButton{}
kbRows := [][]models.InlineKeyboardButton{}
hourRow := []models.InlineKeyboardButton{}
for hh := 0; hh < 24; hh++ {
hourRow = append(hourRow, tgbotapi.NewInlineKeyboardButtonData(
fmt.Sprintf("%02d", hh),
fmt.Sprintf("edittm_%d_%02d", eventID, hh),
))
hourRow = append(hourRow, models.InlineKeyboardButton{
Text: fmt.Sprintf("%02d", hh),
CallbackData: fmt.Sprintf("edittm_%d_%02d", eventID, hh),
})
if len(hourRow) == 6 || hh == 23 {
kbRows = append(kbRows, hourRow)
hourRow = nil
}
}
kbRows = append(kbRows, tgbotapi.NewInlineKeyboardRow(
tgbotapi.NewInlineKeyboardButtonData("Back", fmt.Sprintf("back_day_%d", eventID)),
))
kb := tgbotapi.NewInlineKeyboardMarkup(kbRows...)
edit := tgbotapi.NewEditMessageText(chatID, msgID, text)
edit.ReplyMarkup = &kb
h.Bot.Send(edit)
kbRows = append(kbRows, []models.InlineKeyboardButton{
{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})
}
// editEventTimeMin shows a minute picker (15-min intervals) after hour selection.
func (h *Handler) editEventTimeMin(chatID int64, msgID int, user *db.User, eventID int64, hh int, loc *time.Location) {
func (h *Handler) editEventTimeMin(ctx context.Context, chatID int64, msgID int, user *db.User, eventID int64, hh int, loc *time.Location) {
text := fmt.Sprintf("Select minute for hour %02d:", hh)
kbRows := [][]tgbotapi.InlineKeyboardButton{}
minRow := []tgbotapi.InlineKeyboardButton{}
kbRows := [][]models.InlineKeyboardButton{}
minRow := []models.InlineKeyboardButton{}
for _, mm := range []int{0, 15, 30, 45} {
minRow = append(minRow, tgbotapi.NewInlineKeyboardButtonData(
fmt.Sprintf("%02d", mm),
fmt.Sprintf("edittm_%d_%02d_%02d", eventID, hh, mm),
))
minRow = append(minRow, models.InlineKeyboardButton{
Text: fmt.Sprintf("%02d", mm),
CallbackData: fmt.Sprintf("edittm_%d_%02d_%02d", eventID, hh, mm),
})
}
kbRows = append(kbRows, minRow)
kbRows = append(kbRows, tgbotapi.NewInlineKeyboardRow(
tgbotapi.NewInlineKeyboardButtonData("Back", fmt.Sprintf("edit_time_%d", eventID)),
))
kb := tgbotapi.NewInlineKeyboardMarkup(kbRows...)
edit := tgbotapi.NewEditMessageText(chatID, msgID, text)
edit.ReplyMarkup = &kb
h.Bot.Send(edit)
kbRows = append(kbRows, []models.InlineKeyboardButton{
{Text: "Back", CallbackData: fmt.Sprintf("edit_time_%d", eventID)},
})
kb := models.InlineKeyboardMarkup{InlineKeyboard: kbRows}
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{ChatID: chatID, MessageID: msgID, Text: text, ReplyMarkup: &kb})
}
// editEventTimeSet applies a new time to an event, checking for overlaps.
func (h *Handler) editEventTimeSet(chatID int64, msgID int, user *db.User, eventID int64, hh, mm int, loc *time.Location) {
func (h *Handler) editEventTimeSet(ctx context.Context, chatID int64, msgID int, user *db.User, eventID int64, hh, mm int, loc *time.Location) {
event, err := h.getEventByID(user.ID, eventID)
if err != nil {
return
@@ -380,8 +377,8 @@ func (h *Handler) editEventTimeSet(chatID int64, msgID int, user *db.User, event
}
eTime := time.Unix(e.OccurredAt, 0)
if newTime.Unix() == eTime.Unix() {
h.acknowledgeCallback(chatID, msgID)
h.sendDayView(chatID, msgID, user, date, loc, false)
h.acknowledgeCallback(ctx, chatID, msgID)
h.sendDayView(ctx, chatID, msgID, user, date, loc, false)
return
}
}
@@ -410,37 +407,36 @@ func (h *Handler) editEventTimeSet(chatID int64, msgID int, user *db.User, event
}
for i := 1; i < len(sorted); i++ {
if sorted[i].typ == sorted[i-1].typ {
edit := tgbotapi.NewEditMessageText(chatID, msgID, "Edit rejected: overlapping events. Two consecutive events must be different types (in/out).")
h.Bot.Send(edit)
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{ChatID: chatID, MessageID: msgID, Text: "Edit rejected: overlapping events. Two consecutive events must be different types (in/out)."})
return
}
}
}
h.DB.DB().Exec("UPDATE events SET occurred_at=? WHERE id=?", newTime.Unix(), eventID)
h.sendDayView(chatID, msgID, user, date, loc, false)
h.sendDayView(ctx, chatID, msgID, user, date, loc, false)
}
// acknowledgeCallback sends an empty acknowledgement for a callback query.
func (h *Handler) acknowledgeCallback(chatID int64, msgID int) {
h.Bot.Request(tgbotapi.NewCallback(fmt.Sprintf("cb_%d_%d", chatID, msgID), ""))
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)})
}
// deleteEventPrompt asks the user to confirm event deletion.
func (h *Handler) deleteEventPrompt(chatID int64, msgID int, user *db.User, eventID int64, loc *time.Location) {
edit := tgbotapi.NewEditMessageText(chatID, msgID, "Delete this event?")
kb := tgbotapi.NewInlineKeyboardMarkup(
tgbotapi.NewInlineKeyboardRow(
tgbotapi.NewInlineKeyboardButtonData("Yes, DEL", fmt.Sprintf("delconf_%d", eventID)),
tgbotapi.NewInlineKeyboardButtonData("Cancel", fmt.Sprintf("back_day_%d", eventID)),
),
)
edit.ReplyMarkup = &kb
h.Bot.Send(edit)
func (h *Handler) deleteEventPrompt(ctx context.Context, chatID int64, msgID int, user *db.User, eventID int64, loc *time.Location) {
kb := models.InlineKeyboardMarkup{
InlineKeyboard: [][]models.InlineKeyboardButton{
{
{Text: "Yes, DEL", CallbackData: fmt.Sprintf("delconf_%d", eventID), Style: "danger"},
{Text: "Cancel", CallbackData: fmt.Sprintf("back_day_%d", eventID)},
},
},
}
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{ChatID: chatID, MessageID: msgID, Text: "Delete this event?", ReplyMarkup: &kb})
}
// deleteEventConfirm deletes an event and warns if the timeline becomes invalid.
func (h *Handler) deleteEventConfirm(chatID int64, msgID int, user *db.User, eventID int64, loc *time.Location) {
func (h *Handler) deleteEventConfirm(ctx context.Context, chatID int64, msgID int, user *db.User, eventID int64, loc *time.Location) {
event, err := h.getEventByID(user.ID, eventID)
if err != nil {
return
@@ -461,21 +457,21 @@ func (h *Handler) deleteEventConfirm(chatID int64, msgID int, user *db.User, eve
}
}
if hasIssue {
h.sendText(chatID, "Warning: Timeline has consecutive events of same type after deletion. Manual repair needed.")
h.sendText(ctx, chatID, "Warning: Timeline has consecutive events of same type after deletion. Manual repair needed.")
}
}
h.sendDayView(chatID, msgID, user, date, loc, false)
h.sendDayView(ctx, chatID, msgID, user, date, loc, false)
}
// backToDayView returns from event editing to the day view.
func (h *Handler) backToDayView(chatID int64, msgID int, user *db.User, eventID int64, loc *time.Location) {
func (h *Handler) backToDayView(ctx context.Context, chatID int64, msgID int, user *db.User, eventID int64, loc *time.Location) {
event, err := h.getEventByID(user.ID, eventID)
if err != nil {
return
}
t := time.Unix(event.OccurredAt, 0).In(loc)
h.sendDayView(chatID, msgID, user, t.Format("2006-01-02"), loc, false)
h.sendDayView(ctx, chatID, msgID, user, t.Format("2006-01-02"), loc, false)
}
// getEventByID fetches a single event by ID, scoped to the user.